From bce1c310d3089a34eb7b47531a25facdb90009fc Mon Sep 17 00:00:00 2001 From: hansolnoh95 Date: Wed, 22 Sep 2021 01:05:20 +0900 Subject: [PATCH 1/4] =?UTF-8?q?:construction:=20=EA=B8=B0=EB=B3=B8=20?= =?UTF-8?q?=EC=84=B8=ED=8C=85=20=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../KakaoFriendNodeController.swift | 8 +- .../KakaoTest/Models/KakaoFriendsModel.swift | 2 +- .../BasicExercise/Support/SceneDelegate.swift | 4 +- Starbucks_Clone_TexBrother/Podfile | 10 + Starbucks_Clone_TexBrother/Podfile.lock | 56 + Starbucks_Clone_TexBrother/Pods/Manifest.lock | 56 + .../Pods/PINCache/LICENSE.txt | 201 + .../Pods/PINCache/README.md | 98 + .../Pods/PINCache/Source/PINCache.h | 164 + .../Pods/PINCache/Source/PINCache.m | 420 ++ .../Pods/PINCache/Source/PINCacheMacros.h | 23 + .../Source/PINCacheObjectSubscripting.h | 35 + .../Pods/PINCache/Source/PINCaching.h | 264 ++ .../Pods/PINCache/Source/PINDiskCache.h | 592 +++ .../Pods/PINCache/Source/PINDiskCache.m | 1727 ++++++++ .../Pods/PINCache/Source/PINMemoryCache.h | 245 ++ .../Pods/PINCache/Source/PINMemoryCache.m | 932 ++++ .../Pods/PINOperation/LICENSE.txt | 176 + .../Pods/PINOperation/README.md | 47 + .../Pods/PINOperation/Source/PINOperation.h | 12 + .../PINOperation/Source/PINOperationGroup.h | 38 + .../PINOperation/Source/PINOperationGroup.m | 187 + .../PINOperation/Source/PINOperationMacros.h | 15 + .../PINOperation/Source/PINOperationQueue.h | 141 + .../PINOperation/Source/PINOperationQueue.m | 471 ++ .../PINOperation/Source/PINOperationTypes.h | 15 + .../Pods/PINRemoteImage/LICENSE | 176 + .../Pods/PINRemoteImage/README.md | 252 ++ .../Classes/AnimatedImages/PINAnimatedImage.m | 126 + .../AnimatedImages/PINAnimatedImageView.m | 443 ++ .../AnimatedImages/PINCachedAnimatedImage.m | 528 +++ .../AnimatedImages/PINGIFAnimatedImage.m | 166 + .../AnimatedImages/PINWebPAnimatedImage.m | 403 ++ .../Categories/NSData+ImageDetectors.m | 139 + .../Categories/NSHTTPURLResponse+MaxAge.h | 14 + .../Categories/NSHTTPURLResponse+MaxAge.m | 71 + .../Categories/PINImage+DecodedImage.h | 44 + .../Categories/PINImage+DecodedImage.m | 325 ++ .../Classes/Categories/PINImage+ScaledImage.h | 24 + .../Classes/Categories/PINImage+ScaledImage.m | 56 + .../Source/Classes/Categories/PINImage+WebP.h | 25 + .../Source/Classes/Categories/PINImage+WebP.m | 87 + .../PINRemoteImageTask+Subclassing.h | 20 + .../PINAnimatedImageView+PINRemoteImage.m | 119 + .../PINButton+PINRemoteImage.m | 131 + .../PINImageView+PINRemoteImage.m | 127 + .../PINAlternateRepresentationProvider.m | 30 + .../PINCache/PINCache+PINRemoteImageCaching.m | 113 + .../Source/Classes/PINDisplayLink.h | 25 + .../Source/Classes/PINDisplayLink.m | 133 + .../Source/Classes/PINProgressiveImage.m | 464 ++ .../Source/Classes/PINRemoteImageBasicCache.h | 19 + .../Source/Classes/PINRemoteImageBasicCache.m | 99 + .../Source/Classes/PINRemoteImageCallbacks.h | 20 + .../Source/Classes/PINRemoteImageCallbacks.m | 21 + .../Classes/PINRemoteImageCategoryManager.m | 268 ++ .../Classes/PINRemoteImageDownloadQueue.h | 47 + .../Classes/PINRemoteImageDownloadQueue.m | 177 + .../Classes/PINRemoteImageDownloadTask.h | 37 + .../Classes/PINRemoteImageDownloadTask.m | 380 ++ .../Classes/PINRemoteImageManager+Private.h | 30 + .../Source/Classes/PINRemoteImageManager.m | 1651 +++++++ .../PINRemoteImageManagerConfiguration.h | 46 + .../PINRemoteImageManagerConfiguration.m | 36 + .../Classes/PINRemoteImageManagerResult.m | 118 + .../Classes/PINRemoteImageMemoryContainer.h | 22 + .../Classes/PINRemoteImageMemoryContainer.m | 21 + .../Classes/PINRemoteImageProcessorTask.h | 15 + .../Classes/PINRemoteImageProcessorTask.m | 44 + .../Source/Classes/PINRemoteImageTask.h | 65 + .../Source/Classes/PINRemoteImageTask.m | 160 + .../Source/Classes/PINRemoteLock.h | 28 + .../Source/Classes/PINRemoteLock.m | 112 + .../Source/Classes/PINRemoteWeakProxy.h | 16 + .../Source/Classes/PINRemoteWeakProxy.m | 59 + .../Source/Classes/PINRequestRetryStrategy.m | 69 + .../PINRemoteImage/Source/Classes/PINResume.h | 24 + .../PINRemoteImage/Source/Classes/PINResume.m | 51 + .../Source/Classes/PINSpeedRecorder.h | 51 + .../Source/Classes/PINSpeedRecorder.m | 265 ++ .../Source/Classes/PINURLSessionManager.m | 263 ++ .../Classes/include/NSData+ImageDetectors.h | 22 + .../PINAlternateRepresentationProvider.h | 25 + .../Source/Classes/include/PINAnimatedImage.h | 165 + .../PINAnimatedImageView+PINRemoteImage.h | 14 + .../Classes/include/PINAnimatedImageView.h | 28 + .../include/PINButton+PINRemoteImage.h | 22 + .../include/PINCache+PINRemoteImageCaching.h | 26 + .../Classes/include/PINCachedAnimatedImage.h | 83 + .../Classes/include/PINGIFAnimatedImage.h | 18 + .../include/PINImageView+PINRemoteImage.h | 22 + .../Classes/include/PINProgressiveImage.h | 52 + .../Source/Classes/include/PINRemoteImage.h | 25 + .../Classes/include/PINRemoteImageCaching.h | 52 + .../include/PINRemoteImageCategoryManager.h | 230 + .../Classes/include/PINRemoteImageMacros.h | 70 + .../Classes/include/PINRemoteImageManager.h | 703 +++ .../include/PINRemoteImageManagerResult.h | 71 + .../Classes/include/PINRequestRetryStrategy.h | 28 + .../Classes/include/PINURLSessionManager.h | 51 + .../Classes/include/PINWebPAnimatedImage.h | 19 + .../Pods/Pods.xcodeproj/project.pbxproj | 3237 ++++++++++++++ .../PINCache/PINCache-Info.plist | 26 + .../PINCache/PINCache-dummy.m | 5 + .../PINCache/PINCache-prefix.pch | 15 + .../PINCache/PINCache-umbrella.h | 22 + .../PINCache/PINCache.debug.xcconfig | 13 + .../PINCache/PINCache.modulemap | 6 + .../PINCache/PINCache.release.xcconfig | 13 + .../PINOperation/PINOperation-Info.plist | 26 + .../PINOperation/PINOperation-dummy.m | 5 + .../PINOperation/PINOperation-prefix.pch | 15 + .../PINOperation/PINOperation-umbrella.h | 21 + .../PINOperation/PINOperation.debug.xcconfig | 12 + .../PINOperation/PINOperation.modulemap | 6 + .../PINOperation.release.xcconfig | 12 + .../PINRemoteImage/PINRemoteImage-Info.plist | 26 + .../PINRemoteImage/PINRemoteImage-dummy.m | 5 + .../PINRemoteImage/PINRemoteImage-umbrella.h | 55 + .../PINRemoteImage.debug.xcconfig | 13 + .../PINRemoteImage/PINRemoteImage.modulemap | 6 + .../PINRemoteImage.release.xcconfig | 13 + ...Pods-Starbucks_Clone_TexBrother-Info.plist | 26 + ...Clone_TexBrother-acknowledgements.markdown | 753 ++++ ...ks_Clone_TexBrother-acknowledgements.plist | 803 ++++ .../Pods-Starbucks_Clone_TexBrother-dummy.m | 5 + ...er-frameworks-Debug-input-files.xcfilelist | 5 + ...r-frameworks-Debug-output-files.xcfilelist | 4 + ...-frameworks-Release-input-files.xcfilelist | 5 + ...frameworks-Release-output-files.xcfilelist | 4 + ...s-Starbucks_Clone_TexBrother-frameworks.sh | 191 + ...Pods-Starbucks_Clone_TexBrother-umbrella.h | 16 + ...-Starbucks_Clone_TexBrother.debug.xcconfig | 12 + .../Pods-Starbucks_Clone_TexBrother.modulemap | 6 + ...tarbucks_Clone_TexBrother.release.xcconfig | 12 + .../Texture/Texture-Info.plist | 26 + .../Texture/Texture-dummy.m | 5 + .../Texture/Texture-prefix.pch | 12 + .../Texture/Texture-umbrella.h | 169 + .../Texture/Texture.debug.xcconfig | 15 + .../Texture/Texture.modulemap | 6 + .../Texture/Texture.release.xcconfig | 15 + .../Pods/Texture/LICENSE | 181 + .../Pods/Texture/README.md | 51 + .../Pods/Texture/Source/ASBlockTypes.h | 20 + .../Texture/Source/ASButtonNode+Private.h | 45 + .../Pods/Texture/Source/ASButtonNode+Yoga.h | 20 + .../Pods/Texture/Source/ASButtonNode+Yoga.mm | 105 + .../Pods/Texture/Source/ASButtonNode.h | 130 + .../Pods/Texture/Source/ASButtonNode.mm | 575 +++ .../Pods/Texture/Source/ASCellNode.h | 259 ++ .../Pods/Texture/Source/ASCellNode.mm | 489 +++ .../Texture/Source/ASCollectionNode+Beta.h | 79 + .../Pods/Texture/Source/ASCollectionNode.h | 956 +++++ .../Pods/Texture/Source/ASCollectionNode.mm | 1236 ++++++ .../Pods/Texture/Source/ASCollectionView.h | 492 +++ .../Pods/Texture/Source/ASCollectionView.mm | 2545 +++++++++++ ...SCollectionViewLayoutFacilitatorProtocol.h | 44 + .../Source/ASCollectionViewProtocols.h | 107 + .../Pods/Texture/Source/ASCollections.h | 38 + .../Pods/Texture/Source/ASCollections.mm | 61 + .../Pods/Texture/Source/ASConfiguration.h | 58 + .../Pods/Texture/Source/ASConfiguration.mm | 63 + .../Texture/Source/ASConfigurationDelegate.h | 37 + .../Texture/Source/ASConfigurationInternal.h | 57 + .../Texture/Source/ASConfigurationInternal.mm | 110 + .../Texture/Source/ASContextTransitioning.h | 72 + .../Texture/Source/ASControlNode+Subclasses.h | 68 + .../Pods/Texture/Source/ASControlNode.h | 149 + .../Pods/Texture/Source/ASControlNode.mm | 516 +++ .../Pods/Texture/Source/ASDKViewController.h | 101 + .../Pods/Texture/Source/ASDKViewController.mm | 358 ++ .../Pods/Texture/Source/ASDisplayNode+Beta.h | 171 + .../Source/ASDisplayNode+Convenience.h | 28 + .../Source/ASDisplayNode+Convenience.mm | 38 + .../Source/ASDisplayNode+InterfaceState.h | 148 + .../Texture/Source/ASDisplayNode+Layout.mm | 1098 +++++ .../Texture/Source/ASDisplayNode+LayoutSpec.h | 66 + .../Source/ASDisplayNode+LayoutSpec.mm | 141 + .../Texture/Source/ASDisplayNode+Subclasses.h | 502 +++ .../Pods/Texture/Source/ASDisplayNode+Yoga.h | 103 + .../Pods/Texture/Source/ASDisplayNode+Yoga.mm | 489 +++ .../Pods/Texture/Source/ASDisplayNode.h | 995 +++++ .../Pods/Texture/Source/ASDisplayNode.mm | 3774 +++++++++++++++++ .../Pods/Texture/Source/ASDisplayNodeExtras.h | 213 + .../Texture/Source/ASDisplayNodeExtras.mm | 337 ++ .../Pods/Texture/Source/ASEditableTextNode.h | 203 + .../Pods/Texture/Source/ASEditableTextNode.mm | 880 ++++ .../Texture/Source/ASExperimentalFeatures.h | 41 + .../Texture/Source/ASExperimentalFeatures.mm | 50 + .../Source/ASImageNode+AnimatedImage.mm | 400 ++ .../Pods/Texture/Source/ASImageNode.h | 212 + .../Pods/Texture/Source/ASImageNode.mm | 837 ++++ .../Pods/Texture/Source/ASLocking.h | 158 + .../Texture/Source/ASMainThreadDeallocation.h | 56 + .../Source/ASMainThreadDeallocation.mm | 202 + .../Pods/Texture/Source/ASMapNode.h | 92 + .../Pods/Texture/Source/ASMapNode.mm | 440 ++ .../Texture/Source/ASMultiplexImageNode.h | 282 ++ .../Texture/Source/ASMultiplexImageNode.mm | 940 ++++ .../Texture/Source/ASNavigationController.h | 30 + .../Texture/Source/ASNavigationController.mm | 113 + .../Texture/Source/ASNetworkImageLoadInfo.h | 39 + .../Texture/Source/ASNetworkImageLoadInfo.mm | 31 + .../Pods/Texture/Source/ASNetworkImageNode.h | 253 ++ .../Pods/Texture/Source/ASNetworkImageNode.mm | 945 +++++ .../Texture/Source/ASNodeController+Beta.h | 62 + .../Texture/Source/ASNodeController+Beta.mm | 136 + .../Pods/Texture/Source/ASPagerFlowLayout.h | 18 + .../Pods/Texture/Source/ASPagerFlowLayout.mm | 110 + .../Pods/Texture/Source/ASPagerNode+Beta.h | 15 + .../Pods/Texture/Source/ASPagerNode.h | 133 + .../Pods/Texture/Source/ASPagerNode.mm | 230 + .../Pods/Texture/Source/ASRangeManagingNode.h | 31 + .../Pods/Texture/Source/ASRunLoopQueue.h | 91 + .../Pods/Texture/Source/ASRunLoopQueue.mm | 491 +++ .../Pods/Texture/Source/ASScrollNode.h | 50 + .../Pods/Texture/Source/ASScrollNode.mm | 187 + .../Pods/Texture/Source/ASSectionController.h | 86 + .../Source/ASSupplementaryNodeSource.h | 51 + .../Pods/Texture/Source/ASTabBarController.h | 28 + .../Pods/Texture/Source/ASTabBarController.mm | 84 + .../Pods/Texture/Source/ASTableNode+Beta.h | 21 + .../Pods/Texture/Source/ASTableNode.h | 763 ++++ .../Pods/Texture/Source/ASTableNode.mm | 894 ++++ .../Pods/Texture/Source/ASTableView.h | 245 ++ .../Pods/Texture/Source/ASTableView.mm | 2039 +++++++++ .../Pods/Texture/Source/ASTableViewInternal.h | 62 + .../Texture/Source/ASTableViewProtocols.h | 101 + .../Pods/Texture/Source/ASTextNode+Beta.h | 45 + .../Pods/Texture/Source/ASTextNode.h | 278 ++ .../Pods/Texture/Source/ASTextNode.mm | 1541 +++++++ .../Pods/Texture/Source/ASTextNode2.h | 256 ++ .../Pods/Texture/Source/ASTextNode2.mm | 1512 +++++++ .../Pods/Texture/Source/ASTextNodeCommon.h | 91 + .../Pods/Texture/Source/ASVideoNode.h | 171 + .../Pods/Texture/Source/ASVideoNode.mm | 861 ++++ .../Pods/Texture/Source/ASVideoPlayerNode.h | 225 + .../Pods/Texture/Source/ASVideoPlayerNode.mm | 1017 +++++ .../Texture/Source/ASVisibilityProtocols.h | 145 + .../Texture/Source/ASVisibilityProtocols.mm | 22 + .../Source/AsyncDisplayKit+IGListKitMethods.h | 64 + .../AsyncDisplayKit+IGListKitMethods.mm | 53 + .../Pods/Texture/Source/AsyncDisplayKit.h | 133 + .../Pods/Texture/Source/Base/ASAssert.h | 101 + .../Pods/Texture/Source/Base/ASAssert.mm | 58 + .../Pods/Texture/Source/Base/ASAvailability.h | 95 + .../Pods/Texture/Source/Base/ASBaseDefines.h | 237 ++ .../Source/Base/ASDisplayNode+Ancestry.h | 55 + .../Source/Base/ASDisplayNode+Ancestry.mm | 90 + .../Texture/Source/Base/ASEqualityHelpers.h | 21 + .../Pods/Texture/Source/Base/ASLog.h | 131 + .../Pods/Texture/Source/Base/ASLog.mm | 61 + .../Pods/Texture/Source/Base/ASSignpost.h | 106 + .../Source/Debug/AsyncDisplayKit+Debug.h | 53 + .../Source/Debug/AsyncDisplayKit+Debug.mm | 753 ++++ .../Source/Debug/AsyncDisplayKit+Tips.h | 43 + .../Source/Debug/AsyncDisplayKit+Tips.mm | 48 + .../Details/ASAbstractLayoutController.h | 33 + .../Details/ASAbstractLayoutController.mm | 185 + .../Source/Details/ASBasicImageDownloader.h | 36 + .../Source/Details/ASBasicImageDownloader.mm | 352 ++ .../Texture/Source/Details/ASBatchContext.h | 65 + .../Texture/Source/Details/ASBatchContext.mm | 61 + .../Source/Details/ASBatchFetchingDelegate.h | 23 + .../Source/Details/ASCollectionElement.h | 47 + .../Source/Details/ASCollectionElement.mm | 87 + .../Details/ASCollectionFlowLayoutDelegate.h | 28 + .../Details/ASCollectionFlowLayoutDelegate.mm | 78 + .../ASCollectionGalleryLayoutDelegate.h | 106 + .../ASCollectionGalleryLayoutDelegate.mm | 135 + .../Source/Details/ASCollectionInternal.h | 64 + .../Details/ASCollectionLayoutContext.h | 31 + .../Details/ASCollectionLayoutContext.mm | 102 + .../Details/ASCollectionLayoutDelegate.h | 59 + .../Source/Details/ASCollectionLayoutState.h | 111 + .../Source/Details/ASCollectionLayoutState.mm | 253 ++ .../ASCollectionViewLayoutController.h | 23 + .../ASCollectionViewLayoutController.mm | 126 + .../Details/ASCollectionViewLayoutInspector.h | 83 + .../ASCollectionViewLayoutInspector.mm | 76 + .../Texture/Source/Details/ASDataController.h | 275 ++ .../Source/Details/ASDataController.mm | 934 ++++ .../Texture/Source/Details/ASDelegateProxy.h | 56 + .../Texture/Source/Details/ASDelegateProxy.mm | 268 ++ .../Texture/Source/Details/ASElementMap.h | 133 + .../Texture/Source/Details/ASElementMap.mm | 275 ++ .../Source/Details/ASGraphicsContext.h | 65 + .../Source/Details/ASGraphicsContext.mm | 136 + .../Pods/Texture/Source/Details/ASHashing.h | 40 + .../Pods/Texture/Source/Details/ASHashing.mm | 38 + .../Source/Details/ASHighlightOverlayLayer.h | 51 + .../Source/Details/ASHighlightOverlayLayer.mm | 134 + .../ASImageContainerProtocolCategories.h | 19 + .../ASImageContainerProtocolCategories.mm | 38 + .../Texture/Source/Details/ASImageProtocols.h | 245 ++ .../Texture/Source/Details/ASIntegerMap.h | 68 + .../Texture/Source/Details/ASIntegerMap.mm | 184 + .../Source/Details/ASLayoutController.h | 40 + .../Source/Details/ASLayoutRangeType.h | 68 + .../Source/Details/ASMainSerialQueue.h | 19 + .../Source/Details/ASMainSerialQueue.mm | 76 + .../ASMutableAttributedStringBuilder.h | 65 + .../ASMutableAttributedStringBuilder.mm | 254 ++ .../Details/ASObjectDescriptionHelpers.h | 65 + .../Details/ASObjectDescriptionHelpers.mm | 104 + .../Details/ASPINRemoteImageDownloader.h | 80 + .../Details/ASPINRemoteImageDownloader.mm | 390 ++ .../Pods/Texture/Source/Details/ASPageTable.h | 120 + .../Texture/Source/Details/ASPageTable.mm | 147 + .../Details/ASPhotosFrameworkImageRequest.h | 73 + .../Details/ASPhotosFrameworkImageRequest.mm | 157 + .../Source/Details/ASRangeController.h | 202 + .../Source/Details/ASRangeController.mm | 663 +++ ...SRangeControllerUpdateRangeProtocol+Beta.h | 21 + .../Source/Details/ASRecursiveUnfairLock.h | 47 + .../Source/Details/ASRecursiveUnfairLock.mm | 87 + .../Source/Details/ASScrollDirection.h | 37 + .../Source/Details/ASScrollDirection.mm | 64 + .../Texture/Source/Details/ASSectionContext.h | 22 + .../Source/Details/ASTableLayoutController.h | 29 + .../Source/Details/ASTableLayoutController.mm | 54 + .../Pods/Texture/Source/Details/ASThread.h | 333 ++ .../Source/Details/ASTraitCollection.h | 179 + .../Source/Details/ASTraitCollection.mm | 366 ++ .../Pods/Texture/Source/Details/ASWeakProxy.h | 32 + .../Texture/Source/Details/ASWeakProxy.mm | 72 + .../Pods/Texture/Source/Details/ASWeakSet.h | 50 + .../Pods/Texture/Source/Details/ASWeakSet.mm | 84 + .../Details/CoreGraphics+ASConvenience.h | 51 + .../Texture/Source/Details/NSArray+Diffing.h | 92 + .../Texture/Source/Details/NSArray+Diffing.mm | 177 + .../Source/Details/NSIndexSet+ASHelpers.h | 29 + .../Source/Details/NSIndexSet+ASHelpers.mm | 91 + ...MutableAttributedString+TextKitAdditions.h | 26 + ...utableAttributedString+TextKitAdditions.mm | 49 + .../Transactions/_ASAsyncTransaction.h | 119 + .../Transactions/_ASAsyncTransaction.mm | 461 ++ .../_ASAsyncTransactionContainer+Private.h | 24 + .../_ASAsyncTransactionContainer.h | 81 + .../_ASAsyncTransactionContainer.mm | 126 + .../Transactions/_ASAsyncTransactionGroup.h | 36 + .../Transactions/_ASAsyncTransactionGroup.mm | 87 + .../UICollectionViewLayout+ASConvenience.h | 28 + .../UICollectionViewLayout+ASConvenience.mm | 28 + .../Source/Details/UIView+ASConvenience.h | 101 + .../Details/_ASCollectionReusableView.h | 26 + .../Details/_ASCollectionReusableView.mm | 89 + .../Source/Details/_ASCollectionViewCell.h | 35 + .../Source/Details/_ASCollectionViewCell.mm | 147 + .../Texture/Source/Details/_ASDisplayLayer.h | 149 + .../Texture/Source/Details/_ASDisplayLayer.mm | 197 + .../Texture/Source/Details/_ASDisplayView.h | 36 + .../Texture/Source/Details/_ASDisplayView.mm | 532 +++ .../Details/_ASDisplayViewAccessiblity.h | 30 + .../Details/_ASDisplayViewAccessiblity.mm | 405 ++ .../Source/IGListAdapter+AsyncDisplayKit.h | 37 + .../Source/IGListAdapter+AsyncDisplayKit.mm | 51 + .../Source/Layout/ASAbsoluteLayoutElement.h | 26 + .../Source/Layout/ASAbsoluteLayoutSpec.h | 45 + .../Source/Layout/ASAbsoluteLayoutSpec.mm | 103 + .../Source/Layout/ASAsciiArtBoxCreator.h | 60 + .../Source/Layout/ASAsciiArtBoxCreator.mm | 185 + .../Source/Layout/ASBackgroundLayoutSpec.h | 34 + .../Source/Layout/ASBackgroundLayoutSpec.mm | 91 + .../Source/Layout/ASCenterLayoutSpec.h | 70 + .../Source/Layout/ASCenterLayoutSpec.mm | 74 + .../Source/Layout/ASCornerLayoutSpec.h | 75 + .../Source/Layout/ASCornerLayoutSpec.mm | 164 + .../Pods/Texture/Source/Layout/ASDimension.h | 315 ++ .../Pods/Texture/Source/Layout/ASDimension.mm | 123 + .../Source/Layout/ASDimensionInternal.h | 101 + .../Source/Layout/ASDimensionInternal.mm | 65 + .../Texture/Source/Layout/ASInsetLayoutSpec.h | 43 + .../Source/Layout/ASInsetLayoutSpec.mm | 122 + .../Source/Layout/ASLayout+IGListDiffKit.h | 15 + .../Source/Layout/ASLayout+IGListDiffKit.mm | 30 + .../Pods/Texture/Source/Layout/ASLayout.h | 157 + .../Pods/Texture/Source/Layout/ASLayout.mm | 376 ++ .../Texture/Source/Layout/ASLayoutElement.h | 306 ++ .../Texture/Source/Layout/ASLayoutElement.mm | 884 ++++ .../Layout/ASLayoutElementExtensibility.h | 106 + .../Source/Layout/ASLayoutElementPrivate.h | 106 + .../Source/Layout/ASLayoutSpec+Subclasses.h | 59 + .../Source/Layout/ASLayoutSpec+Subclasses.mm | 86 + .../Pods/Texture/Source/Layout/ASLayoutSpec.h | 100 + .../Texture/Source/Layout/ASLayoutSpec.mm | 342 ++ .../Source/Layout/ASOverlayLayoutSpec.h | 34 + .../Source/Layout/ASOverlayLayoutSpec.mm | 87 + .../Texture/Source/Layout/ASRatioLayoutSpec.h | 42 + .../Source/Layout/ASRatioLayoutSpec.mm | 102 + .../Source/Layout/ASRelativeLayoutSpec.h | 87 + .../Source/Layout/ASRelativeLayoutSpec.mm | 104 + .../Source/Layout/ASStackLayoutDefines.h | 148 + .../Source/Layout/ASStackLayoutElement.h | 73 + .../Texture/Source/Layout/ASStackLayoutSpec.h | 133 + .../Source/Layout/ASStackLayoutSpec.mm | 216 + .../Texture/Source/Layout/ASYogaUtilities.h | 78 + .../Texture/Source/Layout/ASYogaUtilities.mm | 249 ++ ...bstractLayoutController+FrameworkPrivate.h | 21 + .../Private/ASBasicImageDownloaderInternal.h | 20 + .../Texture/Source/Private/ASBatchFetching.h | 73 + .../Texture/Source/Private/ASBatchFetching.mm | 99 + .../Source/Private/ASCellNode+Internal.h | 74 + .../Source/Private/ASCollectionLayout.h | 50 + .../Source/Private/ASCollectionLayout.mm | 396 ++ .../Source/Private/ASCollectionLayoutCache.h | 30 + .../Source/Private/ASCollectionLayoutCache.mm | 88 + .../ASCollectionLayoutContext+Private.h | 32 + .../Private/ASCollectionLayoutDefines.h | 19 + .../Private/ASCollectionLayoutDefines.mm | 23 + .../Private/ASCollectionLayoutState+Private.h | 25 + .../Private/ASCollectionView+Undeprecated.h | 300 ++ .../ASCollectionViewFlowLayoutInspector.h | 30 + .../ASCollectionViewFlowLayoutInspector.mm | 156 + .../Source/Private/ASControlNode+Private.h | 18 + .../Source/Private/ASControlTargetAction.h | 32 + .../Source/Private/ASControlTargetAction.mm | 65 + .../Source/Private/ASDefaultPlayButton.h | 14 + .../Source/Private/ASDefaultPlayButton.mm | 66 + .../Source/Private/ASDefaultPlaybackButton.h | 19 + .../Source/Private/ASDefaultPlaybackButton.mm | 84 + .../Pods/Texture/Source/Private/ASDispatch.h | 27 + .../Pods/Texture/Source/Private/ASDispatch.mm | 63 + .../Private/ASDisplayNode+AsyncDisplay.mm | 494 +++ .../Private/ASDisplayNode+DebugTiming.h | 21 + .../Private/ASDisplayNode+DebugTiming.mm | 85 + .../Private/ASDisplayNode+FrameworkPrivate.h | 335 ++ .../Private/ASDisplayNode+UIViewBridge.mm | 1474 +++++++ .../ASDisplayNodeCornerLayerDelegate.h | 13 + .../ASDisplayNodeCornerLayerDelegate.mm | 19 + .../Source/Private/ASDisplayNodeInternal.h | 400 ++ .../Source/Private/ASDisplayNodeLayout.h | 58 + .../Source/Private/ASDisplayNodeTipState.h | 32 + .../Source/Private/ASDisplayNodeTipState.mm | 25 + .../Private/ASIGListAdapterBasedDataSource.h | 29 + .../Private/ASIGListAdapterBasedDataSource.mm | 405 ++ .../ASImageNode+AnimatedImagePrivate.h | 50 + .../Source/Private/ASImageNode+CGExtras.h | 31 + .../Source/Private/ASImageNode+CGExtras.mm | 122 + .../Source/Private/ASImageNode+Private.h | 17 + .../Source/Private/ASInternalHelpers.h | 129 + .../Source/Private/ASInternalHelpers.mm | 266 ++ .../Private/ASLayerBackingTipProvider.h | 24 + .../Private/ASLayerBackingTipProvider.mm | 45 + .../Source/Private/ASLayoutTransition.h | 93 + .../Source/Private/ASLayoutTransition.mm | 298 ++ .../Source/Private/ASMutableElementMap.h | 62 + .../Source/Private/ASMutableElementMap.mm | 143 + .../Private/ASNetworkImageLoadInfo+Private.h | 22 + .../Source/Private/ASPendingStateController.h | 50 + .../Private/ASPendingStateController.mm | 102 + .../Private/ASResponderChainEnumerator.h | 29 + .../Private/ASResponderChainEnumerator.mm | 45 + .../Pods/Texture/Source/Private/ASSection.h | 36 + .../Pods/Texture/Source/Private/ASSection.mm | 25 + .../Source/Private/ASTableView+Undeprecated.h | 298 ++ .../Pods/Texture/Source/Private/ASTip.h | 49 + .../Pods/Texture/Source/Private/ASTip.mm | 35 + .../Pods/Texture/Source/Private/ASTipNode.h | 39 + .../Pods/Texture/Source/Private/ASTipNode.mm | 28 + .../Texture/Source/Private/ASTipProvider.h | 42 + .../Texture/Source/Private/ASTipProvider.mm | 43 + .../Texture/Source/Private/ASTipsController.h | 40 + .../Source/Private/ASTipsController.mm | 185 + .../Texture/Source/Private/ASTipsWindow.h | 36 + .../Texture/Source/Private/ASTipsWindow.mm | 98 + .../Private/ASTwoDimensionalArrayUtils.h | 46 + .../Private/ASTwoDimensionalArrayUtils.mm | 121 + .../Pods/Texture/Source/Private/ASWeakMap.h | 59 + .../Pods/Texture/Source/Private/ASWeakMap.mm | 78 + .../Layout/ASLayoutElementStylePrivate.h | 31 + .../Private/Layout/ASLayoutSpecPrivate.h | 37 + .../Private/Layout/ASLayoutSpecUtilities.h | 101 + .../Layout/ASStackLayoutSpecUtilities.h | 135 + .../Private/Layout/ASStackPositionedLayout.h | 24 + .../Private/Layout/ASStackPositionedLayout.mm | 185 + .../Layout/ASStackUnpositionedLayout.h | 73 + .../Layout/ASStackUnpositionedLayout.mm | 758 ++++ .../Component/ASTextDebugOption.h | 92 + .../Component/ASTextDebugOption.mm | 135 + .../TextExperiment/Component/ASTextInput.h | 85 + .../TextExperiment/Component/ASTextInput.mm | 150 + .../TextExperiment/Component/ASTextLayout.h | 547 +++ .../TextExperiment/Component/ASTextLayout.mm | 3483 +++++++++++++++ .../TextExperiment/Component/ASTextLine.h | 77 + .../TextExperiment/Component/ASTextLine.mm | 169 + .../TextExperiment/String/ASTextAttribute.h | 346 ++ .../TextExperiment/String/ASTextAttribute.mm | 487 +++ .../TextExperiment/String/ASTextRunDelegate.h | 65 + .../String/ASTextRunDelegate.mm | 68 + .../TextExperiment/Utility/ASTextUtilities.h | 315 ++ .../TextExperiment/Utility/ASTextUtilities.mm | 142 + .../Utility/NSAttributedString+ASText.h | 1375 ++++++ .../Utility/NSAttributedString+ASText.mm | 1208 ++++++ .../Utility/NSParagraphStyle+ASText.h | 34 + .../Utility/NSParagraphStyle+ASText.mm | 219 + .../Private/_ASCollectionGalleryLayoutInfo.h | 26 + .../Private/_ASCollectionGalleryLayoutInfo.mm | 68 + .../Private/_ASCollectionGalleryLayoutItem.h | 34 + .../Private/_ASCollectionGalleryLayoutItem.mm | 82 + .../Source/Private/_ASCoreAnimationExtras.h | 84 + .../Source/Private/_ASCoreAnimationExtras.mm | 186 + .../Source/Private/_ASHierarchyChangeSet.h | 214 + .../Source/Private/_ASHierarchyChangeSet.mm | 1009 +++++ .../Texture/Source/Private/_ASPendingState.h | 41 + .../Texture/Source/Private/_ASPendingState.mm | 1423 +++++++ .../Texture/Source/Private/_ASScopeTimer.h | 56 + .../Texture/Source/TextKit/ASLayoutManager.h | 16 + .../Texture/Source/TextKit/ASLayoutManager.mm | 42 + .../Source/TextKit/ASTextKitAttributes.h | 134 + .../Source/TextKit/ASTextKitAttributes.mm | 50 + .../Source/TextKit/ASTextKitComponents.h | 64 + .../Source/TextKit/ASTextKitComponents.mm | 193 + .../Texture/Source/TextKit/ASTextKitContext.h | 54 + .../Source/TextKit/ASTextKitContext.mm | 98 + .../TextKit/ASTextKitCoreTextAdditions.h | 89 + .../TextKit/ASTextKitCoreTextAdditions.mm | 330 ++ .../Source/TextKit/ASTextKitEntityAttribute.h | 36 + .../TextKit/ASTextKitEntityAttribute.mm | 43 + .../TextKit/ASTextKitFontSizeAdjuster.h | 58 + .../TextKit/ASTextKitFontSizeAdjuster.mm | 240 ++ .../TextKit/ASTextKitRenderer+Positioning.h | 106 + .../TextKit/ASTextKitRenderer+Positioning.mm | 386 ++ .../TextKit/ASTextKitRenderer+TextChecking.h | 32 + .../TextKit/ASTextKitRenderer+TextChecking.mm | 104 + .../Source/TextKit/ASTextKitRenderer.h | 108 + .../Source/TextKit/ASTextKitRenderer.mm | 294 ++ .../Source/TextKit/ASTextKitShadower.h | 78 + .../Source/TextKit/ASTextKitShadower.mm | 177 + .../Source/TextKit/ASTextKitTailTruncater.h | 21 + .../Source/TextKit/ASTextKitTailTruncater.mm | 197 + .../Source/TextKit/ASTextKitTruncating.h | 61 + .../Texture/Source/TextKit/ASTextNodeTypes.h | 13 + .../Source/TextKit/ASTextNodeWordKerner.h | 37 + .../Source/TextKit/ASTextNodeWordKerner.mm | 130 + .../Texture/Source/UIImage+ASConvenience.h | 162 + .../Texture/Source/UIImage+ASConvenience.mm | 225 + .../Source/UIResponder+AsyncDisplayKit.h | 25 + .../Source/UIResponder+AsyncDisplayKit.mm | 31 + .../Texture/Source/_ASTransitionContext.h | 49 + .../Texture/Source/_ASTransitionContext.mm | 104 + .../Texture/Source/tvOS/ASControlNode+tvOS.mm | 92 + .../Texture/Source/tvOS/ASImageNode+tvOS.mm | 191 + .../project.pbxproj | 550 +++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../contents.xcworkspacedata | 10 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/swiftpm/Package.resolved | 25 + .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 98 + .../Assets.xcassets/Contents.json | 6 + .../Base.lproj/LaunchScreen.storyboard | 25 + .../Base.lproj/Main.storyboard | 24 + .../Global/Customs/CustomNavigationBar.swift | 51 + .../Screens/Base/BaseScrollNode.swift | 31 + .../NodeControllers/PayNodeController.swift | 55 + .../Screens/StarbucksTabBarController.swift | 68 + .../Supports/AppDelegate.swift | 36 + .../Supports/Info.plist | 66 + .../Supports/SceneDelegate.swift | 68 + .../ViewController.swift | 19 + 563 files changed, 103957 insertions(+), 7 deletions(-) create mode 100644 Starbucks_Clone_TexBrother/Podfile create mode 100644 Starbucks_Clone_TexBrother/Podfile.lock create mode 100644 Starbucks_Clone_TexBrother/Pods/Manifest.lock create mode 100644 Starbucks_Clone_TexBrother/Pods/PINCache/LICENSE.txt create mode 100644 Starbucks_Clone_TexBrother/Pods/PINCache/README.md create mode 100644 Starbucks_Clone_TexBrother/Pods/PINCache/Source/PINCache.h create mode 100644 Starbucks_Clone_TexBrother/Pods/PINCache/Source/PINCache.m create mode 100644 Starbucks_Clone_TexBrother/Pods/PINCache/Source/PINCacheMacros.h create mode 100644 Starbucks_Clone_TexBrother/Pods/PINCache/Source/PINCacheObjectSubscripting.h create mode 100644 Starbucks_Clone_TexBrother/Pods/PINCache/Source/PINCaching.h create mode 100644 Starbucks_Clone_TexBrother/Pods/PINCache/Source/PINDiskCache.h create mode 100644 Starbucks_Clone_TexBrother/Pods/PINCache/Source/PINDiskCache.m create mode 100644 Starbucks_Clone_TexBrother/Pods/PINCache/Source/PINMemoryCache.h create mode 100644 Starbucks_Clone_TexBrother/Pods/PINCache/Source/PINMemoryCache.m create mode 100644 Starbucks_Clone_TexBrother/Pods/PINOperation/LICENSE.txt create mode 100644 Starbucks_Clone_TexBrother/Pods/PINOperation/README.md create mode 100644 Starbucks_Clone_TexBrother/Pods/PINOperation/Source/PINOperation.h create mode 100644 Starbucks_Clone_TexBrother/Pods/PINOperation/Source/PINOperationGroup.h create mode 100644 Starbucks_Clone_TexBrother/Pods/PINOperation/Source/PINOperationGroup.m create mode 100644 Starbucks_Clone_TexBrother/Pods/PINOperation/Source/PINOperationMacros.h create mode 100644 Starbucks_Clone_TexBrother/Pods/PINOperation/Source/PINOperationQueue.h create mode 100644 Starbucks_Clone_TexBrother/Pods/PINOperation/Source/PINOperationQueue.m create mode 100644 Starbucks_Clone_TexBrother/Pods/PINOperation/Source/PINOperationTypes.h create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/LICENSE create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/README.md create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/AnimatedImages/PINAnimatedImage.m create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/AnimatedImages/PINAnimatedImageView.m create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/AnimatedImages/PINCachedAnimatedImage.m create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/AnimatedImages/PINGIFAnimatedImage.m create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/AnimatedImages/PINWebPAnimatedImage.m create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/Categories/NSData+ImageDetectors.m create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/Categories/NSHTTPURLResponse+MaxAge.h create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/Categories/NSHTTPURLResponse+MaxAge.m create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/Categories/PINImage+DecodedImage.h create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/Categories/PINImage+DecodedImage.m create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/Categories/PINImage+ScaledImage.h create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/Categories/PINImage+ScaledImage.m create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/Categories/PINImage+WebP.h create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/Categories/PINImage+WebP.m create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/Categories/PINRemoteImageTask+Subclassing.h create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/ImageCategories/PINAnimatedImageView+PINRemoteImage.m create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/ImageCategories/PINButton+PINRemoteImage.m create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/ImageCategories/PINImageView+PINRemoteImage.m create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINAlternateRepresentationProvider.m create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINCache/PINCache+PINRemoteImageCaching.m create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINDisplayLink.h create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINDisplayLink.m create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINProgressiveImage.m create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageBasicCache.h create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageBasicCache.m create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageCallbacks.h create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageCallbacks.m create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageCategoryManager.m create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageDownloadQueue.h create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageDownloadQueue.m create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageDownloadTask.h create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageDownloadTask.m create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageManager+Private.h create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageManager.m create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageManagerConfiguration.h create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageManagerConfiguration.m create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageManagerResult.m create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageMemoryContainer.h create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageMemoryContainer.m create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageProcessorTask.h create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageProcessorTask.m create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageTask.h create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageTask.m create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteLock.h create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteLock.m create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteWeakProxy.h create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteWeakProxy.m create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRequestRetryStrategy.m create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINResume.h create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINResume.m create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINSpeedRecorder.h create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINSpeedRecorder.m create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINURLSessionManager.m create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/NSData+ImageDetectors.h create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINAlternateRepresentationProvider.h create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINAnimatedImage.h create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINAnimatedImageView+PINRemoteImage.h create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINAnimatedImageView.h create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINButton+PINRemoteImage.h create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINCache+PINRemoteImageCaching.h create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINCachedAnimatedImage.h create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINGIFAnimatedImage.h create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINImageView+PINRemoteImage.h create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINProgressiveImage.h create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINRemoteImage.h create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINRemoteImageCaching.h create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINRemoteImageCategoryManager.h create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINRemoteImageMacros.h create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINRemoteImageManager.h create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINRemoteImageManagerResult.h create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINRequestRetryStrategy.h create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINURLSessionManager.h create mode 100644 Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINWebPAnimatedImage.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Pods.xcodeproj/project.pbxproj create mode 100644 Starbucks_Clone_TexBrother/Pods/Target Support Files/PINCache/PINCache-Info.plist create mode 100644 Starbucks_Clone_TexBrother/Pods/Target Support Files/PINCache/PINCache-dummy.m create mode 100644 Starbucks_Clone_TexBrother/Pods/Target Support Files/PINCache/PINCache-prefix.pch create mode 100644 Starbucks_Clone_TexBrother/Pods/Target Support Files/PINCache/PINCache-umbrella.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Target Support Files/PINCache/PINCache.debug.xcconfig create mode 100644 Starbucks_Clone_TexBrother/Pods/Target Support Files/PINCache/PINCache.modulemap create mode 100644 Starbucks_Clone_TexBrother/Pods/Target Support Files/PINCache/PINCache.release.xcconfig create mode 100644 Starbucks_Clone_TexBrother/Pods/Target Support Files/PINOperation/PINOperation-Info.plist create mode 100644 Starbucks_Clone_TexBrother/Pods/Target Support Files/PINOperation/PINOperation-dummy.m create mode 100644 Starbucks_Clone_TexBrother/Pods/Target Support Files/PINOperation/PINOperation-prefix.pch create mode 100644 Starbucks_Clone_TexBrother/Pods/Target Support Files/PINOperation/PINOperation-umbrella.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Target Support Files/PINOperation/PINOperation.debug.xcconfig create mode 100644 Starbucks_Clone_TexBrother/Pods/Target Support Files/PINOperation/PINOperation.modulemap create mode 100644 Starbucks_Clone_TexBrother/Pods/Target Support Files/PINOperation/PINOperation.release.xcconfig create mode 100644 Starbucks_Clone_TexBrother/Pods/Target Support Files/PINRemoteImage/PINRemoteImage-Info.plist create mode 100644 Starbucks_Clone_TexBrother/Pods/Target Support Files/PINRemoteImage/PINRemoteImage-dummy.m create mode 100644 Starbucks_Clone_TexBrother/Pods/Target Support Files/PINRemoteImage/PINRemoteImage-umbrella.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Target Support Files/PINRemoteImage/PINRemoteImage.debug.xcconfig create mode 100644 Starbucks_Clone_TexBrother/Pods/Target Support Files/PINRemoteImage/PINRemoteImage.modulemap create mode 100644 Starbucks_Clone_TexBrother/Pods/Target Support Files/PINRemoteImage/PINRemoteImage.release.xcconfig create mode 100644 Starbucks_Clone_TexBrother/Pods/Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother-Info.plist create mode 100644 Starbucks_Clone_TexBrother/Pods/Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother-acknowledgements.markdown create mode 100644 Starbucks_Clone_TexBrother/Pods/Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother-acknowledgements.plist create mode 100644 Starbucks_Clone_TexBrother/Pods/Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother-dummy.m create mode 100644 Starbucks_Clone_TexBrother/Pods/Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother-frameworks-Debug-input-files.xcfilelist create mode 100644 Starbucks_Clone_TexBrother/Pods/Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother-frameworks-Debug-output-files.xcfilelist create mode 100644 Starbucks_Clone_TexBrother/Pods/Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother-frameworks-Release-input-files.xcfilelist create mode 100644 Starbucks_Clone_TexBrother/Pods/Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother-frameworks-Release-output-files.xcfilelist create mode 100755 Starbucks_Clone_TexBrother/Pods/Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother-frameworks.sh create mode 100644 Starbucks_Clone_TexBrother/Pods/Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother-umbrella.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother.debug.xcconfig create mode 100644 Starbucks_Clone_TexBrother/Pods/Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother.modulemap create mode 100644 Starbucks_Clone_TexBrother/Pods/Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother.release.xcconfig create mode 100644 Starbucks_Clone_TexBrother/Pods/Target Support Files/Texture/Texture-Info.plist create mode 100644 Starbucks_Clone_TexBrother/Pods/Target Support Files/Texture/Texture-dummy.m create mode 100644 Starbucks_Clone_TexBrother/Pods/Target Support Files/Texture/Texture-prefix.pch create mode 100644 Starbucks_Clone_TexBrother/Pods/Target Support Files/Texture/Texture-umbrella.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Target Support Files/Texture/Texture.debug.xcconfig create mode 100644 Starbucks_Clone_TexBrother/Pods/Target Support Files/Texture/Texture.modulemap create mode 100644 Starbucks_Clone_TexBrother/Pods/Target Support Files/Texture/Texture.release.xcconfig create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/LICENSE create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/README.md create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASBlockTypes.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASButtonNode+Private.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASButtonNode+Yoga.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASButtonNode+Yoga.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASButtonNode.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASButtonNode.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASCellNode.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASCellNode.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASCollectionNode+Beta.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASCollectionNode.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASCollectionNode.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASCollectionView.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASCollectionView.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASCollectionViewLayoutFacilitatorProtocol.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASCollectionViewProtocols.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASCollections.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASCollections.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASConfiguration.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASConfiguration.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASConfigurationDelegate.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASConfigurationInternal.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASConfigurationInternal.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASContextTransitioning.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASControlNode+Subclasses.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASControlNode.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASControlNode.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDKViewController.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDKViewController.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDisplayNode+Beta.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDisplayNode+Convenience.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDisplayNode+Convenience.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDisplayNode+InterfaceState.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDisplayNode+Layout.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDisplayNode+LayoutSpec.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDisplayNode+LayoutSpec.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDisplayNode+Subclasses.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDisplayNode+Yoga.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDisplayNode+Yoga.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDisplayNode.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDisplayNode.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDisplayNodeExtras.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDisplayNodeExtras.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASEditableTextNode.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASEditableTextNode.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASExperimentalFeatures.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASExperimentalFeatures.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASImageNode+AnimatedImage.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASImageNode.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASImageNode.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASLocking.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASMainThreadDeallocation.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASMainThreadDeallocation.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASMapNode.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASMapNode.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASMultiplexImageNode.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASMultiplexImageNode.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASNavigationController.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASNavigationController.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASNetworkImageLoadInfo.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASNetworkImageLoadInfo.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASNetworkImageNode.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASNetworkImageNode.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASNodeController+Beta.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASNodeController+Beta.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASPagerFlowLayout.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASPagerFlowLayout.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASPagerNode+Beta.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASPagerNode.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASPagerNode.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASRangeManagingNode.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASRunLoopQueue.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASRunLoopQueue.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASScrollNode.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASScrollNode.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASSectionController.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASSupplementaryNodeSource.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASTabBarController.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASTabBarController.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASTableNode+Beta.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASTableNode.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASTableNode.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASTableView.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASTableView.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASTableViewInternal.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASTableViewProtocols.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASTextNode+Beta.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASTextNode.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASTextNode.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASTextNode2.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASTextNode2.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASTextNodeCommon.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASVideoNode.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASVideoNode.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASVideoPlayerNode.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASVideoPlayerNode.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASVisibilityProtocols.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/ASVisibilityProtocols.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/AsyncDisplayKit+IGListKitMethods.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/AsyncDisplayKit+IGListKitMethods.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/AsyncDisplayKit.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Base/ASAssert.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Base/ASAssert.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Base/ASAvailability.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Base/ASBaseDefines.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Base/ASDisplayNode+Ancestry.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Base/ASDisplayNode+Ancestry.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Base/ASEqualityHelpers.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Base/ASLog.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Base/ASLog.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Base/ASSignpost.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Debug/AsyncDisplayKit+Debug.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Debug/AsyncDisplayKit+Debug.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Debug/AsyncDisplayKit+Tips.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Debug/AsyncDisplayKit+Tips.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASAbstractLayoutController.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASAbstractLayoutController.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASBasicImageDownloader.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASBasicImageDownloader.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASBatchContext.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASBatchContext.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASBatchFetchingDelegate.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionElement.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionElement.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionFlowLayoutDelegate.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionFlowLayoutDelegate.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionGalleryLayoutDelegate.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionGalleryLayoutDelegate.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionInternal.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionLayoutContext.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionLayoutContext.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionLayoutDelegate.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionLayoutState.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionLayoutState.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionViewLayoutController.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionViewLayoutController.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionViewLayoutInspector.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionViewLayoutInspector.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASDataController.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASDataController.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASDelegateProxy.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASDelegateProxy.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASElementMap.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASElementMap.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASGraphicsContext.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASGraphicsContext.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASHashing.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASHashing.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASHighlightOverlayLayer.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASHighlightOverlayLayer.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASImageContainerProtocolCategories.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASImageContainerProtocolCategories.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASImageProtocols.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASIntegerMap.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASIntegerMap.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASLayoutController.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASLayoutRangeType.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASMainSerialQueue.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASMainSerialQueue.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASMutableAttributedStringBuilder.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASMutableAttributedStringBuilder.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASObjectDescriptionHelpers.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASObjectDescriptionHelpers.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASPINRemoteImageDownloader.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASPINRemoteImageDownloader.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASPageTable.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASPageTable.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASPhotosFrameworkImageRequest.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASPhotosFrameworkImageRequest.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASRangeController.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASRangeController.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASRangeControllerUpdateRangeProtocol+Beta.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASRecursiveUnfairLock.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASRecursiveUnfairLock.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASScrollDirection.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASScrollDirection.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASSectionContext.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASTableLayoutController.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASTableLayoutController.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASThread.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASTraitCollection.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASTraitCollection.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASWeakProxy.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASWeakProxy.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASWeakSet.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASWeakSet.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/CoreGraphics+ASConvenience.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/NSArray+Diffing.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/NSArray+Diffing.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/NSIndexSet+ASHelpers.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/NSIndexSet+ASHelpers.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/NSMutableAttributedString+TextKitAdditions.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/NSMutableAttributedString+TextKitAdditions.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/Transactions/_ASAsyncTransaction.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/Transactions/_ASAsyncTransaction.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/Transactions/_ASAsyncTransactionContainer+Private.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/Transactions/_ASAsyncTransactionContainer.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/Transactions/_ASAsyncTransactionContainer.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/Transactions/_ASAsyncTransactionGroup.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/Transactions/_ASAsyncTransactionGroup.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/UICollectionViewLayout+ASConvenience.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/UICollectionViewLayout+ASConvenience.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/UIView+ASConvenience.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/_ASCollectionReusableView.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/_ASCollectionReusableView.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/_ASCollectionViewCell.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/_ASCollectionViewCell.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/_ASDisplayLayer.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/_ASDisplayLayer.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/_ASDisplayView.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/_ASDisplayView.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/_ASDisplayViewAccessiblity.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/_ASDisplayViewAccessiblity.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/IGListAdapter+AsyncDisplayKit.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/IGListAdapter+AsyncDisplayKit.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASAbsoluteLayoutElement.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASAbsoluteLayoutSpec.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASAbsoluteLayoutSpec.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASAsciiArtBoxCreator.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASAsciiArtBoxCreator.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASBackgroundLayoutSpec.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASBackgroundLayoutSpec.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASCenterLayoutSpec.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASCenterLayoutSpec.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASCornerLayoutSpec.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASCornerLayoutSpec.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASDimension.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASDimension.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASDimensionInternal.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASDimensionInternal.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASInsetLayoutSpec.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASInsetLayoutSpec.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASLayout+IGListDiffKit.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASLayout+IGListDiffKit.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASLayout.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASLayout.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASLayoutElement.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASLayoutElement.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASLayoutElementExtensibility.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASLayoutElementPrivate.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASLayoutSpec+Subclasses.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASLayoutSpec+Subclasses.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASLayoutSpec.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASLayoutSpec.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASOverlayLayoutSpec.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASOverlayLayoutSpec.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASRatioLayoutSpec.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASRatioLayoutSpec.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASRelativeLayoutSpec.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASRelativeLayoutSpec.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASStackLayoutDefines.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASStackLayoutElement.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASStackLayoutSpec.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASStackLayoutSpec.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASYogaUtilities.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASYogaUtilities.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASAbstractLayoutController+FrameworkPrivate.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASBasicImageDownloaderInternal.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASBatchFetching.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASBatchFetching.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASCellNode+Internal.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASCollectionLayout.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASCollectionLayout.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASCollectionLayoutCache.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASCollectionLayoutCache.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASCollectionLayoutContext+Private.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASCollectionLayoutDefines.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASCollectionLayoutDefines.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASCollectionLayoutState+Private.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASCollectionView+Undeprecated.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASCollectionViewFlowLayoutInspector.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASCollectionViewFlowLayoutInspector.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASControlNode+Private.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASControlTargetAction.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASControlTargetAction.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDefaultPlayButton.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDefaultPlayButton.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDefaultPlaybackButton.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDefaultPlaybackButton.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDispatch.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDispatch.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDisplayNode+AsyncDisplay.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDisplayNode+DebugTiming.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDisplayNode+DebugTiming.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDisplayNode+FrameworkPrivate.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDisplayNode+UIViewBridge.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDisplayNodeCornerLayerDelegate.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDisplayNodeCornerLayerDelegate.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDisplayNodeInternal.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDisplayNodeLayout.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDisplayNodeTipState.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDisplayNodeTipState.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASIGListAdapterBasedDataSource.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASIGListAdapterBasedDataSource.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASImageNode+AnimatedImagePrivate.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASImageNode+CGExtras.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASImageNode+CGExtras.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASImageNode+Private.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASInternalHelpers.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASInternalHelpers.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASLayerBackingTipProvider.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASLayerBackingTipProvider.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASLayoutTransition.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASLayoutTransition.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASMutableElementMap.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASMutableElementMap.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASNetworkImageLoadInfo+Private.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASPendingStateController.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASPendingStateController.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASResponderChainEnumerator.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASResponderChainEnumerator.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASSection.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASSection.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASTableView+Undeprecated.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASTip.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASTip.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASTipNode.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASTipNode.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASTipProvider.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASTipProvider.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASTipsController.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASTipsController.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASTipsWindow.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASTipsWindow.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASTwoDimensionalArrayUtils.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASTwoDimensionalArrayUtils.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASWeakMap.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASWeakMap.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/Layout/ASLayoutElementStylePrivate.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/Layout/ASLayoutSpecPrivate.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/Layout/ASLayoutSpecUtilities.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/Layout/ASStackLayoutSpecUtilities.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/Layout/ASStackPositionedLayout.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/Layout/ASStackPositionedLayout.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/Layout/ASStackUnpositionedLayout.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/Layout/ASStackUnpositionedLayout.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/Component/ASTextDebugOption.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/Component/ASTextDebugOption.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/Component/ASTextInput.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/Component/ASTextInput.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/Component/ASTextLayout.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/Component/ASTextLayout.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/Component/ASTextLine.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/Component/ASTextLine.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/String/ASTextAttribute.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/String/ASTextAttribute.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/String/ASTextRunDelegate.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/String/ASTextRunDelegate.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/Utility/ASTextUtilities.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/Utility/ASTextUtilities.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/Utility/NSAttributedString+ASText.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/Utility/NSAttributedString+ASText.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/Utility/NSParagraphStyle+ASText.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/Utility/NSParagraphStyle+ASText.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/_ASCollectionGalleryLayoutInfo.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/_ASCollectionGalleryLayoutInfo.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/_ASCollectionGalleryLayoutItem.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/_ASCollectionGalleryLayoutItem.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/_ASCoreAnimationExtras.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/_ASCoreAnimationExtras.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/_ASHierarchyChangeSet.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/_ASHierarchyChangeSet.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/_ASPendingState.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/_ASPendingState.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/_ASScopeTimer.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASLayoutManager.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASLayoutManager.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitAttributes.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitAttributes.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitComponents.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitComponents.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitContext.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitContext.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitCoreTextAdditions.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitCoreTextAdditions.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitEntityAttribute.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitEntityAttribute.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitFontSizeAdjuster.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitFontSizeAdjuster.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitRenderer+Positioning.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitRenderer+Positioning.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitRenderer+TextChecking.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitRenderer+TextChecking.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitRenderer.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitRenderer.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitShadower.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitShadower.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitTailTruncater.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitTailTruncater.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitTruncating.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextNodeTypes.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextNodeWordKerner.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextNodeWordKerner.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/UIImage+ASConvenience.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/UIImage+ASConvenience.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/UIResponder+AsyncDisplayKit.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/UIResponder+AsyncDisplayKit.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/_ASTransitionContext.h create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/_ASTransitionContext.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/tvOS/ASControlNode+tvOS.mm create mode 100644 Starbucks_Clone_TexBrother/Pods/Texture/Source/tvOS/ASImageNode+tvOS.mm create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother.xcodeproj/project.pbxproj create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother.xcworkspace/contents.xcworkspacedata create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother.xcworkspace/xcshareddata/swiftpm/Package.resolved create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/Contents.json create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Base.lproj/LaunchScreen.storyboard create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Base.lproj/Main.storyboard create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Global/Customs/CustomNavigationBar.swift create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Screens/Base/BaseScrollNode.swift create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Screens/Pay/NodeControllers/PayNodeController.swift create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Screens/StarbucksTabBarController.swift create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Supports/AppDelegate.swift create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Supports/Info.plist create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Supports/SceneDelegate.swift create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/ViewController.swift diff --git a/BasicExercise/BasicExercise/Screens/KakaoTest/Controllers/KakaoFriendNodeController.swift b/BasicExercise/BasicExercise/Screens/KakaoTest/Controllers/KakaoFriendNodeController.swift index 2342fa7..3bfb334 100644 --- a/BasicExercise/BasicExercise/Screens/KakaoTest/Controllers/KakaoFriendNodeController.swift +++ b/BasicExercise/BasicExercise/Screens/KakaoTest/Controllers/KakaoFriendNodeController.swift @@ -96,10 +96,10 @@ extension KakaoFriendNodeController { private func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec { return ASInsetLayoutSpec( insets: UIEdgeInsets( - top: 56, - left: 14, - bottom: 0, - right: 14), + top: 56, + left: 14, + bottom: 0, + right: 14), child: subnodesStackLayoutSpec()) } diff --git a/BasicExercise/BasicExercise/Screens/KakaoTest/Models/KakaoFriendsModel.swift b/BasicExercise/BasicExercise/Screens/KakaoTest/Models/KakaoFriendsModel.swift index 13daac8..beae839 100644 --- a/BasicExercise/BasicExercise/Screens/KakaoTest/Models/KakaoFriendsModel.swift +++ b/BasicExercise/BasicExercise/Screens/KakaoTest/Models/KakaoFriendsModel.swift @@ -18,7 +18,7 @@ struct KakaoFriendsModel { profileNameText: String?, profileMessageText: String?) { - self.profileImageName = profileImageName ?? "freindtabProfileImg" + self.profileImageName = profileImageName ?? "friendtabProfileImg" self.profileNameText = profileNameText ?? "" self.profileMessageText = profileMessageText ?? "" } diff --git a/BasicExercise/BasicExercise/Support/SceneDelegate.swift b/BasicExercise/BasicExercise/Support/SceneDelegate.swift index 7c96dbf..8ecabde 100644 --- a/BasicExercise/BasicExercise/Support/SceneDelegate.swift +++ b/BasicExercise/BasicExercise/Support/SceneDelegate.swift @@ -16,8 +16,8 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { if let windowScene = scene as? UIWindowScene { let window = UIWindow(windowScene: windowScene) - let rootVC = KakaoFriendNodeController() - rootVC.view.backgroundColor = .white + let rootVC = AppleMusicNodeController() + rootVC.view.backgroundColor = .black window.rootViewController = rootVC self.window = window window.makeKeyAndVisible() diff --git a/Starbucks_Clone_TexBrother/Podfile b/Starbucks_Clone_TexBrother/Podfile new file mode 100644 index 0000000..5e96c57 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Podfile @@ -0,0 +1,10 @@ +# Uncomment the next line to define a global platform for your project +# platform :ios, '9.0' + +target 'Starbucks_Clone_TexBrother' do + # Comment the next line if you don't want to use dynamic frameworks + use_frameworks! + + # Pods for Starbucks_Clone_TexBrother + pod "Texture" +end diff --git a/Starbucks_Clone_TexBrother/Podfile.lock b/Starbucks_Clone_TexBrother/Podfile.lock new file mode 100644 index 0000000..99e6d71 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Podfile.lock @@ -0,0 +1,56 @@ +PODS: + - PINCache (3.0.3): + - PINCache/Arc-exception-safe (= 3.0.3) + - PINCache/Core (= 3.0.3) + - PINCache/Arc-exception-safe (3.0.3): + - PINCache/Core + - PINCache/Core (3.0.3): + - PINOperation (~> 1.2.1) + - PINOperation (1.2.1) + - PINRemoteImage/Core (3.0.3): + - PINOperation + - PINRemoteImage/iOS (3.0.3): + - PINRemoteImage/Core + - PINRemoteImage/PINCache (3.0.3): + - PINCache (~> 3.0.3) + - PINRemoteImage/Core + - Texture (3.0.0): + - Texture/AssetsLibrary (= 3.0.0) + - Texture/Core (= 3.0.0) + - Texture/MapKit (= 3.0.0) + - Texture/Photos (= 3.0.0) + - Texture/PINRemoteImage (= 3.0.0) + - Texture/Video (= 3.0.0) + - Texture/AssetsLibrary (3.0.0): + - Texture/Core + - Texture/Core (3.0.0) + - Texture/MapKit (3.0.0): + - Texture/Core + - Texture/Photos (3.0.0): + - Texture/Core + - Texture/PINRemoteImage (3.0.0): + - PINRemoteImage/iOS (~> 3.0.0) + - PINRemoteImage/PINCache + - Texture/Core + - Texture/Video (3.0.0): + - Texture/Core + +DEPENDENCIES: + - Texture + +SPEC REPOS: + trunk: + - PINCache + - PINOperation + - PINRemoteImage + - Texture + +SPEC CHECKSUMS: + PINCache: 7a8fc1a691173d21dbddbf86cd515de6efa55086 + PINOperation: 00c935935f1e8cf0d1e2d6b542e75b88fc3e5e20 + PINRemoteImage: f1295b29f8c5e640e25335a1b2bd9d805171bd01 + Texture: 2f109e937850d94d1d07232041c9c7313ccddb81 + +PODFILE CHECKSUM: c8a7f79c681b37a0828e8d0f5642236879111c53 + +COCOAPODS: 1.10.1 diff --git a/Starbucks_Clone_TexBrother/Pods/Manifest.lock b/Starbucks_Clone_TexBrother/Pods/Manifest.lock new file mode 100644 index 0000000..99e6d71 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Manifest.lock @@ -0,0 +1,56 @@ +PODS: + - PINCache (3.0.3): + - PINCache/Arc-exception-safe (= 3.0.3) + - PINCache/Core (= 3.0.3) + - PINCache/Arc-exception-safe (3.0.3): + - PINCache/Core + - PINCache/Core (3.0.3): + - PINOperation (~> 1.2.1) + - PINOperation (1.2.1) + - PINRemoteImage/Core (3.0.3): + - PINOperation + - PINRemoteImage/iOS (3.0.3): + - PINRemoteImage/Core + - PINRemoteImage/PINCache (3.0.3): + - PINCache (~> 3.0.3) + - PINRemoteImage/Core + - Texture (3.0.0): + - Texture/AssetsLibrary (= 3.0.0) + - Texture/Core (= 3.0.0) + - Texture/MapKit (= 3.0.0) + - Texture/Photos (= 3.0.0) + - Texture/PINRemoteImage (= 3.0.0) + - Texture/Video (= 3.0.0) + - Texture/AssetsLibrary (3.0.0): + - Texture/Core + - Texture/Core (3.0.0) + - Texture/MapKit (3.0.0): + - Texture/Core + - Texture/Photos (3.0.0): + - Texture/Core + - Texture/PINRemoteImage (3.0.0): + - PINRemoteImage/iOS (~> 3.0.0) + - PINRemoteImage/PINCache + - Texture/Core + - Texture/Video (3.0.0): + - Texture/Core + +DEPENDENCIES: + - Texture + +SPEC REPOS: + trunk: + - PINCache + - PINOperation + - PINRemoteImage + - Texture + +SPEC CHECKSUMS: + PINCache: 7a8fc1a691173d21dbddbf86cd515de6efa55086 + PINOperation: 00c935935f1e8cf0d1e2d6b542e75b88fc3e5e20 + PINRemoteImage: f1295b29f8c5e640e25335a1b2bd9d805171bd01 + Texture: 2f109e937850d94d1d07232041c9c7313ccddb81 + +PODFILE CHECKSUM: c8a7f79c681b37a0828e8d0f5642236879111c53 + +COCOAPODS: 1.10.1 diff --git a/Starbucks_Clone_TexBrother/Pods/PINCache/LICENSE.txt b/Starbucks_Clone_TexBrother/Pods/PINCache/LICENSE.txt new file mode 100644 index 0000000..3af296e --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINCache/LICENSE.txt @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [2013] [Tumblr, Inc.] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/Starbucks_Clone_TexBrother/Pods/PINCache/README.md b/Starbucks_Clone_TexBrother/Pods/PINCache/README.md new file mode 100644 index 0000000..aa81564 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINCache/README.md @@ -0,0 +1,98 @@ +# PINCache + +[![CocoaPods](https://img.shields.io/cocoapods/v/PINCache.svg)](http://cocoadocs.org/docsets/PINCache/) +[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) +[![Build status](https://github.com/pinterest/PINCache/workflows/CI/badge.svg)](https://github.com/pinterest/PINCache/actions?query=workflow%3ACI+branch%3Amaster) + +## Fast, non-deadlocking parallel object cache for iOS and OS X. + +[PINCache](Source/PINCache.h) is a fork of [TMCache](https://github.com/tumblr/TMCache) re-architected to fix issues with deadlocking caused by heavy use. It is a key/value store designed for persisting temporary objects that are expensive to reproduce, such as downloaded data or the results of slow processing. It is comprised of two self-similar stores, one in memory ([PINMemoryCache](Source/PINMemoryCache.h)) and one on disk ([PINDiskCache](Source/PINDiskCache.h)), all backed by GCD and safe to access from multiple threads simultaneously. On iOS, `PINMemoryCache` will clear itself when the app receives a memory warning or goes into the background. Objects stored in `PINDiskCache` remain until you trim the cache yourself, either manually or by setting a byte or age limit. + +`PINCache` and `PINDiskCache` accept any object conforming to [NSCoding](https://developer.apple.com/library/ios/#documentation/Cocoa/Reference/Foundation/Protocols/NSCoding_Protocol/Reference/Reference.html). Put things in like this: + +**Objective-C** +```objective-c +UIImage *img = [[UIImage alloc] initWithData:data scale:[[UIScreen mainScreen] scale]]; +[[PINCache sharedCache] setObject:img forKey:@"image" block:nil]; // returns immediately +``` +**Swift** +```swift +let img = UIImage(data: data, scale:UIScreen.main.scale) +PINCache.shared().setObject(img, forKey: "img") +``` + +Get them back out like this: + +**Objective-C** +```objc +[[PINCache sharedCache] objectForKeyAsync:@"image" block:^(PINCache *cache, NSString *key, id object) { + UIImage *image = (UIImage *)object; + NSLog(@"image scale: %f", image.scale); +}]; +``` +**Swift** +```swift +PINCache.shared().object(forKey: "image") { (cache, key, object) in + if let image = object as? UIImage { + print("image scale: %f", image.scale) + } +} +``` + +Both `PINMemoryCache` and `PINDiskCache` use locks to protect reads and writes. `PINCache` coordinates them so that objects added to memory are available immediately to other threads while being written to disk safely in the background. Both caches are public properties of `PINCache`, so it's easy to manipulate one or the other separately if necessary. + +Collections work too. Thanks to the magic of `NSKeyedArchiver`, objects repeated in a collection only occupy the space of one on disk: + +**Objective-C** +```objective-c +NSArray *images = @[ image, image, image ]; +[[PINCache sharedCache] setObject:images forKey:@"images"]; +NSLog(@"3 for the price of 1: %d", [[[PINCache sharedCache] diskCache] byteCount]); +``` +**Swift** +```swift +// In Swift, Array, String, and Dictionary are all value types. +let images = [image, image, image] as NSArray // Cast to NSArray +PINCache.shared.setObject(images, forKey: "images") +print("3 for the prices of 1: %d", PINCache.shared.diskCache.byteCount) +``` + +## Installation + +### Manually + +[Download the latest tag](https://github.com/pinterest/PINCache/tags) and drag the `PINCache` folder into your Xcode project. + +Install the docs by double clicking the `.docset` file under `docs/`, or view them online at [cocoadocs.org](http://cocoadocs.org/docsets/PINCache/) + +### Git Submodule + + git submodule add https://github.com/pinterest/PINCache.git + git submodule update --init + +### CocoaPods + +Add [PINCache](http://cocoapods.org/?q=name%3APINCache) to your `Podfile` and run `pod install`. + +### Carthage + +Add the following line to your `Cartfile` and run `carthage update --platform ios`. Then follow [this instruction of Carthage](https://github.com/carthage/carthage#adding-frameworks-to-unit-tests-or-a-framework) to embed the framework. + +```github "pinterest/PINCache"``` + +## Requirements + +__PINCache__ requires iOS 8.0, tvOS 9.0, watchOS 2.0 or macOS 10.8 and greater. + +## Contact + +[Garrett Moon](mailto:garrett@pinterest.com) + +## License + +Copyright 2013 Tumblr, Inc. +Copyright 2015 Pinterest, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. [See the License](LICENSE.txt) for the specific language governing permissions and limitations under the License. diff --git a/Starbucks_Clone_TexBrother/Pods/PINCache/Source/PINCache.h b/Starbucks_Clone_TexBrother/Pods/PINCache/Source/PINCache.h new file mode 100644 index 0000000..bfc9d85 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINCache/Source/PINCache.h @@ -0,0 +1,164 @@ +// PINCache is a modified version of TMCache +// Modifications by Garrett Moon +// Copyright (c) 2015 Pinterest. All rights reserved. + +#import + +#import "PINCacheMacros.h" +#import "PINCaching.h" +#import "PINDiskCache.h" +#import "PINMemoryCache.h" + +NS_ASSUME_NONNULL_BEGIN + +@class PINCache; + + +/** + `PINCache` is a thread safe key/value store designed for persisting temporary objects that are expensive to + reproduce, such as downloaded data or the results of slow processing. It is comprised of two self-similar + stores, one in memory () and one on disk (). + + `PINCache` itself actually does very little; its main function is providing a front end for a common use case: + a small, fast memory cache that asynchronously persists itself to a large, slow disk cache. When objects are + removed from the memory cache in response to an "apocalyptic" event they remain in the disk cache and are + repopulated in memory the next time they are accessed. `PINCache` also does the tedious work of creating a + dispatch group to wait for both caches to finish their operations without blocking each other. + + The parallel caches are accessible as public properties ( and ) and can be manipulated + separately if necessary. See the docs for and for more details. + + @warning when using in extension or watch extension, define PIN_APP_EXTENSIONS=1 + */ + +PIN_SUBCLASSING_RESTRICTED +@interface PINCache : NSObject + +#pragma mark - +/// @name Core + +/** + Synchronously retrieves the total byte count of the on the shared disk queue. + */ +@property (readonly) NSUInteger diskByteCount; + +/** + The underlying disk cache, see for additional configuration and trimming options. + */ +@property (readonly) PINDiskCache *diskCache; + +/** + The underlying memory cache, see for additional configuration and trimming options. + */ +@property (readonly) PINMemoryCache *memoryCache; + +#pragma mark - Lifecycle +/// @name Initialization + +/** + A shared cache. + + @result The shared singleton cache instance. + */ +@property (class, strong, readonly) PINCache *sharedCache; + +- (instancetype)init NS_UNAVAILABLE; + +/** + Multiple instances with the same name are *not* allowed and can *not* safely + access the same data on disk. Also used to create the . + + @see name + @param name The name of the cache. + @result A new cache with the specified name. + */ +- (instancetype)initWithName:(nonnull NSString *)name; + +/** + Multiple instances with the same name are *not* allowed and can *not* safely + access the same data on disk. Also used to create the . + + @see name + @param name The name of the cache. + @param rootPath The path of the cache on disk. + @result A new cache with the specified name. + */ +- (instancetype)initWithName:(nonnull NSString *)name rootPath:(nonnull NSString *)rootPath; + +/** + Multiple instances with the same name are *not* allowed and can *not* safely + access the same data on disk.. Also used to create the . + Initializer allows you to override default NSKeyedArchiver/NSKeyedUnarchiver serialization for . + You must provide both serializer and deserializer, or opt-out to default implementation providing nil values. + + @see name + @param name The name of the cache. + @param rootPath The path of the cache on disk. + @param serializer A block used to serialize object before writing to disk. If nil provided, default NSKeyedArchiver serialized will be used. + @param deserializer A block used to deserialize object read from disk. If nil provided, default NSKeyedUnarchiver serialized will be used. + @result A new cache with the specified name. + */ +- (instancetype)initWithName:(NSString *)name + rootPath:(NSString *)rootPath + serializer:(nullable PINDiskCacheSerializerBlock)serializer + deserializer:(nullable PINDiskCacheDeserializerBlock)deserializer; + +/** + Multiple instances with the same name are *not* allowed and can *not* safely + access the same data on disk. Also used to create the . + Initializer allows you to override default NSKeyedArchiver/NSKeyedUnarchiver serialization for . + You must provide both serializer and deserializer, or opt-out to default implementation providing nil values. + + @see name + @param name The name of the cache. + @param rootPath The path of the cache on disk. + @param serializer A block used to serialize object before writing to disk. If nil provided, default NSKeyedArchiver serialized will be used. + @param deserializer A block used to deserialize object read from disk. If nil provided, default NSKeyedUnarchiver serialized will be used. + @param keyEncoder A block used to encode key(filename). If nil provided, default url encoder will be used + @param keyDecoder A block used to decode key(filename). If nil provided, default url decoder will be used + @result A new cache with the specified name. + */ +- (instancetype)initWithName:(nonnull NSString *)name + rootPath:(nonnull NSString *)rootPath + serializer:(nullable PINDiskCacheSerializerBlock)serializer + deserializer:(nullable PINDiskCacheDeserializerBlock)deserializer + keyEncoder:(nullable PINDiskCacheKeyEncoderBlock)keyEncoder + keyDecoder:(nullable PINDiskCacheKeyDecoderBlock)keyDecoder; + +/** + Multiple instances with the same name are *not* allowed and can *not* safely + access the same data on disk. Also used to create the . + Initializer allows you to override default NSKeyedArchiver/NSKeyedUnarchiver serialization for . + You must provide both serializer and deserializer, or opt-out to default implementation providing nil values. + + @see name + @param name The name of the cache. + @param rootPath The path of the cache on disk. + @param serializer A block used to serialize object before writing to disk. If nil provided, default NSKeyedArchiver serialized will be used. + @param deserializer A block used to deserialize object read from disk. If nil provided, default NSKeyedUnarchiver serialized will be used. + @param keyEncoder A block used to encode key(filename). If nil provided, default url encoder will be used + @param keyDecoder A block used to decode key(filename). If nil provided, default url decoder will be used + @param ttlCache Whether or not the cache should behave as a TTL cache. + @result A new cache with the specified name. + */ +- (instancetype)initWithName:(nonnull NSString *)name + rootPath:(nonnull NSString *)rootPath + serializer:(nullable PINDiskCacheSerializerBlock)serializer + deserializer:(nullable PINDiskCacheDeserializerBlock)deserializer + keyEncoder:(nullable PINDiskCacheKeyEncoderBlock)keyEncoder + keyDecoder:(nullable PINDiskCacheKeyDecoderBlock)keyDecoder + ttlCache:(BOOL)ttlCache NS_DESIGNATED_INITIALIZER; + +@end + +@interface PINCache (Deprecated) +- (void)containsObjectForKey:(NSString *)key block:(PINCacheObjectContainmentBlock)block __attribute__((deprecated)); +- (void)objectForKey:(NSString *)key block:(PINCacheObjectBlock)block __attribute__((deprecated)); +- (void)setObject:(id )object forKey:(NSString *)key block:(nullable PINCacheObjectBlock)block __attribute__((deprecated)); +- (void)setObject:(id )object forKey:(NSString *)key withCost:(NSUInteger)cost block:(nullable PINCacheObjectBlock)block __attribute__((deprecated)); +- (void)removeObjectForKey:(NSString *)key block:(nullable PINCacheObjectBlock)block __attribute__((deprecated)); +- (void)trimToDate:(NSDate *)date block:(nullable PINCacheBlock)block __attribute__((deprecated)); +- (void)removeAllObjects:(nullable PINCacheBlock)block __attribute__((deprecated)); +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/PINCache/Source/PINCache.m b/Starbucks_Clone_TexBrother/Pods/PINCache/Source/PINCache.m new file mode 100644 index 0000000..b52f1f6 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINCache/Source/PINCache.m @@ -0,0 +1,420 @@ +// PINCache is a modified version of PINCache +// Modifications by Garrett Moon +// Copyright (c) 2015 Pinterest. All rights reserved. + +#import "PINCache.h" + +#if SWIFT_PACKAGE +@import PINOperation; +#else +#import +#endif + +static NSString * const PINCachePrefix = @"com.pinterest.PINCache"; +static NSString * const PINCacheSharedName = @"PINCacheShared"; + +@interface PINCache () +@property (copy, nonatomic) NSString *name; +@property (strong, nonatomic) PINOperationQueue *operationQueue; +@end + +@implementation PINCache + +#pragma mark - Initialization - + +- (instancetype)init +{ + @throw [NSException exceptionWithName:@"Must initialize with a name" reason:@"PINCache must be initialized with a name. Call initWithName: instead." userInfo:nil]; + return [self initWithName:@""]; +} + +- (instancetype)initWithName:(NSString *)name +{ + return [self initWithName:name rootPath:[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject]]; +} + +- (instancetype)initWithName:(NSString *)name rootPath:(NSString *)rootPath +{ + return [self initWithName:name rootPath:rootPath serializer:nil deserializer:nil]; +} + +- (instancetype)initWithName:(NSString *)name rootPath:(NSString *)rootPath serializer:(PINDiskCacheSerializerBlock)serializer deserializer:(PINDiskCacheDeserializerBlock)deserializer { + return [self initWithName:name rootPath:rootPath serializer:serializer deserializer:deserializer keyEncoder:nil keyDecoder:nil]; +} + +- (instancetype)initWithName:(NSString *)name + rootPath:(NSString *)rootPath + serializer:(PINDiskCacheSerializerBlock)serializer + deserializer:(PINDiskCacheDeserializerBlock)deserializer + keyEncoder:(PINDiskCacheKeyEncoderBlock)keyEncoder + keyDecoder:(PINDiskCacheKeyDecoderBlock)keyDecoder +{ + return [self initWithName:name rootPath:rootPath serializer:serializer deserializer:deserializer keyEncoder:keyEncoder keyDecoder:keyDecoder ttlCache:NO]; +} + +- (instancetype)initWithName:(NSString *)name + rootPath:(NSString *)rootPath + serializer:(PINDiskCacheSerializerBlock)serializer + deserializer:(PINDiskCacheDeserializerBlock)deserializer + keyEncoder:(PINDiskCacheKeyEncoderBlock)keyEncoder + keyDecoder:(PINDiskCacheKeyDecoderBlock)keyDecoder + ttlCache:(BOOL)ttlCache +{ + if (!name) + return nil; + + if (self = [super init]) { + _name = [name copy]; + + //10 may actually be a bit high, but currently much of our threads are blocked on empyting the trash. Until we can resolve that, lets bump this up. + _operationQueue = [[PINOperationQueue alloc] initWithMaxConcurrentOperations:10]; + _diskCache = [[PINDiskCache alloc] initWithName:_name + prefix:PINDiskCachePrefix + rootPath:rootPath + serializer:serializer + deserializer:deserializer + keyEncoder:keyEncoder + keyDecoder:keyDecoder + operationQueue:_operationQueue + ttlCache:ttlCache]; + _memoryCache = [[PINMemoryCache alloc] initWithName:_name operationQueue:_operationQueue ttlCache:ttlCache]; + } + return self; +} + +- (NSString *)description +{ + return [[NSString alloc] initWithFormat:@"%@.%@.%p", PINCachePrefix, _name, (void *)self]; +} + ++ (PINCache *)sharedCache +{ + static PINCache *cache; + static dispatch_once_t predicate; + + dispatch_once(&predicate, ^{ + cache = [[PINCache alloc] initWithName:PINCacheSharedName]; + }); + + return cache; +} + +#pragma mark - Public Asynchronous Methods - + +- (void)containsObjectForKeyAsync:(NSString *)key completion:(PINCacheObjectContainmentBlock)block +{ + if (!key || !block) { + return; + } + + [self.operationQueue scheduleOperation:^{ + BOOL containsObject = [self containsObjectForKey:key]; + block(containsObject); + }]; +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wshadow" + +- (void)objectForKeyAsync:(NSString *)key completion:(PINCacheObjectBlock)block +{ + if (!key || !block) + return; + + [self.operationQueue scheduleOperation:^{ + [self->_memoryCache objectForKeyAsync:key completion:^(id memoryCache, NSString *memoryCacheKey, id memoryCacheObject) { + if (memoryCacheObject) { + // Update file modification date. TODO: make this a separate method? + [self->_diskCache fileURLForKeyAsync:memoryCacheKey completion:^(NSString * _Nonnull key, NSURL * _Nullable fileURL) {}]; + [self->_operationQueue scheduleOperation:^{ + block(self, memoryCacheKey, memoryCacheObject); + }]; + } else { + [self->_diskCache objectForKeyAsync:memoryCacheKey completion:^(PINDiskCache *diskCache, NSString *diskCacheKey, id diskCacheObject) { + + [self->_memoryCache setObjectAsync:diskCacheObject forKey:diskCacheKey completion:nil]; + + [self->_operationQueue scheduleOperation:^{ + block(self, diskCacheKey, diskCacheObject); + }]; + }]; + } + }]; + }]; +} + +#pragma clang diagnostic pop + +- (void)setObjectAsync:(id )object forKey:(NSString *)key completion:(PINCacheObjectBlock)block +{ + [self setObjectAsync:object forKey:key withCost:0 completion:block]; +} + +- (void)setObjectAsync:(id )object forKey:(NSString *)key withAgeLimit:(NSTimeInterval)ageLimit completion:(PINCacheObjectBlock)block +{ + [self setObjectAsync:object forKey:key withCost:0 ageLimit:ageLimit completion:block]; +} + +- (void)setObjectAsync:(id )object forKey:(NSString *)key withCost:(NSUInteger)cost completion:(PINCacheObjectBlock)block +{ + [self setObjectAsync:object forKey:key withCost:cost ageLimit:0.0 completion:block]; +} + +- (void)setObjectAsync:(nonnull id)object forKey:(nonnull NSString *)key withCost:(NSUInteger)cost ageLimit:(NSTimeInterval)ageLimit completion:(nullable PINCacheObjectBlock)block +{ + if (!key || !object) + return; + + PINOperationGroup *group = [PINOperationGroup asyncOperationGroupWithQueue:_operationQueue]; + + [group addOperation:^{ + [self->_memoryCache setObject:object forKey:key withCost:cost ageLimit:ageLimit]; + }]; + [group addOperation:^{ + [self->_diskCache setObject:object forKey:key withAgeLimit:ageLimit]; + }]; + + if (block) { + [group setCompletion:^{ + block(self, key, object); + }]; + } + + [group start]; +} + +- (void)removeObjectForKeyAsync:(NSString *)key completion:(PINCacheObjectBlock)block +{ + if (!key) + return; + + PINOperationGroup *group = [PINOperationGroup asyncOperationGroupWithQueue:_operationQueue]; + + [group addOperation:^{ + [self->_memoryCache removeObjectForKey:key]; + }]; + [group addOperation:^{ + [self->_diskCache removeObjectForKey:key]; + }]; + + if (block) { + [group setCompletion:^{ + block(self, key, nil); + }]; + } + + [group start]; +} + +- (void)removeAllObjectsAsync:(PINCacheBlock)block +{ + PINOperationGroup *group = [PINOperationGroup asyncOperationGroupWithQueue:_operationQueue]; + + [group addOperation:^{ + [self->_memoryCache removeAllObjects]; + }]; + [group addOperation:^{ + [self->_diskCache removeAllObjects]; + }]; + + if (block) { + [group setCompletion:^{ + block(self); + }]; + } + + [group start]; +} + +- (void)trimToDateAsync:(NSDate *)date completion:(PINCacheBlock)block +{ + if (!date) + return; + + PINOperationGroup *group = [PINOperationGroup asyncOperationGroupWithQueue:_operationQueue]; + + [group addOperation:^{ + [self->_memoryCache trimToDate:date]; + }]; + [group addOperation:^{ + [self->_diskCache trimToDate:date]; + }]; + + if (block) { + [group setCompletion:^{ + block(self); + }]; + } + + [group start]; +} + +- (void)removeExpiredObjectsAsync:(PINCacheBlock)block +{ + PINOperationGroup *group = [PINOperationGroup asyncOperationGroupWithQueue:_operationQueue]; + + [group addOperation:^{ + [self->_memoryCache removeExpiredObjects]; + }]; + [group addOperation:^{ + [self->_diskCache removeExpiredObjects]; + }]; + + if (block) { + [group setCompletion:^{ + block(self); + }]; + } + + [group start]; +} + +#pragma mark - Public Synchronous Accessors - + +- (NSUInteger)diskByteCount +{ + __block NSUInteger byteCount = 0; + + [_diskCache synchronouslyLockFileAccessWhileExecutingBlock:^(id diskCache) { + byteCount = ((PINDiskCache *)diskCache).byteCount; + }]; + + return byteCount; +} + +- (BOOL)containsObjectForKey:(NSString *)key +{ + if (!key) + return NO; + + return [_memoryCache containsObjectForKey:key] || [_diskCache containsObjectForKey:key]; +} + +- (nullable id)objectForKey:(NSString *)key +{ + if (!key) + return nil; + + __block id object = nil; + + object = [_memoryCache objectForKey:key]; + + if (object) { + // Update file modification date. TODO: make this a separate method? + [_diskCache fileURLForKeyAsync:key completion:^(NSString * _Nonnull key, NSURL * _Nullable fileURL) {}]; + } else { + object = [_diskCache objectForKey:key]; + [_memoryCache setObject:object forKey:key]; + } + + return object; +} + +- (void)setObject:(id )object forKey:(NSString *)key +{ + [self setObject:object forKey:key withCost:0]; +} + +- (void)setObject:(id )object forKey:(NSString *)key withAgeLimit:(NSTimeInterval)ageLimit +{ + [self setObject:object forKey:key withCost:0 ageLimit:ageLimit]; +} + +- (void)setObject:(id )object forKey:(NSString *)key withCost:(NSUInteger)cost +{ + [self setObject:object forKey:key withCost:cost ageLimit:0.0]; +} + +- (void)setObject:(nullable id)object forKey:(nonnull NSString *)key withCost:(NSUInteger)cost ageLimit:(NSTimeInterval)ageLimit +{ + if (!key || !object) + return; + + [_memoryCache setObject:object forKey:key withCost:cost ageLimit:ageLimit]; + [_diskCache setObject:object forKey:key withAgeLimit:ageLimit]; +} + +- (nullable id)objectForKeyedSubscript:(NSString *)key +{ + return [self objectForKey:key]; +} + +- (void)setObject:(nullable id)obj forKeyedSubscript:(NSString *)key +{ + if (obj == nil) { + [self removeObjectForKey:key]; + } else { + [self setObject:obj forKey:key]; + } +} + +- (void)removeObjectForKey:(NSString *)key +{ + if (!key) + return; + + [_memoryCache removeObjectForKey:key]; + [_diskCache removeObjectForKey:key]; +} + +- (void)trimToDate:(NSDate *)date +{ + if (!date) + return; + + [_memoryCache trimToDate:date]; + [_diskCache trimToDate:date]; +} + +- (void)removeExpiredObjects +{ + [_memoryCache removeExpiredObjects]; + [_diskCache removeExpiredObjects]; +} + +- (void)removeAllObjects +{ + [_memoryCache removeAllObjects]; + [_diskCache removeAllObjects]; +} + +@end + +@implementation PINCache (Deprecated) + +- (void)containsObjectForKey:(NSString *)key block:(PINCacheObjectContainmentBlock)block +{ + [self containsObjectForKeyAsync:key completion:block]; +} + +- (void)objectForKey:(NSString *)key block:(PINCacheObjectBlock)block +{ + [self objectForKeyAsync:key completion:block]; +} + +- (void)setObject:(id )object forKey:(NSString *)key block:(nullable PINCacheObjectBlock)block +{ + [self setObjectAsync:object forKey:key completion:block]; +} + +- (void)setObject:(id )object forKey:(NSString *)key withCost:(NSUInteger)cost block:(nullable PINCacheObjectBlock)block +{ + [self setObjectAsync:object forKey:key withCost:cost completion:block]; +} + +- (void)removeObjectForKey:(NSString *)key block:(nullable PINCacheObjectBlock)block +{ + [self removeObjectForKeyAsync:key completion:block]; +} + +- (void)trimToDate:(NSDate *)date block:(nullable PINCacheBlock)block +{ + [self trimToDateAsync:date completion:block]; +} + +- (void)removeAllObjects:(nullable PINCacheBlock)block +{ + [self removeAllObjectsAsync:block]; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINCache/Source/PINCacheMacros.h b/Starbucks_Clone_TexBrother/Pods/PINCache/Source/PINCacheMacros.h new file mode 100644 index 0000000..e7179a1 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINCache/Source/PINCacheMacros.h @@ -0,0 +1,23 @@ +// +// PINCacheMacros.h +// PINCache +// +// Created by Adlai Holler on 1/31/17. +// Copyright © 2017 Pinterest. All rights reserved. +// + +#ifndef PIN_SUBCLASSING_RESTRICTED +#if defined(__has_attribute) && __has_attribute(objc_subclassing_restricted) +#define PIN_SUBCLASSING_RESTRICTED __attribute__((objc_subclassing_restricted)) +#else +#define PIN_SUBCLASSING_RESTRICTED +#endif // #if defined(__has_attribute) && __has_attribute(objc_subclassing_restricted) +#endif // #ifndef PIN_SUBCLASSING_RESTRICTED + +#ifndef PIN_NOESCAPE +#if defined(__has_attribute) && __has_attribute(noescape) +#define PIN_NOESCAPE __attribute__((noescape)) +#else +#define PIN_NOESCAPE +#endif // #if defined(__has_attribute) && __has_attribute(noescape) +#endif // #ifndef PIN_NOESCAPE diff --git a/Starbucks_Clone_TexBrother/Pods/PINCache/Source/PINCacheObjectSubscripting.h b/Starbucks_Clone_TexBrother/Pods/PINCache/Source/PINCacheObjectSubscripting.h new file mode 100644 index 0000000..a7bd20b --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINCache/Source/PINCacheObjectSubscripting.h @@ -0,0 +1,35 @@ +// +// PINCacheObjectSubscripting.h +// PINCache +// +// Created by Rocir Marcos Leite Santiago on 4/2/16. +// Copyright © 2016 Pinterest. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@protocol PINCacheObjectSubscripting + +@required + +/** + This method enables using literals on the receiving object, such as `id object = cache[@"key"];`. + + @param key The key associated with the object. + @result The object for the specified key. + */ +- (nullable id)objectForKeyedSubscript:(NSString *)key; + +/** + This method enables using literals on the receiving object, such as `cache[@"key"] = object;`. + + @param object An object to be assigned for the key. Pass `nil` to remove the existing object for this key. + @param key A key to associate with the object. This string will be copied. + */ +- (void)setObject:(nullable id)object forKeyedSubscript:(NSString *)key; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/PINCache/Source/PINCaching.h b/Starbucks_Clone_TexBrother/Pods/PINCache/Source/PINCaching.h new file mode 100644 index 0000000..326cd45 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINCache/Source/PINCaching.h @@ -0,0 +1,264 @@ +// +// PINCaching.h +// PINCache +// +// Created by Michael Schneider on 1/31/17. +// Copyright © 2017 Pinterest. All rights reserved. +// + +#pragma once +#import + + +NS_ASSUME_NONNULL_BEGIN + +@protocol PINCaching; + +/** + A callback block which provides only the cache as an argument + */ +typedef void (^PINCacheBlock)(__kindof id cache); + +/** + A callback block which provides the cache, key and object as arguments + */ +typedef void (^PINCacheObjectBlock)(__kindof id cache, NSString *key, id _Nullable object); + +/** + A callback block used for enumeration which provides the cache, key and object as arguments plus a stop flag that + may be flipped by the caller. + */ +typedef void (^PINCacheObjectEnumerationBlock)(__kindof id cache, NSString *key, id _Nullable object, BOOL *stop); + +/** + A callback block which provides a BOOL value as argument + */ +typedef void (^PINCacheObjectContainmentBlock)(BOOL containsObject); + +@protocol PINCaching + +#pragma mark - Core + +/** + The name of this cache, used to create a directory under Library/Caches and also appearing in stack traces. + */ +@property (readonly) NSString *name; + +#pragma mark - Asynchronous Methods + +/// @name Asynchronous Methods + +/** + This method determines whether an object is present for the given key in the cache. This method returns immediately + and executes the passed block after the object is available, potentially in parallel with other blocks on the + . + + @see containsObjectForKey: + @param key The key associated with the object. + @param block A block to be executed concurrently after the containment check happened + */ +- (void)containsObjectForKeyAsync:(NSString *)key completion:(PINCacheObjectContainmentBlock)block; + +/** + Retrieves the object for the specified key. This method returns immediately and executes the passed + block after the object is available, potentially in parallel with other blocks on the . + + @param key The key associated with the requested object. + @param block A block to be executed concurrently when the object is available. + */ +- (void)objectForKeyAsync:(NSString *)key completion:(PINCacheObjectBlock)block; + +/** + Stores an object in the cache for the specified key. This method returns immediately and executes the + passed block after the object has been stored, potentially in parallel with other blocks on the . + + @param object An object to store in the cache. + @param key A key to associate with the object. This string will be copied. + @param block A block to be executed concurrently after the object has been stored, or nil. + */ +- (void)setObjectAsync:(id)object forKey:(NSString *)key completion:(nullable PINCacheObjectBlock)block; + +/** + Stores an object in the cache for the specified key and the specified age limit. This method returns immediately + and executes the passed block after the object has been stored, potentially in parallel with other blocks + on the . + + @param object An object to store in the cache. + @param key A key to associate with the object. This string will be copied. + @param ageLimit The age limit (in seconds) to associate with the object. An age limit <= 0 means there is no object-level age limit and the + cache-level TTL will be used for this object. + @param block A block to be executed concurrently after the object has been stored, or nil. + */ +- (void)setObjectAsync:(id)object forKey:(NSString *)key withAgeLimit:(NSTimeInterval)ageLimit completion:(nullable PINCacheObjectBlock)block; + +/** + Stores an object in the cache for the specified key and the specified memory cost. If the cost causes the total + to go over the the cache is trimmed (oldest objects first). This method returns immediately + and executes the passed block after the object has been stored, potentially in parallel with other blocks + on the . + + @param object An object to store in the cache. + @param key A key to associate with the object. This string will be copied. + @param cost An amount to add to the . + @param block A block to be executed concurrently after the object has been stored, or nil. + */ +- (void)setObjectAsync:(id)object forKey:(NSString *)key withCost:(NSUInteger)cost completion:(nullable PINCacheObjectBlock)block; + +/** + Stores an object in the cache for the specified key and the specified memory cost and age limit. If the cost causes the total + to go over the the cache is trimmed (oldest objects first). This method returns immediately + and executes the passed block after the object has been stored, potentially in parallel with other blocks + on the . + + @param object An object to store in the cache. + @param key A key to associate with the object. This string will be copied. + @param cost An amount to add to the . + @param ageLimit The age limit (in seconds) to associate with the object. An age limit <= 0 means there is no object-level age limit and the cache-level TTL will be + used for this object. + @param block A block to be executed concurrently after the object has been stored, or nil. + */ +- (void)setObjectAsync:(id)object forKey:(NSString *)key withCost:(NSUInteger)cost ageLimit:(NSTimeInterval)ageLimit completion:(nullable PINCacheObjectBlock)block; + +/** + Removes the object for the specified key. This method returns immediately and executes the passed + block after the object has been removed, potentially in parallel with other blocks on the . + + @param key The key associated with the object to be removed. + @param block A block to be executed concurrently after the object has been removed, or nil. + */ +- (void)removeObjectForKeyAsync:(NSString *)key completion:(nullable PINCacheObjectBlock)block; + +/** + Removes all objects from the cache that have not been used since the specified date. This method returns immediately and + executes the passed block after the cache has been trimmed, potentially in parallel with other blocks on the . + + @param date Objects that haven't been accessed since this date are removed from the cache. + @param block A block to be executed concurrently after the cache has been trimmed, or nil. + */ +- (void)trimToDateAsync:(NSDate *)date completion:(nullable PINCacheBlock)block; + +/** + Removes all expired objects from the cache. This includes objects that are considered expired due to the cache-level ageLimit + as well as object-level ageLimits. This method returns immediately and executes the passed block after the objects have been removed, + potentially in parallel with other blocks on the . + + @param block A block to be executed concurrently after the objects have been removed, or nil. + */ +- (void)removeExpiredObjectsAsync:(nullable PINCacheBlock)block; + +/** + Removes all objects from the cache.This method returns immediately and executes the passed block after the + cache has been cleared, potentially in parallel with other blocks on the . + + @param block A block to be executed concurrently after the cache has been cleared, or nil. + */ +- (void)removeAllObjectsAsync:(nullable PINCacheBlock)block; + + +#pragma mark - Synchronous Methods +/// @name Synchronous Methods + +/** + This method determines whether an object is present for the given key in the cache. + + @see containsObjectForKeyAsync:completion: + @param key The key associated with the object. + @result YES if an object is present for the given key in the cache, otherwise NO. + */ +- (BOOL)containsObjectForKey:(NSString *)key; + +/** + Retrieves the object for the specified key. This method blocks the calling thread until the object is available. + Uses a lock to achieve synchronicity on the disk cache. + + @see objectForKeyAsync:completion: + @param key The key associated with the object. + @result The object for the specified key. + */ +- (nullable id)objectForKey:(NSString *)key; + +/** + Stores an object in the cache for the specified key. This method blocks the calling thread until the object has been set. + Uses a lock to achieve synchronicity on the disk cache. + + @see setObjectAsync:forKey:completion: + @param object An object to store in the cache. + @param key A key to associate with the object. This string will be copied. + */ +- (void)setObject:(nullable id)object forKey:(NSString *)key; + +/** + Stores an object in the cache for the specified key and age limit. This method blocks the calling thread until the + object has been set. Uses a lock to achieve synchronicity on the disk cache. + + @see setObjectAsync:forKey:completion: + @param object An object to store in the cache. + @param key A key to associate with the object. This string will be copied. + @param ageLimit The age limit (in seconds) to associate with the object. An age limit <= 0 means there is no + object-level age limit and the cache-level TTL will be used for this object. + */ +- (void)setObject:(nullable id)object forKey:(NSString *)key withAgeLimit:(NSTimeInterval)ageLimit; + +/** + Stores an object in the cache for the specified key and the specified memory cost. If the cost causes the total + to go over the the cache is trimmed (oldest objects first). This method blocks the calling thread + until the object has been stored. + + @param object An object to store in the cache. + @param key A key to associate with the object. This string will be copied. + @param cost An amount to add to the . + */ +- (void)setObject:(nullable id)object forKey:(NSString *)key withCost:(NSUInteger)cost; + +/** + Stores an object in the cache for the specified key and the specified memory cost and age limit. If the cost causes the total + to go over the the cache is trimmed (oldest objects first). This method blocks the calling thread + until the object has been stored. + + @param object An object to store in the cache. + @param key A key to associate with the object. This string will be copied. + @param cost An amount to add to the . + @param ageLimit The age limit (in seconds) to associate with the object. An age limit <= 0 means there is no object-level age + limit and the cache-level TTL will be used for this object. + */ +- (void)setObject:(nullable id)object forKey:(NSString *)key withCost:(NSUInteger)cost ageLimit:(NSTimeInterval)ageLimit; + +/** + Removes the object for the specified key. This method blocks the calling thread until the object + has been removed. + Uses a lock to achieve synchronicity on the disk cache. + + @see removeObjectForKeyAsync:completion: + @param key The key associated with the object to be removed. + */ +- (void)removeObjectForKey:(NSString *)key; + +/** + Removes all objects from the cache that have not been used since the specified date. + This method blocks the calling thread until the cache has been trimmed. + Uses a lock to achieve synchronicity on the disk cache. + + @see trimToDateAsync:completion: + @param date Objects that haven't been accessed since this date are removed from the cache. + */ +- (void)trimToDate:(NSDate *)date; + +/** + Removes all expired objects from the cache. This includes objects that are considered expired due to the cache-level ageLimit + as well as object-level ageLimits. This method blocks the calling thread until the objects have been removed. + Uses a lock to achieve synchronicity on the disk cache. + */ +- (void)removeExpiredObjects; + +/** + Removes all objects from the cache. This method blocks the calling thread until the cache has been cleared. + Uses a lock to achieve synchronicity on the disk cache. + + @see removeAllObjectsAsync: + */ +- (void)removeAllObjects; + +@end + +NS_ASSUME_NONNULL_END + diff --git a/Starbucks_Clone_TexBrother/Pods/PINCache/Source/PINDiskCache.h b/Starbucks_Clone_TexBrother/Pods/PINCache/Source/PINDiskCache.h new file mode 100644 index 0000000..81bb26a --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINCache/Source/PINDiskCache.h @@ -0,0 +1,592 @@ +// PINCache is a modified version of TMCache +// Modifications by Garrett Moon +// Copyright (c) 2015 Pinterest. All rights reserved. + +#import + +#import "PINCacheMacros.h" +#import "PINCaching.h" +#import "PINCacheObjectSubscripting.h" + +NS_ASSUME_NONNULL_BEGIN + +@class PINDiskCache; +@class PINOperationQueue; + +extern NSString * const PINDiskCacheErrorDomain; +extern NSErrorUserInfoKey const PINDiskCacheErrorReadFailureCodeKey; +extern NSErrorUserInfoKey const PINDiskCacheErrorWriteFailureCodeKey; +extern NSString * const PINDiskCachePrefix; + +typedef NS_ENUM(NSInteger, PINDiskCacheError) { + PINDiskCacheErrorReadFailure = -1000, + PINDiskCacheErrorWriteFailure = -1001, +}; + +/** + A callback block which provides the cache, key and object as arguments + */ +typedef void (^PINDiskCacheObjectBlock)(PINDiskCache *cache, NSString *key, id _Nullable object); + +/** + A callback block which provides the key and fileURL of the object + */ +typedef void (^PINDiskCacheFileURLBlock)(NSString *key, NSURL * _Nullable fileURL); + +/** + A callback block used for enumeration which provides the key and fileURL of the object plus a stop flag that + may be flipped by the caller. + */ +typedef void (^PINDiskCacheFileURLEnumerationBlock)(NSString *key, NSURL * _Nullable fileURL, BOOL *stop); + +/** + A callback block which provides a BOOL value as argument + */ +typedef void (^PINDiskCacheContainsBlock)(BOOL containsObject); + +/** + * A block used to serialize object before writing to disk + * + * @param object Object to serialize + * @param key The key associated with the object + * + * @return Serialized object representation + */ +typedef NSData* _Nonnull(^PINDiskCacheSerializerBlock)(id object, NSString *key); + +/** + * A block used to deserialize objects + * + * @param data Serialized object data + * @param key The key associated with the object + * + * @return Deserialized object + */ +typedef id _Nonnull(^PINDiskCacheDeserializerBlock)(NSData* data, NSString *key); + +/** + * A block used to encode keys + * + * @param decodedKey Original/decoded key + * + * @return encoded key + */ +typedef NSString *_Nonnull(^PINDiskCacheKeyEncoderBlock)(NSString *decodedKey); + +/** + * A block used to decode keys + * + * @param encodedKey An encoded key + * + * @return decoded key + */ +typedef NSString *_Nonnull(^PINDiskCacheKeyDecoderBlock)(NSString *encodedKey); + + +/** + `PINDiskCache` is a thread safe key/value store backed by the file system. It accepts any object conforming + to the `NSCoding` protocol, which includes the basic Foundation data types and collection classes and also + many UIKit classes, notably `UIImage`. All work is performed on a serial queue shared by all instances in + the app, and archiving is handled by `NSKeyedArchiver`. This is a particular advantage for `UIImage` because + it skips `UIImagePNGRepresentation()` and retains information like scale and orientation. + + The designated initializer for `PINDiskCache` is . The string is used to create a directory + under Library/Caches that scopes disk access for this instance. Multiple instances with the same name are *not* + allowed as they would conflict with each other. + + Unless otherwise noted, all properties and methods are safe to access from any thread at any time. All blocks + will cause the queue to wait, making it safe to access and manipulate the actual cache files on disk for the + duration of the block. + + Because this cache is bound by disk I/O it can be much slower than , although values stored in + `PINDiskCache` persist after application relaunch. Using is recommended over using `PINDiskCache` + by itself, as it adds a fast layer of additional memory caching while still writing to disk. + + All access to the cache is dated so the that the least-used objects can be trimmed first. Setting an optional + will trigger a GCD timer to periodically to trim the cache with . + */ + +PIN_SUBCLASSING_RESTRICTED +@interface PINDiskCache : NSObject + +#pragma mark - Class + +/** + @param rootPath The path for where the cache should be stored. + @param prefix The prefix for the cache name. + @param name The name of the cache. + @result The full URL of the cache. + */ ++ (NSURL *)cacheURLWithRootPath:(NSString *)rootPath prefix:(NSString *)prefix name:(NSString *)name; + +#pragma mark - Properties +/// @name Core + +/** + The prefix to the name of this cache, used to create a directory under Library/Caches and also appearing in stack traces. + */ +@property (readonly) NSString *prefix; + +/** + The URL of the directory used by this cache, usually `Library/Caches/com.pinterest.PINDiskCache.(name)` + + @warning Do not interact with files under this URL except in or + . + */ +@property (readonly) NSURL *cacheURL; + +/** + The total number of bytes used on disk, as reported by `NSURLTotalFileAllocatedSizeKey`. + + @warning This property should only be read from a call to or + its asynchronous equivalent + + For example: + + // some background thread + + __block NSUInteger byteCount = 0; + + [_diskCache synchronouslyLockFileAccessWhileExecutingBlock:^(PINDiskCache *diskCache) { + byteCount = diskCache.byteCount; + }]; + */ +@property (readonly) NSUInteger byteCount; + +/** + The maximum number of bytes allowed on disk. This value is checked every time an object is set, if the written + size exceeds the limit a trim call is queued. Defaults to 50MB. + + */ +@property (assign) NSUInteger byteLimit; + +/** + The maximum number of seconds an object is allowed to exist in the cache. Setting this to a value + greater than `0.0` will start a recurring GCD timer with the same period that calls . + Setting it back to `0.0` will stop the timer. Defaults to 30 days. + + */ +@property (assign) NSTimeInterval ageLimit; + +/** + The writing protection option used when writing a file on disk. This value is used every time an object is set. + NSDataWritingAtomic and NSDataWritingWithoutOverwriting are ignored if set + Defaults to NSDataWritingFileProtectionNone. + + @warning Only new files are affected by the new writing protection. If you need all files to be affected, + you'll have to purge and set the objects back to the cache + + Only available on iOS + */ +#if TARGET_OS_IPHONE +@property (assign) NSDataWritingOptions writingProtectionOption; +#endif + +/** + If ttlCache is YES, the cache behaves like a ttlCache. This means that once an object enters the + cache, it only lives as long as self.ageLimit. This has the following implications: + - Accessing an object in the cache does not extend that object's lifetime in the cache + - When attempting to access an object in the cache that has lived longer than self.ageLimit, + the cache will behave as if the object does not exist + + @note If an object-level age limit is set via one of the @c -setObject:forKey:withAgeLimit methods, + that age limit overrides self.ageLimit. The overridden object age limit could be greater or less + than self.agelimit but must be greater than zero. + + */ +@property (nonatomic, readonly, getter=isTTLCache) BOOL ttlCache; + +#pragma mark - Event Blocks +/// @name Event Blocks + +/** + A block to be executed just before an object is added to the cache. The queue waits during execution. + */ +@property (nullable, copy) PINDiskCacheObjectBlock willAddObjectBlock; + +/** + A block to be executed just before an object is removed from the cache. The queue waits during execution. + */ +@property (nullable, copy) PINDiskCacheObjectBlock willRemoveObjectBlock; + +/** + A block to be executed just before all objects are removed from the cache as a result of . + The queue waits during execution. + */ +@property (nullable, copy) PINCacheBlock willRemoveAllObjectsBlock; + +/** + A block to be executed just after an object is added to the cache. The queue waits during execution. + */ +@property (nullable, copy) PINDiskCacheObjectBlock didAddObjectBlock; + +/** + A block to be executed just after an object is removed from the cache. The queue waits during execution. + */ +@property (nullable, copy) PINDiskCacheObjectBlock didRemoveObjectBlock; + +/** + A block to be executed just after all objects are removed from the cache as a result of . + The queue waits during execution. + */ +@property (nullable, copy) PINCacheBlock didRemoveAllObjectsBlock; + +#pragma mark - Lifecycle +/// @name Initialization + +/** + A shared cache. + + @result The shared singleton cache instance. + */ +@property (class, readonly, strong) PINDiskCache *sharedCache; + +/** + Empties the trash with `DISPATCH_QUEUE_PRIORITY_BACKGROUND`. Does not use lock. + */ ++ (void)emptyTrash; + +- (instancetype)init NS_UNAVAILABLE; + +/** + Multiple instances with the same name are *not* allowed as they would conflict + with each other. + + @see name + @param name The name of the cache. + @result A new cache with the specified name. + */ +- (instancetype)initWithName:(nonnull NSString *)name; + +/** + Multiple instances with the same name are *not* allowed as they would conflict + with each other. + + @see name + @param name The name of the cache. + @param rootPath The path of the cache. + @result A new cache with the specified name. + */ +- (instancetype)initWithName:(nonnull NSString *)name rootPath:(nonnull NSString *)rootPath; + +/** + @see name + @param name The name of the cache. + @param rootPath The path of the cache. + @param serializer A block used to serialize object. If nil provided, default NSKeyedArchiver serialized will be used. + @param deserializer A block used to deserialize object. If nil provided, default NSKeyedUnarchiver serialized will be used. + @result A new cache with the specified name. + */ +- (instancetype)initWithName:(nonnull NSString *)name rootPath:(nonnull NSString *)rootPath serializer:(nullable PINDiskCacheSerializerBlock)serializer deserializer:(nullable PINDiskCacheDeserializerBlock)deserializer; + +/** + @see name + @param name The name of the cache. + @param rootPath The path of the cache. + @param serializer A block used to serialize object. If nil provided, default NSKeyedArchiver serialized will be used. + @param deserializer A block used to deserialize object. If nil provided, default NSKeyedUnarchiver serialized will be used. + @param operationQueue A PINOperationQueue to run asynchronous operations + @result A new cache with the specified name. + */ +- (instancetype)initWithName:(nonnull NSString *)name rootPath:(nonnull NSString *)rootPath serializer:(nullable PINDiskCacheSerializerBlock)serializer deserializer:(nullable PINDiskCacheDeserializerBlock)deserializer operationQueue:(nonnull PINOperationQueue *)operationQueue __attribute__((deprecated)); + +/** + @see name + @param name The name of the cache. + @param prefix The prefix for the cache name. Defaults to com.pinterest.PINDiskCache + @param rootPath The path of the cache. + @param serializer A block used to serialize object. If nil provided, default NSKeyedArchiver serialized will be used. + @param deserializer A block used to deserialize object. If nil provided, default NSKeyedUnarchiver serialized will be used. + @param keyEncoder A block used to encode key(filename). If nil provided, default url encoder will be used + @param keyDecoder A block used to decode key(filename). If nil provided, default url decoder will be used + @param operationQueue A PINOperationQueue to run asynchronous operations + @result A new cache with the specified name. + */ +- (instancetype)initWithName:(nonnull NSString *)name + prefix:(nonnull NSString *)prefix + rootPath:(nonnull NSString *)rootPath + serializer:(nullable PINDiskCacheSerializerBlock)serializer + deserializer:(nullable PINDiskCacheDeserializerBlock)deserializer + keyEncoder:(nullable PINDiskCacheKeyEncoderBlock)keyEncoder + keyDecoder:(nullable PINDiskCacheKeyDecoderBlock)keyDecoder + operationQueue:(nonnull PINOperationQueue *)operationQueue; + +/** + The designated initializer allowing you to override default NSKeyedArchiver/NSKeyedUnarchiver serialization. + + @see name + @param name The name of the cache. + @param prefix The prefix for the cache name. Defaults to com.pinterest.PINDiskCache + @param rootPath The path of the cache. + @param serializer A block used to serialize object. If nil provided, default NSKeyedArchiver serialized will be used. + @param deserializer A block used to deserialize object. If nil provided, default NSKeyedUnarchiver serialized will be used. + @param keyEncoder A block used to encode key(filename). If nil provided, default url encoder will be used + @param keyDecoder A block used to decode key(filename). If nil provided, default url decoder will be used + @param operationQueue A PINOperationQueue to run asynchronous operations + @param ttlCache Whether or not the cache should behave as a TTL cache. + @result A new cache with the specified name. + */ +- (instancetype)initWithName:(nonnull NSString *)name + prefix:(nonnull NSString *)prefix + rootPath:(nonnull NSString *)rootPath + serializer:(nullable PINDiskCacheSerializerBlock)serializer + deserializer:(nullable PINDiskCacheDeserializerBlock)deserializer + keyEncoder:(nullable PINDiskCacheKeyEncoderBlock)keyEncoder + keyDecoder:(nullable PINDiskCacheKeyDecoderBlock)keyDecoder + operationQueue:(nonnull PINOperationQueue *)operationQueue + ttlCache:(BOOL)ttlCache NS_DESIGNATED_INITIALIZER; + +#pragma mark - Asynchronous Methods +/// @name Asynchronous Methods +/** + Locks access to ivars and allows safe interaction with files on disk. This method returns immediately. + + @warning Calling synchronous methods on the diskCache inside this block will likely cause a deadlock. + + @param block A block to be executed when a lock is available. + */ +- (void)lockFileAccessWhileExecutingBlockAsync:(PINCacheBlock)block; + +/** + Retrieves the object for the specified key. This method returns immediately and executes the passed + block as soon as the object is available. + + @param key The key associated with the requested object. + @param block A block to be executed serially when the object is available. + */ +- (void)objectForKeyAsync:(NSString *)key completion:(nullable PINDiskCacheObjectBlock)block; + +/** + Retrieves the fileURL for the specified key without actually reading the data from disk. This method + returns immediately and executes the passed block as soon as the object is available. + + @warning Access is protected for the duration of the block, but to maintain safe disk access do not + access this fileURL after the block has ended. + + @warning The PINDiskCache lock is held while block is executed. Any synchronous calls to the diskcache + or a cache which owns the instance of the disk cache are likely to cause a deadlock. This is why the block is + *not* passed the instance of the disk cache. You should also avoid doing extensive work while this + lock is held. + + @param key The key associated with the requested object. + @param block A block to be executed serially when the file URL is available. + */ +- (void)fileURLForKeyAsync:(NSString *)key completion:(PINDiskCacheFileURLBlock)block; + +/** + Stores an object in the cache for the specified key. This method returns immediately and executes the + passed block as soon as the object has been stored. + + @param object An object to store in the cache. + @param key A key to associate with the object. This string will be copied. + @param block A block to be executed serially after the object has been stored, or nil. + */ +- (void)setObjectAsync:(id )object forKey:(NSString *)key completion:(nullable PINDiskCacheObjectBlock)block; + +/** + Stores an object in the cache for the specified key and age limit. This method returns immediately and executes the + passed block as soon as the object has been stored. + + @param object An object to store in the cache. + @param key A key to associate with the object. This string will be copied. + @param ageLimit The age limit (in seconds) to associate with the object. An age limit <= 0 means there is no object-level age limit and the cache-level TTL + will be used for this object. + @param block A block to be executed serially after the object has been stored, or nil. + */ +- (void)setObjectAsync:(id )object forKey:(NSString *)key withAgeLimit:(NSTimeInterval)ageLimit completion:(nullable PINDiskCacheObjectBlock)block; + +/** + Stores an object in the cache for the specified key and the specified memory cost. If the cost causes the total + to go over the the cache is trimmed (oldest objects first). This method returns immediately + and executes the passed block after the object has been stored, potentially in parallel with other blocks + on the . + + @param object An object to store in the cache. + @param key A key to associate with the object. This string will be copied. + @param cost An amount to add to the . + @param block A block to be executed concurrently after the object has been stored, or nil. + */ +- (void)setObjectAsync:(id )object forKey:(NSString *)key withCost:(NSUInteger)cost completion:(nullable PINCacheObjectBlock)block; + +/** + Stores an object in the cache for the specified key and the specified memory cost and age limit. If the cost causes the total + to go over the the cache is trimmed (oldest objects first). This method returns immediately + and executes the passed block after the object has been stored, potentially in parallel with other blocks + on the . + + @param object An object to store in the cache. + @param key A key to associate with the object. This string will be copied. + @param cost An amount to add to the . + @param ageLimit The age limit (in seconds) to associate with the object. An age limit <= 0 means there is no object-level age limit and the cache-level TTL will be used for + this object. + @param block A block to be executed concurrently after the object has been stored, or nil. + */ +- (void)setObjectAsync:(id )object forKey:(NSString *)key withCost:(NSUInteger)cost ageLimit:(NSTimeInterval)ageLimit completion:(nullable PINCacheObjectBlock)block; + +/** + Removes the object for the specified key. This method returns immediately and executes the passed block + as soon as the object has been removed. + + @param key The key associated with the object to be removed. + @param block A block to be executed serially after the object has been removed, or nil. + */ +- (void)removeObjectForKeyAsync:(NSString *)key completion:(nullable PINDiskCacheObjectBlock)block; + +/** + Removes objects from the cache, largest first, until the cache is equal to or smaller than the specified byteCount. + This method returns immediately and executes the passed block as soon as the cache has been trimmed. + + @param byteCount The cache will be trimmed equal to or smaller than this size. + @param block A block to be executed serially after the cache has been trimmed, or nil. + */ +- (void)trimToSizeAsync:(NSUInteger)byteCount completion:(nullable PINCacheBlock)block; + +/** + Removes objects from the cache, ordered by date (least recently used first), until the cache is equal to or smaller + than the specified byteCount. This method returns immediately and executes the passed block as soon as the cache has + been trimmed. + + @param byteCount The cache will be trimmed equal to or smaller than this size. + @param block A block to be executed serially after the cache has been trimmed, or nil. + + @note This will not remove objects that have been added via one of the @c -setObject:forKey:withAgeLimit methods. + */ +- (void)trimToSizeByDateAsync:(NSUInteger)byteCount completion:(nullable PINCacheBlock)block; + +/** + Loops through all objects in the cache (reads and writes are suspended during the enumeration). Data is not actually + read from disk, the `object` parameter of the block will be `nil` but the `fileURL` will be available. + This method returns immediately. + + @param block A block to be executed for every object in the cache. + @param completionBlock An optional block to be executed after the enumeration is complete. + + @warning The PINDiskCache lock is held while block is executed. Any synchronous calls to the diskcache + or a cache which owns the instance of the disk cache are likely to cause a deadlock. This is why the block is + *not* passed the instance of the disk cache. You should also avoid doing extensive work while this + lock is held. + + */ +- (void)enumerateObjectsWithBlockAsync:(PINDiskCacheFileURLEnumerationBlock)block completionBlock:(nullable PINCacheBlock)completionBlock; + +#pragma mark - Synchronous Methods +/// @name Synchronous Methods + +/** + Locks access to ivars and allows safe interaction with files on disk. This method only returns once the block + has been run. + + @warning Calling synchronous methods on the diskCache inside this block will likely cause a deadlock. + + @param block A block to be executed when a lock is available. + */ +- (void)synchronouslyLockFileAccessWhileExecutingBlock:(PIN_NOESCAPE PINCacheBlock)block; + +/** + Retrieves the object for the specified key. This method blocks the calling thread until the + object is available. + + @see objectForKeyAsync:completion: + @param key The key associated with the object. + @result The object for the specified key. + */ +- (nullable id )objectForKey:(NSString *)key; + +/** + Retrieves the file URL for the specified key. This method blocks the calling thread until the + url is available. Do not use this URL anywhere except with . This method probably + shouldn't even exist, just use the asynchronous one. + + @see fileURLForKeyAsync:completion: + @param key The key associated with the object. + @result The file URL for the specified key. + */ +- (nullable NSURL *)fileURLForKey:(nullable NSString *)key; + +/** + Stores an object in the cache for the specified key. This method blocks the calling thread until + the object has been stored. + + @see setObjectAsync:forKey:completion: + @param object An object to store in the cache. + @param key A key to associate with the object. This string will be copied. + */ +- (void)setObject:(nullable id )object forKey:(NSString *)key; + +/** + Stores an object in the cache for the specified key and age limit. This method blocks the calling thread until + the object has been stored. + + @see setObjectAsync:forKey:completion: + @param object An object to store in the cache. + @param key A key to associate with the object. This string will be copied. + @param ageLimit The age limit (in seconds) to associate with the object. An age limit <= 0 means there is + no object-level age limit and the cache-level TTL will be used for this object. + */ +- (void)setObject:(nullable id )object forKey:(NSString *)key withAgeLimit:(NSTimeInterval)ageLimit; + +/** + Removes objects from the cache, largest first, until the cache is equal to or smaller than the + specified byteCount. This method blocks the calling thread until the cache has been trimmed. + + @see trimToSizeAsync: + @param byteCount The cache will be trimmed equal to or smaller than this size. + */ +- (void)trimToSize:(NSUInteger)byteCount; + +/** + Removes objects from the cache, ordered by date (least recently used first), until the cache is equal to or + smaller than the specified byteCount. This method blocks the calling thread until the cache has been trimmed. + + @see trimToSizeByDateAsync: + @param byteCount The cache will be trimmed equal to or smaller than this size. + + @note This will not remove objects that have been added via one of the @c -setObject:forKey:withAgeLimit methods. + */ +- (void)trimToSizeByDate:(NSUInteger)byteCount; + +/** + Loops through all objects in the cache (reads and writes are suspended during the enumeration). Data is not actually + read from disk, the `object` parameter of the block will be `nil` but the `fileURL` will be available. + This method blocks the calling thread until all objects have been enumerated. + + @see enumerateObjectsWithBlockAsync:completionBlock + @param block A block to be executed for every object in the cache. + + @warning Do not call this method within the event blocks (, etc.) + Instead use the asynchronous version, . + + @warning The PINDiskCache lock is held while block is executed. Any synchronous calls to the diskcache + or a cache which owns the instance of the disk cache are likely to cause a deadlock. This is why the block is + *not* passed the instance of the disk cache. You should also avoid doing extensive work while this + lock is held. + + */ +- (void)enumerateObjectsWithBlock:(PIN_NOESCAPE PINDiskCacheFileURLEnumerationBlock)block; + +@end + + +#pragma mark - Deprecated + +/** + A callback block which provides only the cache as an argument + */ +typedef void (^PINDiskCacheBlock)(PINDiskCache *cache); + +@interface PINDiskCache (Deprecated) +- (void)lockFileAccessWhileExecutingBlock:(nullable PINCacheBlock)block __attribute__((deprecated)); +- (void)containsObjectForKey:(NSString *)key block:(PINDiskCacheContainsBlock)block __attribute__((deprecated)); +- (void)objectForKey:(NSString *)key block:(nullable PINDiskCacheObjectBlock)block __attribute__((deprecated)); +- (void)fileURLForKey:(NSString *)key block:(nullable PINDiskCacheFileURLBlock)block __attribute__((deprecated)); +- (void)setObject:(id )object forKey:(NSString *)key block:(nullable PINDiskCacheObjectBlock)block __attribute__((deprecated)); +- (void)removeObjectForKey:(NSString *)key block:(nullable PINDiskCacheObjectBlock)block __attribute__((deprecated)); +- (void)trimToDate:(NSDate *)date block:(nullable PINDiskCacheBlock)block __attribute__((deprecated)); +- (void)trimToSize:(NSUInteger)byteCount block:(nullable PINDiskCacheBlock)block __attribute__((deprecated)); +- (void)trimToSizeByDate:(NSUInteger)byteCount block:(nullable PINDiskCacheBlock)block __attribute__((deprecated)); +- (void)removeAllObjects:(nullable PINDiskCacheBlock)block __attribute__((deprecated)); +- (void)enumerateObjectsWithBlock:(PINDiskCacheFileURLBlock)block completionBlock:(nullable PINDiskCacheBlock)completionBlock __attribute__((deprecated)); +- (void)setTtlCache:(BOOL)ttlCache DEPRECATED_MSG_ATTRIBUTE("ttlCache is no longer a settable property and must now be set via initializer."); +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/PINCache/Source/PINDiskCache.m b/Starbucks_Clone_TexBrother/Pods/PINCache/Source/PINDiskCache.m new file mode 100644 index 0000000..f30a5ad --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINCache/Source/PINDiskCache.m @@ -0,0 +1,1727 @@ +// PINCache is a modified version of TMCache +// Modifications by Garrett Moon +// Copyright (c) 2015 Pinterest. All rights reserved. + +#import "PINDiskCache.h" + +#if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_4_0 +#import +#endif + +#import +#import + +#if SWIFT_PACKAGE +@import PINOperation; +#else +#import +#endif + +#define PINDiskCacheError(error) if (error) { NSLog(@"%@ (%d) ERROR: %@", \ +[[NSString stringWithUTF8String:__FILE__] lastPathComponent], \ +__LINE__, [error localizedDescription]); } + +#define PINDiskCacheException(exception) if (exception) { NSAssert(NO, [exception reason]); } + +const char * PINDiskCacheAgeLimitAttributeName = "com.pinterest.PINDiskCache.ageLimit"; +NSString * const PINDiskCacheErrorDomain = @"com.pinterest.PINDiskCache"; +NSErrorUserInfoKey const PINDiskCacheErrorReadFailureCodeKey = @"PINDiskCacheErrorReadFailureCodeKey"; +NSErrorUserInfoKey const PINDiskCacheErrorWriteFailureCodeKey = @"PINDiskCacheErrorWriteFailureCodeKey"; +NSString * const PINDiskCachePrefix = @"com.pinterest.PINDiskCache"; +static NSString * const PINDiskCacheSharedName = @"PINDiskCacheShared"; + +static NSString * const PINDiskCacheOperationIdentifierTrimToDate = @"PINDiskCacheOperationIdentifierTrimToDate"; +static NSString * const PINDiskCacheOperationIdentifierTrimToSize = @"PINDiskCacheOperationIdentifierTrimToSize"; +static NSString * const PINDiskCacheOperationIdentifierTrimToSizeByDate = @"PINDiskCacheOperationIdentifierTrimToSizeByDate"; + +typedef NS_ENUM(NSUInteger, PINDiskCacheCondition) { + PINDiskCacheConditionNotReady = 0, + PINDiskCacheConditionReady = 1, +}; + +static PINOperationDataCoalescingBlock PINDiskTrimmingSizeCoalescingBlock = ^id(NSNumber *existingSize, NSNumber *newSize) { + NSComparisonResult result = [existingSize compare:newSize]; + return (result == NSOrderedDescending) ? newSize : existingSize; +}; + +static PINOperationDataCoalescingBlock PINDiskTrimmingDateCoalescingBlock = ^id(NSDate *existingDate, NSDate *newDate) { + NSComparisonResult result = [existingDate compare:newDate]; + return (result == NSOrderedDescending) ? newDate : existingDate; +}; + +const char * PINDiskCacheFileSystemRepresentation(NSURL *url) +{ +#ifdef __MAC_10_13 // Xcode >= 9 + // -fileSystemRepresentation is available on macOS >= 10.9 + if (@available(macOS 10.9, iOS 7.0, watchOS 2.0, tvOS 9.0, *)) { + return url.fileSystemRepresentation; + } +#endif + return [url.path cStringUsingEncoding:NSUTF8StringEncoding]; +} + +@interface PINDiskCacheMetadata : NSObject +// When the object was added to the disk cache +@property (nonatomic, strong) NSDate *createdDate; +// Last time the object was accessed +@property (nonatomic, strong) NSDate *lastModifiedDate; +@property (nonatomic, strong) NSNumber *size; +// Age limit is used in conjuction with ttl +@property (nonatomic) NSTimeInterval ageLimit; +@end + +@interface PINDiskCache () { + PINDiskCacheSerializerBlock _serializer; + PINDiskCacheDeserializerBlock _deserializer; + + PINDiskCacheKeyEncoderBlock _keyEncoder; + PINDiskCacheKeyDecoderBlock _keyDecoder; +} + +@property (assign, nonatomic) pthread_mutex_t mutex; +@property (copy, nonatomic) NSString *name; +@property (assign) NSUInteger byteCount; +@property (strong, nonatomic) NSURL *cacheURL; +@property (strong, nonatomic) PINOperationQueue *operationQueue; +@property (strong, nonatomic) NSMutableDictionary *metadata; +@property (assign, nonatomic) pthread_cond_t diskWritableCondition; +@property (assign, nonatomic) BOOL diskWritable; +@property (assign, nonatomic) pthread_cond_t diskStateKnownCondition; +@property (assign, nonatomic) BOOL diskStateKnown; +@property (assign, nonatomic) BOOL writingProtectionOptionSet; +@end + +@implementation PINDiskCache + +static NSURL *_sharedTrashURL; + +@synthesize willAddObjectBlock = _willAddObjectBlock; +@synthesize willRemoveObjectBlock = _willRemoveObjectBlock; +@synthesize willRemoveAllObjectsBlock = _willRemoveAllObjectsBlock; +@synthesize didAddObjectBlock = _didAddObjectBlock; +@synthesize didRemoveObjectBlock = _didRemoveObjectBlock; +@synthesize didRemoveAllObjectsBlock = _didRemoveAllObjectsBlock; +@synthesize byteLimit = _byteLimit; +@synthesize ageLimit = _ageLimit; +@synthesize ttlCache = _ttlCache; + +#if TARGET_OS_IPHONE +@synthesize writingProtectionOption = _writingProtectionOption; +@synthesize writingProtectionOptionSet = _writingProtectionOptionSet; +#endif + +#pragma mark - Initialization - + +- (void)dealloc +{ + __unused int result = pthread_mutex_destroy(&_mutex); + NSCAssert(result == 0, @"Failed to destroy lock in PINDiskCache %p. Code: %d", (void *)self, result); + pthread_cond_destroy(&_diskWritableCondition); + pthread_cond_destroy(&_diskStateKnownCondition); +} + +- (instancetype)init +{ + @throw [NSException exceptionWithName:@"Must initialize with a name" reason:@"PINDiskCache must be initialized with a name. Call initWithName: instead." userInfo:nil]; + return [self initWithName:@""]; +} + +- (instancetype)initWithName:(NSString *)name +{ + return [self initWithName:name rootPath:[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0]]; +} + +- (instancetype)initWithName:(NSString *)name rootPath:(NSString *)rootPath +{ + return [self initWithName:name rootPath:rootPath serializer:nil deserializer:nil]; +} + +- (instancetype)initWithName:(NSString *)name rootPath:(NSString *)rootPath serializer:(PINDiskCacheSerializerBlock)serializer deserializer:(PINDiskCacheDeserializerBlock)deserializer +{ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + return [self initWithName:name rootPath:rootPath serializer:serializer deserializer:deserializer operationQueue:[PINOperationQueue sharedOperationQueue]]; +#pragma clang diagnostic pop +} + +- (instancetype)initWithName:(NSString *)name + rootPath:(NSString *)rootPath + serializer:(PINDiskCacheSerializerBlock)serializer + deserializer:(PINDiskCacheDeserializerBlock)deserializer + operationQueue:(PINOperationQueue *)operationQueue +{ + return [self initWithName:name + prefix:PINDiskCachePrefix + rootPath:rootPath + serializer:serializer + deserializer:deserializer + keyEncoder:nil + keyDecoder:nil + operationQueue:operationQueue]; +} + +- (instancetype)initWithName:(NSString *)name + prefix:(NSString *)prefix + rootPath:(NSString *)rootPath + serializer:(PINDiskCacheSerializerBlock)serializer + deserializer:(PINDiskCacheDeserializerBlock)deserializer + keyEncoder:(PINDiskCacheKeyEncoderBlock)keyEncoder + keyDecoder:(PINDiskCacheKeyDecoderBlock)keyDecoder + operationQueue:(PINOperationQueue *)operationQueue +{ + return [self initWithName:name prefix:prefix + rootPath:rootPath + serializer:serializer + deserializer:deserializer + keyEncoder:keyEncoder + keyDecoder:keyDecoder + operationQueue:operationQueue + ttlCache:NO]; +} + +- (instancetype)initWithName:(NSString *)name + prefix:(NSString *)prefix + rootPath:(NSString *)rootPath + serializer:(PINDiskCacheSerializerBlock)serializer + deserializer:(PINDiskCacheDeserializerBlock)deserializer + keyEncoder:(PINDiskCacheKeyEncoderBlock)keyEncoder + keyDecoder:(PINDiskCacheKeyDecoderBlock)keyDecoder + operationQueue:(PINOperationQueue *)operationQueue + ttlCache:(BOOL)ttlCache +{ + if (!name) { + return nil; + } + + NSAssert(((!serializer && !deserializer) || (serializer && deserializer)), + @"PINDiskCache must be initialized with a serializer AND deserializer."); + + NSAssert(((!keyEncoder && !keyDecoder) || (keyEncoder && keyDecoder)), + @"PINDiskCache must be initialized with an encoder AND decoder."); + + if (self = [super init]) { + __unused int result = pthread_mutex_init(&_mutex, NULL); + NSAssert(result == 0, @"Failed to init lock in PINDiskCache %@. Code: %d", self, result); + + _name = [name copy]; + _prefix = [prefix copy]; + _operationQueue = operationQueue; + _ttlCache = ttlCache; + _willAddObjectBlock = nil; + _willRemoveObjectBlock = nil; + _willRemoveAllObjectsBlock = nil; + _didAddObjectBlock = nil; + _didRemoveObjectBlock = nil; + _didRemoveAllObjectsBlock = nil; + + _byteCount = 0; + + // 50 MB by default + _byteLimit = 50 * 1024 * 1024; + // 30 days by default + _ageLimit = 60 * 60 * 24 * 30; + +#if TARGET_OS_IPHONE + _writingProtectionOptionSet = NO; + // This is currently the default for files, but we'd rather not write it if it's unspecified. + _writingProtectionOption = NSDataWritingFileProtectionCompleteUntilFirstUserAuthentication; +#endif + + _metadata = [[NSMutableDictionary alloc] init]; + _diskStateKnown = NO; + + _cacheURL = [[self class] cacheURLWithRootPath:rootPath prefix:_prefix name:_name]; + + //setup serializers + if(serializer) { + _serializer = [serializer copy]; + } else { + _serializer = self.defaultSerializer; + } + + if(deserializer) { + _deserializer = [deserializer copy]; + } else { + _deserializer = self.defaultDeserializer; + } + + //setup key encoder/decoder + if(keyEncoder) { + _keyEncoder = [keyEncoder copy]; + } else { + _keyEncoder = self.defaultKeyEncoder; + } + + if(keyDecoder) { + _keyDecoder = [keyDecoder copy]; + } else { + _keyDecoder = self.defaultKeyDecoder; + } + + pthread_cond_init(&_diskWritableCondition, NULL); + pthread_cond_init(&_diskStateKnownCondition, NULL); + + //we don't want to do anything without setting up the disk cache, but we also don't want to block init, it can take a while to initialize. This must *not* be done on _operationQueue because other operations added may hold the lock and fill up the queue. + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + [self lock]; + [self _locked_createCacheDirectory]; + [self unlock]; + [self initializeDiskProperties]; + }); + } + return self; +} + +- (NSString *)description +{ + return [[NSString alloc] initWithFormat:@"%@.%@.%p", PINDiskCachePrefix, _name, (__bridge void *)self]; +} + ++ (PINDiskCache *)sharedCache +{ + static PINDiskCache *cache; + static dispatch_once_t predicate; + + dispatch_once(&predicate, ^{ + cache = [[PINDiskCache alloc] initWithName:PINDiskCacheSharedName]; + }); + + return cache; +} + ++ (NSURL *)cacheURLWithRootPath:(NSString *)rootPath prefix:(NSString *)prefix name:(NSString *)name +{ + NSString *pathComponent = [[NSString alloc] initWithFormat:@"%@.%@", prefix, name]; + return [NSURL fileURLWithPathComponents:@[ rootPath, pathComponent ]]; +} + +#pragma mark - Private Methods - + +- (NSURL *)encodedFileURLForKey:(NSString *)key +{ + if (![key length]) + return nil; + + //Significantly improve performance by indicating that the URL will *not* result in a directory. + //Also note that accessing _cacheURL is safe without the lock because it is only set on init. + return [_cacheURL URLByAppendingPathComponent:[self encodedString:key] isDirectory:NO]; +} + +- (NSString *)keyForEncodedFileURL:(NSURL *)url +{ + NSString *fileName = [url lastPathComponent]; + if (!fileName) + return nil; + + return [self decodedString:fileName]; +} + +- (NSString *)encodedString:(NSString *)string +{ + return _keyEncoder(string); +} + +- (NSString *)decodedString:(NSString *)string +{ + return _keyDecoder(string); +} + +- (PINDiskCacheSerializerBlock)defaultSerializer +{ + return ^NSData*(id object, NSString *key){ + if (@available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, *)) { + NSError *error = nil; + NSData *data = [NSKeyedArchiver archivedDataWithRootObject:object requiringSecureCoding:NO error:&error]; + PINDiskCacheError(error); + return data; + } else { + return [NSKeyedArchiver archivedDataWithRootObject:object]; + } + }; +} + +- (PINDiskCacheDeserializerBlock)defaultDeserializer +{ + return ^id(NSData * data, NSString *key){ + return [NSKeyedUnarchiver unarchiveObjectWithData:data]; + }; +} + +- (PINDiskCacheKeyEncoderBlock)defaultKeyEncoder +{ + return ^NSString *(NSString *decodedKey) { + if (![decodedKey length]) { + return @""; + } + + if (@available(macOS 10.9, iOS 7.0, tvOS 9.0, watchOS 2.0, *)) { + NSString *encodedString = [decodedKey stringByAddingPercentEncodingWithAllowedCharacters:[[NSCharacterSet characterSetWithCharactersInString:@".:/%"] invertedSet]]; + return encodedString; + } else { + CFStringRef static const charsToEscape = CFSTR(".:/%"); +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + CFStringRef escapedString = CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, + (__bridge CFStringRef)decodedKey, + NULL, + charsToEscape, + kCFStringEncodingUTF8); +#pragma clang diagnostic pop + + return (__bridge_transfer NSString *)escapedString; + } + }; +} + +- (PINDiskCacheKeyEncoderBlock)defaultKeyDecoder +{ + return ^NSString *(NSString *encodedKey) { + if (![encodedKey length]) { + return @""; + } + + if (@available(macOS 10.9, iOS 7.0, tvOS 9.0, watchOS 2.0, *)) { + return [encodedKey stringByRemovingPercentEncoding]; + } else { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + CFStringRef unescapedString = CFURLCreateStringByReplacingPercentEscapesUsingEncoding(kCFAllocatorDefault, + (__bridge CFStringRef)encodedKey, + CFSTR(""), + kCFStringEncodingUTF8); +#pragma clang diagnostic pop + return (__bridge_transfer NSString *)unescapedString; + } + }; +} + + +#pragma mark - Private Trash Methods - + ++ (dispatch_queue_t)sharedTrashQueue +{ + static dispatch_queue_t trashQueue; + static dispatch_once_t predicate; + + dispatch_once(&predicate, ^{ + NSString *queueName = [[NSString alloc] initWithFormat:@"%@.trash", PINDiskCachePrefix]; + trashQueue = dispatch_queue_create([queueName UTF8String], DISPATCH_QUEUE_SERIAL); + dispatch_set_target_queue(trashQueue, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)); + }); + + return trashQueue; +} + ++ (NSLock *)sharedLock +{ + static NSLock *sharedLock; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + sharedLock = [NSLock new]; + }); + return sharedLock; +} + ++ (NSURL *)sharedTrashURL +{ + NSURL *trashURL = nil; + + [[PINDiskCache sharedLock] lock]; + if (_sharedTrashURL == nil) { + NSString *uniqueString = [[NSProcessInfo processInfo] globallyUniqueString]; + _sharedTrashURL = [[[NSURL alloc] initFileURLWithPath:NSTemporaryDirectory()] URLByAppendingPathComponent:uniqueString isDirectory:YES]; + + NSError *error = nil; + [[NSFileManager defaultManager] createDirectoryAtURL:_sharedTrashURL + withIntermediateDirectories:YES + attributes:nil + error:&error]; + PINDiskCacheError(error); + } + trashURL = _sharedTrashURL; + [[PINDiskCache sharedLock] unlock]; + + return trashURL; +} + ++ (BOOL)moveItemAtURLToTrash:(NSURL *)itemURL +{ + if (![[NSFileManager defaultManager] fileExistsAtPath:[itemURL path]]) + return NO; + + NSError *error = nil; + NSString *uniqueString = [[NSProcessInfo processInfo] globallyUniqueString]; + NSURL *uniqueTrashURL = [[PINDiskCache sharedTrashURL] URLByAppendingPathComponent:uniqueString isDirectory:NO]; + BOOL moved = [[NSFileManager defaultManager] moveItemAtURL:itemURL toURL:uniqueTrashURL error:&error]; + PINDiskCacheError(error); + return moved; +} + ++ (void)emptyTrash +{ + dispatch_async([PINDiskCache sharedTrashQueue], ^{ + NSURL *trashURL = nil; + + // If _sharedTrashURL is unset, there's nothing left to do because it hasn't been accessed and therefore items haven't been added to it. + // If it is set, we can just remove it. + // We also need to nil out _sharedTrashURL so that a new one will be created if there's an attempt to move a new file to the trash. + [[PINDiskCache sharedLock] lock]; + if (_sharedTrashURL != nil) { + trashURL = _sharedTrashURL; + _sharedTrashURL = nil; + } + [[PINDiskCache sharedLock] unlock]; + + if (trashURL != nil) { + NSError *removeTrashedItemError = nil; + [[NSFileManager defaultManager] removeItemAtURL:trashURL error:&removeTrashedItemError]; + PINDiskCacheError(removeTrashedItemError); + } + }); +} + +#pragma mark - Private Queue Methods - + +- (BOOL)_locked_createCacheDirectory +{ + BOOL created = NO; + if ([[NSFileManager defaultManager] fileExistsAtPath:[_cacheURL path]] == NO) { + NSError *error = nil; + created = [[NSFileManager defaultManager] createDirectoryAtURL:_cacheURL + withIntermediateDirectories:YES + attributes:nil + error:&error]; + PINDiskCacheError(error); + } + + + + // while this may not be true if success is false, it's better than deadlocking later. + _diskWritable = YES; + pthread_cond_broadcast(&_diskWritableCondition); + + return created; +} + ++ (NSArray *)resourceKeys +{ + static NSArray *resourceKeys = nil; + static dispatch_once_t predicate; + + dispatch_once(&predicate, ^{ + resourceKeys = @[ NSURLCreationDateKey, NSURLContentModificationDateKey, NSURLTotalFileAllocatedSizeKey ]; + }); + + return resourceKeys; +} + +/** + * @return File size in bytes. + */ +- (NSUInteger)_locked_initializeDiskPropertiesForFile:(NSURL *)fileURL fileKey:(NSString *)fileKey +{ + NSError *error = nil; + + NSDictionary *dictionary = [fileURL resourceValuesForKeys:[PINDiskCache resourceKeys] error:&error]; + PINDiskCacheError(error); + + if (_metadata[fileKey] == nil) { + _metadata[fileKey] = [[PINDiskCacheMetadata alloc] init]; + } + + NSDate *createdDate = dictionary[NSURLCreationDateKey]; + if (createdDate && fileKey) + _metadata[fileKey].createdDate = createdDate; + + NSDate *lastModifiedDate = dictionary[NSURLContentModificationDateKey]; + if (lastModifiedDate && fileKey) + _metadata[fileKey].lastModifiedDate = lastModifiedDate; + + NSNumber *fileSize = dictionary[NSURLTotalFileAllocatedSizeKey]; + if (fileSize) { + _metadata[fileKey].size = fileSize; + } + + if (_ttlCache) { + NSTimeInterval ageLimit; + ssize_t res = getxattr(PINDiskCacheFileSystemRepresentation(fileURL), PINDiskCacheAgeLimitAttributeName, &ageLimit, sizeof(NSTimeInterval), 0, 0); + if(res > 0) { + _metadata[fileKey].ageLimit = ageLimit; + } else if (res == -1) { + // Ignore if the extended attribute was never recorded for this file. + if (errno != ENOATTR) { + NSDictionary *userInfo = @{ PINDiskCacheErrorReadFailureCodeKey : @(errno)}; + error = [NSError errorWithDomain:PINDiskCacheErrorDomain code:PINDiskCacheErrorReadFailure userInfo:userInfo]; + PINDiskCacheError(error); + } + } + } + + return [fileSize unsignedIntegerValue]; +} + +- (void)initializeDiskProperties +{ + NSUInteger byteCount = 0; + + NSError *error = nil; + + [self lock]; + NSArray *files = [[NSFileManager defaultManager] contentsOfDirectoryAtURL:_cacheURL + includingPropertiesForKeys:[PINDiskCache resourceKeys] + options:NSDirectoryEnumerationSkipsHiddenFiles + error:&error]; + [self unlock]; + + PINDiskCacheError(error); + + for (NSURL *fileURL in files) { + NSString *fileKey = [self keyForEncodedFileURL:fileURL]; + // Continually grab and release lock while processing files to avoid contention + [self lock]; + if (_metadata[fileKey] == nil) { + byteCount += [self _locked_initializeDiskPropertiesForFile:fileURL fileKey:fileKey]; + } + [self unlock]; + } + + [self lock]; + if (byteCount > 0) + _byteCount = byteCount; + + if (self->_byteLimit > 0 && self->_byteCount > self->_byteLimit) + [self trimToSizeByDateAsync:self->_byteLimit completion:nil]; + + if (self->_ttlCache) + [self removeExpiredObjectsAsync:nil]; + + _diskStateKnown = YES; + pthread_cond_broadcast(&_diskStateKnownCondition); + [self unlock]; +} + +- (void)asynchronouslySetFileModificationDate:(NSDate *)date forURL:(NSURL *)fileURL +{ + [self.operationQueue scheduleOperation:^{ + [self lockForWriting]; + [self _locked_setFileModificationDate:date forURL:fileURL]; + [self unlock]; + } withPriority:PINOperationQueuePriorityLow]; +} + +- (BOOL)_locked_setFileModificationDate:(NSDate *)date forURL:(NSURL *)fileURL +{ + if (!date || !fileURL) { + return NO; + } + + NSError *error = nil; + BOOL success = [[NSFileManager defaultManager] setAttributes:@{ NSFileModificationDate: date } + ofItemAtPath:[fileURL path] + error:&error]; + PINDiskCacheError(error); + + return success; +} + +- (void)asynchronouslySetAgeLimit:(NSTimeInterval)ageLimit forURL:(NSURL *)fileURL +{ + [self.operationQueue scheduleOperation:^{ + [self lockForWriting]; + [self _locked_setAgeLimit:ageLimit forURL:fileURL]; + [self unlock]; + } withPriority:PINOperationQueuePriorityLow]; +} + +- (BOOL)_locked_setAgeLimit:(NSTimeInterval)ageLimit forURL:(NSURL *)fileURL +{ + if (!fileURL) { + return NO; + } + + NSError *error = nil; + if (ageLimit <= 0.0) { + if (removexattr(PINDiskCacheFileSystemRepresentation(fileURL), PINDiskCacheAgeLimitAttributeName, 0) != 0) { + // Ignore if the extended attribute was never recorded for this file. + if (errno != ENOATTR) { + NSDictionary *userInfo = @{ PINDiskCacheErrorWriteFailureCodeKey : @(errno)}; + error = [NSError errorWithDomain:PINDiskCacheErrorDomain code:PINDiskCacheErrorWriteFailure userInfo:userInfo]; + PINDiskCacheError(error); + } + } + } else { + if (setxattr(PINDiskCacheFileSystemRepresentation(fileURL), PINDiskCacheAgeLimitAttributeName, &ageLimit, sizeof(NSTimeInterval), 0, 0) != 0) { + NSDictionary *userInfo = @{ PINDiskCacheErrorWriteFailureCodeKey : @(errno)}; + error = [NSError errorWithDomain:PINDiskCacheErrorDomain code:PINDiskCacheErrorWriteFailure userInfo:userInfo]; + PINDiskCacheError(error); + } + } + + if (!error) { + NSString *key = [self keyForEncodedFileURL:fileURL]; + if (key) { + _metadata[key].ageLimit = ageLimit; + } + } + + return !error; +} + +- (BOOL)removeFileAndExecuteBlocksForKey:(NSString *)key +{ + NSURL *fileURL = [self encodedFileURLForKey:key]; + if (!fileURL) { + return NO; + } + + // We only need to lock until writable at the top because once writable, always writable + [self lockForWriting]; + if (![[NSFileManager defaultManager] fileExistsAtPath:[fileURL path]]) { + [self unlock]; + return NO; + } + + PINDiskCacheObjectBlock willRemoveObjectBlock = _willRemoveObjectBlock; + if (willRemoveObjectBlock) { + [self unlock]; + willRemoveObjectBlock(self, key, nil); + [self lock]; + } + + BOOL trashed = [PINDiskCache moveItemAtURLToTrash:fileURL]; + if (!trashed) { + [self unlock]; + return NO; + } + + [PINDiskCache emptyTrash]; + + NSNumber *byteSize = _metadata[key].size; + if (byteSize) + self.byteCount = _byteCount - [byteSize unsignedIntegerValue]; // atomic + + [_metadata removeObjectForKey:key]; + + PINDiskCacheObjectBlock didRemoveObjectBlock = _didRemoveObjectBlock; + if (didRemoveObjectBlock) { + [self unlock]; + _didRemoveObjectBlock(self, key, nil); + [self lock]; + } + + [self unlock]; + + return YES; +} + +- (void)trimDiskToSize:(NSUInteger)trimByteCount +{ + NSMutableArray *keysToRemove = nil; + + [self lockForWriting]; + if (_byteCount > trimByteCount) { + keysToRemove = [[NSMutableArray alloc] init]; + + NSArray *keysSortedBySize = [_metadata keysSortedByValueUsingComparator:^NSComparisonResult(PINDiskCacheMetadata * _Nonnull obj1, PINDiskCacheMetadata * _Nonnull obj2) { + return [obj1.size compare:obj2.size]; + }]; + + NSUInteger bytesSaved = 0; + for (NSString *key in [keysSortedBySize reverseObjectEnumerator]) { // largest objects first + [keysToRemove addObject:key]; + NSNumber *byteSize = _metadata[key].size; + if (byteSize) { + bytesSaved += [byteSize unsignedIntegerValue]; + } + if (_byteCount - bytesSaved <= trimByteCount) { + break; + } + } + } + [self unlock]; + + for (NSString *key in keysToRemove) { + [self removeFileAndExecuteBlocksForKey:key]; + } +} + +// This is the default trimming method which happens automatically +- (void)trimDiskToSizeByDate:(NSUInteger)trimByteCount +{ + if (self.isTTLCache) { + [self removeExpiredObjects]; + } + + NSMutableArray *keysToRemove = nil; + + [self lockForWriting]; + if (_byteCount > trimByteCount) { + keysToRemove = [[NSMutableArray alloc] init]; + + // last modified represents last access. + NSArray *keysSortedByLastModifiedDate = [_metadata keysSortedByValueUsingComparator:^NSComparisonResult(PINDiskCacheMetadata * _Nonnull obj1, PINDiskCacheMetadata * _Nonnull obj2) { + return [obj1.lastModifiedDate compare:obj2.lastModifiedDate]; + }]; + + NSUInteger bytesSaved = 0; + // objects accessed last first. + for (NSString *key in keysSortedByLastModifiedDate) { + [keysToRemove addObject:key]; + NSNumber *byteSize = _metadata[key].size; + if (byteSize) { + bytesSaved += [byteSize unsignedIntegerValue]; + } + if (_byteCount - bytesSaved <= trimByteCount) { + break; + } + } + } + [self unlock]; + + for (NSString *key in keysToRemove) { + [self removeFileAndExecuteBlocksForKey:key]; + } +} + +- (void)trimDiskToDate:(NSDate *)trimDate +{ + [self lockForWriting]; + NSArray *keysSortedByCreatedDate = [_metadata keysSortedByValueUsingComparator:^NSComparisonResult(PINDiskCacheMetadata * _Nonnull obj1, PINDiskCacheMetadata * _Nonnull obj2) { + return [obj1.createdDate compare:obj2.createdDate]; + }]; + + NSMutableArray *keysToRemove = [[NSMutableArray alloc] init]; + + for (NSString *key in keysSortedByCreatedDate) { // oldest files first + NSDate *createdDate = _metadata[key].createdDate; + if (!createdDate || _metadata[key].ageLimit > 0.0) + continue; + + if ([createdDate compare:trimDate] == NSOrderedAscending) { // older than trim date + [keysToRemove addObject:key]; + } else { + break; + } + } + [self unlock]; + + for (NSString *key in keysToRemove) { + [self removeFileAndExecuteBlocksForKey:key]; + } +} + +- (void)trimToAgeLimitRecursively +{ + [self lock]; + NSTimeInterval ageLimit = _ageLimit; + [self unlock]; + if (ageLimit == 0.0) + return; + + NSDate *date = [[NSDate alloc] initWithTimeIntervalSinceNow:-ageLimit]; + [self trimToDateAsync:date completion:nil]; + + dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(ageLimit * NSEC_PER_SEC)); + dispatch_after(time, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) { + // Ensure that ageLimit is the same as when we were scheduled, otherwise, we've been + // rescheduled (another dispatch_after was issued) and should cancel. + BOOL shouldReschedule = YES; + [self lock]; + if (ageLimit != self->_ageLimit) { + shouldReschedule = NO; + } + [self unlock]; + + if (shouldReschedule) { + [self.operationQueue scheduleOperation:^{ + [self trimToAgeLimitRecursively]; + } withPriority:PINOperationQueuePriorityLow]; + } + }); +} + +#pragma mark - Public Asynchronous Methods - + +- (void)lockFileAccessWhileExecutingBlockAsync:(PINCacheBlock)block +{ + if (block == nil) { + return; + } + + [self.operationQueue scheduleOperation:^{ + [self lockForWriting]; + block(self); + [self unlock]; + } withPriority:PINOperationQueuePriorityLow]; +} + +- (void)containsObjectForKeyAsync:(NSString *)key completion:(PINDiskCacheContainsBlock)block +{ + if (!key || !block) + return; + + [self.operationQueue scheduleOperation:^{ + block([self containsObjectForKey:key]); + } withPriority:PINOperationQueuePriorityLow]; +} + +- (void)objectForKeyAsync:(NSString *)key completion:(PINDiskCacheObjectBlock)block +{ + [self.operationQueue scheduleOperation:^{ + NSURL *fileURL = nil; + id object = [self objectForKey:key fileURL:&fileURL]; + + block(self, key, object); + } withPriority:PINOperationQueuePriorityLow]; +} + +- (void)fileURLForKeyAsync:(NSString *)key completion:(PINDiskCacheFileURLBlock)block +{ + if (block == nil) { + return; + } + + [self.operationQueue scheduleOperation:^{ + NSURL *fileURL = [self fileURLForKey:key]; + + [self lockForWriting]; + block(key, fileURL); + [self unlock]; + } withPriority:PINOperationQueuePriorityLow]; +} + +- (void)setObjectAsync:(id )object forKey:(NSString *)key completion:(PINDiskCacheObjectBlock)block +{ + [self setObjectAsync:object forKey:key withAgeLimit:0.0 completion:(PINDiskCacheObjectBlock)block]; +} + +- (void)setObjectAsync:(id )object forKey:(NSString *)key withAgeLimit:(NSTimeInterval)ageLimit completion:(nullable PINDiskCacheObjectBlock)block +{ + [self.operationQueue scheduleOperation:^{ + NSURL *fileURL = nil; + [self setObject:object forKey:key withAgeLimit:ageLimit fileURL:&fileURL]; + + if (block) { + block(self, key, object); + } + } withPriority:PINOperationQueuePriorityLow]; +} + +- (void)setObjectAsync:(id )object forKey:(NSString *)key withCost:(NSUInteger)cost completion:(nullable PINCacheObjectBlock)block +{ + [self setObjectAsync:object forKey:key completion:(PINDiskCacheObjectBlock)block]; +} + +- (void)setObjectAsync:(id )object forKey:(NSString *)key withCost:(NSUInteger)cost ageLimit:(NSTimeInterval)ageLimit completion:(nullable PINCacheObjectBlock)block +{ + [self setObjectAsync:object forKey:key withAgeLimit:ageLimit completion:(PINDiskCacheObjectBlock)block]; +} + +- (void)removeObjectForKeyAsync:(NSString *)key completion:(PINDiskCacheObjectBlock)block +{ + [self.operationQueue scheduleOperation:^{ + NSURL *fileURL = nil; + [self removeObjectForKey:key fileURL:&fileURL]; + + if (block) { + block(self, key, nil); + } + } withPriority:PINOperationQueuePriorityLow]; +} + +- (void)trimToSizeAsync:(NSUInteger)trimByteCount completion:(PINCacheBlock)block +{ + PINOperationBlock operation = ^(id data) { + [self trimToSize:((NSNumber *)data).unsignedIntegerValue]; + }; + + dispatch_block_t completion = nil; + if (block) { + completion = ^{ + block(self); + }; + } + + [self.operationQueue scheduleOperation:operation + withPriority:PINOperationQueuePriorityLow + identifier:PINDiskCacheOperationIdentifierTrimToSize + coalescingData:[NSNumber numberWithUnsignedInteger:trimByteCount] + dataCoalescingBlock:PINDiskTrimmingSizeCoalescingBlock + completion:completion]; +} + +- (void)trimToDateAsync:(NSDate *)trimDate completion:(PINCacheBlock)block +{ + PINOperationBlock operation = ^(id data){ + [self trimToDate:(NSDate *)data]; + }; + + dispatch_block_t completion = nil; + if (block) { + completion = ^{ + block(self); + }; + } + + [self.operationQueue scheduleOperation:operation + withPriority:PINOperationQueuePriorityLow + identifier:PINDiskCacheOperationIdentifierTrimToDate + coalescingData:trimDate + dataCoalescingBlock:PINDiskTrimmingDateCoalescingBlock + completion:completion]; +} + +- (void)trimToSizeByDateAsync:(NSUInteger)trimByteCount completion:(PINCacheBlock)block +{ + PINOperationBlock operation = ^(id data){ + [self trimToSizeByDate:((NSNumber *)data).unsignedIntegerValue]; + }; + + dispatch_block_t completion = nil; + if (block) { + completion = ^{ + block(self); + }; + } + + [self.operationQueue scheduleOperation:operation + withPriority:PINOperationQueuePriorityLow + identifier:PINDiskCacheOperationIdentifierTrimToSizeByDate + coalescingData:[NSNumber numberWithUnsignedInteger:trimByteCount] + dataCoalescingBlock:PINDiskTrimmingSizeCoalescingBlock + completion:completion]; +} + +- (void)removeExpiredObjectsAsync:(PINCacheBlock)block +{ + [self.operationQueue scheduleOperation:^{ + [self removeExpiredObjects]; + + if (block) { + block(self); + } + } withPriority:PINOperationQueuePriorityLow]; +} + +- (void)removeAllObjectsAsync:(PINCacheBlock)block +{ + [self.operationQueue scheduleOperation:^{ + [self removeAllObjects]; + + if (block) { + block(self); + } + } withPriority:PINOperationQueuePriorityLow]; +} + +- (void)enumerateObjectsWithBlockAsync:(PINDiskCacheFileURLEnumerationBlock)block completionBlock:(PINCacheBlock)completionBlock +{ + [self.operationQueue scheduleOperation:^{ + [self enumerateObjectsWithBlock:block]; + + if (completionBlock) { + completionBlock(self); + } + } withPriority:PINOperationQueuePriorityLow]; +} + +#pragma mark - Public Synchronous Methods - + +- (void)synchronouslyLockFileAccessWhileExecutingBlock:(PIN_NOESCAPE PINCacheBlock)block +{ + if (block) { + [self lockForWriting]; + block(self); + [self unlock]; + } +} + +- (BOOL)containsObjectForKey:(NSString *)key +{ + [self lock]; + if (_metadata[key] != nil || _diskStateKnown == NO) { + BOOL objectExpired = NO; + if (self->_ttlCache && _metadata[key].createdDate != nil) { + NSTimeInterval ageLimit = _metadata[key].ageLimit > 0.0 ? _metadata[key].ageLimit : self->_ageLimit; + objectExpired = ageLimit > 0 && fabs([_metadata[key].createdDate timeIntervalSinceDate:[NSDate date]]) > ageLimit; + } + [self unlock]; + return (!objectExpired && [self fileURLForKey:key updateFileModificationDate:NO] != nil); + } + [self unlock]; + return NO; +} + +- (nullable id)objectForKey:(NSString *)key +{ + return [self objectForKey:key fileURL:nil]; +} + +- (id)objectForKeyedSubscript:(NSString *)key +{ + return [self objectForKey:key]; +} + +- (nullable id )objectForKey:(NSString *)key fileURL:(NSURL **)outFileURL +{ + [self lock]; + BOOL containsKey = _metadata[key] != nil || _diskStateKnown == NO; + [self unlock]; + + if (!key || !containsKey) + return nil; + + id object = nil; + NSURL *fileURL = [self encodedFileURLForKey:key]; + + NSDate *now = [NSDate date]; + [self lock]; + if (self->_ttlCache) { + if (!_diskStateKnown) { + if (_metadata[key] == nil) { + NSString *fileKey = [self keyForEncodedFileURL:fileURL]; + [self _locked_initializeDiskPropertiesForFile:fileURL fileKey:fileKey]; + } + } + } + + NSTimeInterval ageLimit = _metadata[key].ageLimit > 0.0 ? _metadata[key].ageLimit : self->_ageLimit; + if (!self->_ttlCache || ageLimit <= 0 || fabs([_metadata[key].createdDate timeIntervalSinceDate:now]) < ageLimit) { + // If the cache should behave like a TTL cache, then only fetch the object if there's a valid ageLimit and the object is still alive + + NSData *objectData = [[NSData alloc] initWithContentsOfFile:[fileURL path]]; + + if (objectData) { + //Be careful with locking below. We unlock here so that we're not locked while deserializing, we re-lock after. + [self unlock]; + @try { + object = _deserializer(objectData, key); + } + @catch (NSException *exception) { + NSError *error = nil; + [self lock]; + [[NSFileManager defaultManager] removeItemAtPath:[fileURL path] error:&error]; + [self unlock]; + PINDiskCacheError(error) + PINDiskCacheException(exception); + } + [self lock]; + } + if (object) { + _metadata[key].lastModifiedDate = now; + [self asynchronouslySetFileModificationDate:now forURL:fileURL]; + } + } + [self unlock]; + + if (outFileURL) { + *outFileURL = fileURL; + } + + return object; +} + +/// Helper function to call fileURLForKey:updateFileModificationDate: +- (NSURL *)fileURLForKey:(NSString *)key +{ + // Don't update the file modification time, if self is a ttlCache + return [self fileURLForKey:key updateFileModificationDate:!self->_ttlCache]; +} + +- (NSURL *)fileURLForKey:(NSString *)key updateFileModificationDate:(BOOL)updateFileModificationDate +{ + if (!key) { + return nil; + } + + NSDate *now = [NSDate date]; + NSURL *fileURL = [self encodedFileURLForKey:key]; + + [self lockForWriting]; + if (fileURL.path && [[NSFileManager defaultManager] fileExistsAtPath:fileURL.path]) { + if (updateFileModificationDate) { + _metadata[key].lastModifiedDate = now; + [self asynchronouslySetFileModificationDate:now forURL:fileURL]; + } + } else { + fileURL = nil; + } + [self unlock]; + return fileURL; +} + +- (void)setObject:(id )object forKey:(NSString *)key +{ + [self setObject:object forKey:key withAgeLimit:0.0]; +} + +- (void)setObject:(id )object forKey:(NSString *)key withAgeLimit:(NSTimeInterval)ageLimit +{ + [self setObject:object forKey:key withAgeLimit:ageLimit fileURL:nil]; +} + +- (void)setObject:(id )object forKey:(NSString *)key withCost:(NSUInteger)cost ageLimit:(NSTimeInterval)ageLimit +{ + [self setObject:object forKey:key withAgeLimit:ageLimit]; +} + +- (void)setObject:(id )object forKey:(NSString *)key withCost:(NSUInteger)cost +{ + [self setObject:object forKey:key]; +} + +- (void)setObject:(id)object forKeyedSubscript:(NSString *)key +{ + if (object == nil) { + [self removeObjectForKey:key]; + } else { + [self setObject:object forKey:key]; + } +} + +- (void)setObject:(id )object forKey:(NSString *)key withAgeLimit:(NSTimeInterval)ageLimit fileURL:(NSURL **)outFileURL +{ + NSAssert(ageLimit <= 0.0 || (ageLimit > 0.0 && _ttlCache), @"ttlCache must be set to YES if setting an object-level age limit."); + + if (!key || !object) + return; + + NSDataWritingOptions writeOptions = NSDataWritingAtomic; + #if TARGET_OS_IPHONE + if (self.writingProtectionOptionSet) { + writeOptions |= self.writingProtectionOption; + } + #endif + + // Remain unlocked here so that we're not locked while serializing. + NSData *data = _serializer(object, key); + NSURL *fileURL = nil; + + NSUInteger byteLimit = self.byteLimit; + if (data.length <= byteLimit || byteLimit == 0) { + // The cache is large enough to fit this object (although we may need to evict others). + fileURL = [self encodedFileURLForKey:key]; + } else { + // The cache isn't large enough to fit this object (even if all others were evicted). + // We should not write it to disk because it will be deleted immediately after. + if (outFileURL) { + *outFileURL = nil; + } + return; + } + + [self lockForWriting]; + PINDiskCacheObjectBlock willAddObjectBlock = self->_willAddObjectBlock; + if (willAddObjectBlock) { + [self unlock]; + willAddObjectBlock(self, key, object); + [self lock]; + } + + NSError *writeError = nil; + BOOL written = [data writeToURL:fileURL options:writeOptions error:&writeError]; + PINDiskCacheError(writeError); + + if (written) { + if (_metadata[key] == nil) { + _metadata[key] = [[PINDiskCacheMetadata alloc] init]; + } + + NSError *error = nil; + NSDictionary *values = [fileURL resourceValuesForKeys:@[ NSURLCreationDateKey, NSURLContentModificationDateKey, NSURLTotalFileAllocatedSizeKey ] error:&error]; + PINDiskCacheError(error); + + NSNumber *diskFileSize = [values objectForKey:NSURLTotalFileAllocatedSizeKey]; + if (diskFileSize) { + NSNumber *prevDiskFileSize = self->_metadata[key].size; + if (prevDiskFileSize) { + self.byteCount = self->_byteCount - [prevDiskFileSize unsignedIntegerValue]; + } + self->_metadata[key].size = diskFileSize; + self.byteCount = self->_byteCount + [diskFileSize unsignedIntegerValue]; // atomic + } + NSDate *createdDate = [values objectForKey:NSURLCreationDateKey]; + if (createdDate) { + self->_metadata[key].createdDate = createdDate; + } + NSDate *lastModifiedDate = [values objectForKey:NSURLContentModificationDateKey]; + if (lastModifiedDate) { + self->_metadata[key].lastModifiedDate = lastModifiedDate; + } + [self asynchronouslySetAgeLimit:ageLimit forURL:fileURL]; + if (self->_byteLimit > 0 && self->_byteCount > self->_byteLimit) + [self trimToSizeByDateAsync:self->_byteLimit completion:nil]; + } else { + fileURL = nil; + } + + PINDiskCacheObjectBlock didAddObjectBlock = self->_didAddObjectBlock; + if (didAddObjectBlock) { + [self unlock]; + didAddObjectBlock(self, key, object); + [self lock]; + } + [self unlock]; + + if (outFileURL) { + *outFileURL = fileURL; + } +} + +- (void)removeObjectForKey:(NSString *)key +{ + [self removeObjectForKey:key fileURL:nil]; +} + +- (void)removeObjectForKey:(NSString *)key fileURL:(NSURL **)outFileURL +{ + if (!key) + return; + + NSURL *fileURL = nil; + + fileURL = [self encodedFileURLForKey:key]; + + [self removeFileAndExecuteBlocksForKey:key]; + + if (outFileURL) { + *outFileURL = fileURL; + } +} + +- (void)trimToSize:(NSUInteger)trimByteCount +{ + if (trimByteCount == 0) { + [self removeAllObjects]; + return; + } + + [self trimDiskToSize:trimByteCount]; +} + +- (void)trimToDate:(NSDate *)trimDate +{ + if (!trimDate) + return; + + if ([trimDate isEqualToDate:[NSDate distantPast]]) { + [self removeAllObjects]; + return; + } + + [self trimDiskToDate:trimDate]; +} + +- (void)trimToSizeByDate:(NSUInteger)trimByteCount +{ + if (trimByteCount == 0) { + [self removeAllObjects]; + return; + } + + [self trimDiskToSizeByDate:trimByteCount]; +} + +- (void)removeExpiredObjects +{ + [self lockForWriting]; + NSDate *now = [NSDate date]; + NSMutableArray *expiredObjectKeys = [NSMutableArray array]; + [_metadata enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, PINDiskCacheMetadata * _Nonnull obj, BOOL * _Nonnull stop) { + NSTimeInterval ageLimit = obj.ageLimit > 0.0 ? obj.ageLimit : self->_ageLimit; + NSDate *expirationDate = [obj.createdDate dateByAddingTimeInterval:ageLimit]; + if ([expirationDate compare:now] == NSOrderedAscending) { // Expiration date has passed + [expiredObjectKeys addObject:key]; + } + }]; + [self unlock]; + + for (NSString *key in expiredObjectKeys) { + //unlock, removeFileAndExecuteBlocksForKey handles locking itself + [self removeFileAndExecuteBlocksForKey:key]; + } +} + +- (void)removeAllObjects +{ + // We don't need to know the disk state since we're just going to remove everything. + [self lockForWriting]; + PINCacheBlock willRemoveAllObjectsBlock = self->_willRemoveAllObjectsBlock; + if (willRemoveAllObjectsBlock) { + [self unlock]; + willRemoveAllObjectsBlock(self); + [self lock]; + } + + [PINDiskCache moveItemAtURLToTrash:self->_cacheURL]; + [PINDiskCache emptyTrash]; + + [self _locked_createCacheDirectory]; + + [self->_metadata removeAllObjects]; + self.byteCount = 0; // atomic + + PINCacheBlock didRemoveAllObjectsBlock = self->_didRemoveAllObjectsBlock; + if (didRemoveAllObjectsBlock) { + [self unlock]; + didRemoveAllObjectsBlock(self); + [self lock]; + } + + [self unlock]; +} + +- (void)enumerateObjectsWithBlock:(PIN_NOESCAPE PINDiskCacheFileURLEnumerationBlock)block +{ + if (!block) + return; + + [self lockAndWaitForKnownState]; + NSDate *now = [NSDate date]; + + for (NSString *key in _metadata) { + NSURL *fileURL = [self encodedFileURLForKey:key]; + // If the cache should behave like a TTL cache, then only fetch the object if there's a valid ageLimit and the object is still alive + NSDate *createdDate = _metadata[key].createdDate; + NSTimeInterval ageLimit = _metadata[key].ageLimit > 0.0 ? _metadata[key].ageLimit : self->_ageLimit; + if (!self->_ttlCache || ageLimit <= 0 || (createdDate && fabs([createdDate timeIntervalSinceDate:now]) < ageLimit)) { + BOOL stop = NO; + block(key, fileURL, &stop); + if (stop) + break; + } + } + [self unlock]; +} + +#pragma mark - Public Thread Safe Accessors - + +- (PINDiskCacheObjectBlock)willAddObjectBlock +{ + PINDiskCacheObjectBlock block = nil; + + [self lock]; + block = _willAddObjectBlock; + [self unlock]; + + return block; +} + +- (void)setWillAddObjectBlock:(PINDiskCacheObjectBlock)block +{ + [self.operationQueue scheduleOperation:^{ + [self lock]; + self->_willAddObjectBlock = [block copy]; + [self unlock]; + } withPriority:PINOperationQueuePriorityHigh]; +} + +- (PINDiskCacheObjectBlock)willRemoveObjectBlock +{ + PINDiskCacheObjectBlock block = nil; + + [self lock]; + block = _willRemoveObjectBlock; + [self unlock]; + + return block; +} + +- (void)setWillRemoveObjectBlock:(PINDiskCacheObjectBlock)block +{ + [self.operationQueue scheduleOperation:^{ + [self lock]; + self->_willRemoveObjectBlock = [block copy]; + [self unlock]; + } withPriority:PINOperationQueuePriorityHigh]; +} + +- (PINCacheBlock)willRemoveAllObjectsBlock +{ + PINCacheBlock block = nil; + + [self lock]; + block = _willRemoveAllObjectsBlock; + [self unlock]; + + return block; +} + +- (void)setWillRemoveAllObjectsBlock:(PINCacheBlock)block +{ + [self.operationQueue scheduleOperation:^{ + [self lock]; + self->_willRemoveAllObjectsBlock = [block copy]; + [self unlock]; + } withPriority:PINOperationQueuePriorityHigh]; +} + +- (PINDiskCacheObjectBlock)didAddObjectBlock +{ + PINDiskCacheObjectBlock block = nil; + + [self lock]; + block = _didAddObjectBlock; + [self unlock]; + + return block; +} + +- (void)setDidAddObjectBlock:(PINDiskCacheObjectBlock)block +{ + [self.operationQueue scheduleOperation:^{ + [self lock]; + self->_didAddObjectBlock = [block copy]; + [self unlock]; + } withPriority:PINOperationQueuePriorityHigh]; +} + +- (PINDiskCacheObjectBlock)didRemoveObjectBlock +{ + PINDiskCacheObjectBlock block = nil; + + [self lock]; + block = _didRemoveObjectBlock; + [self unlock]; + + return block; +} + +- (void)setDidRemoveObjectBlock:(PINDiskCacheObjectBlock)block +{ + [self.operationQueue scheduleOperation:^{ + [self lock]; + self->_didRemoveObjectBlock = [block copy]; + [self unlock]; + } withPriority:PINOperationQueuePriorityHigh]; +} + +- (PINCacheBlock)didRemoveAllObjectsBlock +{ + PINCacheBlock block = nil; + + [self lock]; + block = _didRemoveAllObjectsBlock; + [self unlock]; + + return block; +} + +- (void)setDidRemoveAllObjectsBlock:(PINCacheBlock)block +{ + [self.operationQueue scheduleOperation:^{ + [self lock]; + self->_didRemoveAllObjectsBlock = [block copy]; + [self unlock]; + } withPriority:PINOperationQueuePriorityHigh]; +} + +- (NSUInteger)byteLimit +{ + NSUInteger byteLimit; + + [self lock]; + byteLimit = _byteLimit; + [self unlock]; + + return byteLimit; +} + +- (void)setByteLimit:(NSUInteger)byteLimit +{ + [self.operationQueue scheduleOperation:^{ + [self lock]; + self->_byteLimit = byteLimit; + [self unlock]; + + if (byteLimit > 0) + [self trimDiskToSizeByDate:byteLimit]; + } withPriority:PINOperationQueuePriorityHigh]; +} + +- (NSTimeInterval)ageLimit +{ + NSTimeInterval ageLimit; + + [self lock]; + ageLimit = _ageLimit; + [self unlock]; + + return ageLimit; +} + +- (void)setAgeLimit:(NSTimeInterval)ageLimit +{ + [self.operationQueue scheduleOperation:^{ + [self lock]; + self->_ageLimit = ageLimit; + [self unlock]; + + [self.operationQueue scheduleOperation:^{ + [self trimToAgeLimitRecursively]; + } withPriority:PINOperationQueuePriorityLow]; + } withPriority:PINOperationQueuePriorityHigh]; +} + +- (BOOL)isTTLCache +{ + BOOL isTTLCache; + + [self lock]; + isTTLCache = _ttlCache; + [self unlock]; + + return isTTLCache; +} + +#if TARGET_OS_IPHONE +- (NSDataWritingOptions)writingProtectionOption +{ + NSDataWritingOptions option; + + [self lock]; + option = _writingProtectionOption; + [self unlock]; + + return option; +} + +- (void)setWritingProtectionOption:(NSDataWritingOptions)writingProtectionOption +{ + [self.operationQueue scheduleOperation:^{ + NSDataWritingOptions option = NSDataWritingFileProtectionMask & writingProtectionOption; + + [self lock]; + self->_writingProtectionOptionSet = YES; + self->_writingProtectionOption = option; + [self unlock]; + } withPriority:PINOperationQueuePriorityHigh]; +} +#endif + +- (void)lockForWriting +{ + [self lock]; + + // Lock if the disk isn't writable. + if (_diskWritable == NO) { + pthread_cond_wait(&_diskWritableCondition, &_mutex); + } +} + +- (void)lockAndWaitForKnownState +{ + [self lock]; + + // Lock if the disk state isn't known. + if (_diskStateKnown == NO) { + pthread_cond_wait(&_diskStateKnownCondition, &_mutex); + } +} + +- (void)lock +{ + __unused int result = pthread_mutex_lock(&_mutex); + NSAssert(result == 0, @"Failed to lock PINDiskCache %@. Code: %d", self, result); +} + +- (void)unlock +{ + __unused int result = pthread_mutex_unlock(&_mutex); + NSAssert(result == 0, @"Failed to unlock PINDiskCache %@. Code: %d", self, result); +} + +@end + +@implementation PINDiskCache (Deprecated) + +- (void)lockFileAccessWhileExecutingBlock:(nullable PINCacheBlock)block +{ + [self lockFileAccessWhileExecutingBlockAsync:block]; +} + +- (void)containsObjectForKey:(NSString *)key block:(PINDiskCacheContainsBlock)block +{ + [self containsObjectForKeyAsync:key completion:block]; +} + +- (void)objectForKey:(NSString *)key block:(nullable PINDiskCacheObjectBlock)block +{ + [self objectForKeyAsync:key completion:block]; +} + +- (void)fileURLForKey:(NSString *)key block:(nullable PINDiskCacheFileURLBlock)block +{ + [self fileURLForKeyAsync:key completion:block]; +} + +- (void)setObject:(id )object forKey:(NSString *)key block:(nullable PINDiskCacheObjectBlock)block +{ + [self setObjectAsync:object forKey:key completion:block]; +} + +- (void)removeObjectForKey:(NSString *)key block:(nullable PINDiskCacheObjectBlock)block +{ + [self removeObjectForKeyAsync:key completion:block]; +} + +- (void)trimToDate:(NSDate *)date block:(nullable PINDiskCacheBlock)block +{ + [self trimToDateAsync:date completion:^(id diskCache) { + if (block) { + block((PINDiskCache *)diskCache); + } + }]; +} + +- (void)trimToSize:(NSUInteger)byteCount block:(nullable PINDiskCacheBlock)block +{ + [self trimToSizeAsync:byteCount completion:^(id diskCache) { + if (block) { + block((PINDiskCache *)diskCache); + } + }]; +} + +- (void)trimToSizeByDate:(NSUInteger)byteCount block:(nullable PINDiskCacheBlock)block +{ + [self trimToSizeAsync:byteCount completion:^(id diskCache) { + if (block) { + block((PINDiskCache *)diskCache); + } + }]; +} + +- (void)removeAllObjects:(nullable PINDiskCacheBlock)block +{ + [self removeAllObjectsAsync:^(id diskCache) { + if (block) { + block((PINDiskCache *)diskCache); + } + }]; +} + +- (void)enumerateObjectsWithBlock:(PINDiskCacheFileURLBlock)block completionBlock:(nullable PINDiskCacheBlock)completionBlock +{ + [self enumerateObjectsWithBlockAsync:^(NSString * _Nonnull key, NSURL * _Nullable fileURL, BOOL * _Nonnull stop) { + block(key, fileURL); + } completionBlock:^(id diskCache) { + if (completionBlock) { + completionBlock((PINDiskCache *)diskCache); + } + }]; +} + +- (void)setTtlCache:(BOOL)ttlCache +{ + [self.operationQueue scheduleOperation:^{ + [self lock]; + self->_ttlCache = ttlCache; + [self unlock]; + } withPriority:PINOperationQueuePriorityHigh]; +} + +@end + +@implementation PINDiskCacheMetadata +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINCache/Source/PINMemoryCache.h b/Starbucks_Clone_TexBrother/Pods/PINCache/Source/PINMemoryCache.h new file mode 100644 index 0000000..1cf9ff8 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINCache/Source/PINMemoryCache.h @@ -0,0 +1,245 @@ +// PINCache is a modified version of TMCache +// Modifications by Garrett Moon +// Copyright (c) 2015 Pinterest. All rights reserved. + +#import + +#import "PINCacheMacros.h" +#import "PINCaching.h" +#import "PINCacheObjectSubscripting.h" + +NS_ASSUME_NONNULL_BEGIN + +@class PINMemoryCache; +@class PINOperationQueue; + + +/** + `PINMemoryCache` is a fast, thread safe key/value store similar to `NSCache`. On iOS it will clear itself + automatically to reduce memory usage when the app receives a memory warning or goes into the background. + + Access is natively synchronous. Asynchronous variations are provided. Every asynchronous method accepts a + callback block that runs on a concurrent , with cache reads and writes protected by a lock. + + All access to the cache is dated so the that the least-used objects can be trimmed first. Setting an + optional will trigger a GCD timer to periodically to trim the cache to that age. + + Objects can optionally be set with a "cost", which could be a byte count or any other meaningful integer. + Setting a will automatically keep the cache below that value with . + + Values will not persist after application relaunch or returning from the background. See for + a memory cache backed by a disk cache. + */ + +PIN_SUBCLASSING_RESTRICTED +@interface PINMemoryCache : NSObject + +#pragma mark - Properties +/// @name Core + +/** + The total accumulated cost. + */ +@property (readonly) NSUInteger totalCost; + +/** + The maximum cost allowed to accumulate before objects begin to be removed with . + */ +@property (assign) NSUInteger costLimit; + +/** + The maximum number of seconds an object is allowed to exist in the cache. Setting this to a value + greater than `0.0` will start a recurring GCD timer with the same period that calls . + Setting it back to `0.0` will stop the timer. Defaults to `0.0`. + */ +@property (assign) NSTimeInterval ageLimit; + +/** + If ttlCache is YES, the cache behaves like a ttlCache. This means that once an object enters the + cache, it only lives as long as self.ageLimit. This has the following implications: + - Accessing an object in the cache does not extend that object's lifetime in the cache + - When attempting to access an object in the cache that has lived longer than self.ageLimit, + the cache will behave as if the object does not exist + + @note If an object-level age limit is set via one of the @c -setObject:forKey:withAgeLimit methods, + that age limit overrides self.ageLimit. The overridden object age limit could be greater or + less than self.agelimit but must be greater than zero. + */ +@property (nonatomic, readonly, getter=isTTLCache) BOOL ttlCache; + +/** + When `YES` on iOS the cache will remove all objects when the app receives a memory warning. + Defaults to `YES`. + */ +@property (assign) BOOL removeAllObjectsOnMemoryWarning; + +/** + When `YES` on iOS the cache will remove all objects when the app enters the background. + Defaults to `YES`. + */ +@property (assign) BOOL removeAllObjectsOnEnteringBackground; + +#pragma mark - Event Blocks +/// @name Event Blocks + +/** + A block to be executed just before an object is added to the cache. This block will be excuted within + a lock, i.e. all reads and writes are suspended for the duration of the block. + Calling synchronous methods on the cache within this callback will likely cause a deadlock. + */ +@property (nullable, copy) PINCacheObjectBlock willAddObjectBlock; + +/** + A block to be executed just before an object is removed from the cache. This block will be excuted + within a lock, i.e. all reads and writes are suspended for the duration of the block. + Calling synchronous methods on the cache within this callback will likely cause a deadlock. + */ +@property (nullable, copy) PINCacheObjectBlock willRemoveObjectBlock; + +/** + A block to be executed just before all objects are removed from the cache as a result of . + This block will be excuted within a lock, i.e. all reads and writes are suspended for the duration of the block. + Calling synchronous methods on the cache within this callback will likely cause a deadlock. + */ +@property (nullable, copy) PINCacheBlock willRemoveAllObjectsBlock; + +/** + A block to be executed just after an object is added to the cache. This block will be excuted within + a lock, i.e. all reads and writes are suspended for the duration of the block. + Calling synchronous methods on the cache within this callback will likely cause a deadlock. + */ +@property (nullable, copy) PINCacheObjectBlock didAddObjectBlock; + +/** + A block to be executed just after an object is removed from the cache. This block will be excuted + within a lock, i.e. all reads and writes are suspended for the duration of the block. + Calling synchronous methods on the cache within this callback will likely cause a deadlock. + */ +@property (nullable, copy) PINCacheObjectBlock didRemoveObjectBlock; + +/** + A block to be executed just after all objects are removed from the cache as a result of . + This block will be excuted within a lock, i.e. all reads and writes are suspended for the duration of the block. + Calling synchronous methods on the cache within this callback will likely cause a deadlock. + */ +@property (nullable, copy) PINCacheBlock didRemoveAllObjectsBlock; + +/** + A block to be executed upon receiving a memory warning (iOS only) potentially in parallel with other blocks on the . + This block will be executed regardless of the value of . Defaults to `nil`. + */ +@property (nullable, copy) PINCacheBlock didReceiveMemoryWarningBlock; + +/** + A block to be executed when the app enters the background (iOS only) potentially in parallel with other blocks on the . + This block will be executed regardless of the value of . Defaults to `nil`. + */ +@property (nullable, copy) PINCacheBlock didEnterBackgroundBlock; + +#pragma mark - Lifecycle +/// @name Shared Cache + +/** + A shared cache. + + @result The shared singleton cache instance. + */ +@property (class, strong, readonly) PINMemoryCache *sharedCache; + +- (instancetype)initWithOperationQueue:(PINOperationQueue *)operationQueue; + +- (instancetype)initWithName:(NSString *)name operationQueue:(PINOperationQueue *)operationQueue; + +- (instancetype)initWithName:(NSString *)name operationQueue:(PINOperationQueue *)operationQueue ttlCache:(BOOL)ttlCache NS_DESIGNATED_INITIALIZER; + +#pragma mark - Asynchronous Methods +/// @name Asynchronous Methods + +/** + Removes objects from the cache, costliest objects first, until the is below the specified + value. This method returns immediately and executes the passed block after the cache has been trimmed, + potentially in parallel with other blocks on the . + + @param cost The total accumulation allowed to remain after the cache has been trimmed. + @param block A block to be executed concurrently after the cache has been trimmed, or nil. + */ +- (void)trimToCostAsync:(NSUInteger)cost completion:(nullable PINCacheBlock)block; + +/** + Removes objects from the cache, ordered by date (least recently used first), until the is below + the specified value. This method returns immediately and executes the passed block after the cache has been + trimmed, potentially in parallel with other blocks on the . + + @param cost The total accumulation allowed to remain after the cache has been trimmed. + @param block A block to be executed concurrently after the cache has been trimmed, or nil. + */ +- (void)trimToCostByDateAsync:(NSUInteger)cost completion:(nullable PINCacheBlock)block; + +/** + Loops through all objects in the cache with reads and writes suspended. Calling serial methods which + write to the cache inside block may be unsafe and may result in a deadlock. This method returns immediately. + + @param block A block to be executed for every object in the cache. + @param completionBlock An optional block to be executed concurrently when the enumeration is complete. + */ +- (void)enumerateObjectsWithBlockAsync:(PINCacheObjectEnumerationBlock)block completionBlock:(nullable PINCacheBlock)completionBlock; + +#pragma mark - Synchronous Methods +/// @name Synchronous Methods + +/** + Removes objects from the cache, costliest objects first, until the is below the specified + value. This method blocks the calling thread until the cache has been trimmed. + + @see trimToCostAsync: + @param cost The total accumulation allowed to remain after the cache has been trimmed. + */ +- (void)trimToCost:(NSUInteger)cost; + +/** + Removes objects from the cache, ordered by date (least recently used first), until the is below + the specified value. This method blocks the calling thread until the cache has been trimmed. + + @see trimToCostByDateAsync: + @param cost The total accumulation allowed to remain after the cache has been trimmed. + */ +- (void)trimToCostByDate:(NSUInteger)cost; + +/** + Loops through all objects in the cache within a memory lock (reads and writes are suspended during the enumeration). + This method blocks the calling thread until all objects have been enumerated. + Calling synchronous methods on the cache within this callback will likely cause a deadlock. + + @see enumerateObjectsWithBlockAsync:completionBlock: + @param block A block to be executed for every object in the cache. + + @warning Do not call this method within the event blocks (, etc.) + Instead use the asynchronous version, . + + */ +- (void)enumerateObjectsWithBlock:(PIN_NOESCAPE PINCacheObjectEnumerationBlock)block; + +@end + + +#pragma mark - Deprecated + +typedef void (^PINMemoryCacheBlock)(PINMemoryCache *cache); +typedef void (^PINMemoryCacheObjectBlock)(PINMemoryCache *cache, NSString *key, id _Nullable object); +typedef void (^PINMemoryCacheContainmentBlock)(BOOL containsObject); + +@interface PINMemoryCache (Deprecated) +- (void)containsObjectForKey:(NSString *)key block:(PINMemoryCacheContainmentBlock)block __attribute__((deprecated)); +- (void)objectForKey:(NSString *)key block:(nullable PINMemoryCacheObjectBlock)block __attribute__((deprecated)); +- (void)setObject:(id)object forKey:(NSString *)key block:(nullable PINMemoryCacheObjectBlock)block __attribute__((deprecated)); +- (void)setObject:(id)object forKey:(NSString *)key withCost:(NSUInteger)cost block:(nullable PINMemoryCacheObjectBlock)block __attribute__((deprecated)); +- (void)removeObjectForKey:(NSString *)key block:(nullable PINMemoryCacheObjectBlock)block __attribute__((deprecated)); +- (void)trimToDate:(NSDate *)date block:(nullable PINMemoryCacheBlock)block __attribute__((deprecated)); +- (void)trimToCost:(NSUInteger)cost block:(nullable PINMemoryCacheBlock)block __attribute__((deprecated)); +- (void)trimToCostByDate:(NSUInteger)cost block:(nullable PINMemoryCacheBlock)block __attribute__((deprecated)); +- (void)removeAllObjects:(nullable PINMemoryCacheBlock)block __attribute__((deprecated)); +- (void)enumerateObjectsWithBlock:(PINMemoryCacheObjectBlock)block completionBlock:(nullable PINMemoryCacheBlock)completionBlock __attribute__((deprecated)); +- (void)setTtlCache:(BOOL)ttlCache DEPRECATED_MSG_ATTRIBUTE("ttlCache is no longer a settable property and must now be set via initializer."); +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/PINCache/Source/PINMemoryCache.m b/Starbucks_Clone_TexBrother/Pods/PINCache/Source/PINMemoryCache.m new file mode 100644 index 0000000..85c45f5 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINCache/Source/PINMemoryCache.m @@ -0,0 +1,932 @@ +// PINCache is a modified version of TMCache +// Modifications by Garrett Moon +// Copyright (c) 2015 Pinterest. All rights reserved. + +#import "PINMemoryCache.h" + +#import + +#if SWIFT_PACKAGE +@import PINOperation; +#else +#import +#endif + +#if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_4_0 +#import +#endif + +static NSString * const PINMemoryCachePrefix = @"com.pinterest.PINMemoryCache"; +static NSString * const PINMemoryCacheSharedName = @"PINMemoryCacheSharedName"; + +@interface PINMemoryCache () +@property (copy, nonatomic) NSString *name; +@property (strong, nonatomic) PINOperationQueue *operationQueue; +@property (assign, nonatomic) pthread_mutex_t mutex; +@property (strong, nonatomic) NSMutableDictionary *dictionary; +@property (strong, nonatomic) NSMutableDictionary *createdDates; +@property (strong, nonatomic) NSMutableDictionary *accessDates; +@property (strong, nonatomic) NSMutableDictionary *costs; +@property (strong, nonatomic) NSMutableDictionary *ageLimits; +@end + +@implementation PINMemoryCache + +@synthesize name = _name; +@synthesize ageLimit = _ageLimit; +@synthesize costLimit = _costLimit; +@synthesize totalCost = _totalCost; +@synthesize ttlCache = _ttlCache; +@synthesize willAddObjectBlock = _willAddObjectBlock; +@synthesize willRemoveObjectBlock = _willRemoveObjectBlock; +@synthesize willRemoveAllObjectsBlock = _willRemoveAllObjectsBlock; +@synthesize didAddObjectBlock = _didAddObjectBlock; +@synthesize didRemoveObjectBlock = _didRemoveObjectBlock; +@synthesize didRemoveAllObjectsBlock = _didRemoveAllObjectsBlock; +@synthesize didReceiveMemoryWarningBlock = _didReceiveMemoryWarningBlock; +@synthesize didEnterBackgroundBlock = _didEnterBackgroundBlock; + +#pragma mark - Initialization - + +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; + + __unused int result = pthread_mutex_destroy(&_mutex); + NSCAssert(result == 0, @"Failed to destroy lock in PINMemoryCache %p. Code: %d", (void *)self, result); +} + +- (instancetype)init +{ + return [self initWithOperationQueue:[PINOperationQueue sharedOperationQueue]]; +} + +- (instancetype)initWithOperationQueue:(PINOperationQueue *)operationQueue +{ + return [self initWithName:PINMemoryCacheSharedName operationQueue:operationQueue]; +} + +- (instancetype)initWithName:(NSString *)name operationQueue:(PINOperationQueue *)operationQueue +{ + return [self initWithName:name operationQueue:operationQueue ttlCache:NO]; +} + +- (instancetype)initWithName:(NSString *)name operationQueue:(PINOperationQueue *)operationQueue ttlCache:(BOOL)ttlCache +{ + if (self = [super init]) { + __unused int result = pthread_mutex_init(&_mutex, NULL); + NSAssert(result == 0, @"Failed to init lock in PINMemoryCache %@. Code: %d", self, result); + + _name = [name copy]; + _operationQueue = operationQueue; + _ttlCache = ttlCache; + + _dictionary = [[NSMutableDictionary alloc] init]; + _createdDates = [[NSMutableDictionary alloc] init]; + _accessDates = [[NSMutableDictionary alloc] init]; + _costs = [[NSMutableDictionary alloc] init]; + _ageLimits = [[NSMutableDictionary alloc] init]; + + _willAddObjectBlock = nil; + _willRemoveObjectBlock = nil; + _willRemoveAllObjectsBlock = nil; + + _didAddObjectBlock = nil; + _didRemoveObjectBlock = nil; + _didRemoveAllObjectsBlock = nil; + + _didReceiveMemoryWarningBlock = nil; + _didEnterBackgroundBlock = nil; + + _ageLimit = 0.0; + _costLimit = 0; + _totalCost = 0; + + _removeAllObjectsOnMemoryWarning = YES; + _removeAllObjectsOnEnteringBackground = YES; + +#if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_4_0 && !TARGET_OS_WATCH + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(didReceiveEnterBackgroundNotification:) + name:UIApplicationDidEnterBackgroundNotification + object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(didReceiveMemoryWarningNotification:) + name:UIApplicationDidReceiveMemoryWarningNotification + object:nil]; + +#endif + } + return self; +} + ++ (PINMemoryCache *)sharedCache +{ + static PINMemoryCache *cache; + static dispatch_once_t predicate; + + dispatch_once(&predicate, ^{ + cache = [[PINMemoryCache alloc] init]; + }); + + return cache; +} + +#pragma mark - Private Methods - + +- (void)didReceiveMemoryWarningNotification:(NSNotification *)notification { + if (self.removeAllObjectsOnMemoryWarning) { + [self removeAllObjectsAsync:nil]; + } else { + [self removeExpiredObjects]; + } + + [self.operationQueue scheduleOperation:^{ + [self lock]; + PINCacheBlock didReceiveMemoryWarningBlock = self->_didReceiveMemoryWarningBlock; + [self unlock]; + + if (didReceiveMemoryWarningBlock) + didReceiveMemoryWarningBlock(self); + } withPriority:PINOperationQueuePriorityHigh]; +} + +- (void)didReceiveEnterBackgroundNotification:(NSNotification *)notification +{ + if (self.removeAllObjectsOnEnteringBackground) + [self removeAllObjectsAsync:nil]; + + [self.operationQueue scheduleOperation:^{ + [self lock]; + PINCacheBlock didEnterBackgroundBlock = self->_didEnterBackgroundBlock; + [self unlock]; + + if (didEnterBackgroundBlock) + didEnterBackgroundBlock(self); + } withPriority:PINOperationQueuePriorityHigh]; +} + +- (void)removeObjectAndExecuteBlocksForKey:(NSString *)key +{ + [self lock]; + id object = _dictionary[key]; + NSNumber *cost = _costs[key]; + PINCacheObjectBlock willRemoveObjectBlock = _willRemoveObjectBlock; + PINCacheObjectBlock didRemoveObjectBlock = _didRemoveObjectBlock; + [self unlock]; + + if (willRemoveObjectBlock) + willRemoveObjectBlock(self, key, object); + + [self lock]; + if (cost) + _totalCost -= [cost unsignedIntegerValue]; + + [_dictionary removeObjectForKey:key]; + [_createdDates removeObjectForKey:key]; + [_accessDates removeObjectForKey:key]; + [_costs removeObjectForKey:key]; + [_ageLimits removeObjectForKey:key]; + [self unlock]; + + if (didRemoveObjectBlock) + didRemoveObjectBlock(self, key, nil); +} + +- (void)trimMemoryToDate:(NSDate *)trimDate +{ + [self lock]; + NSDictionary *createdDates = [_createdDates copy]; + NSDictionary *ageLimits = [_ageLimits copy]; + [self unlock]; + + [createdDates enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, NSDate * _Nonnull createdDate, BOOL * _Nonnull stop) { + NSTimeInterval ageLimit = [ageLimits[key] doubleValue]; + if (!createdDate || ageLimit > 0.0) { + return; + } + if ([createdDate compare:trimDate] == NSOrderedAscending) { // older than trim date + [self removeObjectAndExecuteBlocksForKey:key]; + } + }]; +} + +- (void)removeExpiredObjects +{ + [self lock]; + NSDictionary *createdDates = [_createdDates copy]; + NSDictionary *ageLimits = [_ageLimits copy]; + NSTimeInterval globalAgeLimit = self->_ageLimit; + [self unlock]; + + NSDate *now = [NSDate date]; + for (NSString *key in ageLimits) { + NSDate *createdDate = createdDates[key]; + NSTimeInterval ageLimit = [ageLimits[key] doubleValue] ?: globalAgeLimit; + if (!createdDate) + continue; + + NSDate *expirationDate = [createdDate dateByAddingTimeInterval:ageLimit]; + if ([expirationDate compare:now] == NSOrderedAscending) { // Expiration date has passed + [self removeObjectAndExecuteBlocksForKey:key]; + } + } +} + +- (void)trimToCostLimit:(NSUInteger)limit +{ + NSUInteger totalCost = 0; + + [self lock]; + totalCost = _totalCost; + NSArray *keysSortedByCost = [_costs keysSortedByValueUsingSelector:@selector(compare:)]; + [self unlock]; + + if (totalCost <= limit) { + return; + } + + for (NSString *key in [keysSortedByCost reverseObjectEnumerator]) { // costliest objects first + [self removeObjectAndExecuteBlocksForKey:key]; + + [self lock]; + totalCost = _totalCost; + [self unlock]; + + if (totalCost <= limit) + break; + } +} + +- (void)trimToCostLimitByDate:(NSUInteger)limit +{ + if (self.isTTLCache) { + [self removeExpiredObjects]; + } + + NSUInteger totalCost = 0; + + [self lock]; + totalCost = _totalCost; + NSArray *keysSortedByAccessDate = [_accessDates keysSortedByValueUsingSelector:@selector(compare:)]; + [self unlock]; + + if (totalCost <= limit) + return; + + for (NSString *key in keysSortedByAccessDate) { // oldest objects first + [self removeObjectAndExecuteBlocksForKey:key]; + + [self lock]; + totalCost = _totalCost; + [self unlock]; + if (totalCost <= limit) + break; + } +} + +- (void)trimToAgeLimitRecursively +{ + [self lock]; + NSTimeInterval ageLimit = _ageLimit; + [self unlock]; + + if (ageLimit == 0.0) + return; + + NSDate *date = [[NSDate alloc] initWithTimeIntervalSinceNow:-ageLimit]; + + [self trimMemoryToDate:date]; + + dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(ageLimit * NSEC_PER_SEC)); + dispatch_after(time, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){ + // Ensure that ageLimit is the same as when we were scheduled, otherwise, we've been + // rescheduled (another dispatch_after was issued) and should cancel. + BOOL shouldReschedule = YES; + [self lock]; + if (ageLimit != self->_ageLimit) { + shouldReschedule = NO; + } + [self unlock]; + + if (shouldReschedule) { + [self.operationQueue scheduleOperation:^{ + [self trimToAgeLimitRecursively]; + } withPriority:PINOperationQueuePriorityLow]; + } + }); +} + +#pragma mark - Public Asynchronous Methods - + +- (void)containsObjectForKeyAsync:(NSString *)key completion:(PINCacheObjectContainmentBlock)block +{ + if (!key || !block) + return; + + [self.operationQueue scheduleOperation:^{ + BOOL containsObject = [self containsObjectForKey:key]; + + block(containsObject); + } withPriority:PINOperationQueuePriorityHigh]; +} + +- (void)objectForKeyAsync:(NSString *)key completion:(PINCacheObjectBlock)block +{ + if (block == nil) { + return; + } + + [self.operationQueue scheduleOperation:^{ + id object = [self objectForKey:key]; + + block(self, key, object); + } withPriority:PINOperationQueuePriorityHigh]; +} + +- (void)setObjectAsync:(id)object forKey:(NSString *)key completion:(PINCacheObjectBlock)block +{ + [self setObjectAsync:object forKey:key withCost:0 completion:block]; +} + +- (void)setObjectAsync:(id)object forKey:(NSString *)key withAgeLimit:(NSTimeInterval)ageLimit completion:(PINCacheObjectBlock)block +{ + [self setObjectAsync:object forKey:key withCost:0 ageLimit:ageLimit completion:block]; +} + +- (void)setObjectAsync:(id)object forKey:(NSString *)key withCost:(NSUInteger)cost completion:(PINCacheObjectBlock)block +{ + [self setObjectAsync:object forKey:key withCost:cost ageLimit:0.0 completion:block]; +} + +- (void)setObjectAsync:(id)object forKey:(NSString *)key withCost:(NSUInteger)cost ageLimit:(NSTimeInterval)ageLimit completion:(PINCacheObjectBlock)block +{ + [self.operationQueue scheduleOperation:^{ + [self setObject:object forKey:key withCost:cost ageLimit:ageLimit]; + + if (block) + block(self, key, object); + } withPriority:PINOperationQueuePriorityHigh]; +} + +- (void)removeObjectForKeyAsync:(NSString *)key completion:(PINCacheObjectBlock)block +{ + [self.operationQueue scheduleOperation:^{ + [self removeObjectForKey:key]; + + if (block) + block(self, key, nil); + } withPriority:PINOperationQueuePriorityLow]; +} + +- (void)trimToDateAsync:(NSDate *)trimDate completion:(PINCacheBlock)block +{ + [self.operationQueue scheduleOperation:^{ + [self trimToDate:trimDate]; + + if (block) + block(self); + } withPriority:PINOperationQueuePriorityLow]; +} + +- (void)trimToCostAsync:(NSUInteger)cost completion:(PINCacheBlock)block +{ + [self.operationQueue scheduleOperation:^{ + [self trimToCost:cost]; + + if (block) + block(self); + } withPriority:PINOperationQueuePriorityLow]; +} + +- (void)trimToCostByDateAsync:(NSUInteger)cost completion:(PINCacheBlock)block +{ + [self.operationQueue scheduleOperation:^{ + [self trimToCostByDate:cost]; + + if (block) + block(self); + } withPriority:PINOperationQueuePriorityLow]; +} + +- (void)removeExpiredObjectsAsync:(PINCacheBlock)block +{ + [self.operationQueue scheduleOperation:^{ + [self removeExpiredObjects]; + + if (block) + block(self); + } withPriority:PINOperationQueuePriorityLow]; +} + +- (void)removeAllObjectsAsync:(PINCacheBlock)block +{ + [self.operationQueue scheduleOperation:^{ + [self removeAllObjects]; + + if (block) + block(self); + } withPriority:PINOperationQueuePriorityLow]; +} + +- (void)enumerateObjectsWithBlockAsync:(PINCacheObjectEnumerationBlock)block completionBlock:(PINCacheBlock)completionBlock +{ + [self.operationQueue scheduleOperation:^{ + [self enumerateObjectsWithBlock:block]; + + if (completionBlock) + completionBlock(self); + } withPriority:PINOperationQueuePriorityLow]; +} + +#pragma mark - Public Synchronous Methods - + +- (BOOL)containsObjectForKey:(NSString *)key +{ + if (!key) + return NO; + + [self lock]; + BOOL containsObject = (_dictionary[key] != nil); + [self unlock]; + return containsObject; +} + +- (nullable id)objectForKey:(NSString *)key +{ + if (!key) + return nil; + + NSDate *now = [NSDate date]; + [self lock]; + id object = nil; + // If the cache should behave like a TTL cache, then only fetch the object if there's a valid ageLimit and the object is still alive + NSTimeInterval ageLimit = [_ageLimits[key] doubleValue] ?: self->_ageLimit; + if (!self->_ttlCache || ageLimit <= 0 || fabs([[_createdDates objectForKey:key] timeIntervalSinceDate:now]) < ageLimit) { + object = _dictionary[key]; + } + [self unlock]; + + if (object) { + [self lock]; + _accessDates[key] = now; + [self unlock]; + } + + return object; +} + +- (id)objectForKeyedSubscript:(NSString *)key +{ + return [self objectForKey:key]; +} + +- (void)setObject:(id)object forKey:(NSString *)key +{ + [self setObject:object forKey:key withCost:0]; +} + +- (void)setObject:(id)object forKey:(NSString *)key withAgeLimit:(NSTimeInterval)ageLimit +{ + [self setObject:object forKey:key withCost:0 ageLimit:ageLimit]; +} + +- (void)setObject:(id)object forKeyedSubscript:(NSString *)key +{ + if (object == nil) { + [self removeObjectForKey:key]; + } else { + [self setObject:object forKey:key]; + } +} + +- (void)setObject:(id)object forKey:(NSString *)key withCost:(NSUInteger)cost +{ + [self setObject:object forKey:key withCost:cost ageLimit:0.0]; +} + +- (void)setObject:(id)object forKey:(NSString *)key withCost:(NSUInteger)cost ageLimit:(NSTimeInterval)ageLimit +{ + NSAssert(ageLimit <= 0.0 || (ageLimit > 0.0 && _ttlCache), @"ttlCache must be set to YES if setting an object-level age limit."); + + if (!key || !object) + return; + + [self lock]; + PINCacheObjectBlock willAddObjectBlock = _willAddObjectBlock; + PINCacheObjectBlock didAddObjectBlock = _didAddObjectBlock; + NSUInteger costLimit = _costLimit; + [self unlock]; + + if (willAddObjectBlock) + willAddObjectBlock(self, key, object); + + [self lock]; + NSNumber* oldCost = _costs[key]; + if (oldCost) + _totalCost -= [oldCost unsignedIntegerValue]; + + NSDate *now = [NSDate date]; + _dictionary[key] = object; + _createdDates[key] = now; + _accessDates[key] = now; + _costs[key] = @(cost); + + if (ageLimit > 0.0) { + _ageLimits[key] = @(ageLimit); + } else { + [_ageLimits removeObjectForKey:key]; + } + + _totalCost += cost; + [self unlock]; + + if (didAddObjectBlock) + didAddObjectBlock(self, key, object); + + if (costLimit > 0) + [self trimToCostByDate:costLimit]; +} + +- (void)removeObjectForKey:(NSString *)key +{ + if (!key) + return; + + [self removeObjectAndExecuteBlocksForKey:key]; +} + +- (void)trimToDate:(NSDate *)trimDate +{ + if (!trimDate) + return; + + if ([trimDate isEqualToDate:[NSDate distantPast]]) { + [self removeAllObjects]; + return; + } + + [self trimMemoryToDate:trimDate]; +} + +- (void)trimToCost:(NSUInteger)cost +{ + [self trimToCostLimit:cost]; +} + +- (void)trimToCostByDate:(NSUInteger)cost +{ + [self trimToCostLimitByDate:cost]; +} + +- (void)removeAllObjects +{ + [self lock]; + PINCacheBlock willRemoveAllObjectsBlock = _willRemoveAllObjectsBlock; + PINCacheBlock didRemoveAllObjectsBlock = _didRemoveAllObjectsBlock; + [self unlock]; + + if (willRemoveAllObjectsBlock) + willRemoveAllObjectsBlock(self); + + [self lock]; + [_dictionary removeAllObjects]; + [_createdDates removeAllObjects]; + [_accessDates removeAllObjects]; + [_costs removeAllObjects]; + [_ageLimits removeAllObjects]; + + _totalCost = 0; + [self unlock]; + + if (didRemoveAllObjectsBlock) + didRemoveAllObjectsBlock(self); + +} + +- (void)enumerateObjectsWithBlock:(PIN_NOESCAPE PINCacheObjectEnumerationBlock)block +{ + if (!block) + return; + + [self lock]; + NSDate *now = [NSDate date]; + NSArray *keysSortedByCreatedDate = [_createdDates keysSortedByValueUsingSelector:@selector(compare:)]; + + for (NSString *key in keysSortedByCreatedDate) { + // If the cache should behave like a TTL cache, then only fetch the object if there's a valid ageLimit and the object is still alive + NSTimeInterval ageLimit = [_ageLimits[key] doubleValue] ?: self->_ageLimit; + if (!self->_ttlCache || ageLimit <= 0 || fabs([[_createdDates objectForKey:key] timeIntervalSinceDate:now]) < ageLimit) { + BOOL stop = NO; + block(self, key, _dictionary[key], &stop); + if (stop) + break; + } + } + [self unlock]; +} + +#pragma mark - Public Thread Safe Accessors - + +- (PINCacheObjectBlock)willAddObjectBlock +{ + [self lock]; + PINCacheObjectBlock block = _willAddObjectBlock; + [self unlock]; + + return block; +} + +- (void)setWillAddObjectBlock:(PINCacheObjectBlock)block +{ + [self lock]; + _willAddObjectBlock = [block copy]; + [self unlock]; +} + +- (PINCacheObjectBlock)willRemoveObjectBlock +{ + [self lock]; + PINCacheObjectBlock block = _willRemoveObjectBlock; + [self unlock]; + + return block; +} + +- (void)setWillRemoveObjectBlock:(PINCacheObjectBlock)block +{ + [self lock]; + _willRemoveObjectBlock = [block copy]; + [self unlock]; +} + +- (PINCacheBlock)willRemoveAllObjectsBlock +{ + [self lock]; + PINCacheBlock block = _willRemoveAllObjectsBlock; + [self unlock]; + + return block; +} + +- (void)setWillRemoveAllObjectsBlock:(PINCacheBlock)block +{ + [self lock]; + _willRemoveAllObjectsBlock = [block copy]; + [self unlock]; +} + +- (PINCacheObjectBlock)didAddObjectBlock +{ + [self lock]; + PINCacheObjectBlock block = _didAddObjectBlock; + [self unlock]; + + return block; +} + +- (void)setDidAddObjectBlock:(PINCacheObjectBlock)block +{ + [self lock]; + _didAddObjectBlock = [block copy]; + [self unlock]; +} + +- (PINCacheObjectBlock)didRemoveObjectBlock +{ + [self lock]; + PINCacheObjectBlock block = _didRemoveObjectBlock; + [self unlock]; + + return block; +} + +- (void)setDidRemoveObjectBlock:(PINCacheObjectBlock)block +{ + [self lock]; + _didRemoveObjectBlock = [block copy]; + [self unlock]; +} + +- (PINCacheBlock)didRemoveAllObjectsBlock +{ + [self lock]; + PINCacheBlock block = _didRemoveAllObjectsBlock; + [self unlock]; + + return block; +} + +- (void)setDidRemoveAllObjectsBlock:(PINCacheBlock)block +{ + [self lock]; + _didRemoveAllObjectsBlock = [block copy]; + [self unlock]; +} + +- (PINCacheBlock)didReceiveMemoryWarningBlock +{ + [self lock]; + PINCacheBlock block = _didReceiveMemoryWarningBlock; + [self unlock]; + + return block; +} + +- (void)setDidReceiveMemoryWarningBlock:(PINCacheBlock)block +{ + [self lock]; + _didReceiveMemoryWarningBlock = [block copy]; + [self unlock]; +} + +- (PINCacheBlock)didEnterBackgroundBlock +{ + [self lock]; + PINCacheBlock block = _didEnterBackgroundBlock; + [self unlock]; + + return block; +} + +- (void)setDidEnterBackgroundBlock:(PINCacheBlock)block +{ + [self lock]; + _didEnterBackgroundBlock = [block copy]; + [self unlock]; +} + +- (NSTimeInterval)ageLimit +{ + [self lock]; + NSTimeInterval ageLimit = _ageLimit; + [self unlock]; + + return ageLimit; +} + +- (void)setAgeLimit:(NSTimeInterval)ageLimit +{ + [self lock]; + _ageLimit = ageLimit; + [self unlock]; + + [self trimToAgeLimitRecursively]; +} + +- (NSUInteger)costLimit +{ + [self lock]; + NSUInteger costLimit = _costLimit; + [self unlock]; + + return costLimit; +} + +- (void)setCostLimit:(NSUInteger)costLimit +{ + [self lock]; + _costLimit = costLimit; + [self unlock]; + + if (costLimit > 0) + [self trimToCostLimitByDate:costLimit]; +} + +- (NSUInteger)totalCost +{ + [self lock]; + NSUInteger cost = _totalCost; + [self unlock]; + + return cost; +} + +- (BOOL)isTTLCache { + BOOL isTTLCache; + + [self lock]; + isTTLCache = _ttlCache; + [self unlock]; + + return isTTLCache; +} + +- (void)lock +{ + __unused int result = pthread_mutex_lock(&_mutex); + NSAssert(result == 0, @"Failed to lock PINMemoryCache %@. Code: %d", self, result); +} + +- (void)unlock +{ + __unused int result = pthread_mutex_unlock(&_mutex); + NSAssert(result == 0, @"Failed to unlock PINMemoryCache %@. Code: %d", self, result); +} + +@end + + +#pragma mark - Deprecated + +@implementation PINMemoryCache (Deprecated) + +- (void)containsObjectForKey:(NSString *)key block:(PINMemoryCacheContainmentBlock)block +{ + [self containsObjectForKeyAsync:key completion:block]; +} + +- (void)objectForKey:(NSString *)key block:(nullable PINMemoryCacheObjectBlock)block +{ + [self objectForKeyAsync:key completion:^(id memoryCache, NSString *memoryCacheKey, id memoryCacheObject) { + if (block) { + block((PINMemoryCache *)memoryCache, memoryCacheKey, memoryCacheObject); + } + }]; +} + +- (void)setObject:(id)object forKey:(NSString *)key block:(nullable PINMemoryCacheObjectBlock)block +{ + [self setObjectAsync:object forKey:key completion:^(id memoryCache, NSString *memoryCacheKey, id memoryCacheObject) { + if (block) { + block((PINMemoryCache *)memoryCache, memoryCacheKey, memoryCacheObject); + } + }]; +} + +- (void)setObject:(id)object forKey:(NSString *)key withCost:(NSUInteger)cost block:(nullable PINMemoryCacheObjectBlock)block +{ + [self setObjectAsync:object forKey:key withCost:cost completion:^(id memoryCache, NSString *memoryCacheKey, id memoryCacheObject) { + if (block) { + block((PINMemoryCache *)memoryCache, memoryCacheKey, memoryCacheObject); + } + }]; +} + +- (void)removeObjectForKey:(NSString *)key block:(nullable PINMemoryCacheObjectBlock)block +{ + [self removeObjectForKeyAsync:key completion:^(id memoryCache, NSString *memoryCacheKey, id memoryCacheObject) { + if (block) { + block((PINMemoryCache *)memoryCache, memoryCacheKey, memoryCacheObject); + } + }]; +} + +- (void)trimToDate:(NSDate *)date block:(nullable PINMemoryCacheBlock)block +{ + [self trimToDateAsync:date completion:^(id memoryCache) { + if (block) { + block((PINMemoryCache *)memoryCache); + } + }]; +} + +- (void)trimToCost:(NSUInteger)cost block:(nullable PINMemoryCacheBlock)block +{ + [self trimToCostAsync:cost completion:^(id memoryCache) { + if (block) { + block((PINMemoryCache *)memoryCache); + } + }]; +} + +- (void)trimToCostByDate:(NSUInteger)cost block:(nullable PINMemoryCacheBlock)block +{ + [self trimToCostByDateAsync:cost completion:^(id memoryCache) { + if (block) { + block((PINMemoryCache *)memoryCache); + } + }]; +} + +- (void)removeAllObjects:(nullable PINMemoryCacheBlock)block +{ + [self removeAllObjectsAsync:^(id memoryCache) { + if (block) { + block((PINMemoryCache *)memoryCache); + } + }]; +} + +- (void)enumerateObjectsWithBlock:(PINMemoryCacheObjectBlock)block completionBlock:(nullable PINMemoryCacheBlock)completionBlock +{ + [self enumerateObjectsWithBlockAsync:^(id _Nonnull cache, NSString * _Nonnull key, id _Nullable object, BOOL * _Nonnull stop) { + if ([cache isKindOfClass:[PINMemoryCache class]]) { + PINMemoryCache *memoryCache = (PINMemoryCache *)cache; + block(memoryCache, key, object); + } + } completionBlock:^(id memoryCache) { + if (completionBlock) { + completionBlock((PINMemoryCache *)memoryCache); + } + }]; +} + +- (void)setTtlCache:(BOOL)ttlCache +{ + [self lock]; + _ttlCache = ttlCache; + [self unlock]; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINOperation/LICENSE.txt b/Starbucks_Clone_TexBrother/Pods/PINOperation/LICENSE.txt new file mode 100644 index 0000000..d0381d6 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINOperation/LICENSE.txt @@ -0,0 +1,176 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/Starbucks_Clone_TexBrother/Pods/PINOperation/README.md b/Starbucks_Clone_TexBrother/Pods/PINOperation/README.md new file mode 100644 index 0000000..c7ac84e --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINOperation/README.md @@ -0,0 +1,47 @@ +# PINOperation + +[![CocoaPods](https://img.shields.io/cocoapods/v/PINOperation.svg)](http://cocoadocs.org/docsets/PINOperation/) +[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) +[![Build status](https://github.com/pinterest/PINOperation/workflows/CI/badge.svg)](https://github.com/pinterest/PINOperation/actions?query=workflow%3ACI+branch%3Amaster) + +## Fast, concurrency-limited task queue for iOS and macOS. + +## Installation + +### Manually + +[Download the latest tag](https://github.com/pinterest/PINOperation/tags) and drag the `PINOperation` folder into your Xcode project. + +Install the docs by double clicking the `.docset` file under `docs/`, or view them online at [cocoadocs.org](http://cocoadocs.org/docsets/PINOperation/) + +### Git Submodule + + git submodule add https://github.com/pinterest/PINOperation.git + git submodule update --init + +### CocoaPods + +Add [PINOperation](http://cocoapods.org/?q=name%3APINOperation) to your `Podfile` and run `pod install`. + +### Carthage + +Add the following line to your `Cartfile` and run `carthage update --platform ios`. Then follow [this instruction of Carthage](https://github.com/carthage/carthage#adding-frameworks-to-unit-tests-or-a-framework) to embed the framework. + +```github "pinterest/PINOperation"``` + +## Requirements + +__PINOperation__ requires iOS 8.0, tvOS 9.0, macOS 10.8 or watchOS 2.0 and greater. + +## Contact + +[Garrett Moon](mailto:garrett@pinterest.com) + +## License + +Copyright 2013 Tumblr, Inc. +Copyright 2015 Pinterest, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. [See the License](LICENSE.txt) for the specific language governing permissions and limitations under the License. diff --git a/Starbucks_Clone_TexBrother/Pods/PINOperation/Source/PINOperation.h b/Starbucks_Clone_TexBrother/Pods/PINOperation/Source/PINOperation.h new file mode 100644 index 0000000..68b074d --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINOperation/Source/PINOperation.h @@ -0,0 +1,12 @@ +// +// PINOperation.h +// PINOperation +// +// Created by Adlai Holler on 1/10/17. +// Copyright © 2017 Pinterest. All rights reserved. +// + +#import "PINOperationMacros.h" +#import "PINOperationTypes.h" +#import "PINOperationQueue.h" +#import "PINOperationGroup.h" diff --git a/Starbucks_Clone_TexBrother/Pods/PINOperation/Source/PINOperationGroup.h b/Starbucks_Clone_TexBrother/Pods/PINOperation/Source/PINOperationGroup.h new file mode 100644 index 0000000..0ae7dba --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINOperation/Source/PINOperationGroup.h @@ -0,0 +1,38 @@ +// +// PINOperationGroup.h +// PINQueue +// +// Created by Garrett Moon on 10/8/16. +// Copyright © 2016 Pinterest. All rights reserved. +// + +#import +#import "PINOperationTypes.h" +#import "PINOperationMacros.h" + +@class PINOperationQueue; + +NS_ASSUME_NONNULL_BEGIN + +@protocol PINGroupOperationReference; + +PINOP_SUBCLASSING_RESTRICTED +@interface PINOperationGroup : NSObject + +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)asyncOperationGroupWithQueue:(PINOperationQueue *)operationQueue; + +- (nullable id )addOperation:(dispatch_block_t)operation; +- (nullable id )addOperation:(dispatch_block_t)operation withPriority:(PINOperationQueuePriority)priority; +- (void)start; +- (void)cancel; +- (void)setCompletion:(dispatch_block_t)completion; +- (void)waitUntilComplete; + +@end + +@protocol PINGroupOperationReference + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/PINOperation/Source/PINOperationGroup.m b/Starbucks_Clone_TexBrother/Pods/PINOperation/Source/PINOperationGroup.m new file mode 100644 index 0000000..ddd2ed0 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINOperation/Source/PINOperationGroup.m @@ -0,0 +1,187 @@ +// +// PINOperationGroup.m +// PINQueue +// +// Created by Garrett Moon on 10/8/16. +// Copyright © 2016 Pinterest. All rights reserved. +// + +#import "PINOperationGroup.h" +#import "PINOperation.h" +#import + +@interface NSNumber (PINGroupOperationQueue) +@end + +@interface PINOperationGroup () +{ + pthread_mutex_t _lock; + + PINOperationQueue *_operationQueue; + NSMutableArray *_operations; + NSMutableArray *_operationPriorities; + NSMutableArray > *_operationReferences; + NSMapTable , id > *_groupToOperationReferences; + NSUInteger _operationReferenceCount; + + dispatch_group_t _group; + + dispatch_block_t _completion; + + BOOL _started; + BOOL _canceled; +} + +- (instancetype)initWithOperationQueue:(PINOperationQueue *)operationQueue NS_DESIGNATED_INITIALIZER; + +@end + +@implementation PINOperationGroup + +- (instancetype)initWithOperationQueue:(PINOperationQueue *)operationQueue +{ + if (self = [super init]) { + pthread_mutex_init(&_lock, NULL); + + _operationQueue = operationQueue; + + _operations = [[NSMutableArray alloc] init]; + _operationReferences = [[NSMutableArray alloc] init]; + _operationPriorities = [[NSMutableArray alloc] init]; + + _groupToOperationReferences = [NSMapTable weakToStrongObjectsMapTable]; + _group = dispatch_group_create(); + } + return self; +} + +- (void)dealloc +{ + pthread_mutex_destroy(&_lock); +} + ++ (instancetype)asyncOperationGroupWithQueue:(PINOperationQueue *)operationQueue +{ + return [[self alloc] initWithOperationQueue:operationQueue]; +} + +- (id )locked_nextOperationReference +{ + id reference = [NSNumber numberWithUnsignedInteger:++_operationReferenceCount]; + return reference; +} + +- (void)start +{ + [self lock]; + NSAssert(_canceled == NO, @"Operation group canceled."); + if (_started == NO && _canceled == NO) { + for (NSUInteger idx = 0; idx < _operations.count; idx++) { + dispatch_group_enter(_group); + dispatch_block_t originalOperation = _operations[idx]; + dispatch_block_t groupBlock = ^{ + originalOperation(); + dispatch_group_leave(self->_group); + }; + + id operationReference = [_operationQueue scheduleOperation:groupBlock withPriority:[_operationPriorities[idx] unsignedIntegerValue]]; + [_groupToOperationReferences setObject:operationReference forKey:_operationReferences[idx]]; + } + + if (_completion) { + dispatch_queue_t completionQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + dispatch_group_notify(_group, completionQueue, ^{ + [self runCompletionIfNeeded]; + }); + } + + _operations = nil; + _operationPriorities = nil; + _operationReferences = nil; + } + [self unlock]; +} + +- (void)cancel +{ + [self lock]; + _canceled = YES; + + for (id operationReference in [_groupToOperationReferences objectEnumerator]) { + if ([_operationQueue cancelOperation:operationReference]) { + dispatch_group_leave(_group); + } + } + + //TODO just nil out instead? Does it make sense to support adding operations after cancelation? + [_groupToOperationReferences removeAllObjects]; + [_operations removeAllObjects]; + [_operationPriorities removeAllObjects]; + [_operationReferences removeAllObjects]; + + _completion = nil; + [self unlock]; +} + +- (id )addOperation:(dispatch_block_t)operation +{ + return [self addOperation:operation withPriority:PINOperationQueuePriorityDefault]; +} + +- (id )addOperation:(dispatch_block_t)operation withPriority:(PINOperationQueuePriority)priority +{ + [self lock]; + id reference = nil; + NSAssert(_started == NO && _canceled == NO, @"Operation group already started or canceled."); + if (_started == NO && _canceled == NO) { + reference = [self locked_nextOperationReference]; + [_operations addObject:operation]; + [_operationPriorities addObject:@(priority)]; + [_operationReferences addObject:reference]; + } + [self unlock]; + + return reference; +} + +- (void)setCompletion:(dispatch_block_t)completion +{ + [self lock]; + NSAssert(_started == NO && _canceled == NO, @"Operation group already started or canceled."); + if (_started == NO && _canceled == NO) { + _completion = completion; + } + [self unlock]; +} + +- (void)waitUntilComplete +{ + [self start]; + dispatch_group_wait(_group, DISPATCH_TIME_FOREVER); + [self runCompletionIfNeeded]; +} + +- (void)runCompletionIfNeeded +{ + dispatch_block_t completion; + [self lock]; + completion = _completion; + _completion = nil; + [self unlock]; + + if (completion) { + completion(); + } +} + +- (void)lock +{ + pthread_mutex_lock(&_lock); +} + +- (void)unlock +{ + pthread_mutex_unlock(&_lock); +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINOperation/Source/PINOperationMacros.h b/Starbucks_Clone_TexBrother/Pods/PINOperation/Source/PINOperationMacros.h new file mode 100644 index 0000000..5be3a21 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINOperation/Source/PINOperationMacros.h @@ -0,0 +1,15 @@ +// +// PINOperationMacros.h +// PINOperation +// +// Created by Adlai Holler on 1/10/17. +// Copyright © 2017 Pinterest. All rights reserved. +// + +#ifndef PINOP_SUBCLASSING_RESTRICTED +#if defined(__has_attribute) && __has_attribute(objc_subclassing_restricted) +#define PINOP_SUBCLASSING_RESTRICTED __attribute__((objc_subclassing_restricted)) +#else +#define PINOP_SUBCLASSING_RESTRICTED +#endif // #if defined(__has_attribute) && __has_attribute(objc_subclassing_restricted) +#endif // #ifndef PINOP_SUBCLASSING_RESTRICTED diff --git a/Starbucks_Clone_TexBrother/Pods/PINOperation/Source/PINOperationQueue.h b/Starbucks_Clone_TexBrother/Pods/PINOperation/Source/PINOperationQueue.h new file mode 100644 index 0000000..07eacbe --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINOperation/Source/PINOperationQueue.h @@ -0,0 +1,141 @@ +// +// PINOperationQueue.h +// Pods +// +// Created by Garrett Moon on 8/23/16. +// +// + +#import +#import "PINOperationTypes.h" +#import "PINOperationMacros.h" + +NS_ASSUME_NONNULL_BEGIN + +typedef void(^PINOperationBlock)(id _Nullable data); +typedef _Nullable id(^PINOperationDataCoalescingBlock)(id _Nullable existingData, id _Nullable newData); + +@protocol PINOperationReference; + +PINOP_SUBCLASSING_RESTRICTED +@interface PINOperationQueue : NSObject + +- (instancetype)init NS_UNAVAILABLE; + +/** + * Initializes and returns a newly allocated operation queue with the specified number of maximum concurrent operations. + * + * @param maxConcurrentOperations The maximum number of queued operations that can execute at the same time. + * + */ +- (instancetype)initWithMaxConcurrentOperations:(NSUInteger)maxConcurrentOperations; + +/** + * Initializes and returns a newly allocated operation queue with the specified number of maximum concurrent operations and the concurrent queue they will be scheduled on. + * + * @param maxConcurrentOperations The maximum number of queued operations that can execute at the same time. + * @param concurrentQueue The operation queue to schedule concurrent operations + * + */ +- (instancetype)initWithMaxConcurrentOperations:(NSUInteger)maxConcurrentOperations concurrentQueue:(dispatch_queue_t)concurrentQueue NS_DESIGNATED_INITIALIZER; + +/** + * Returns the shared instance of the PINOperationQueue class. + */ ++ (instancetype)sharedOperationQueue; + +/** + * Adds the specified operation object to the receiver. + * + * @param operation The operation object to be added to the queue. + * + */ +- (id )scheduleOperation:(dispatch_block_t)operation; + +/** + * Adds the specified operation object to the receiver. + * + * @param operation The operation object to be added to the queue. + * @param priority The execution priority of the operation in an operation queue. + */ +- (id )scheduleOperation:(dispatch_block_t)operation withPriority:(PINOperationQueuePriority)priority; + +/** + * Adds the specified operation object to the receiver. + * + * @param operation The operation object to be added to the queue. + * @param priority The execution priority of the operation in an operation queue. + * @param identifier A string that identifies the operations that can be coalesced. + * @param coalescingData The optional data consumed by this operation that needs to be updated/coalesced with data of a new operation when coalescing the two operations happens. + * @param dataCoalescingBlock The optional block called to update/coalesce the data of this operation with data of a new operation when coalescing the two operations happens. + * @param completion The block to execute after the operation finished. + */ +- (id )scheduleOperation:(PINOperationBlock)operation + withPriority:(PINOperationQueuePriority)priority + identifier:(nullable NSString *)identifier + coalescingData:(nullable id)coalescingData + dataCoalescingBlock:(nullable PINOperationDataCoalescingBlock)dataCoalescingBlock + completion:(nullable dispatch_block_t)completion; + +/** + * The maximum number of queued operations that can execute at the same time. + * + * @discussion The value in this property affects only the operations that the current queue has executing at the same time. Other operation queues can also execute their maximum number of operations in parallel. + * Reducing the number of concurrent operations does not affect any operations that are currently executing. + * + * Setting this value to 1 the operations will not be processed by priority as the operations will processed in a FIFO order to prevent deadlocks if operations depend on certain other operations to run in order. + * + */ +@property (assign) NSUInteger maxConcurrentOperations; + +/** + * Marks the operation as cancelled + */ +- (BOOL)cancelOperation:(id )operationReference; + +/** + * Cancels all queued operations + */ +- (void)cancelAllOperations; + +/** + * Blocks the current thread until all of the receiver’s queued and executing operations finish executing. + * + * @discussion When called, this method blocks the current thread and waits for the receiver’s current and queued + * operations to finish executing. While the current thread is blocked, the receiver continues to launch already + * queued operations and monitor those that are executing. + * + * @warning This should never be called from within an operation submitted to the PINOperationQueue as this will result + * in a deadlock. + */ +- (void)waitUntilAllOperationsAreFinished; + +/** + * Sets the priority for a operation via it's reference + * + * @param priority The new priority for the operation + * @param reference The reference for the operation + * + */ +- (void)setOperationPriority:(PINOperationQueuePriority)priority withReference:(id )reference; + +#pragma mark - Deprecated + +- (id )addOperation:(dispatch_block_t)operation __deprecated_msg("Use scheduleOperation: instead."); + +- (id )addOperation:(dispatch_block_t)operation withPriority:(PINOperationQueuePriority)priority __deprecated_msg("Use scheduleOperation:withPriority: instead."); + +- (id )addOperation:(PINOperationBlock)operation + withPriority:(PINOperationQueuePriority)priority + identifier:(nullable NSString *)identifier + coalescingData:(nullable id)coalescingData + dataCoalescingBlock:(nullable PINOperationDataCoalescingBlock)dataCoalescingBlock + completion:(nullable dispatch_block_t)completion __deprecated_msg("Use scheduleOperation:withPriority:identifier:coalescingData:dataCoalescingBlock:completion: instead."); + +@end + +@protocol PINOperationReference + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/PINOperation/Source/PINOperationQueue.m b/Starbucks_Clone_TexBrother/Pods/PINOperation/Source/PINOperationQueue.m new file mode 100644 index 0000000..5d0a38f --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINOperation/Source/PINOperationQueue.m @@ -0,0 +1,471 @@ +// +// PINOperationQueue.m +// Pods +// +// Created by Garrett Moon on 8/23/16. +// +// + +#import "PINOperationQueue.h" +#import + +@class PINOperation; + +@interface NSNumber (PINOperationQueue) + +@end + +@interface PINOperationQueue () { + pthread_mutex_t _lock; + //increments with every operation to allow cancelation + NSUInteger _operationReferenceCount; + NSUInteger _maxConcurrentOperations; + + dispatch_group_t _group; + + dispatch_queue_t _serialQueue; + BOOL _serialQueueBusy; + + dispatch_semaphore_t _concurrentSemaphore; + dispatch_queue_t _concurrentQueue; + dispatch_queue_t _semaphoreQueue; + + NSMutableOrderedSet *_queuedOperations; + NSMutableOrderedSet *_lowPriorityOperations; + NSMutableOrderedSet *_defaultPriorityOperations; + NSMutableOrderedSet *_highPriorityOperations; + + NSMapTable, PINOperation *> *_referenceToOperations; + NSMapTable *_identifierToOperations; +} + +@end + +@interface PINOperation : NSObject + +@property (nonatomic, strong) PINOperationBlock block; +@property (nonatomic, strong) id reference; +@property (nonatomic, assign) PINOperationQueuePriority priority; +@property (nonatomic, strong) NSMutableArray *completions; +@property (nonatomic, strong) NSString *identifier; +@property (nonatomic, strong) id data; + ++ (instancetype)operationWithBlock:(PINOperationBlock)block reference:(id )reference priority:(PINOperationQueuePriority)priority identifier:(nullable NSString *)identifier data:(nullable id)data completion:(nullable dispatch_block_t)completion; + +- (void)addCompletion:(nullable dispatch_block_t)completion; + +@end + +@implementation PINOperation + ++ (instancetype)operationWithBlock:(PINOperationBlock)block reference:(id)reference priority:(PINOperationQueuePriority)priority identifier:(NSString *)identifier data:(id)data completion:(dispatch_block_t)completion +{ + PINOperation *operation = [[self alloc] init]; + operation.block = block; + operation.reference = reference; + operation.priority = priority; + operation.identifier = identifier; + operation.data = data; + [operation addCompletion:completion]; + + return operation; +} + +- (void)addCompletion:(dispatch_block_t)completion +{ + if (completion == nil) { + return; + } + if (_completions == nil) { + _completions = [NSMutableArray array]; + } + [_completions addObject:completion]; +} + +@end + +@implementation PINOperationQueue + +- (instancetype)initWithMaxConcurrentOperations:(NSUInteger)maxConcurrentOperations +{ + return [self initWithMaxConcurrentOperations:maxConcurrentOperations concurrentQueue:dispatch_queue_create("PINOperationQueue Concurrent Queue", DISPATCH_QUEUE_CONCURRENT)]; +} + +- (instancetype)initWithMaxConcurrentOperations:(NSUInteger)maxConcurrentOperations concurrentQueue:(dispatch_queue_t)concurrentQueue +{ + if (self = [super init]) { + NSAssert(maxConcurrentOperations > 0, @"Max concurrent operations must be greater than 0."); + _maxConcurrentOperations = maxConcurrentOperations; + _operationReferenceCount = 0; + + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + //mutex must be recursive to allow scheduling of operations from within operations + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&_lock, &attr); + + _group = dispatch_group_create(); + + _serialQueue = dispatch_queue_create("PINOperationQueue Serial Queue", DISPATCH_QUEUE_SERIAL); + + _concurrentQueue = concurrentQueue; + + //Create a queue with max - 1 because this plus the serial queue add up to max. + _concurrentSemaphore = dispatch_semaphore_create(_maxConcurrentOperations - 1); + _semaphoreQueue = dispatch_queue_create("PINOperationQueue Serial Semaphore Queue", DISPATCH_QUEUE_SERIAL); + + _queuedOperations = [[NSMutableOrderedSet alloc] init]; + _lowPriorityOperations = [[NSMutableOrderedSet alloc] init]; + _defaultPriorityOperations = [[NSMutableOrderedSet alloc] init]; + _highPriorityOperations = [[NSMutableOrderedSet alloc] init]; + + _referenceToOperations = [NSMapTable weakToWeakObjectsMapTable]; + _identifierToOperations = [NSMapTable weakToWeakObjectsMapTable]; + } + return self; +} + +- (void)dealloc +{ + pthread_mutex_destroy(&_lock); +} + ++ (instancetype)sharedOperationQueue +{ + static PINOperationQueue *sharedOperationQueue = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + sharedOperationQueue = [[PINOperationQueue alloc] initWithMaxConcurrentOperations:MAX([[NSProcessInfo processInfo] activeProcessorCount], 2)]; + }); + return sharedOperationQueue; +} + +- (id )nextOperationReference +{ + [self lock]; + id reference = [NSNumber numberWithUnsignedInteger:++_operationReferenceCount]; + [self unlock]; + return reference; +} + +// Deprecated +- (id )addOperation:(dispatch_block_t)block +{ + return [self scheduleOperation:block]; +} + +- (id )scheduleOperation:(dispatch_block_t)block +{ + return [self scheduleOperation:block withPriority:PINOperationQueuePriorityDefault]; +} + +// Deprecated +- (id )addOperation:(dispatch_block_t)block withPriority:(PINOperationQueuePriority)priority +{ + return [self scheduleOperation:block withPriority:priority]; +} + +- (id )scheduleOperation:(dispatch_block_t)block withPriority:(PINOperationQueuePriority)priority +{ + PINOperation *operation = [PINOperation operationWithBlock:^(id data) { block(); } + reference:[self nextOperationReference] + priority:priority + identifier:nil + data:nil + completion:nil]; + [self lock]; + [self locked_addOperation:operation]; + [self unlock]; + + [self scheduleNextOperations:NO]; + + return operation.reference; +} + +// Deprecated +- (id)addOperation:(PINOperationBlock)block + withPriority:(PINOperationQueuePriority)priority + identifier:(NSString *)identifier + coalescingData:(id)coalescingData + dataCoalescingBlock:(PINOperationDataCoalescingBlock)dataCoalescingBlock + completion:(dispatch_block_t)completion +{ + return [self scheduleOperation:block + withPriority:priority + identifier:identifier + coalescingData:coalescingData + dataCoalescingBlock:dataCoalescingBlock + completion:completion]; +} + +- (id)scheduleOperation:(PINOperationBlock)block + withPriority:(PINOperationQueuePriority)priority + identifier:(NSString *)identifier + coalescingData:(id)coalescingData + dataCoalescingBlock:(PINOperationDataCoalescingBlock)dataCoalescingBlock + completion:(dispatch_block_t)completion +{ + id reference = nil; + BOOL isNewOperation = NO; + + [self lock]; + PINOperation *operation = nil; + if (identifier != nil && (operation = [_identifierToOperations objectForKey:identifier]) != nil) { + // There is an exisiting operation with the provided identifier, let's coalesce these operations + if (dataCoalescingBlock != nil) { + operation.data = dataCoalescingBlock(operation.data, coalescingData); + } + + [operation addCompletion:completion]; + } else { + isNewOperation = YES; + operation = [PINOperation operationWithBlock:block + reference:[self nextOperationReference] + priority:priority + identifier:identifier + data:coalescingData + completion:completion]; + [self locked_addOperation:operation]; + } + reference = operation.reference; + [self unlock]; + + if (isNewOperation) { + [self scheduleNextOperations:NO]; + } + + return reference; +} + +- (void)locked_addOperation:(PINOperation *)operation +{ + NSMutableOrderedSet *queue = [self operationQueueWithPriority:operation.priority]; + + dispatch_group_enter(_group); + [queue addObject:operation]; + [_queuedOperations addObject:operation]; + [_referenceToOperations setObject:operation forKey:operation.reference]; + if (operation.identifier != nil) { + [_identifierToOperations setObject:operation forKey:operation.identifier]; + } +} + +- (void)cancelAllOperations +{ + [self lock]; + for (PINOperation *operation in [[_referenceToOperations copy] objectEnumerator]) { + [self locked_cancelOperation:operation.reference]; + } + [self unlock]; +} + + +- (BOOL)cancelOperation:(id )operationReference +{ + [self lock]; + BOOL success = [self locked_cancelOperation:operationReference]; + [self unlock]; + return success; +} + +- (NSUInteger)maxConcurrentOperations +{ + [self lock]; + NSUInteger maxConcurrentOperations = _maxConcurrentOperations; + [self unlock]; + return maxConcurrentOperations; +} + +- (void)setMaxConcurrentOperations:(NSUInteger)maxConcurrentOperations +{ + NSAssert(maxConcurrentOperations > 0, @"Max concurrent operations must be greater than 0."); + [self lock]; + __block NSInteger difference = maxConcurrentOperations - _maxConcurrentOperations; + _maxConcurrentOperations = maxConcurrentOperations; + [self unlock]; + + if (difference == 0) { + return; + } + + dispatch_async(_semaphoreQueue, ^{ + while (difference != 0) { + if (difference > 0) { + dispatch_semaphore_signal(self->_concurrentSemaphore); + difference--; + } else { + dispatch_semaphore_wait(self->_concurrentSemaphore, DISPATCH_TIME_FOREVER); + difference++; + } + } + }); +} + +#pragma mark - private methods + +- (BOOL)locked_cancelOperation:(id )operationReference +{ + PINOperation *operation = [_referenceToOperations objectForKey:operationReference]; + BOOL success = [self locked_removeOperation:operation]; + if (success) { + dispatch_group_leave(_group); + } + return success; +} + +- (void)setOperationPriority:(PINOperationQueuePriority)priority withReference:(id )operationReference +{ + [self lock]; + PINOperation *operation = [_referenceToOperations objectForKey:operationReference]; + if (operation && operation.priority != priority) { + NSMutableOrderedSet *oldQueue = [self operationQueueWithPriority:operation.priority]; + [oldQueue removeObject:operation]; + + operation.priority = priority; + + NSMutableOrderedSet *queue = [self operationQueueWithPriority:priority]; + [queue addObject:operation]; + } + [self unlock]; +} + +/** + Schedule next operations schedules the next operation by queue order onto the serial queue if + it's available and one operation by priority order onto the concurrent queue. + */ +- (void)scheduleNextOperations:(BOOL)onlyCheckSerial +{ + [self lock]; + + //get next available operation in order, ignoring priority and run it on the serial queue + if (_serialQueueBusy == NO) { + PINOperation *operation = [self locked_nextOperationByQueue]; + if (operation) { + _serialQueueBusy = YES; + dispatch_async(_serialQueue, ^{ + operation.block(operation.data); + for (dispatch_block_t completion in operation.completions) { + completion(); + } + dispatch_group_leave(self->_group); + + [self lock]; + self->_serialQueueBusy = NO; + [self unlock]; + + //see if there are any other operations + [self scheduleNextOperations:YES]; + }); + } + } + + NSInteger maxConcurrentOperations = _maxConcurrentOperations; + + [self unlock]; + + if (onlyCheckSerial) { + return; + } + + //if only one concurrent operation is set, let's just use the serial queue for executing it + if (maxConcurrentOperations < 2) { + return; + } + + dispatch_async(_semaphoreQueue, ^{ + dispatch_semaphore_wait(self->_concurrentSemaphore, DISPATCH_TIME_FOREVER); + [self lock]; + PINOperation *operation = [self locked_nextOperationByPriority]; + [self unlock]; + + if (operation) { + dispatch_async(self->_concurrentQueue, ^{ + operation.block(operation.data); + for (dispatch_block_t completion in operation.completions) { + completion(); + } + dispatch_group_leave(self->_group); + dispatch_semaphore_signal(self->_concurrentSemaphore); + }); + } else { + dispatch_semaphore_signal(self->_concurrentSemaphore); + } + }); +} + +- (NSMutableOrderedSet *)operationQueueWithPriority:(PINOperationQueuePriority)priority +{ + switch (priority) { + case PINOperationQueuePriorityLow: + return _lowPriorityOperations; + + case PINOperationQueuePriorityDefault: + return _defaultPriorityOperations; + + case PINOperationQueuePriorityHigh: + return _highPriorityOperations; + + default: + NSAssert(NO, @"Invalid priority set"); + return _defaultPriorityOperations; + } +} + +//Call with lock held +- (PINOperation *)locked_nextOperationByPriority +{ + PINOperation *operation = [_highPriorityOperations firstObject]; + if (operation == nil) { + operation = [_defaultPriorityOperations firstObject]; + } + if (operation == nil) { + operation = [_lowPriorityOperations firstObject]; + } + if (operation) { + [self locked_removeOperation:operation]; + } + return operation; +} + +//Call with lock held +- (PINOperation *)locked_nextOperationByQueue +{ + PINOperation *operation = [_queuedOperations firstObject]; + [self locked_removeOperation:operation]; + return operation; +} + +- (void)waitUntilAllOperationsAreFinished +{ + [self scheduleNextOperations:NO]; + dispatch_group_wait(_group, DISPATCH_TIME_FOREVER); +} + +//Call with lock held +- (BOOL)locked_removeOperation:(PINOperation *)operation +{ + if (operation) { + NSMutableOrderedSet *priorityQueue = [self operationQueueWithPriority:operation.priority]; + if ([priorityQueue containsObject:operation]) { + [priorityQueue removeObject:operation]; + [_queuedOperations removeObject:operation]; + if (operation.identifier) { + [_identifierToOperations removeObjectForKey:operation.identifier]; + } + return YES; + } + } + return NO; +} + +- (void)lock +{ + pthread_mutex_lock(&_lock); +} + +- (void)unlock +{ + pthread_mutex_unlock(&_lock); +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINOperation/Source/PINOperationTypes.h b/Starbucks_Clone_TexBrother/Pods/PINOperation/Source/PINOperationTypes.h new file mode 100644 index 0000000..be7293d --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINOperation/Source/PINOperationTypes.h @@ -0,0 +1,15 @@ +// +// PINOperationTypes.h +// PINOperation +// +// Created by Adlai Holler on 1/10/17. +// Copyright © 2017 Pinterest. All rights reserved. +// + +#import + +typedef NS_ENUM(NSUInteger, PINOperationQueuePriority) { + PINOperationQueuePriorityLow, + PINOperationQueuePriorityDefault, + PINOperationQueuePriorityHigh, +}; diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/LICENSE b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/LICENSE new file mode 100644 index 0000000..d0381d6 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/LICENSE @@ -0,0 +1,176 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/README.md b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/README.md new file mode 100644 index 0000000..d192821 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/README.md @@ -0,0 +1,252 @@ +# PINRemoteImage + +## Fast, non-deadlocking parallel image downloader and cache for iOS + +[![CocoaPods compatible](https://img.shields.io/cocoapods/v/PINRemoteImage.svg?style=flat)](https://cocoapods.org/pods/PINRemoteImage) +[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) +[![Build status](https://github.com/pinterest/PINRemoteImage/workflows/CI/badge.svg)](https://github.com/pinterest/PINRemoteImage/actions?query=workflow%3ACI+branch%3Amaster) + +[PINRemoteImageManager](Source/Classes/PINRemoteImageManager.h) is an image downloading, processing and caching manager. It uses the concept of download and processing tasks to ensure that even if multiple calls to download or process an image are made, it only occurs one time (unless an item is no longer in the cache). PINRemoteImageManager is backed by **GCD** and safe to **access** from **multiple threads** simultaneously. It ensures that images are decoded off the main thread so that animation performance isn't affected. None of its exposed methods allow for synchronous access. However, it is optimized to call completions on the calling thread if an item is in its memory cache. + +PINRemoteImage supports downloading many types of files. It, of course, **supports** both **PNGs** and **JPGs**. It also supports decoding **WebP** images if Google's library is available. It even supports **GIFs** and **Animated WebP** via PINAnimatedImageView. + +PINRemoteImage also has two methods to improve the experience of downloading images on slow network connections. The first is support for **progressive JPGs**. This isn't any old support for progressive JPGs though: PINRemoteImage adds an attractive blur to progressive scans before returning them. + +![Progressive JPG with Blur](/progressive.gif "Looks better on device.") + +[PINRemoteImageCategoryManager](Pod/Classes/PINRemoteImageCategoryManager.h) defines a protocol which UIView subclasses can implement and provide easy access to +PINRemoteImageManager's methods. There are **built-in categories** on **UIImageView**, **PINAnimatedImageView** and **UIButton**, and it's very easy to implement a new category. See [UIImageView+PINRemoteImage](/Pod/Classes/Image Categories/UIImageView+PINRemoteImage.h) of the existing categories for reference. + + +### Download an image and set it on an image view: + +**Objective-C** +```objc +UIImageView *imageView = [[UIImageView alloc] init]; +[imageView pin_setImageFromURL:[NSURL URLWithString:@"http://pinterest.com/kitten.jpg"]]; +``` + +**Swift** +```swift +let imageView = UIImageView() +imageView.pin_setImage(from: URL(string: "https://pinterest.com/kitten.jpg")!) +``` + +### Download a progressive jpeg and get attractive blurred updates: + +**Objective-C** +```objc +UIImageView *imageView = [[UIImageView alloc] init]; +[imageView setPin_updateWithProgress:YES]; +[imageView pin_setImageFromURL:[NSURL URLWithString:@"http://pinterest.com/progressiveKitten.jpg"]]; +``` + +**Swift** +```swift +let imageView = UIImageView() +imageView.pin_updateWithProgress = true +imageView.pin_setImage(from: URL(string: "https://pinterest.com/progressiveKitten.jpg")!) +``` + +### Download a WebP file + +**Objective-C** +```objc +UIImageView *imageView = [[UIImageView alloc] init]; +[imageView pin_setImageFromURL:[NSURL URLWithString:@"http://pinterest.com/googleKitten.webp"]]; +``` + +**Swift** +```swift +let imageView = UIImageView() +imageView.pin_setImage(from: URL(string: "https://pinterest.com/googleKitten.webp")!) +``` + +### Download a GIF and display with PINAnimatedImageView + +**Objective-C** +```objc +PINAnimatedImageView *animatedImageView = [[PINAnimatedImageView alloc] init]; +[animatedImageView pin_setImageFromURL:[NSURL URLWithString:@"http://pinterest.com/flyingKitten.gif"]]; +``` + +**Swift** +```swift +let animatedImageView = PINAnimatedImageView() +animatedImageView.pin_setImage(from: URL(string: "http://pinterest.com/flyingKitten.gif")!) +``` + +### Download and process an image + +**Objective-C** +```objc +UIImageView *imageView = [[UIImageView alloc] init]; +[self.imageView pin_setImageFromURL:[NSURL URLWithString:@"https://i.pinimg.com/736x/5b/c6/c5/5bc6c5387ff6f104fd642f2b375efba3.jpg"] processorKey:@"rounded" processor:^UIImage *(PINRemoteImageManagerResult *result, NSUInteger *cost) + { + CGSize targetSize = CGSizeMake(200, 300); + CGRect imageRect = CGRectMake(0, 0, targetSize.width, targetSize.height); + UIGraphicsBeginImageContext(imageRect.size); + UIBezierPath *bezierPath = [UIBezierPath bezierPathWithRoundedRect:imageRect cornerRadius:7.0]; + [bezierPath addClip]; + + CGFloat sizeMultiplier = MAX(targetSize.width / result.image.size.width, targetSize.height / result.image.size.height); + + CGRect drawRect = CGRectMake(0, 0, result.image.size.width * sizeMultiplier, result.image.size.height * sizeMultiplier); + if (CGRectGetMaxX(drawRect) > CGRectGetMaxX(imageRect)) { + drawRect.origin.x -= (CGRectGetMaxX(drawRect) - CGRectGetMaxX(imageRect)) / 2.0; + } + if (CGRectGetMaxY(drawRect) > CGRectGetMaxY(imageRect)) { + drawRect.origin.y -= (CGRectGetMaxY(drawRect) - CGRectGetMaxY(imageRect)) / 2.0; + } + + [result.image drawInRect:drawRect]; + + UIImage *processedImage = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + return processedImage; + }]; +``` + +**Swift** +```swift +let imageView = FLAnimatedImageView() +imageView.pin_setImage(from: URL(string: "https://i.pinimg.com/736x/5b/c6/c5/5bc6c5387ff6f104fd642f2b375efba3.jpg")!, processorKey: "rounded") { (result, unsafePointer) -> UIImage? in + + guard let image = result.image else { return nil } + + let radius : CGFloat = 7.0 + let targetSize = CGSize(width: 200, height: 300) + let imageRect = CGRect(x: 0, y: 0, width: targetSize.width, height: targetSize.height) + + UIGraphicsBeginImageContext(imageRect.size) + + let bezierPath = UIBezierPath(roundedRect: imageRect, cornerRadius: radius) + bezierPath.addClip() + + let widthMultiplier : CGFloat = targetSize.width / image.size.width + let heightMultiplier : CGFloat = targetSize.height / image.size.height + let sizeMultiplier = max(widthMultiplier, heightMultiplier) + + var drawRect = CGRect(x: 0, y: 0, width: image.size.width * sizeMultiplier, height: image.size.height * sizeMultiplier) + if (drawRect.maxX > imageRect.maxX) { + drawRect.origin.x -= (drawRect.maxX - imageRect.maxX) / 2 + } + if (drawRect.maxY > imageRect.maxY) { + drawRect.origin.y -= (drawRect.maxY - imageRect.maxY) / 2 + } + + image.draw(in: drawRect) + + UIColor.red.setStroke() + bezierPath.lineWidth = 5.0 + bezierPath.stroke() + + let ctx = UIGraphicsGetCurrentContext() + ctx?.setBlendMode(CGBlendMode.overlay) + ctx?.setAlpha(0.5) + + let logo = UIImage(named: "white-pinterest-logo") + ctx?.scaleBy(x: 1.0, y: -1.0) + ctx?.translateBy(x: 0.0, y: -drawRect.size.height) + + if let coreGraphicsImage = logo?.cgImage { + ctx?.draw(coreGraphicsImage, in: CGRect(x: 90, y: 10, width: logo!.size.width, height: logo!.size.height)) + } + + let processedImage = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + + return processedImage + +} +``` + +### Handle Authentication + +**Objective-C** +```objc +[[PINRemoteImageManager sharedImageManager] setAuthenticationChallenge:^(NSURLSessionTask *task, NSURLAuthenticationChallenge *challenge, PINRemoteImageManagerAuthenticationChallengeCompletionHandler aCompletion) { +aCompletion(NSURLSessionAuthChallengePerformDefaultHandling, nil)]; +``` + +**Swift** +```swift +PINRemoteImageManager.shared().setAuthenticationChallenge { (task, challenge, completion) in + completion?(.performDefaultHandling, nil) +} +``` + +### Support for high resolution images +Currently there are two ways PINRemoteImage is supporting high resolution images: +1. If the URL contains an `_2x.` or an `_3x.` postfix it will be automatically handled by PINRemoteImage and the resulting image will be returned at the right scale. +2. If it's not possible to provide an URL with an `_2x.` or `_3x.` postfix, you can also handle it with a completion handler: +```objc +NSURL *url = ...; +__weak UIImageView *weakImageView = self.imageView; +[self.imageView pin_setImageFromURL:url completion:^(PINRemoteImageManagerResult * _Nonnull result) { + CGFloat scale = UIScreen.mainScreen.scale; + if (scale > 1.0) { + UIImage *image = result.image; + weakImageView.image = [UIImage imageWithCGImage:image.CGImage scale:scale orientation:image.imageOrientation]; + } +}]; +``` + +### Set some limits +``` +// cache is an instance of PINCache as long as you haven't overridden defaultImageCache +PINCache *cache = (PINCache *)[[PINRemoteImageManager sharedImageManager] cache]; +// Max memory cost is based on number of pixels, we estimate the size of one hundred 600x600 images as our max memory image cache. +[[cache memoryCache] setCostLimit:600 * [[UIScreen mainScreen] scale] * 600 * [[UIScreen mainScreen] scale] * 100]; + +// ~50 MB +[[cache diskCache] setByteLimit:50 * 1024 * 1024]; +// 30 days +[[cache diskCache] setAgeLimit:60 * 60 * 24 * 30]; +``` + +## Installation + +### CocoaPods + +Add [PINRemoteImage](http://cocoapods.org/?q=name%3APINRemoteImage) to your `Podfile` and run `pod install`. + +If you'd like to use WebP images, add [PINRemoteImage/WebP](http://cocoapods.org/?q=name%3APINRemoteImage) to your `Podfile` and run `pod install`. + + +### Carthage + +Add `github "pinterest/PINRemoteImage"` to your Cartfile . See [Carthage's readme](https://github.com/Carthage/Carthage) for more information on integrating Carthage-built frameworks into your project. + +### Manually + +[Download the latest tag](https://github.com/Pinterest/PINRemoteImage/tags) and drag the `Pod/Classes` folder into your Xcode project. You must also manually link against [PINCache](https://github.com/pinterest/PINCache). + +Install the docs by double clicking the `.docset` file under `docs/`, or view them online at [cocoadocs.org](http://cocoadocs.org/docsets/PINRemoteImage/) + +### Git Submodule + +You can set up PINRemoteImage as a submodule of your repo instead of cloning and copying all the files into your repo. Add the submodule using the commands below and then follow the manual instructions above. + + git submodule add https://github.com/pinterest/PINRemoteImage.git + git submodule update --init + + + +## Requirements + +__PINRemoteImage__ requires iOS 7.0 or greater. + +## Contact + +[Garrett Moon](mailto:garrett@pinterest.com) +[@garrettmoon](https://twitter.com/garrettmoon) +[Pinterest](https://www.pinterest.com/garrettlunar/) + +## License + +Copyright 2015 Pinterest, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. [See the License](LICENSE.txt) for the specific language governing permissions and limitations under the License. diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/AnimatedImages/PINAnimatedImage.m b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/AnimatedImages/PINAnimatedImage.m new file mode 100644 index 0000000..9204b4e --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/AnimatedImages/PINAnimatedImage.m @@ -0,0 +1,126 @@ +// +// PINAnimatedImage.m +// PINRemoteImage +// +// Created by Garrett Moon on 9/17/17. +// Copyright © 2017 Pinterest. All rights reserved. +// + +#import "PINAnimatedImage.h" + +NSErrorDomain const kPINAnimatedImageErrorDomain = @"kPINAnimatedImageErrorDomain"; + +//http://nullsleep.tumblr.com/post/16524517190/animated-gif-minimum-frame-delay-browser +const Float32 kPINAnimatedImageDefaultDuration = 0.1; + +@interface PINAnimatedImage () +{ + CFTimeInterval _totalDuration; +} +@end + +@implementation PINAnimatedImage + ++ (NSInteger)maximumFramesPerSecond +{ + static dispatch_once_t onceToken; + static NSInteger maximumFramesPerSecond = 60; + + dispatch_once(&onceToken, ^{ +#if PIN_TARGET_IOS + if (@available(iOS 10.3, tvOS 10.3, *)) { + maximumFramesPerSecond = 0; + for (UIScreen *screen in [UIScreen screens]) { + if ([screen maximumFramesPerSecond] > maximumFramesPerSecond) { + maximumFramesPerSecond = [screen maximumFramesPerSecond]; + } + } + } +#endif + }); + return maximumFramesPerSecond; +} + +- (instancetype)init +{ + if (self = [super init]) { + _totalDuration = -1; + } + return self; +} + +- (CFTimeInterval)durationAtIndex:(NSUInteger)index +{ + NSAssert(NO, @"Must be overridden by subclass"); + return 0; +} + +- (size_t)frameCount +{ + NSAssert(NO, @"Must be overridden by subclass"); + return 0; +} + + +- (CFTimeInterval)totalDuration +{ + if (_totalDuration == -1) { + _totalDuration = 0; + for (NSUInteger idx = 0; idx < self.frameCount; idx++) { + _totalDuration += [self durationAtIndex:idx]; + } + } + + return _totalDuration; +} + +- (NSUInteger)frameInterval +{ + return MAX(self.minimumFrameInterval * [PINAnimatedImage maximumFramesPerSecond], 1); +} + +//Credit to FLAnimatedImage ( https://github.com/Flipboard/FLAnimatedImage ) for display link interval calculations +- (NSTimeInterval)minimumFrameInterval +{ + static dispatch_once_t onceToken; + static NSTimeInterval kGreatestCommonDivisorPrecision; + dispatch_once(&onceToken, ^{ + kGreatestCommonDivisorPrecision = 2.0 / (1.0 / [PINAnimatedImage maximumFramesPerSecond]); + }); + + // Scales the frame delays by `kGreatestCommonDivisorPrecision` + // then converts it to an UInteger for in order to calculate the GCD. + NSUInteger scaledGCD = lrint([self durationAtIndex:0] * kGreatestCommonDivisorPrecision); + for (NSUInteger durationIdx = 1; durationIdx < self.frameCount; durationIdx++) { + CFTimeInterval duration = [self durationAtIndex:durationIdx]; + scaledGCD = gcd(lrint(duration * kGreatestCommonDivisorPrecision), scaledGCD); + } + + // Reverse to scale to get the value back into seconds. + return (scaledGCD / kGreatestCommonDivisorPrecision); +} + +// This likely isn't the most efficient but it's easy to reason about and we don't call it +// with super large numbers. +static NSUInteger gcd(NSUInteger a, NSUInteger b) +{ + // http://en.wikipedia.org/wiki/Greatest_common_divisor + NSCAssert(a > 0 && b > 0, @"A and B must be greater than 0"); + + while (a != b) { + if (a > b) { + a = a - b; + } else { + b = b - a; + } + } + return a; +} + +// Used only in testing ++ (NSUInteger)greatestCommonDivisorOfA:(NSUInteger)a andB:(NSUInteger)b +{ + return gcd(a, b); +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/AnimatedImages/PINAnimatedImageView.m b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/AnimatedImages/PINAnimatedImageView.m new file mode 100644 index 0000000..e54fa7e --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/AnimatedImages/PINAnimatedImageView.m @@ -0,0 +1,443 @@ +// +// PINAnimatedImageView.m +// Pods +// +// Created by Garrett Moon on 4/17/18. +// + +#import "PINAnimatedImageView.h" + +#import "PINRemoteLock.h" +#import "PINDisplayLink.h" +#import "PINImage+DecodedImage.h" +#import "PINRemoteWeakProxy.h" + +@interface PINAnimatedImageView () +{ + CFTimeInterval _playHead; + NSUInteger _playedLoops; + NSUInteger _lastSuccessfulFrameIndex; + CFTimeInterval *_durations; +} + +@property (nonatomic, assign) CGImageRef frameImage; +@property (nonatomic, strong) PINDisplayLink *displayLink; + +@property (nonatomic, assign) CFTimeInterval lastDisplayLinkFire; + +@end + +@implementation PINAnimatedImageView + +@synthesize animatedImage = _animatedImage; +@synthesize displayLink = _displayLink; +@synthesize playbackPaused = _playbackPaused; +@synthesize animatedImageRunLoopMode = _animatedImageRunLoopMode; + +- (instancetype)initWithAnimatedImage:(PINCachedAnimatedImage *)animatedImage +{ + if (self = [super initWithFrame:CGRectZero]) { + [self commonInit:animatedImage]; + } + return self; +} + +- (instancetype)initWithFrame:(CGRect)frame +{ + if (self = [super initWithFrame:frame]) { + [self commonInit:nil]; + } + return self; +} + +- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder +{ + if (self = [super initWithCoder:aDecoder]) { + [self commonInit:nil]; + } + return self; +} + +- (void)commonInit:(PINCachedAnimatedImage *)animatedImage +{ + _animatedImage = animatedImage; + _animatedImageRunLoopMode = NSRunLoopCommonModes; + _durations = NULL; + + if (animatedImage) { + [self initializeAnimatedImage:animatedImage]; + } +} + +- (void)initializeAnimatedImage:(nonnull PINCachedAnimatedImage *)animatedImage +{ + PINWeakify(self); + animatedImage.coverImageReadyCallback = ^(PINImage *coverImage) { + dispatch_async(dispatch_get_main_queue(), ^{ + PINStrongify(self); + // In this case the lock is already gone we have to call the unlocked version therefore + [self coverImageCompleted:coverImage]; + }); + }; + + animatedImage.playbackReadyCallback = ^{ + dispatch_async(dispatch_get_main_queue(), ^{ + // In this case the lock is already gone we have to call the unlocked version therefore + PINStrongify(self); + [self checkIfShouldAnimate]; + }); + }; + if (animatedImage.playbackReady) { + [self checkIfShouldAnimate]; + } + + [self resetDurationsWithAnimatedImage:animatedImage]; +} + +- (void)dealloc +{ + if (_frameImage) { + CGImageRelease(_frameImage); + } + if (_durations) { + free(_durations); + } +} + +#pragma mark - Public + +- (void)setAnimatedImage:(PINCachedAnimatedImage *)animatedImage +{ + PINAssertMain(); + if (_animatedImage == animatedImage && animatedImage.playbackReady) { + return; + } + + PINCachedAnimatedImage *previousAnimatedImage = _animatedImage; + + _animatedImage = animatedImage; + + if (animatedImage != nil) { + [self initializeAnimatedImage:animatedImage]; + } else { + // Clean up after ourselves. + self.layer.contents = nil; + [self setCoverImage:nil]; + } + + // Animated Image can take a while to dealloc, let's try and do it off main. + __block PINCachedAnimatedImage *strongAnimatedImage = previousAnimatedImage; + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + strongAnimatedImage = nil; + }); +} + +- (PINCachedAnimatedImage *)animatedImage +{ + PINAssertMain(); + return _animatedImage; +} + +- (NSString *)animatedImageRunLoopMode +{ + PINAssertMain(); + return _animatedImageRunLoopMode; +} + +- (void)setAnimatedImageRunLoopMode:(NSString *)newRunLoopMode +{ + PINAssertMain(); + + NSString *runLoopMode = newRunLoopMode ?: NSRunLoopCommonModes; + + if (_displayLink != nil) { + [_displayLink removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:_animatedImageRunLoopMode]; + [_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:runLoopMode]; + } + _animatedImageRunLoopMode = runLoopMode; +} + +- (BOOL)isPlaybackPaused +{ + PINAssertMain(); + return _playbackPaused; +} + +- (void)setPlaybackPaused:(BOOL)playbackPaused +{ + PINAssertMain(); + + _playbackPaused = playbackPaused; + [self checkIfShouldAnimate]; +} + +- (void)coverImageCompleted:(PINImage *)coverImage +{ + PINAssertMain(); + BOOL setCoverImage = (_displayLink == nil) || _displayLink.paused; + + if (setCoverImage) { + [self setCoverImage:coverImage]; + } +} + +- (void)setCoverImage:(PINImage *)coverImage +{ + PINAssertMain(); + if (_frameImage) { + CGImageRelease(_frameImage); + } + _frameImage = CGImageRetain([coverImage CGImage]); +} + +#pragma mark - Animating + +- (void)checkIfShouldAnimate +{ + PINAssertMain(); + BOOL shouldAnimate = _playbackPaused == NO && _animatedImage.playbackReady && [self canBeVisible]; + if (shouldAnimate) { + [self startAnimating]; + } else { + [self stopAnimating]; + } +} + +- (void)startAnimating +{ + PINAssertMain(); + + if (_playbackPaused) { + return; + } + + if (_animatedImage.playbackReady == NO) { + return; + } + + if ([self canBeVisible] == NO) { + return; + } + + NSUInteger frameInterval = self.animatedImage.frameInterval; + + if (_displayLink == nil) { + _playHead = 0; + _displayLink = [PINDisplayLink displayLinkWithTarget:[PINRemoteWeakProxy weakProxyWithTarget:self] selector:@selector(displayLinkFired:)]; +#if PIN_TARGET_IOS + if (@available(iOS 10.0, tvOS 10.0, *)) { + // Convert from display link fractional value to fps (note: frameInterval is always at least 1) + NSInteger frameRate = ceil([PINAnimatedImage maximumFramesPerSecond] / ((double) frameInterval)); + _displayLink.preferredFramesPerSecond = frameRate; + } else { +#endif + _displayLink.frameInterval = frameInterval; +#if PIN_TARGET_IOS + } +#endif + _lastSuccessfulFrameIndex = NSUIntegerMax; + + [_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:self.animatedImageRunLoopMode]; + } else { + _displayLink.paused = NO; + } +} + +- (void)stopAnimating +{ + PINAssertMain(); + + _displayLink.paused = YES; + _lastDisplayLinkFire = 0; + + [_animatedImage clearAnimatedImageCache]; +} + +#pragma mark - Overrides + +- (PINImage *)image +{ + PINAssertMain(); + if (_animatedImage) { + return [PINImage imageWithCGImage:_frameImage]; + } + return [super image]; +} + +- (CGImageRef)imageRef +{ + PINAssertMain(); + PINImage *underlyingImage = nil; + if (_animatedImage) { + return _frameImage; + } else if ((underlyingImage = [super image])) { + return (CGImageRef)CFAutorelease(CFRetain([underlyingImage CGImage])); + } + return nil; +} + +- (void)setImage:(PINImage *)image +{ + PINAssertMain(); + if (image) { + self.animatedImage = nil; + } + + super.image = image; +} + +- (void)displayLayer:(CALayer *)layer +{ + PINAssertMain(); + layer.contents = (__bridge id)[self imageRef]; +} + +#if PIN_TARGET_MAC + +- (void)_setImage:(PINImage *)image +{ + super.image = image; +} + +- (void)setAlphaValue:(CGFloat)alphaValue +{ + [super setAlphaValue:alphaValue]; + [self updateAnimationForPossibleVisibility]; +} + +- (void)viewDidMoveToWindow +{ + [super viewDidMoveToWindow]; + [self updateAnimationForPossibleVisibility]; +} + +- (void)viewDidMoveToSuperview +{ + [super viewDidMoveToSuperview]; + [self updateAnimationForPossibleVisibility]; +} +#else +- (void)setAlpha:(CGFloat)alpha +{ + [super setAlpha:alpha]; + [self updateAnimationForPossibleVisibility]; +} + +- (void)didMoveToWindow +{ + [super didMoveToWindow]; + [self updateAnimationForPossibleVisibility]; +} + +- (void)didMoveToSuperview +{ + [super didMoveToSuperview]; + [self updateAnimationForPossibleVisibility]; +} +#endif + +- (void)setHidden:(BOOL)hidden +{ + [super setHidden:hidden]; + [self updateAnimationForPossibleVisibility]; +} + +#pragma mark - Display Link Callbacks + +- (BOOL)canBeVisible +{ +#if PIN_TARGET_MAC + return self.window && self.superview && self.isHidden == NO && self.alphaValue > 0.0; +#else + return self.window && self.superview && self.isHidden == NO && self.alpha > 0.0; +#endif +} + +- (void)updateAnimationForPossibleVisibility +{ + [self checkIfShouldAnimate]; +} + +- (void)displayLinkFired:(PINDisplayLink *)displayLink +{ + PINAssertMain(); + CFTimeInterval timeBetweenLastFire; + if (_lastDisplayLinkFire == 0) { + timeBetweenLastFire = 0; + } else { + timeBetweenLastFire = CACurrentMediaTime() - self.lastDisplayLinkFire; + } + + self.lastDisplayLinkFire = CACurrentMediaTime(); + + _playHead += timeBetweenLastFire; + + while (_playHead > self.animatedImage.totalDuration) { + // Set playhead to zero to keep from showing different frames on different playthroughs + _playHead = 0; + _playedLoops++; + } + + if (self.animatedImage.loopCount > 0 && _playedLoops >= self.animatedImage.loopCount) { + [self stopAnimating]; + return; + } + + NSUInteger frameIndex = [self frameIndexAtPlayHeadPosition:_playHead]; + if (frameIndex == _lastSuccessfulFrameIndex) { + return; + } + CGImageRef frameImage = [self.animatedImage imageAtIndex:frameIndex]; + + if (frameImage == nil) { + //Pause the display link until we get a file ready notification + displayLink.paused = YES; + self.lastDisplayLinkFire = 0; + } else { + if (_frameImage) { + CGImageRelease(_frameImage); + } + _frameImage = CGImageRetain(frameImage); + _lastSuccessfulFrameIndex = frameIndex; +#if PIN_TARGET_MAC + [self _setImage:[NSImage imageWithCGImage:_frameImage]]; +#else + [self.layer setNeedsDisplay]; +#endif + } +} + +- (void)resetDurationsWithAnimatedImage:(PINCachedAnimatedImage *)animatedImage +{ + PINAssertMain(); + if (!animatedImage) { + return; + } + if (_durations) { + free(_durations); + } + _durations = malloc(sizeof(CFTimeInterval) * animatedImage.frameCount); + CFTimeInterval sum = 0.0f; + for (int i = 0; i < animatedImage.frameCount; i++) { + sum += [animatedImage durationAtIndex:i]; + _durations[i] = sum; + } +} + +- (NSUInteger)frameIndexAtPlayHeadPosition:(CFTimeInterval)playHead +{ + PINAssertMain(); + int low = 0, high = (int)_animatedImage.frameCount - 1; + + while (low <= high) { + int mid = low + (high - low) / 2; + if (_durations[mid] < playHead) { + low = mid + 1; + } else { + high = mid - 1; + } + } + return MAX(MIN(low, (int)_animatedImage.frameCount - 1), 0); +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/AnimatedImages/PINCachedAnimatedImage.m b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/AnimatedImages/PINCachedAnimatedImage.m new file mode 100644 index 0000000..8f41063 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/AnimatedImages/PINCachedAnimatedImage.m @@ -0,0 +1,528 @@ +// +// PINCachedAnimatedImage.m +// PINRemoteImage +// +// Created by Garrett Moon on 9/17/17. +// Copyright © 2017 Pinterest. All rights reserved. +// + +#import "PINCachedAnimatedImage.h" + +#import "PINRemoteLock.h" +#import "PINGIFAnimatedImage.h" +#if PIN_WEBP +#import "PINWebPAnimatedImage.h" +#endif + +#if SWIFT_PACKAGE +@import PINOperation; +#else +#import +#endif + +#import "NSData+ImageDetectors.h" + +static const NSUInteger kFramesToRenderForLargeFrames = 4; +static const NSUInteger kFramesToRenderMinimum = 2; + +static const CFTimeInterval kSecondsAfterMemWarningToMinimumCache = 1; +static const CFTimeInterval kSecondsAfterMemWarningToLargeCache = 5; +static const CFTimeInterval kSecondsAfterMemWarningToAllCache = 10; +#if PIN_TARGET_IOS +static const CFTimeInterval kSecondsBetweenMemoryWarnings = 15; +#endif + +@interface PINCachedAnimatedImage () +{ + // Since _animatedImage is set on init it is thread-safe. + id _animatedImage; + + PINImage *_coverImage; + PINAnimatedImageInfoReady _coverImageReadyCallback; + dispatch_block_t _playbackReadyCallback; + NSMutableDictionary *_frameCache; + NSInteger _frameRenderCount; // Number of frames to cache until playback is ready. + BOOL _playbackReady; + PINOperationQueue *_operationQueue; + dispatch_queue_t _cachingQueue; + + NSUInteger _playhead; + BOOL _notifyOnReady; + NSMutableIndexSet *_cachedOrCachingFrames; + PINRemoteLock *_lock; + BOOL _cacheCleared; // Flag used to cancel any caching operations after clear cache is called. +} + +@property (atomic, strong) NSDate *lastMemoryWarning; + +// Set to YES if we continually see memory warnings after ramping up the number of cached frames. +@property (atomic, assign) BOOL cachingFramesCausingMemoryWarnings; + +@end + +@implementation PINCachedAnimatedImage + +- (instancetype)initWithAnimatedImageData:(NSData *)animatedImageData +{ + if ([animatedImageData pin_isAnimatedGIF]) { + return [self initWithAnimatedImage:[[PINGIFAnimatedImage alloc] initWithAnimatedImageData:animatedImageData]]; + } +#if PIN_WEBP + if ([animatedImageData pin_isAnimatedWebP]) { + return [self initWithAnimatedImage:[[PINWebPAnimatedImage alloc] initWithAnimatedImageData:animatedImageData]]; + } +#endif + return nil; +} + +- (instancetype)initWithAnimatedImage:(id )animatedImage +{ + if (self = [super init]) { + _animatedImage = animatedImage; + _frameCache = [[NSMutableDictionary alloc] init]; + _frameRenderCount = 0; + _playhead = 0; + _notifyOnReady = YES; + _cachedOrCachingFrames = [[NSMutableIndexSet alloc] init]; + _lock = [[PINRemoteLock alloc] initWithName:@"PINCachedAnimatedImage Lock"]; + +#if PIN_TARGET_IOS + _lastMemoryWarning = [NSDate distantPast]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(didReceiveMemoryWarningNotification:) + name:UIApplicationDidReceiveMemoryWarningNotification + object:nil]; +#endif + + _operationQueue = [[PINOperationQueue alloc] initWithMaxConcurrentOperations:kFramesToRenderForLargeFrames]; + _cachingQueue = dispatch_queue_create("Caching Queue", DISPATCH_QUEUE_SERIAL); + + // dispatch later so that blocks can be set after init this runloop + PINWeakify(self); + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + PINStrongify(self); + [self imageAtIndex:0]; + }); + } + return self; +} + +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +#if PIN_TARGET_IOS +- (void)didReceiveMemoryWarningNotification:(NSNotification *)notification +{ + NSDate *now = [NSDate date]; + if (-[self.lastMemoryWarning timeIntervalSinceDate:now] < kSecondsBetweenMemoryWarnings) { + self.cachingFramesCausingMemoryWarnings = YES; + } + self.lastMemoryWarning = now; + [self cleanupFrames]; +} +#endif + +- (PINImage *)coverImage +{ + __block PINImage *coverImage = nil; + __block PINAnimatedImageInfoReady coverImageReadyCallback = nil; + [_lock lockWithBlock:^{ + if (self->_coverImage == nil) { + CGImageRef coverImageRef = [self->_animatedImage imageAtIndex:0 cacheProvider:self]; + BOOL notifyCallback = [self _locked_updateCoverImage:coverImageRef]; + coverImageReadyCallback = notifyCallback ? self->_coverImageReadyCallback : nil; + } + coverImage = self->_coverImage; + }]; + if (coverImageReadyCallback) { + coverImageReadyCallback(coverImage); + } + return coverImage; +} + +// Update _coverImage property and return if it should notify the callback +- (BOOL)_locked_updateCoverImage:(CGImageRef)coverImageRef +{ + BOOL notifyCallback = NO; + if (coverImageRef) { + notifyCallback = (_coverImage == nil && coverImageRef != nil); +#if PIN_TARGET_IOS + _coverImage = [UIImage imageWithCGImage:coverImageRef]; +#elif PIN_TARGET_MAC + _coverImage = [[NSImage alloc] initWithCGImage:coverImageRef size:CGSizeMake(_animatedImage.width, _animatedImage.height)]; +#endif + } else { + _coverImage = nil; + } + return notifyCallback; +} + +- (BOOL)coverImageReady +{ + __block PINImage *coverImage = nil; + __block PINAnimatedImageInfoReady coverImageReadyCallback = nil; + [_lock lockWithBlock:^{ + if (self->_coverImage == nil) { + CGImageRef coverImageRef = (__bridge CGImageRef)[self->_frameCache objectForKey:@(0)]; + if (coverImageRef) { + BOOL notifyCallback = [self _locked_updateCoverImage:coverImageRef]; + coverImageReadyCallback = notifyCallback ? self->_coverImageReadyCallback : nil; + } + } + + coverImage = self->_coverImage; + }]; + if (coverImageReadyCallback) { + coverImageReadyCallback(coverImage); + } + return (coverImage != nil); +} + +#pragma mark - passthrough +- (CGSize)size +{ + return CGSizeMake(_animatedImage.width, _animatedImage.height); +} + +- (NSData *)data +{ + return _animatedImage.data; +} + +- (CFTimeInterval)totalDuration +{ + return _animatedImage.totalDuration; +} + +- (NSUInteger)frameInterval +{ + return _animatedImage.frameInterval; +} + +- (size_t)loopCount +{ + return _animatedImage.loopCount; +} + +- (size_t)frameCount +{ + return _animatedImage.frameCount; +} + +- (NSError *)error +{ + return _animatedImage.error; +} + +- (CGImageRef)imageAtIndex:(NSUInteger)index +{ + __block CGImageRef imageRef; + __block BOOL cachingDisabled = NO; + [_lock lockWithBlock:^{ + // Reset cache cleared flag if it's been set. + self->_cacheCleared = NO; + imageRef = (__bridge CGImageRef)[self->_frameCache objectForKey:@(index)]; + + self->_playhead = index; + if (imageRef == NULL) { + if ([self framesToCache] == 0) { + // We're not caching so we should just generate the frame. + cachingDisabled = YES; + } else { + PINLog(@"cache miss, aww."); + self->_notifyOnReady = YES; + } + } + + // Retain and autorelease while we have the lock, another thread could remove it from the cache + // and allow it to be released. + if (imageRef) { + CGImageRetain(imageRef); + CFAutorelease(imageRef); + } + }]; + + if (cachingDisabled && imageRef == NULL) { + imageRef = [_animatedImage imageAtIndex:index cacheProvider:self]; + } else { + PINWeakify(self); + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + PINStrongify(self); + [self updateCache]; + }); + } + + return imageRef; +} + +- (void)_updateCacheOnQueue +{ + // Kick off, in order, caching frames which need to be cached + NSRange endKeepRange; + NSRange beginningKeepRange; + + [self getKeepRanges:&endKeepRange beginningKeepRange:&beginningKeepRange]; + + [self->_lock lockWithBlock:^{ + for (NSUInteger idx = endKeepRange.location; idx < NSMaxRange(endKeepRange); idx++) { + if ([self->_cachedOrCachingFrames containsIndex:idx] == NO) { + [self _locked_cacheFrame:idx]; + } + } + + if (beginningKeepRange.location != NSNotFound) { + for (NSUInteger idx = beginningKeepRange.location; idx < NSMaxRange(beginningKeepRange); idx++) { + if ([self->_cachedOrCachingFrames containsIndex:idx] == NO) { + [self _locked_cacheFrame:idx]; + } + } + } + }]; +} + +- (void)updateCache +{ + PINWeakify(self); + // skip if we don't have any frames to cache + if ([self framesToCache] > 0) { + [_operationQueue scheduleOperation:^{ + PINStrongify(self); + [self _updateCacheOnQueue]; + }]; + } + + [_operationQueue scheduleOperation:^{ + PINStrongify(self); + [self cleanupFrames]; + }]; +} + +- (void)getKeepRanges:(nonnull out NSRange *)endKeepRangeIn beginningKeepRange:(nonnull out NSRange *)beginningKeepRangeIn +{ + __block NSRange endKeepRange; + __block NSRange beginningKeepRange; + + NSUInteger framesToCache = [self framesToCache]; + + [self->_lock lockWithBlock:^{ + // find the range of frames we want to keep + endKeepRange = NSMakeRange(self->_playhead, framesToCache); + beginningKeepRange = NSMakeRange(NSNotFound, 0); + if (NSMaxRange(endKeepRange) > self->_animatedImage.frameCount) { + beginningKeepRange = NSMakeRange(0, NSMaxRange(endKeepRange) - self->_animatedImage.frameCount); + endKeepRange.length = self->_animatedImage.frameCount - self->_playhead; + } + }]; + + if (endKeepRangeIn) { + *endKeepRangeIn = endKeepRange; + } + if (beginningKeepRangeIn) { + *beginningKeepRangeIn = beginningKeepRange; + } +} + +- (void)cleanupFrames +{ + NSRange endKeepRange; + NSRange beginningKeepRange; + [self getKeepRanges:&endKeepRange beginningKeepRange:&beginningKeepRange]; + + [_lock lockWithBlock:^{ + NSMutableIndexSet *removedFrames = [[NSMutableIndexSet alloc] init]; + PINLog(@"Checking if frames need removing: %lu", _cachedOrCachingFrames.count); + [self->_cachedOrCachingFrames enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL * _Nonnull stop) { + BOOL shouldKeepFrame = NSLocationInRange(idx, endKeepRange); + if (beginningKeepRange.location != NSNotFound) { + shouldKeepFrame |= NSLocationInRange(idx, beginningKeepRange); + } + if (shouldKeepFrame == NO) { + [removedFrames addIndex:idx]; + [self->_frameCache removeObjectForKey:@(idx)]; + PINLog(@"Removing: %lu", (unsigned long)idx); + } + }]; + [self->_cachedOrCachingFrames removeIndexes:removedFrames]; + }]; +} + +- (void)_cacheWithFrameIndex:(NSUInteger)frameIndex +{ + CGImageRef imageRef = [self->_animatedImage imageAtIndex:frameIndex cacheProvider:self]; + PINLog(@"Generating: %lu", (unsigned long)frameIndex); + + if (imageRef) { + __block PINImage *coverImage = nil; + __block PINAnimatedImageInfoReady coverImageReadyCallback = nil; + [self->_lock lockWithBlock:^{ + [self->_frameCache setObject:(__bridge id _Nonnull)(imageRef) forKey:@(frameIndex)]; + + // Update the cover image + if (frameIndex == 0) { + BOOL notifyCallback = [self _locked_updateCoverImage:imageRef]; + coverImageReadyCallback = notifyCallback ? self->_coverImageReadyCallback : nil; + coverImage = self->_coverImage; + } + + self->_frameRenderCount--; + NSAssert(self->_frameRenderCount >= 0, @"playback ready is less than zero, something is wrong :("); + + PINLog(@"Frames left: %ld", (long)_frameRenderCount); + + dispatch_block_t notify = nil; + if (self->_frameRenderCount == 0 && self->_notifyOnReady) { + self->_notifyOnReady = NO; + if (self->_playbackReadyCallback) { + notify = self->_playbackReadyCallback; + [self->_operationQueue scheduleOperation:^{ + notify(); + }]; + } + } + }]; + if (coverImageReadyCallback) { + coverImageReadyCallback(coverImage); + } + } +} + +- (void)_locked_cacheFrame:(NSUInteger)frameIndex +{ + if ([_cachedOrCachingFrames containsIndex:frameIndex] == NO && _cacheCleared == NO) { + PINLog(@"Requesting: %lu", (unsigned long)frameIndex); + [_cachedOrCachingFrames addIndex:frameIndex]; + _frameRenderCount++; + + PINWeakify(self); + dispatch_async(_cachingQueue, ^{ + PINStrongify(self); + [self _cacheWithFrameIndex:frameIndex]; + }); + } +} + +// Returns the number of frames that should be cached +- (NSUInteger)framesToCache +{ + unsigned long long totalBytes = [NSProcessInfo processInfo].physicalMemory; + NSUInteger framesToCache = 0; + + NSUInteger frameCost = _animatedImage.bytesPerFrame; + if (frameCost * _animatedImage.frameCount < totalBytes / 250) { + // If the total number of bytes takes up less than a 250th of total memory, lets just cache 'em all. + framesToCache = _animatedImage.frameCount; + } else if (frameCost < totalBytes / 1000) { + // If the cost of a frame is less than 1000th of physical memory, cache 4 frames to smooth animation. + framesToCache = kFramesToRenderForLargeFrames; + } else if (frameCost < totalBytes / 500) { + // Oooph, lets just try to get ahead of things by one. + framesToCache = kFramesToRenderMinimum; + } else { + // No caching :( + framesToCache = 0; + } + + // If it's been less than 5 seconds, we're not caching + CFTimeInterval timeSinceLastWarning = -[self.lastMemoryWarning timeIntervalSinceNow]; + if (self.cachingFramesCausingMemoryWarnings || timeSinceLastWarning < kSecondsAfterMemWarningToMinimumCache) { + framesToCache = 0; + } else if (timeSinceLastWarning < kSecondsAfterMemWarningToLargeCache) { + framesToCache = MIN(framesToCache, kFramesToRenderMinimum); + } else if (timeSinceLastWarning < kSecondsAfterMemWarningToAllCache) { + framesToCache = MIN(framesToCache, kFramesToRenderForLargeFrames); + } + + return framesToCache; +} + +- (CFTimeInterval)durationAtIndex:(NSUInteger)index +{ + return [_animatedImage durationAtIndex:index]; +} + +- (BOOL)playbackReady +{ + __block BOOL playbackReady = NO; + [_lock lockWithBlock:^{ + if (self->_playbackReady == NO) { + self->_playbackReady = self->_frameRenderCount == 0; + } + playbackReady = self->_playbackReady; + }]; + return playbackReady; +} + +- (dispatch_block_t)playbackReadyCallback +{ + __block dispatch_block_t playbackReadyCallback = nil; + [_lock lockWithBlock:^{ + playbackReadyCallback = self->_playbackReadyCallback; + }]; + return playbackReadyCallback; +} + +- (void)setPlaybackReadyCallback:(dispatch_block_t)playbackReadyCallback +{ + [_lock lockWithBlock:^{ + self->_playbackReadyCallback = playbackReadyCallback; + }]; +} + +- (PINAnimatedImageInfoReady)coverImageReadyCallback +{ + __block PINAnimatedImageInfoReady coverImageReadyCallback; + [_lock lockWithBlock:^{ + coverImageReadyCallback = self->_coverImageReadyCallback; + }]; + return coverImageReadyCallback; +} + +- (void)setCoverImageReadyCallback:(PINAnimatedImageInfoReady)coverImageReadyCallback +{ + [_lock lockWithBlock:^{ + self->_coverImageReadyCallback = coverImageReadyCallback; + }]; +} + +- (void)_clearAnimatedImageCache +{ + [self->_lock lockWithBlock:^{ + self->_cacheCleared = YES; + self->_coverImage = nil; + [self->_cachedOrCachingFrames enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL * _Nonnull stop) { + [self->_frameCache removeObjectForKey:@(idx)]; + }]; + [self->_cachedOrCachingFrames removeAllIndexes]; + }]; +} + +/** + @abstract Clear any cached data. Called when playback is paused. + */ +- (void)clearAnimatedImageCache +{ + PINWeakify(self); + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + PINStrongify(self); + [self _clearAnimatedImageCache]; + }); +} + +# pragma mark - PINCachedAnimatedFrameProvider + +- (CGImageRef)cachedFrameImageAtIndex:(NSUInteger)index +{ + __block CGImageRef imageRef; + [_lock lockWithBlock:^{ + imageRef = (__bridge CGImageRef)[self->_frameCache objectForKey:@(index)]; + if (imageRef) { + CGImageRetain(imageRef); + CFAutorelease(imageRef); + } + }]; + return imageRef; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/AnimatedImages/PINGIFAnimatedImage.m b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/AnimatedImages/PINGIFAnimatedImage.m new file mode 100644 index 0000000..2b5016f --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/AnimatedImages/PINGIFAnimatedImage.m @@ -0,0 +1,166 @@ +// +// PINGIFAnimatedImage.m +// PINRemoteImage +// +// Created by Garrett Moon on 9/17/17. +// Copyright © 2017 Pinterest. All rights reserved. +// + +#import "PINGIFAnimatedImage.h" + +#import +#if PIN_TARGET_IOS +#import +#elif PIN_TARGET_MAC +#import +#endif + +#import "PINImage+DecodedImage.h" +#import "NSData+ImageDetectors.h" + +@interface PINGIFAnimatedImage () +{ + NSData *_animatedImageData; + CGImageSourceRef _imageSource; + uint32_t _width; + uint32_t _height; + BOOL _hasAlpha; + size_t _frameCount; + size_t _loopCount; + CFTimeInterval *_durations; + NSError *_error; +} +@end + +@implementation PINGIFAnimatedImage + +- (instancetype)initWithAnimatedImageData:(NSData *)animatedImageData +{ + if (self = [super init]) { + _animatedImageData = animatedImageData; + _imageSource = + CGImageSourceCreateWithData((CFDataRef)animatedImageData, + (CFDictionaryRef)@{(__bridge NSString *)kCGImageSourceTypeIdentifierHint: + (__bridge NSString *)kUTTypeGIF, + (__bridge NSString *)kCGImageSourceShouldCache: + (__bridge NSNumber *)kCFBooleanFalse}); + if (_imageSource && [animatedImageData pin_isGIF]) { + _frameCount = (uint32_t)CGImageSourceGetCount(_imageSource); + NSDictionary *imageProperties = (__bridge_transfer NSDictionary *)CGImageSourceCopyProperties(_imageSource, nil); + _loopCount = (uint32_t)[[[imageProperties objectForKey:(__bridge NSString *)kCGImagePropertyGIFDictionary] + objectForKey:(__bridge NSString *)kCGImagePropertyGIFLoopCount] unsignedLongValue]; + _durations = malloc(sizeof(CFTimeInterval) * _frameCount); + imageProperties = (__bridge_transfer NSDictionary *) + CGImageSourceCopyPropertiesAtIndex(_imageSource, + 0, + (CFDictionaryRef)@{(__bridge NSString *)kCGImageSourceShouldCache: + (__bridge NSNumber *)kCFBooleanFalse}); + _width = (uint32_t)[(NSNumber *)imageProperties[(__bridge NSString *)kCGImagePropertyPixelWidth] unsignedIntegerValue]; + _height = (uint32_t)[(NSNumber *)imageProperties[(__bridge NSString *)kCGImagePropertyPixelHeight] unsignedIntegerValue]; + + for (NSUInteger frameIdx = 0; frameIdx < _frameCount; frameIdx++) { + _durations[frameIdx] = [PINGIFAnimatedImage frameDurationAtIndex:frameIdx source:_imageSource]; + } + } else { + return nil; + } + } + return self; +} + ++ (Float32)frameDurationAtIndex:(NSUInteger)index source:(CGImageSourceRef)source +{ + Float32 frameDuration = kPINAnimatedImageDefaultDuration; + NSDictionary *frameProperties = (__bridge_transfer NSDictionary *)CGImageSourceCopyPropertiesAtIndex(source, index, nil); + // use unclamped delay time before delay time before default + NSNumber *unclamedDelayTime = frameProperties[(__bridge NSString *)kCGImagePropertyGIFDictionary][(__bridge NSString *)kCGImagePropertyGIFUnclampedDelayTime]; + if (unclamedDelayTime != nil) { + frameDuration = [unclamedDelayTime floatValue]; + } else { + NSNumber *delayTime = frameProperties[(__bridge NSString *)kCGImagePropertyGIFDictionary][(__bridge NSString *)kCGImagePropertyGIFDelayTime]; + if (delayTime != nil) { + frameDuration = [delayTime floatValue]; + } + } + + static dispatch_once_t onceToken; + static Float32 maximumFrameDuration; + dispatch_once(&onceToken, ^{ + maximumFrameDuration = 1.0 / [PINAnimatedImage maximumFramesPerSecond]; + }); + + if (frameDuration < maximumFrameDuration) { + frameDuration = kPINAnimatedImageDefaultDuration; + } + + return frameDuration; +} + +- (void)dealloc +{ + if (_imageSource) { + CFRelease(_imageSource); + } + if (_durations) { + free(_durations); + } +} + +- (NSData *)data +{ + return _animatedImageData; +} + +- (size_t)frameCount +{ + return _frameCount; +} + +- (size_t)loopCount +{ + return _loopCount; +} + +- (uint32_t)width +{ + return _width; +} + +- (uint32_t)height +{ + return _height; +} + +- (uint32_t)bytesPerFrame +{ + return _width * _height * 4; +} + +- (NSError *)error +{ + return _error; +} + +- (CFTimeInterval)durationAtIndex:(NSUInteger)index +{ + return _durations[index]; +} + +- (CGImageRef)imageAtIndex:(NSUInteger)index cacheProvider:(nullable id)cacheProvider +{ + // I believe this is threadsafe as CGImageSource *seems* immutable… + CGImageRef imageRef = + CGImageSourceCreateImageAtIndex(_imageSource, + index, + (CFDictionaryRef)@{(__bridge NSString *)kCGImageSourceShouldCache: + (__bridge NSNumber *)kCFBooleanFalse}); + if (imageRef) { + CGImageRef decodedImageRef = [PINImage pin_decodedImageRefWithCGImageRef:imageRef]; + CGImageRelease(imageRef); + imageRef = decodedImageRef; + } + + return imageRef; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/AnimatedImages/PINWebPAnimatedImage.m b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/AnimatedImages/PINWebPAnimatedImage.m new file mode 100644 index 0000000..da13aa4 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/AnimatedImages/PINWebPAnimatedImage.m @@ -0,0 +1,403 @@ +// +// PINWebPAnimatedImage.m +// PINRemoteImage +// +// Created by Garrett Moon on 9/14/17. +// Copyright © 2017 Pinterest. All rights reserved. +// + +#if PIN_WEBP + +#import "PINWebPAnimatedImage.h" + +#import "NSData+ImageDetectors.h" + +#if SWIFT_PACKAGE +@import libwebp; +#else +#import "webp/demux.h" +#endif + +@interface PINWebPAnimatedImage () +{ + NSData *_animatedImageData; + WebPData _underlyingData; + WebPDemuxer *_demux; + CGImageRef previousFrame; + uint32_t _width; + uint32_t _height; + BOOL _hasAlpha; + size_t _frameCount; + size_t _loopCount; + CGColorRef _backgroundColor; + CFTimeInterval *_durations; + NSError *_error; +} + +@end + +static void releaseData(void *info, const void *data, size_t size) +{ + WebPFree((void *)data); +} + +@implementation PINWebPAnimatedImage + +- (instancetype)initWithAnimatedImageData:(NSData *)animatedImageData +{ + if (self = [super init]) { + _animatedImageData = animatedImageData; + _underlyingData.bytes = [animatedImageData bytes]; + _underlyingData.size = [animatedImageData length]; + _demux = WebPDemux(&_underlyingData); + + if (_demux != NULL) { + _width = WebPDemuxGetI(_demux, WEBP_FF_CANVAS_WIDTH); + _height = WebPDemuxGetI(_demux, WEBP_FF_CANVAS_HEIGHT); + _frameCount = WebPDemuxGetI(_demux, WEBP_FF_FRAME_COUNT); + _loopCount = WebPDemuxGetI(_demux, WEBP_FF_LOOP_COUNT); + uint32_t flags = WebPDemuxGetI(_demux, WEBP_FF_FORMAT_FLAGS); + _hasAlpha = flags & ALPHA_FLAG; + _durations = malloc(sizeof(CFTimeInterval) * _frameCount); + + uint32_t backgroundColorInt = WebPDemuxGetI(_demux, WEBP_FF_BACKGROUND_COLOR); + CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB(); + CGFloat components[4]; + components[0] = (CGFloat)(((backgroundColorInt & 0xFF000000) >> 24)/255.0); + components[1] = (CGFloat)(((backgroundColorInt & 0x00FF0000) >> 16)/255.0); + components[2] = (CGFloat)(((backgroundColorInt & 0x0000FF00) >> 8)/255.0); + components[3] = (CGFloat)((backgroundColorInt & 0x000000FF)/255.0); + _backgroundColor = CGColorCreate(rgbColorSpace, components); + CGColorSpaceRelease(rgbColorSpace); + + // Iterate over the frames to gather duration + WebPIterator iter; + if (WebPDemuxGetFrame(_demux, 1, &iter)) { + do { + CFTimeInterval duration = iter.duration / 1000.0; + static dispatch_once_t onceToken; + static CFTimeInterval maximumDuration; + dispatch_once(&onceToken, ^{ + maximumDuration = 1.0 / [PINAnimatedImage maximumFramesPerSecond]; + }); + if (duration < maximumDuration) { + duration = kPINAnimatedImageDefaultDuration; + } + _durations[iter.frame_num - 1] = duration; + } while (WebPDemuxNextFrame(&iter)); + WebPDemuxReleaseIterator(&iter); + } + } else { + return nil; + } + } + return self; +} + +- (void)dealloc +{ + if (_demux) { + WebPDemuxDelete(_demux); + } + if (_durations) { + free(_durations); + } + if (_backgroundColor) { + CGColorRelease(_backgroundColor); + } +} + +- (NSData *)data +{ + return _animatedImageData; +} + +- (size_t)frameCount +{ + return _frameCount; +} + +- (size_t)loopCount +{ + return _loopCount; +} + +- (uint32_t)width +{ + return _width; +} + +- (uint32_t)height +{ + return _height; +} + +- (uint32_t)bytesPerFrame +{ + return _width * _height * (_hasAlpha ? 4 : 3); +} + +- (NSError *)error +{ + return _error; +} + +- (CFTimeInterval)durationAtIndex:(NSUInteger)index +{ + return _durations[index]; +} + +- (CGImageRef)canvasWithPreviousFrame:(CGImageRef)previousFrame + previousFrameRect:(CGRect)previousFrameRect + clearPreviousFrame:(BOOL)clearPreviousFrame + backgroundColor:(CGColorRef)backgroundColor + image:(CGImageRef)image + clearCurrentFrame:(BOOL)clearCurrentFrame + atRect:(CGRect)rect +{ + CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB(); + CGContextRef context = CGBitmapContextCreate(NULL, + _width, + _height, + 8, + 0, + colorSpaceRef, + _hasAlpha ? kCGImageAlphaPremultipliedFirst : kCGImageAlphaNone); + if (backgroundColor) { + CGContextSetFillColorWithColor(context, backgroundColor); + } + + if (previousFrame) { + CGContextDrawImage(context, CGRectMake(0, 0, _width, _height), previousFrame); + if (clearPreviousFrame) { + CGContextFillRect(context, previousFrameRect); + } + } + + if (image) { + CGRect currentRect = CGRectMake(rect.origin.x, _height - rect.size.height - rect.origin.y, rect.size.width, rect.size.height); + if (clearCurrentFrame) { + CGContextFillRect(context, currentRect); + } + CGContextDrawImage(context, currentRect, image); + } + + CGImageRef canvas = CGBitmapContextCreateImage(context); + if (canvas) { + CFAutorelease(canvas); + } + CGContextRelease(context); + CGColorSpaceRelease(colorSpaceRef); + + return canvas; +} + +- (CGImageRef)rawImageWithIterator:(WebPIterator)iterator +{ + CGImageRef imageRef = NULL; + uint8_t *data = NULL; + int pixelLength = 0; + + if (iterator.has_alpha) { + data = WebPDecodeRGBA(iterator.fragment.bytes, iterator.fragment.size, NULL, NULL); + pixelLength = 4; + } else { + data = WebPDecodeRGB(iterator.fragment.bytes, iterator.fragment.size, NULL, NULL); + pixelLength = 3; + } + + if (data) { + CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, data, iterator.width * iterator.height * pixelLength, releaseData); + + CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB(); + CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault; + + if (iterator.has_alpha) { + bitmapInfo |= kCGImageAlphaLast; + } else { + bitmapInfo |= kCGImageAlphaNone; + } + + CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault; + imageRef = CGImageCreate(iterator.width, + iterator.height, + 8, + 8 * pixelLength, + pixelLength * iterator.width, + colorSpaceRef, + bitmapInfo, + provider, + NULL, + NO, + renderingIntent); + + CGColorSpaceRelease(colorSpaceRef); + CGDataProviderRelease(provider); + } + + if (imageRef) { + CFAutorelease(imageRef); + } + + return imageRef; +} + +- (CGImageRef)imageAtIndex:(NSUInteger)index cacheProvider:(nullable id)cacheProvider +{ + PINLog(@"Drawing webp image at index: %lu", (unsigned long)index); + // This all *appears* to be threadsafe as I believe demux is immutable… + WebPIterator iterator, previousIterator; + + if (index > 0) { + if (WebPDemuxGetFrame(_demux, (int)index, &previousIterator) == NO) { + return nil; + } + } + if (WebPDemuxGetFrame(_demux, (int)index + 1, &iterator) == NO) { + return nil; + } + + BOOL isKeyFrame = [self isKeyFrame:&iterator previousIterator:(index > 0) ? &previousIterator : nil]; + + CGImageRef imageRef = [self rawImageWithIterator:iterator]; + CGImageRef canvas = NULL; + + if (imageRef) { + if (isKeyFrame) { + // If the current frame is a keyframe, we can just copy it into a blank + // canvas. + if (iterator.x_offset == 0 && iterator.y_offset == 0 && iterator.width == _width && iterator.height == _height) { + // Output will be the same size as the canvas, just return it directly. + canvas = imageRef; + } else { + canvas = [self canvasWithPreviousFrame:nil + previousFrameRect:CGRectZero + clearPreviousFrame:NO + backgroundColor:_backgroundColor + image:imageRef + clearCurrentFrame:iterator.blend_method == WEBP_MUX_NO_BLEND + atRect:CGRectMake(iterator.x_offset, iterator.y_offset, iterator.width, iterator.height)]; + } + } else { + // If we have a cached image provider, try to get the last frame from them + CGImageRef previousFrame = [cacheProvider cachedFrameImageAtIndex:index - 1]; + if (previousFrame) { + // We need an iterator from the previous frame to dispose to background if + // necessary. + WebPDemuxReleaseIterator(&previousIterator); + WebPDemuxGetFrame(_demux, (int)index, &previousIterator); + CGRect previousFrameRect = CGRectMake(previousIterator.x_offset, _height - previousIterator.height - previousIterator.y_offset, previousIterator.width, previousIterator.height); + canvas = [self canvasWithPreviousFrame:previousFrame + previousFrameRect:previousFrameRect + clearPreviousFrame:previousIterator.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND + backgroundColor:_backgroundColor + image:imageRef + clearCurrentFrame:iterator.blend_method == WEBP_MUX_NO_BLEND + atRect:CGRectMake(iterator.x_offset, iterator.y_offset, iterator.width, iterator.height)]; + } else if (index > 0) { + // Sadly, we need to draw *all* the frames from the previous key frame previousIterator to the current one :( + CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB(); + CGContextRef context = CGBitmapContextCreate(NULL, + _width, + _height, + 8, + 0, + colorSpaceRef, + _hasAlpha ? kCGImageAlphaPremultipliedFirst : kCGImageAlphaNone); + CGContextSetFillColorWithColor(context, _backgroundColor); + + while (previousIterator.frame_num <= iterator.frame_num) { + CGImageRef previousFrame = [self rawImageWithIterator:previousIterator]; + if (previousFrame) { + CGRect previousFrameRect = CGRectMake(previousIterator.x_offset, _height - previousIterator.height - previousIterator.y_offset, previousIterator.width, previousIterator.height); + if (previousIterator.blend_method == WEBP_MUX_NO_BLEND) { + CGContextFillRect(context, previousFrameRect); + } + + if (previousIterator.frame_num == iterator.frame_num) { + CGContextDrawImage(context, previousFrameRect, previousFrame); + // We have to break here because we're not getting the next frame! Basically + // the while loop is a sham and only here to illustrate what we want to iterate. + break; + } else { + if (previousIterator.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND) { + CGContextFillRect(context, previousFrameRect); + } else { + CGContextDrawImage(context, previousFrameRect, previousFrame); + } + WebPDemuxNextFrame(&previousIterator); + } + } + } + + canvas = CGBitmapContextCreateImage(context); + if (canvas) { + CFAutorelease(canvas); + } + CGContextRelease(context); + CGColorSpaceRelease(colorSpaceRef); + } + } + } + + WebPDemuxReleaseIterator(&iterator); + if (index > 0) { + WebPDemuxReleaseIterator(&previousIterator); + } + + return canvas; +} + +// Checks to see if the iterator is a 'key frame' without taking previous frames into +// account. +- (BOOL)helperIsKeyFrame:(WebPIterator *)iterator +{ + if (iterator->frame_num == 1) { + //The first frame is always a key frame + return YES; + } else if ((iterator->has_alpha == NO || iterator->blend_method == WEBP_MUX_NO_BLEND) && iterator->width == _width && iterator->height == _height) { + //If the current frame has no alpha, or we're instructed not to blend, just make sure this fills the canvas. + return YES; + } + return NO; +} + +// Checks if the iterator is at a 'key frame' and rewinds previousIterator back to the last +// key frame if it's not. If this frame *is* a keyframe, the previousIterator's position is undefined. +// This takes previous frames into account to determine if the current frame is key. +- (BOOL)isKeyFrame:(WebPIterator *)iterator previousIterator:(WebPIterator *)previousIterator +{ + if ([self helperIsKeyFrame:iterator]) { + // Check if we're a key frame regardless of previous frame. + return YES; + } + + if (previousIterator == nil) { + return NO; + } + + BOOL previousFrameMadeThisKeyFrame = previousIterator->dispose_method == WEBP_MUX_DISPOSE_BACKGROUND; + BOOL foundKeyframe = NO; + while (foundKeyframe == NO) { + if ([self helperIsKeyFrame:previousIterator] || + (previousIterator->dispose_method == WEBP_MUX_DISPOSE_BACKGROUND && previousIterator->width == _width && previousIterator->height == _height)) { + foundKeyframe = YES; + } else { + // we need to rewind previous back to see if it was a keyframe + WebPDemuxPrevFrame(previousIterator); + if (previousIterator->dispose_method == WEBP_MUX_DISPOSE_BACKGROUND) { + // need to check previous frame + continue; + } else { + previousFrameMadeThisKeyFrame = NO; + continue; + } + } + } + + return previousFrameMadeThisKeyFrame; +} + +@end + +#endif diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/Categories/NSData+ImageDetectors.m b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/Categories/NSData+ImageDetectors.m new file mode 100644 index 0000000..f58f557 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/Categories/NSData+ImageDetectors.m @@ -0,0 +1,139 @@ +// +// NSData+ImageDetectors.m +// Pods +// +// Created by Garrett Moon on 11/19/14. +// +// + +#import "NSData+ImageDetectors.h" + +#if PIN_WEBP + #if SWIFT_PACKAGE + @import libwebp; + #else + #import "webp/demux.h" + #endif +#endif + +@implementation NSData (PINImageDetectors) + +- (BOOL)pin_isGIF +{ + const NSInteger length = 3; + Byte firstBytes[length]; + if ([self length] >= length) { + [self getBytes:&firstBytes length:length]; + //G, I, F + if (firstBytes[0] == 0x47 && firstBytes[1] == 0x49 && firstBytes[2] == 0x46) { + return YES; + } + } + return NO; +} + +#define BREAK_IF_INVALID(position) if (position >= length) break; + +static inline BOOL advancePositionWithCount(NSUInteger *position, NSUInteger length, NSUInteger count) +{ + if (*position + count >= length) { + return NO; + } + *position = *position + count; + + return YES; +} + +static inline BOOL advancePositionWithBytes(NSUInteger *position, Byte *bytes, NSUInteger length, NSUInteger count) +{ + BOOL readAgain; + do { + readAgain = NO; + if (*position + count >= length ) { + return NO; + } + *position = *position + count; + NSUInteger bytesToAdvance = *(bytes + *position); + if (bytesToAdvance == 0xFF) { + readAgain = YES; + count = 0; + } + // Advance the byte read as well. + bytesToAdvance++; + + if (*position + bytesToAdvance >= length) { + return NO; + } + *position = *position + bytesToAdvance; + } while (readAgain); + + return YES; +} + +- (BOOL)pin_isAnimatedGIF +{ + if ([self pin_isGIF] == NO) { + return NO; + } + + Byte *bytes = (Byte *)self.bytes; + NSUInteger length = self.length; + NSUInteger position = 0; + NSUInteger GCECount = 0; + + while (bytes && position < length) { + // Look for Graphic Control Extension + if (*(bytes + position) == 0x21) { + if (!advancePositionWithCount(&position, length, 1)) break; + if (*(bytes + position) == 0xF9) { + GCECount++; + if (GCECount > 1) { + break; + } + // Found GCE, advance to image. Next byte is size of GCE + if (!advancePositionWithBytes(&position, bytes, length, 1)) break; + // Advance 1 for 00 at the end of GCE + if (!advancePositionWithCount(&position, length, 1)) break; + // Advance image descriptor + if (!advancePositionWithCount(&position, length, 11)) break; + // Advance image + if (!advancePositionWithBytes(&position, bytes, length, 0)) break; + // Advance 1 for 00 at the end of image + if (!advancePositionWithCount(&position, length, 1)) break; + } + continue; + } + if (!advancePositionWithCount(&position, length, 1)) break; + } + + return GCECount > 1; +} + +#if PIN_WEBP +- (BOOL)pin_isWebP +{ + const NSInteger length = 12; + Byte firstBytes[length]; + if ([self length] >= length) { + [self getBytes:&firstBytes length:length]; + //R, I, F, F, -, -, -, -, W, E, B, P + if (firstBytes[0] == 0x52 && firstBytes[1] == 0x49 && firstBytes[2] == 0x46 && firstBytes[3] == 0x46 && firstBytes[8] == 0x57 && firstBytes[9] == 0x45 && firstBytes[10] == 0x42 && firstBytes[11] == 0x50) { + return YES; + } + } + return NO; +} + +- (BOOL)pin_isAnimatedWebP +{ + WebPBitstreamFeatures features; + if (WebPGetFeatures([self bytes], [self length], &features) == VP8_STATUS_OK) { + return features.has_animation; + } + + return NO; +} + +#endif + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/Categories/NSHTTPURLResponse+MaxAge.h b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/Categories/NSHTTPURLResponse+MaxAge.h new file mode 100644 index 0000000..a145372 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/Categories/NSHTTPURLResponse+MaxAge.h @@ -0,0 +1,14 @@ +// +// NSHTTPURLResponse+MaxAge.m +// +// Created by Kevin Smith on 6/15/18. +// +// + +#import + +@interface NSHTTPURLResponse (MaxAge) + +- (NSNumber *)findMaxAge; + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/Categories/NSHTTPURLResponse+MaxAge.m b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/Categories/NSHTTPURLResponse+MaxAge.m new file mode 100644 index 0000000..6acdcc9 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/Categories/NSHTTPURLResponse+MaxAge.m @@ -0,0 +1,71 @@ +// +// NSHTTPURLResponse+MaxAge.m +// +// Created by Kevin Smith on 6/15/18. +// +// + +#import "NSHTTPURLResponse+MaxAge.h" + +@implementation NSHTTPURLResponse (MaxAge) + +static NSDateFormatter *sharedFormatter; +static dispatch_once_t sharedFormatterToken; + ++ (NSDateFormatter *)RFC7231PreferredDateFormatter +{ + dispatch_once(&sharedFormatterToken, ^{ + NSLocale *enUSPOSIXLocale; + + sharedFormatter = [[NSDateFormatter alloc] init]; + + enUSPOSIXLocale = [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"]; + + [sharedFormatter setLocale:enUSPOSIXLocale]; + [sharedFormatter setDateFormat:@"E, d MMM yyyy HH:mm:ss Z"]; + + }); + return sharedFormatter; +} + +- (NSNumber *)findMaxAge +{ + NSDictionary * headerFields = [self allHeaderFields]; + NSNumber *maxAge = nil; + + for (NSString * component in [headerFields[@"Cache-Control"] componentsSeparatedByString:@","]) { + NSString * trimmed = [[component stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] lowercaseString]; + + if ([trimmed isEqualToString:@"no-store"] || [trimmed isEqualToString:@"must-revalidate"] || [trimmed isEqualToString:@"no-cache"]) { + maxAge = @(0); + break; + } else { + // max-age + NSArray * split = [trimmed componentsSeparatedByString:@"max-age="]; + if ([split count] == 2) { + // if the max-age provided is invalid (does not parse into an + // int), we wind up with 0 which will be treated as do-not-cache. + // This is the RFC defined behavior for a malformed "expires" header, + // and while I cannot find any explicit instruction of how to behave + // with a malformed "max-age" header, it seems like a reasonable approach. + maxAge = @([split[1] integerValue]); + } else if ([split count] > 2) { + // very weird case "maxage=maxage=123" + maxAge = @(0); + } + } + } + + // If there is a Cache-Control header with the "max-age" directive in the response, the Expires header is ignored. + if (!maxAge && headerFields[@"Expires"]) { + NSString * expires = headerFields[@"Expires"]; + NSDate * date = [[NSHTTPURLResponse RFC7231PreferredDateFormatter] dateFromString:expires]; + + // Invalid dates (notably "0") or dates in the past must not be cached (RFC7231 5.3) + maxAge = @((NSInteger) MAX(([date timeIntervalSinceNow]), 0)); + } + + return maxAge; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/Categories/PINImage+DecodedImage.h b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/Categories/PINImage+DecodedImage.h new file mode 100644 index 0000000..08d0e5a --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/Categories/PINImage+DecodedImage.h @@ -0,0 +1,44 @@ +// +// UIImage+DecodedImage.h +// Pods +// +// Created by Garrett Moon on 11/19/14. +// +// + +#import + +#import "PINRemoteImageMacros.h" + +#if PIN_TARGET_IOS +#import +#elif PIN_TARGET_MAC +#import +#endif + +#if !PIN_TARGET_IOS +@interface NSImage (PINiOSMapping) + +@property(nonatomic, readonly, nullable) CGImageRef CGImage; + ++ (nullable NSImage *)imageWithData:(nonnull NSData *)imageData; ++ (nullable NSImage *)imageWithContentsOfFile:(nonnull NSString *)path; ++ (nonnull NSImage *)imageWithCGImage:(nonnull CGImageRef)imageRef; + +@end +#endif + +NSData * __nullable PINImageJPEGRepresentation(PINImage * __nonnull image, CGFloat compressionQuality); +NSData * __nullable PINImagePNGRepresentation(PINImage * __nonnull image); + +@interface PINImage (PINDecodedImage) + ++ (nullable PINImage *)pin_decodedImageWithData:(nonnull NSData *)data; ++ (nullable PINImage *)pin_decodedImageWithData:(nonnull NSData *)data skipDecodeIfPossible:(BOOL)skipDecodeIfPossible; ++ (nullable PINImage *)pin_decodedImageWithCGImageRef:(nonnull CGImageRef)imageRef; +#if PIN_TARGET_IOS ++ (nullable PINImage *)pin_decodedImageWithCGImageRef:(nonnull CGImageRef)imageRef orientation:(UIImageOrientation) orientation; +#endif ++ (nullable CGImageRef)pin_decodedImageRefWithCGImageRef:(nonnull CGImageRef)imageRef; + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/Categories/PINImage+DecodedImage.m b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/Categories/PINImage+DecodedImage.m new file mode 100644 index 0000000..da75e5b --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/Categories/PINImage+DecodedImage.m @@ -0,0 +1,325 @@ +// +// UIImage+DecodedImage.m +// Pods +// +// Created by Garrett Moon on 11/19/14. +// +// + +#import "PINImage+DecodedImage.h" + +#import + +#ifdef PIN_WEBP +#import "PINImage+WebP.h" +#endif + +#import "NSData+ImageDetectors.h" + +NS_INLINE BOOL pin_CGImageRefIsOpaque(CGImageRef imageRef) { + CGImageAlphaInfo alpha = CGImageGetAlphaInfo(imageRef); + switch (alpha) { + case kCGImageAlphaNone: + case kCGImageAlphaNoneSkipLast: + case kCGImageAlphaNoneSkipFirst: + return YES; + default: + return NO; + } +} + +#if PIN_TARGET_IOS +NS_INLINE void pin_degreesFromOrientation(UIImageOrientation orientation, void (^completion)(CGFloat degrees, BOOL horizontalFlip, BOOL verticalFlip)) { + switch (orientation) { + case UIImageOrientationUp: // default orientation + completion(0.0, NO, NO); + break; + case UIImageOrientationDown: // 180 deg rotation + completion(180.0, NO, NO); + break; + case UIImageOrientationLeft: + completion(270.0, NO, NO); // 90 deg CCW + break; + case UIImageOrientationRight: + completion(90.0, NO, NO); // 90 deg CW + break; + case UIImageOrientationUpMirrored: // as above but image mirrored along other axis. horizontal flip + completion(0.0, YES, NO); + break; + case UIImageOrientationDownMirrored: // horizontal flip + completion(180.0, YES, NO); + break; + case UIImageOrientationLeftMirrored: // vertical flip + completion(270.0, NO, YES); + break; + case UIImageOrientationRightMirrored: // vertical flip + completion(90.0, NO, YES); + break; + } +} +#endif + +#if !PIN_TARGET_IOS +@implementation NSImage (PINiOSMapping) + +- (CGImageRef)CGImage +{ + NSGraphicsContext *context = [NSGraphicsContext currentContext]; + NSRect rect = NSMakeRect(0.0, 0.0, self.size.width, self.size.height); + return [self CGImageForProposedRect:&rect context:context hints:NULL]; +} + ++ (NSImage *)imageWithData:(NSData *)imageData; +{ + return [[self alloc] initWithData:imageData]; +} + ++ (NSImage *)imageWithContentsOfFile:(NSString *)path +{ + return path ? [[self alloc] initWithContentsOfFile:path] : nil; +} + ++ (NSImage *)imageWithCGImage:(CGImageRef)imageRef +{ + return [[self alloc] initWithCGImage:imageRef size:CGSizeZero]; +} + +@end +#endif + +NSData * __nullable PINImageJPEGRepresentation(PINImage * __nonnull image, CGFloat compressionQuality) +{ +#if PIN_TARGET_IOS + return UIImageJPEGRepresentation(image, compressionQuality); +#elif PIN_TARGET_MAC + NSBitmapImageRep *imageRep = [NSBitmapImageRep imageRepWithData:[image TIFFRepresentation]]; + NSDictionary *imageProperties = @{NSImageCompressionFactor : @(compressionQuality)}; + return [imageRep representationUsingType:NSJPEGFileType properties:imageProperties]; +#endif +} + +NSData * __nullable PINImagePNGRepresentation(PINImage * __nonnull image) { +#if PIN_TARGET_IOS + return UIImagePNGRepresentation(image); +#elif PIN_TARGET_MAC + NSBitmapImageRep *imageRep = [NSBitmapImageRep imageRepWithData:[image TIFFRepresentation]]; + NSDictionary *imageProperties = @{NSImageCompressionFactor : @1}; + return [imageRep representationUsingType:NSPNGFileType properties:imageProperties]; +#endif +} + + +@implementation PINImage (PINDecodedImage) + ++ (PINImage *)pin_decodedImageWithData:(NSData *)data +{ + return [self pin_decodedImageWithData:data skipDecodeIfPossible:NO]; +} + ++ (PINImage *)pin_decodedImageWithData:(NSData *)data skipDecodeIfPossible:(BOOL)skipDecodeIfPossible +{ + if (data == nil) { + return nil; + } + +#if PIN_WEBP + if ([data pin_isWebP]) { + return [PINImage pin_imageWithWebPData:data]; + } +#endif + + PINImage *decodedImage = nil; + + CGImageSourceRef imageSourceRef = CGImageSourceCreateWithData((CFDataRef)data, NULL); + + if (imageSourceRef) { + CGImageRef imageRef = CGImageSourceCreateImageAtIndex(imageSourceRef, 0, (CFDictionaryRef)@{(NSString *)kCGImageSourceShouldCache : (NSNumber *)kCFBooleanFalse}); + if (imageRef) { +#if PIN_TARGET_IOS + UIImageOrientation orientation = pin_UIImageOrientationFromImageSource(imageSourceRef); + if (skipDecodeIfPossible) { + decodedImage = [PINImage imageWithCGImage:imageRef scale:1.0 orientation:orientation]; + } else { + decodedImage = [self pin_decodedImageWithCGImageRef:imageRef orientation:orientation]; + } +#elif PIN_TARGET_MAC + if (skipDecodeIfPossible) { + CGSize imageSize = CGSizeMake(CGImageGetWidth(imageRef), CGImageGetHeight(imageRef)); + decodedImage = [[NSImage alloc] initWithCGImage:imageRef size:imageSize]; + } else { + decodedImage = [self pin_decodedImageWithCGImageRef:imageRef]; + } +#endif + CGImageRelease(imageRef); + } + + CFRelease(imageSourceRef); + } + + return decodedImage; +} + ++ (PINImage *)pin_decodedImageWithCGImageRef:(CGImageRef)imageRef +{ +#if PIN_TARGET_IOS + return [self pin_decodedImageWithCGImageRef:imageRef orientation:UIImageOrientationUp]; +} + ++ (PINImage *)pin_decodedImageWithCGImageRef:(CGImageRef)imageRef orientation:(UIImageOrientation)orientation +{ +#endif +#if PIN_TARGET_IOS + if (@available(iOS 10.0, tvOS 10.0, *)) { + return [self pin_decodedImageUsingGraphicsImageRendererRefWithCGImageRef:imageRef scale:1.0 orientation:orientation]; + } else { + return [UIImage imageWithCGImage:[self pin_decodedImageRefWithCGImageRef:imageRef] scale:1.0 orientation:orientation]; + } +#elif PIN_TARGET_MAC + return [[NSImage alloc] initWithCGImage:[self pin_decodedImageRefWithCGImageRef:imageRef] size:NSZeroSize]; +#endif +} + +#if PIN_TARGET_IOS ++ (PINImage *)pin_decodedImageUsingGraphicsImageRendererRefWithCGImageRef:(CGImageRef)imageRef + scale:(CGFloat)scale + orientation:(UIImageOrientation)orientation API_AVAILABLE(ios(10.0), tvos(10.0)) { + UIGraphicsImageRendererFormat *format = nil; + if (@available(iOS 11.0, tvOS 11.0, *)) { + format = [UIGraphicsImageRendererFormat preferredFormat]; + } else { + format = [UIGraphicsImageRendererFormat defaultFormat]; + } + + format.scale = scale; + format.opaque = pin_CGImageRefIsOpaque(imageRef); + + __block CGFloat radians = 0.0; + __block BOOL doHorizontalFlip = NO; + __block BOOL doVerticalFlip = NO; + + pin_degreesFromOrientation(orientation, ^(CGFloat degrees, BOOL horizontalFlip, BOOL verticalFlip) { + // Convert degrees to radians + radians = [[[NSMeasurement alloc] initWithDoubleValue:degrees + unit:[NSUnitAngle degrees]] + measurementByConvertingToUnit:[NSUnitAngle radians]].doubleValue; + doHorizontalFlip = horizontalFlip; + doVerticalFlip = verticalFlip; + }); + + // Create rotation out of radians + CGAffineTransform transform = CGAffineTransformMakeRotation(radians); + + // Grab image size + CGSize imageSize = CGSizeMake(CGImageGetWidth(imageRef), CGImageGetHeight(imageRef)); + + // Rotate rect by transformation + CGRect rotatedRect = CGRectApplyAffineTransform(CGRectMake(0.0, 0.0, imageSize.width, imageSize.height), transform); + + // Use graphics renderer to render image + UIGraphicsImageRenderer *renderer = [[UIGraphicsImageRenderer alloc] initWithSize:rotatedRect.size format:format]; + + return [renderer imageWithActions:^(UIGraphicsImageRendererContext * _Nonnull rendererContext) { + CGContextRef ctx = rendererContext.CGContext; + + // Flip the default coordinate system for iOS/tvOS: https://developer.apple.com/library/archive/documentation/2DDrawing/Conceptual/DrawingPrintingiOS/GraphicsDrawingOverview/GraphicsDrawingOverview.html#//apple_ref/doc/uid/TP40010156-CH14-SW4 + CGContextTranslateCTM(ctx, rotatedRect.size.width / 2.0, rotatedRect.size.height / 2.0); + CGContextScaleCTM(ctx, (doHorizontalFlip ? -1.0 : 1.0), (doVerticalFlip ? 1.0 : -1.0)); + + // Apply transformation + CGContextConcatCTM(ctx, transform); + + // Draw image + CGContextDrawImage(ctx, CGRectMake(-(imageSize.width / 2.0), -(imageSize.height / 2.0), imageSize.width, imageSize.height), imageRef); + }]; +} +#endif + ++ (CGImageRef)pin_decodedImageRefWithCGImageRef:(CGImageRef)imageRef +{ + CGSize imageSize = CGSizeMake(CGImageGetWidth(imageRef), CGImageGetHeight(imageRef)); + + CGBitmapInfo info = pin_CGImageRefIsOpaque(imageRef) ? (kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host) : (kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host); + CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB(); + + //Use UIGraphicsBeginImageContext parameters from docs: https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIKitFunctionReference/#//apple_ref/c/func/UIGraphicsBeginImageContextWithOptions + CGContextRef ctx = CGBitmapContextCreate(NULL, imageSize.width, imageSize.height, + 8, + 0, + colorspace, + info); + + CGColorSpaceRelease(colorspace); + + if (ctx) { + CGContextSetBlendMode(ctx, kCGBlendModeCopy); + CGContextDrawImage(ctx, CGRectMake(0, 0, imageSize.width, imageSize.height), imageRef); + + CGImageRef decodedImageRef = CGBitmapContextCreateImage(ctx); + if (decodedImageRef) { + CFAutorelease(decodedImageRef); + } + CGContextRelease(ctx); + return decodedImageRef; + + } + + return imageRef; +} + +#if PIN_TARGET_IOS +UIImageOrientation pin_UIImageOrientationFromImageSource(CGImageSourceRef imageSourceRef) { + UIImageOrientation orientation = UIImageOrientationUp; + + if (imageSourceRef != nil) { + NSDictionary *dict = (NSDictionary *)CFBridgingRelease(CGImageSourceCopyPropertiesAtIndex(imageSourceRef, 0, NULL)); + + if (dict != nil) { + + NSNumber* exifOrientation = dict[(id)kCGImagePropertyOrientation]; + if (exifOrientation != nil) { + + switch (exifOrientation.intValue) { + case 1: /*kCGImagePropertyOrientationUp*/ + orientation = UIImageOrientationUp; + break; + + case 2: /*kCGImagePropertyOrientationUpMirrored*/ + orientation = UIImageOrientationUpMirrored; + break; + + case 3: /*kCGImagePropertyOrientationDown*/ + orientation = UIImageOrientationDown; + break; + + case 4: /*kCGImagePropertyOrientationDownMirrored*/ + orientation = UIImageOrientationDownMirrored; + break; + case 5: /*kCGImagePropertyOrientationLeftMirrored*/ + orientation = UIImageOrientationLeftMirrored; + break; + + case 6: /*kCGImagePropertyOrientationRight*/ + orientation = UIImageOrientationRight; + break; + + case 7: /*kCGImagePropertyOrientationRightMirrored*/ + orientation = UIImageOrientationRightMirrored; + break; + + case 8: /*kCGImagePropertyOrientationLeft*/ + orientation = UIImageOrientationLeft; + break; + + default: + break; + } + } + } + } + + return orientation; +} + +#endif + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/Categories/PINImage+ScaledImage.h b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/Categories/PINImage+ScaledImage.h new file mode 100644 index 0000000..b82f931 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/Categories/PINImage+ScaledImage.h @@ -0,0 +1,24 @@ +// +// UIImage+ScaledImage.h +// Pods +// +// Created by Michael Schneider on 2/9/17. +// +// + +#import + +#import "PINRemoteImageMacros.h" + +#if PIN_TARGET_IOS +#import +#elif PIN_TARGET_MAC +#import +#endif + +@interface PINImage (PINScaledImage) + +- (PINImage *)pin_scaledImageForKey:(NSString *)key; ++ (PINImage *)pin_scaledImageForImage:(PINImage *)image withKey:(NSString *)key; + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/Categories/PINImage+ScaledImage.m b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/Categories/PINImage+ScaledImage.m new file mode 100644 index 0000000..9c4326e --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/Categories/PINImage+ScaledImage.m @@ -0,0 +1,56 @@ +// +// UIImage+ScaledImage.m +// Pods +// +// Created by Michael Schneider on 2/9/17. +// +// + +#import "PINImage+ScaledImage.h" + +static inline PINImage *PINScaledImageForKey(NSString * __nullable key, PINImage * __nullable image) { + if (image == nil) { + return nil; + } + +#if PIN_TARGET_IOS + + NSCAssert(image.CGImage != NULL, @"CGImage should not be NULL"); + + CGFloat scale = 1.0; + if (key.length >= 8) { + if ([key rangeOfString:@"_2x."].location != NSNotFound || + [key rangeOfString:@"@2x."].location != NSNotFound) { + scale = 2.0; + } + + if ([key rangeOfString:@"_3x."].location != NSNotFound || + [key rangeOfString:@"@3x."].location != NSNotFound) { + scale = 3.0; + } + } + + if (scale != image.scale) { + return [[UIImage alloc] initWithCGImage:image.CGImage scale:scale orientation:image.imageOrientation]; + } + + return image; + +#elif PIN_TARGET_MAC + return image; +#endif +} + +@implementation PINImage (ScaledImage) + +- (PINImage *)pin_scaledImageForKey:(NSString *)key +{ + return PINScaledImageForKey(key, self); +} + ++ (PINImage *)pin_scaledImageForImage:(PINImage *)image withKey:(NSString *)key +{ + return PINScaledImageForKey(key, image); +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/Categories/PINImage+WebP.h b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/Categories/PINImage+WebP.h new file mode 100644 index 0000000..8c38407 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/Categories/PINImage+WebP.h @@ -0,0 +1,25 @@ +// +// UIImage+WebP.h +// Pods +// +// Created by Garrett Moon on 11/18/14. +// +// + +#import "PINRemoteImageMacros.h" + +#if PIN_WEBP + +#if PIN_TARGET_IOS +#import +#elif PIN_TARGET_MAC +#import +#endif + +@interface PINImage (PINWebP) + ++ (PINImage *)pin_imageWithWebPData:(NSData *)webPData; + +@end + +#endif diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/Categories/PINImage+WebP.m b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/Categories/PINImage+WebP.m new file mode 100644 index 0000000..d2021d3 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/Categories/PINImage+WebP.m @@ -0,0 +1,87 @@ +// +// UIImage+WebP.m +// Pods +// +// Created by Garrett Moon on 11/18/14. +// +// + +#import "PINImage+WebP.h" + +#if PIN_WEBP + +#if SWIFT_PACKAGE +@import libwebp; +#else +#import "webp/decode.h" +#endif + +static void releaseData(void *info, const void *data, size_t size) +{ + WebPFree((void *)data); +} + +@implementation PINImage (PINWebP) + ++ (PINImage *)pin_imageWithWebPData:(NSData *)webPData +{ + WebPBitstreamFeatures features; + if (WebPGetFeatures([webPData bytes], [webPData length], &features) == VP8_STATUS_OK) { + // Decode the WebP image data into a RGBA value array + int height, width; + uint8_t *data = NULL; + int pixelLength = 0; + + if (features.has_alpha) { + data = WebPDecodeRGBA([webPData bytes], [webPData length], &width, &height); + pixelLength = 4; + } else { + data = WebPDecodeRGB([webPData bytes], [webPData length], &width, &height); + pixelLength = 3; + } + + if (data) { + CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, data, width * height * pixelLength, releaseData); + + CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB(); + CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault; + + if (features.has_alpha) { + bitmapInfo |= kCGImageAlphaLast; + } else { + bitmapInfo |= kCGImageAlphaNone; + } + + CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault; + CGImageRef imageRef = CGImageCreate(width, + height, + 8, + 8 * pixelLength, + pixelLength * width, + colorSpaceRef, + bitmapInfo, + provider, + NULL, + NO, + renderingIntent); + + PINImage *image = nil; +#if PIN_TARGET_IOS + image = [UIImage imageWithCGImage:imageRef]; +#elif PIN_TARGET_MAC + image = [[self alloc] initWithCGImage:imageRef size:CGSizeZero]; +#endif + + CGImageRelease(imageRef); + CGColorSpaceRelease(colorSpaceRef); + CGDataProviderRelease(provider); + + return image; + } + } + return nil; +} + +@end + +#endif diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/Categories/PINRemoteImageTask+Subclassing.h b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/Categories/PINRemoteImageTask+Subclassing.h new file mode 100644 index 0000000..8e0c600 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/Categories/PINRemoteImageTask+Subclassing.h @@ -0,0 +1,20 @@ +// +// PINRemoteImageTask+Subclassing.h +// PINRemoteImage +// +// Created by Garrett Moon on 5/22/17. +// Copyright © 2017 Pinterest. All rights reserved. +// + +#import "PINRemoteImageTask.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface PINRemoteImageTask (Subclassing) + +- (NSMutableDictionary *)l_callbackBlocks; +- (BOOL)l_cancelWithUUID:(NSUUID *)UUID; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/ImageCategories/PINAnimatedImageView+PINRemoteImage.m b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/ImageCategories/PINAnimatedImageView+PINRemoteImage.m new file mode 100644 index 0000000..34edd84 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/ImageCategories/PINAnimatedImageView+PINRemoteImage.m @@ -0,0 +1,119 @@ +// +// PINAnimatedImageView+PINRemoteImage.m +// Pods +// +// Created by Garrett Moon on 4/19/18. +// + +#import "PINAnimatedImageView+PINRemoteImage.h" + +#import "PINAnimatedImage.h" + +@implementation PINAnimatedImageView (PINRemoteImage) + +- (void)pin_setImageFromURL:(NSURL *)url +{ + [PINRemoteImageCategoryManager setImageOnView:self fromURL:url]; +} + +- (void)pin_setImageFromURL:(NSURL *)url placeholderImage:(PINImage *)placeholderImage +{ + [PINRemoteImageCategoryManager setImageOnView:self fromURL:url placeholderImage:placeholderImage]; +} + +- (void)pin_setImageFromURL:(NSURL *)url completion:(PINRemoteImageManagerImageCompletion)completion +{ + [PINRemoteImageCategoryManager setImageOnView:self fromURL:url completion:completion]; +} + +- (void)pin_setImageFromURL:(NSURL *)url placeholderImage:(PINImage *)placeholderImage completion:(PINRemoteImageManagerImageCompletion)completion +{ + [PINRemoteImageCategoryManager setImageOnView:self fromURL:url placeholderImage:placeholderImage completion:completion]; +} + +- (void)pin_setImageFromURL:(NSURL *)url processorKey:(NSString *)processorKey processor:(PINRemoteImageManagerImageProcessor)processor +{ + [PINRemoteImageCategoryManager setImageOnView:self fromURL:url processorKey:processorKey processor:processor]; +} + +- (void)pin_setImageFromURL:(NSURL *)url placeholderImage:(PINImage *)placeholderImage processorKey:(NSString *)processorKey processor:(PINRemoteImageManagerImageProcessor)processor +{ + [PINRemoteImageCategoryManager setImageOnView:self fromURL:url placeholderImage:placeholderImage processorKey:processorKey processor:processor]; +} + +- (void)pin_setImageFromURL:(NSURL *)url processorKey:(NSString *)processorKey processor:(PINRemoteImageManagerImageProcessor)processor completion:(PINRemoteImageManagerImageCompletion)completion +{ + [PINRemoteImageCategoryManager setImageOnView:self fromURL:url processorKey:processorKey processor:processor completion:completion]; +} + +- (void)pin_setImageFromURL:(NSURL *)url placeholderImage:(PINImage *)placeholderImage processorKey:(NSString *)processorKey processor:(PINRemoteImageManagerImageProcessor)processor completion:(PINRemoteImageManagerImageCompletion)completion +{ + [PINRemoteImageCategoryManager setImageOnView:self fromURLs:@[url] placeholderImage:placeholderImage processorKey:processorKey processor:processor completion:completion]; +} + +- (void)pin_setImageFromURLs:(NSArray *)urls +{ + [PINRemoteImageCategoryManager setImageOnView:self fromURLs:urls]; +} + +- (void)pin_setImageFromURLs:(NSArray *)urls placeholderImage:(PINImage *)placeholderImage +{ + [PINRemoteImageCategoryManager setImageOnView:self fromURLs:urls placeholderImage:placeholderImage]; +} + +- (void)pin_setImageFromURLs:(NSArray *)urls placeholderImage:(PINImage *)placeholderImage completion:(PINRemoteImageManagerImageCompletion)completion +{ + [PINRemoteImageCategoryManager setImageOnView:self fromURLs:urls placeholderImage:placeholderImage completion:completion]; +} + +- (void)pin_cancelImageDownload +{ + [PINRemoteImageCategoryManager cancelImageDownloadOnView:self]; +} + +- (NSUUID *)pin_downloadImageOperationUUID +{ + return [PINRemoteImageCategoryManager downloadImageOperationUUIDOnView:self]; +} + +- (void)pin_setDownloadImageOperationUUID:(NSUUID *)downloadImageOperationUUID +{ + [PINRemoteImageCategoryManager setDownloadImageOperationUUID:downloadImageOperationUUID onView:self]; +} + +- (BOOL)pin_updateWithProgress +{ + return [PINRemoteImageCategoryManager updateWithProgressOnView:self]; +} + +- (void)setPin_updateWithProgress:(BOOL)updateWithProgress +{ + [PINRemoteImageCategoryManager setUpdateWithProgressOnView:updateWithProgress onView:self]; +} + +- (void)pin_setPlaceholderWithImage:(PINImage *)image +{ + self.image = image; +} + +- (void)pin_updateUIWithRemoteImageManagerResult:(PINRemoteImageManagerResult *)result +{ + if (result.alternativeRepresentation && [result.alternativeRepresentation isKindOfClass:[PINCachedAnimatedImage class]]) { + self.animatedImage = (PINCachedAnimatedImage *)result.alternativeRepresentation; + } else if (result.image) { + self.image = result.image; + } +} + +- (void)pin_clearImages +{ + self.animatedImage = nil; + self.image = nil; +} + +- (BOOL)pin_ignoreGIFs +{ + return NO; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/ImageCategories/PINButton+PINRemoteImage.m b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/ImageCategories/PINButton+PINRemoteImage.m new file mode 100644 index 0000000..50f5d80 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/ImageCategories/PINButton+PINRemoteImage.m @@ -0,0 +1,131 @@ +// +// UIButton+PINRemoteImage.m +// Pods +// +// Created by Garrett Moon on 8/18/14. +// +// + +#import "PINButton+PINRemoteImage.h" + +@implementation PINButton (PINRemoteImage) + +- (void)pin_setImageFromURL:(NSURL *)url +{ + [PINRemoteImageCategoryManager setImageOnView:self fromURL:url]; +} + +- (void)pin_setImageFromURL:(NSURL *)url placeholderImage:(PINImage *)placeholderImage +{ + [PINRemoteImageCategoryManager setImageOnView:self fromURL:url placeholderImage:placeholderImage]; +} + +- (void)pin_setImageFromURL:(NSURL *)url completion:(PINRemoteImageManagerImageCompletion)completion +{ + [PINRemoteImageCategoryManager setImageOnView:self fromURL:url completion:completion]; +} + +- (void)pin_setImageFromURL:(NSURL *)url placeholderImage:(PINImage *)placeholderImage completion:(PINRemoteImageManagerImageCompletion)completion +{ + [PINRemoteImageCategoryManager setImageOnView:self fromURL:url placeholderImage:placeholderImage completion:completion]; +} + +- (void)pin_setImageFromURL:(NSURL *)url processorKey:(NSString *)processorKey processor:(PINRemoteImageManagerImageProcessor)processor +{ + [PINRemoteImageCategoryManager setImageOnView:self fromURL:url processorKey:processorKey processor:processor]; +} + +- (void)pin_setImageFromURL:(NSURL *)url placeholderImage:(PINImage *)placeholderImage processorKey:(NSString *)processorKey processor:(PINRemoteImageManagerImageProcessor)processor +{ + [PINRemoteImageCategoryManager setImageOnView:self fromURL:url placeholderImage:placeholderImage processorKey:processorKey processor:processor]; +} + +- (void)pin_setImageFromURL:(NSURL *)url processorKey:(NSString *)processorKey processor:(PINRemoteImageManagerImageProcessor)processor completion:(PINRemoteImageManagerImageCompletion)completion +{ + [PINRemoteImageCategoryManager setImageOnView:self fromURL:url processorKey:processorKey processor:processor completion:completion]; +} + +- (void)pin_setImageFromURL:(NSURL *)url placeholderImage:(PINImage *)placeholderImage processorKey:(NSString *)processorKey processor:(PINRemoteImageManagerImageProcessor)processor completion:(PINRemoteImageManagerImageCompletion)completion +{ + [PINRemoteImageCategoryManager setImageOnView:self fromURLs:url?@[url]:nil placeholderImage:placeholderImage processorKey:processorKey processor:processor completion:completion]; +} + +- (void)pin_setImageFromURLs:(NSArray *)urls +{ + [PINRemoteImageCategoryManager setImageOnView:self fromURLs:urls]; +} + +- (void)pin_setImageFromURLs:(NSArray *)urls placeholderImage:(PINImage *)placeholderImage +{ + [PINRemoteImageCategoryManager setImageOnView:self fromURLs:urls placeholderImage:placeholderImage]; +} + +- (void)pin_setImageFromURLs:(NSArray *)urls placeholderImage:(PINImage *)placeholderImage completion:(PINRemoteImageManagerImageCompletion)completion +{ + [PINRemoteImageCategoryManager setImageOnView:self fromURLs:urls placeholderImage:placeholderImage completion:completion]; +} + +- (void)pin_cancelImageDownload +{ + [PINRemoteImageCategoryManager cancelImageDownloadOnView:self]; +} + +- (NSUUID *)pin_downloadImageOperationUUID +{ + return [PINRemoteImageCategoryManager downloadImageOperationUUIDOnView:self]; +} + +- (void)pin_setDownloadImageOperationUUID:(NSUUID *)downloadImageOperationUUID +{ + [PINRemoteImageCategoryManager setDownloadImageOperationUUID:downloadImageOperationUUID onView:self]; +} + +- (BOOL)pin_updateWithProgress +{ + return [PINRemoteImageCategoryManager updateWithProgressOnView:self]; +} + +- (void)setPin_updateWithProgress:(BOOL)updateWithProgress +{ + [PINRemoteImageCategoryManager setUpdateWithProgressOnView:updateWithProgress onView:self]; +} + +- (void)pin_setPlaceholderWithImage:(PINImage *)image +{ +#if PIN_TARGET_IOS + [self setImage:image forState:UIControlStateNormal]; +#elif PIN_TARGET_MAC + [self setImage:image]; +#endif +} + +- (void)pin_updateUIWithRemoteImageManagerResult:(PINRemoteImageManagerResult *)result +{ + if (result.image) { +#if PIN_TARGET_IOS + [self setImage:result.image forState:UIControlStateNormal]; + [self setNeedsLayout]; +#elif PIN_TARGET_MAC + [self setImage:result.image]; + [self setNeedsLayout:YES]; +#endif + } +} + +- (void)pin_clearImages +{ +#if PIN_TARGET_IOS + [self setImage:nil forState:UIControlStateNormal]; + [self setNeedsLayout]; +#elif PIN_TARGET_MAC + [self setImage:nil]; + [self setNeedsLayout:YES]; +#endif +} + +- (BOOL)pin_ignoreGIFs +{ + return YES; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/ImageCategories/PINImageView+PINRemoteImage.m b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/ImageCategories/PINImageView+PINRemoteImage.m new file mode 100644 index 0000000..1e6a6ab --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/ImageCategories/PINImageView+PINRemoteImage.m @@ -0,0 +1,127 @@ +// +// UIImageView+PINRemoteImage.m +// Pods +// +// Created by Garrett Moon on 8/17/14. +// +// + +#import "PINImageView+PINRemoteImage.h" + +@implementation PINImageView (PINRemoteImage) + +- (void)pin_setImageFromURL:(NSURL *)url +{ + [PINRemoteImageCategoryManager setImageOnView:self fromURL:url]; +} + +- (void)pin_setImageFromURL:(NSURL *)url placeholderImage:(PINImage *)placeholderImage +{ + [PINRemoteImageCategoryManager setImageOnView:self fromURL:url placeholderImage:placeholderImage]; +} + +- (void)pin_setImageFromURL:(NSURL *)url completion:(PINRemoteImageManagerImageCompletion)completion +{ + [PINRemoteImageCategoryManager setImageOnView:self fromURL:url completion:completion]; +} + +- (void)pin_setImageFromURL:(NSURL *)url placeholderImage:(PINImage *)placeholderImage completion:(PINRemoteImageManagerImageCompletion)completion +{ + [PINRemoteImageCategoryManager setImageOnView:self fromURL:url placeholderImage:placeholderImage completion:completion]; +} + +- (void)pin_setImageFromURL:(NSURL *)url processorKey:(NSString *)processorKey processor:(PINRemoteImageManagerImageProcessor)processor +{ + [PINRemoteImageCategoryManager setImageOnView:self fromURL:url processorKey:processorKey processor:processor]; +} + +- (void)pin_setImageFromURL:(NSURL *)url placeholderImage:(PINImage *)placeholderImage processorKey:(NSString *)processorKey processor:(PINRemoteImageManagerImageProcessor)processor +{ + [PINRemoteImageCategoryManager setImageOnView:self fromURL:url placeholderImage:placeholderImage processorKey:processorKey processor:processor]; +} + +- (void)pin_setImageFromURL:(NSURL *)url processorKey:(NSString *)processorKey processor:(PINRemoteImageManagerImageProcessor)processor completion:(PINRemoteImageManagerImageCompletion)completion +{ + [PINRemoteImageCategoryManager setImageOnView:self fromURL:url processorKey:processorKey processor:processor completion:completion]; +} + +- (void)pin_setImageFromURL:(NSURL *)url placeholderImage:(PINImage *)placeholderImage processorKey:(NSString *)processorKey processor:(PINRemoteImageManagerImageProcessor)processor completion:(PINRemoteImageManagerImageCompletion)completion +{ + [PINRemoteImageCategoryManager setImageOnView:self fromURLs:url?@[url]:nil placeholderImage:placeholderImage processorKey:processorKey processor:processor completion:completion]; +} + +- (void)pin_setImageFromURLs:(NSArray *)urls +{ + [PINRemoteImageCategoryManager setImageOnView:self fromURLs:urls]; +} + +- (void)pin_setImageFromURLs:(NSArray *)urls placeholderImage:(PINImage *)placeholderImage +{ + [PINRemoteImageCategoryManager setImageOnView:self fromURLs:urls placeholderImage:placeholderImage]; +} + +- (void)pin_setImageFromURLs:(NSArray *)urls placeholderImage:(PINImage *)placeholderImage completion:(PINRemoteImageManagerImageCompletion)completion +{ + [PINRemoteImageCategoryManager setImageOnView:self fromURLs:urls placeholderImage:placeholderImage completion:completion]; +} + +- (void)pin_cancelImageDownload +{ + [PINRemoteImageCategoryManager cancelImageDownloadOnView:self]; +} + +- (NSUUID *)pin_downloadImageOperationUUID +{ + return [PINRemoteImageCategoryManager downloadImageOperationUUIDOnView:self]; +} + +- (void)pin_setDownloadImageOperationUUID:(NSUUID *)downloadImageOperationUUID +{ + [PINRemoteImageCategoryManager setDownloadImageOperationUUID:downloadImageOperationUUID onView:self]; +} + +- (BOOL)pin_updateWithProgress +{ + return [PINRemoteImageCategoryManager updateWithProgressOnView:self]; +} + +- (void)setPin_updateWithProgress:(BOOL)updateWithProgress +{ + [PINRemoteImageCategoryManager setUpdateWithProgressOnView:updateWithProgress onView:self]; +} + +- (void)pin_setPlaceholderWithImage:(PINImage *)image +{ + self.image = image; +} + +- (void)pin_updateUIWithRemoteImageManagerResult:(PINRemoteImageManagerResult *)result +{ + if (result.image) { + self.image = result.image; + +#if PIN_TARGET_IOS + [self setNeedsLayout]; +#elif PIN_TARGET_MAC + [self setNeedsLayout:YES]; +#endif + } +} + +- (void)pin_clearImages +{ + self.image = nil; + +#if PIN_TARGET_IOS + [self setNeedsLayout]; +#elif PIN_TARGET_MAC + [self setNeedsLayout:YES]; +#endif +} + +- (BOOL)pin_ignoreGIFs +{ + return YES; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINAlternateRepresentationProvider.m b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINAlternateRepresentationProvider.m new file mode 100644 index 0000000..ea87911 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINAlternateRepresentationProvider.m @@ -0,0 +1,30 @@ +// +// PINAlternateRepresentationProvider.m +// Pods +// +// Created by Garrett Moon on 3/17/16. +// +// + +#import "PINAlternateRepresentationProvider.h" + +#import "PINCachedAnimatedImage.h" +#import "NSData+ImageDetectors.h" + +@implementation PINAlternateRepresentationProvider + +- (id)alternateRepresentationWithData:(NSData *)data options:(PINRemoteImageManagerDownloadOptions)options +{ +#if PIN_WEBP + if ([data pin_isAnimatedGIF] || [data pin_isAnimatedWebP]) { + return [[PINCachedAnimatedImage alloc] initWithAnimatedImageData:data]; + } +#else + if ([data pin_isAnimatedGIF]) { + return [[PINCachedAnimatedImage alloc] initWithAnimatedImageData:data]; + } +#endif + return nil; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINCache/PINCache+PINRemoteImageCaching.m b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINCache/PINCache+PINRemoteImageCaching.m new file mode 100644 index 0000000..5641d78 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINCache/PINCache+PINRemoteImageCaching.m @@ -0,0 +1,113 @@ +// +// PINCache+PINRemoteImageCaching.m +// Pods +// +// Created by Aleksei Shevchenko on 7/28/16. +// +// + +#import "PINCache+PINRemoteImageCaching.h" + +@implementation PINCache (PINRemoteImageCaching) + +//****************************************************************************************************** +// Memory cache methods +//****************************************************************************************************** +- (nullable id)objectFromMemoryForKey:(NSString *)key +{ + return [self.memoryCache objectForKey:key]; +} + +- (void)setObjectInMemory:(id)object forKey:(NSString *)key withCost:(NSUInteger)cost withAgeLimit:(NSTimeInterval)ageLimit +{ + [self.memoryCache setObject:object forKey:key withCost:cost ageLimit:ageLimit]; +} + +- (void)setObjectInMemory:(id)object forKey:(NSString *)key withCost:(NSUInteger)cost +{ + [self.memoryCache setObject:object forKey:key withCost:cost]; +} + +- (void)removeObjectForKeyFromMemory:(NSString *)key +{ + [self.memoryCache removeObjectForKey:key]; +} + +//****************************************************************************************************** +// Disk cache methods +//****************************************************************************************************** +- (nullable id)objectFromDiskForKey:(NSString *)key +{ + return [self.diskCache objectForKey:key]; +} + +- (void)objectFromDiskForKey:(NSString *)key completion:(PINRemoteImageCachingObjectBlock)completion +{ + __weak typeof(self) weakSelf = self; + [self.diskCache objectForKeyAsync:key completion:^(PINDiskCache * _Nonnull cache, NSString * _Nonnull key, id _Nullable object) { + if(completion) { + typeof(self) strongSelf = weakSelf; + completion(strongSelf, key, object); + } + }]; +} + +- (void)setObjectOnDisk:(id)object forKey:(NSString *)key withAgeLimit:(NSTimeInterval)ageLimit +{ + [self.diskCache setObject:object forKey:key withAgeLimit:ageLimit]; +} + +- (void)setObjectOnDisk:(id)object forKey:(NSString *)key +{ + [self.diskCache setObject:object forKey:key withAgeLimit:0]; +} + +- (BOOL)objectExistsForKey:(NSString *)key +{ + return [self containsObjectForKey:key]; +} + +//****************************************************************************************************** +// Common cache methods +//****************************************************************************************************** +- (void)removeObjectForKey:(NSString *)key completion:(PINRemoteImageCachingObjectBlock)completion +{ + if (completion) { + __weak typeof(self) weakSelf = self; + [self removeObjectForKeyAsync:key completion:^(id _Nonnull cache, NSString * _Nonnull key, id _Nullable object) { + typeof(self) strongSelf = weakSelf; + completion(strongSelf, key, object); + }]; + } else { + [self removeObjectForKeyAsync:key completion:nil]; + } +} + +- (BOOL)diskCacheIsTTLCache +{ + return self.diskCache.isTTLCache; +} + +- (BOOL)memoryCacheIsTTLCache +{ + return self.memoryCache.isTTLCache; +} + +@end + +@implementation PINRemoteImageManager (PINCache) + +- (PINCache *)pinCache +{ + static BOOL isCachePINCache = NO; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + isCachePINCache = [self.cache isKindOfClass:[PINCache class]]; + }); + if (isCachePINCache) { + return (PINCache *)self.cache; + } + return nil; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINDisplayLink.h b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINDisplayLink.h new file mode 100644 index 0000000..0b43e7b --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINDisplayLink.h @@ -0,0 +1,25 @@ +// +// PINDisplayLink.h +// Pods +// +// Created by Garrett Moon on 4/23/18. +// + +#import + +#import "PINRemoteImageMacros.h" + +#if PIN_TARGET_IOS +#define PINDisplayLink CADisplayLink +#elif PIN_TARGET_MAC +@interface PINDisplayLink : NSObject + ++ (PINDisplayLink *)displayLinkWithTarget:(id)target selector:(SEL)sel; +- (void)addToRunLoop:(NSRunLoop *)runloop forMode:(NSRunLoopMode)mode; +- (void)removeFromRunLoop:(NSRunLoop *)runloop forMode:(NSRunLoopMode)mode; + +@property(getter=isPaused, nonatomic) BOOL paused; +@property(nonatomic) NSInteger frameInterval; + +@end +#endif diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINDisplayLink.m b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINDisplayLink.m new file mode 100644 index 0000000..8235265 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINDisplayLink.m @@ -0,0 +1,133 @@ +// +// PINDisplayLink.m +// Pods +// +// Created by Garrett Moon on 4/23/18. +// + +#import "PINDisplayLink.h" + +#if PIN_TARGET_MAC + +#if SWIFT_PACKAGE +@import CoreVideo.CVDisplayLink; +#else +#import +#endif + +@interface PINDisplayLink () + +@property (nonatomic, readonly) id target; +@property (nonatomic, readonly) SEL selector; +@property (nonatomic, readonly) NSRunLoop *runloop; +@property (nonatomic, readonly) NSRunLoopMode mode; + +- (void)displayLinkFired; + +@end + +static CVReturn displayLinkFired (CVDisplayLinkRef displayLink, + const CVTimeStamp *inNow, + const CVTimeStamp *inOutputTime, + CVOptionFlags flagsIn, + CVOptionFlags *flagsOut, + void *displayLinkContext) +{ + PINDisplayLink *link = (__bridge PINDisplayLink *)displayLinkContext; + [link displayLinkFired]; + return kCVReturnSuccess; +} + +@implementation PINDisplayLink +{ + CVDisplayLinkRef _displayLinkRef; + + BOOL _paused; + NSInteger _frameInterval; +} + ++ (PINDisplayLink *)displayLinkWithTarget:(id)target selector:(SEL)sel +{ + return [[PINDisplayLink alloc] initWithTarget:target selector:sel]; +} + +- (PINDisplayLink *)initWithTarget:(id)target selector:(SEL)sel +{ + if (self = [super init]) { + _target = target; + _selector = sel; + CVDisplayLinkCreateWithActiveCGDisplays(&_displayLinkRef); + CVDisplayLinkSetOutputCallback(_displayLinkRef, &displayLinkFired, (__bridge void * _Nullable)(self)); + } + return self; +} + +- (void)dealloc +{ + if (_displayLinkRef) { + CVDisplayLinkRelease(_displayLinkRef); + } +} + +- (void)displayLinkFired +{ + dispatch_async(dispatch_get_main_queue(), ^{ + [self.runloop performSelector:self.selector target:self.target argument:self order:NSUIntegerMax modes:@[self.mode]]; + }); +} + +- (void)addToRunLoop:(NSRunLoop *)runloop forMode:(NSRunLoopMode)mode +{ + PINAssertMain(); + NSAssert(runloop && mode, @"Must set a runloop and a mode."); + _runloop = runloop; + _mode = mode; + if (_paused == NO) { + CVDisplayLinkStart(_displayLinkRef); + } +} + +- (void)removeFromRunLoop:(NSRunLoop *)runloop forMode:(NSRunLoopMode)mode +{ + _runloop = nil; + _mode = nil; + if (_paused == NO) { + CVDisplayLinkStop(_displayLinkRef); + } +} + +- (BOOL)isPaused +{ + PINAssertMain(); + return _paused; +} + +- (void)setPaused:(BOOL)paused +{ + PINAssertMain(); + if (_paused == paused) { + return; + } + + _paused = paused; + if (paused) { + CVDisplayLinkStop(_displayLinkRef); + } else { + CVDisplayLinkStart(_displayLinkRef); + } +} + +- (NSInteger)frameInterval +{ + PINAssertMain(); + return _frameInterval; +} + +- (void)setFrameInterval:(NSInteger)frameInterval +{ + PINAssertMain(); + _frameInterval = frameInterval; +} + +@end +#endif diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINProgressiveImage.m b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINProgressiveImage.m new file mode 100644 index 0000000..8d530b9 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINProgressiveImage.m @@ -0,0 +1,464 @@ +// +// PINProgressiveImage.m +// Pods +// +// Created by Garrett Moon on 2/9/15. +// +// + +#import "PINProgressiveImage.h" + +#import +#import + +#import "PINRemoteImage.h" +#import "PINImage+DecodedImage.h" +#import "PINRemoteImageDownloadTask.h" +#import "PINSpeedRecorder.h" + +@interface PINProgressiveImage () + +@property (nonatomic, strong) NSURLSessionDataTask *dataTask; +@property (nonatomic, strong) NSMutableData *mutableData; +@property (nonatomic, assign) int64_t expectedNumberOfBytes; +@property (nonatomic, assign) CGImageSourceRef imageSource; +@property (nonatomic, assign) CGSize size; +@property (nonatomic, assign) BOOL isProgressiveJPEG; +@property (nonatomic, assign) NSUInteger currentThreshold; +@property (nonatomic, assign) NSUInteger startingBytes; +@property (nonatomic, assign) NSUInteger scannedByte; +@property (nonatomic, assign) NSInteger sosCount; +@property (nonatomic, strong) PINRemoteLock *lock; +#if DEBUG +@property (nonatomic, assign) CFTimeInterval scanTime; +#endif + +@end + +@implementation PINProgressiveImage + +@synthesize progressThresholds = _progressThresholds; +@synthesize estimatedRemainingTimeThreshold = _estimatedRemainingTimeThreshold; + +- (nonnull instancetype)initWithDataTask:(nonnull NSURLSessionDataTask *)dataTask +{ + if (self = [super init]) { + self.lock = [[PINRemoteLock alloc] initWithName:@"PINProgressiveImage"]; + + _dataTask = dataTask; + _imageSource = CGImageSourceCreateIncremental(NULL);; + self.size = CGSizeZero; + self.isProgressiveJPEG = NO; + self.currentThreshold = 0; + self.progressThresholds = @[@0.00, @0.35, @0.65]; + self.estimatedRemainingTimeThreshold = -1; + self.sosCount = 0; + self.scannedByte = 0; +#if DEBUG + self.scanTime = 0; +#endif + } + return self; +} + +- (void)dealloc +{ + [self.lock lock]; + if (self.imageSource) { + CFRelease(_imageSource); + } + [self.lock unlock]; +} + +#pragma mark - public + +- (void)setProgressThresholds:(NSArray *)progressThresholds +{ + // There's no reason to set an empty progress thresholds array, instead don't use the progressive feature + if (progressThresholds.count > 0) { + [self.lock lock]; + _progressThresholds = [progressThresholds copy]; + [self.lock unlock]; + } +} + +- (NSArray *)progressThresholds +{ + [self.lock lock]; + NSArray *progressThresholds = _progressThresholds; + [self.lock unlock]; + return progressThresholds; +} + +- (void)setEstimatedRemainingTimeThreshold:(CFTimeInterval)estimatedRemainingTimeThreshold +{ + [self.lock lock]; + _estimatedRemainingTimeThreshold = estimatedRemainingTimeThreshold; + [self.lock unlock]; +} + +- (CFTimeInterval)estimatedRemainingTimeThreshold +{ + [self.lock lock]; + CFTimeInterval estimatedRemainingTimeThreshold = _estimatedRemainingTimeThreshold; + [self.lock unlock]; + return estimatedRemainingTimeThreshold; +} + +- (CFTimeInterval)estimatedRemainingTime +{ + __block CFTimeInterval estimatedRemainingTime; + [self.lock lockWithBlock:^{ + estimatedRemainingTime = [self l_estimatedRemainingTime]; + }]; + return estimatedRemainingTime; +} + +- (CFTimeInterval)l_estimatedRemainingTime +{ + if (_dataTask.countOfBytesExpectedToReceive == NSURLSessionTransferSizeUnknown) { + return MAXFLOAT; + } + NSUInteger remainingBytes = (NSUInteger)_dataTask.countOfBytesExpectedToReceive - (NSUInteger)_dataTask.countOfBytesReceived; + if (remainingBytes == 0) { + return 0; + } + + float bytesPerSecond = [[PINSpeedRecorder sharedRecorder] weightedAdjustedBytesPerSecondForHost:[_dataTask.currentRequest.URL host]]; + if (bytesPerSecond == -1) { + return MAXFLOAT; + } + return remainingBytes / bytesPerSecond; +} + +- (void)updateProgressiveImageWithData:(nonnull NSData *)data expectedNumberOfBytes:(int64_t)expectedNumberOfBytes isResume:(BOOL)isResume +{ + [self.lock lock]; + if (isResume) { + NSAssert(self.mutableData == nil, @"If we're resuming, data shouldn't be setup yet."); + self.startingBytes = data.length; + } + + if (self.mutableData == nil) { + NSUInteger bytesToAlloc = 0; + if (expectedNumberOfBytes > 0) { + bytesToAlloc = (NSUInteger)expectedNumberOfBytes; + } + self.mutableData = [[NSMutableData alloc] initWithCapacity:bytesToAlloc]; + self.expectedNumberOfBytes = expectedNumberOfBytes; + } + [self.mutableData appendData:data]; + + while ([self l_hasCompletedFirstScan] == NO && self.scannedByte < self.mutableData.length) { + #if DEBUG + CFTimeInterval start = CACurrentMediaTime(); + #endif + NSUInteger startByte = self.scannedByte; + if (startByte > 0) { + startByte--; + } + if ([self l_scanForSOSinData:self.mutableData startByte:startByte scannedByte:&_scannedByte]) { + self.sosCount++; + } + #if DEBUG + CFTimeInterval total = CACurrentMediaTime() - start; + self.scanTime += total; + #endif + } + + if (self.imageSource) { + CGImageSourceUpdateData(self.imageSource, (CFDataRef)self.mutableData, NO); + } + [self.lock unlock]; +} + +- (PINImage *)currentImageBlurred:(BOOL)blurred maxProgressiveRenderSize:(CGSize)maxProgressiveRenderSize renderedImageQuality:(out CGFloat *)renderedImageQuality +{ + [self.lock lock]; + if (self.imageSource == nil) { + [self.lock unlock]; + return nil; + } + + if (self.currentThreshold == _progressThresholds.count) { + [self.lock unlock]; + return nil; + } + + if (_estimatedRemainingTimeThreshold > 0 && [self l_estimatedRemainingTime] < _estimatedRemainingTimeThreshold) { + [self.lock unlock]; + return nil; + } + + if ([self l_hasCompletedFirstScan] == NO) { + [self.lock unlock]; + return nil; + } + + #if DEBUG + if (self.scanTime > 0) { + PINLog(@"scan time: %f", self.scanTime); + self.scanTime = 0; + } + #endif + + PINImage *currentImage = nil; + + //Size information comes after JFIF so jpeg properties should be available at or before size? + if (self.size.width <= 0 || self.size.height <= 0) { + //attempt to get size info + NSDictionary *imageProperties = (NSDictionary *)CFBridgingRelease(CGImageSourceCopyPropertiesAtIndex(self.imageSource, 0, NULL)); + CGSize size = self.size; + if (size.width <= 0 && imageProperties[(NSString *)kCGImagePropertyPixelWidth]) { + size.width = [imageProperties[(NSString *)kCGImagePropertyPixelWidth] floatValue]; + } + + if (size.height <= 0 && imageProperties[(NSString *)kCGImagePropertyPixelHeight]) { + size.height = [imageProperties[(NSString *)kCGImagePropertyPixelHeight] floatValue]; + } + + self.size = size; + + NSDictionary *jpegProperties = imageProperties[(NSString *)kCGImagePropertyJFIFDictionary]; + NSNumber *isProgressive = jpegProperties[(NSString *)kCGImagePropertyJFIFIsProgressive]; + self.isProgressiveJPEG = jpegProperties && [isProgressive boolValue]; + } + + if (self.size.width > maxProgressiveRenderSize.width || self.size.height > maxProgressiveRenderSize.height) { + [self.lock unlock]; + return nil; + } + + float progress = 0; + if (self.expectedNumberOfBytes > 0) { + progress = (float)self.mutableData.length / (float)self.expectedNumberOfBytes; + } + + //Don't bother if we're basically done + if (progress >= 0.99) { + [self.lock unlock]; + return nil; + } + + if (self.isProgressiveJPEG && self.size.width > 0 && self.size.height > 0 && progress > [_progressThresholds[self.currentThreshold] floatValue]) { + while (self.currentThreshold < _progressThresholds.count && progress > [_progressThresholds[self.currentThreshold] floatValue]) { + self.currentThreshold++; + } + PINLog(@"Generating preview image"); + CGImageRef image = CGImageSourceCreateImageAtIndex(self.imageSource, 0, NULL); + if (image) { + if (blurred) { + currentImage = [self l_postProcessImage:[PINImage imageWithCGImage:image] withProgress:progress]; + } else { + currentImage = [PINImage imageWithCGImage:image]; + } + CGImageRelease(image); + if (renderedImageQuality) { + *renderedImageQuality = progress; + } + } + } + + [self.lock unlock]; + return currentImage; +} + +- (NSData *)data +{ + [self.lock lock]; + NSData *data = [self.mutableData copy]; + [self.lock unlock]; + return data; +} + +#pragma mark - private + +- (BOOL)l_scanForSOSinData:(NSData *)data startByte:(NSUInteger)startByte scannedByte:(NSUInteger *)scannedByte +{ + //check if we have a complete scan + Byte scanMarker[2]; + //SOS marker + scanMarker[0] = 0xFF; + scanMarker[1] = 0xDA; + + //scan one byte back in case we only got half the SOS on the last data append + NSRange scanRange; + scanRange.location = startByte; + scanRange.length = data.length - scanRange.location; + NSRange sosRange = [data rangeOfData:[NSData dataWithBytes:scanMarker length:2] options:0 range:scanRange]; + if (sosRange.location != NSNotFound) { + if (scannedByte) { + *scannedByte = NSMaxRange(sosRange); + } + return YES; + } + if (scannedByte) { + *scannedByte = NSMaxRange(scanRange); + } + return NO; +} + +- (BOOL)l_hasCompletedFirstScan +{ + return self.sosCount >= 2; +} + +//Heavily cribbed from https://developer.apple.com/library/ios/samplecode/UIImageEffects/Listings/UIImageEffects_UIImageEffects_m.html#//apple_ref/doc/uid/DTS40013396-UIImageEffects_UIImageEffects_m-DontLinkElementID_9 +- (PINImage *)l_postProcessImage:(PINImage *)inputImage withProgress:(float)progress +{ + PINImage *outputImage = nil; + CGImageRef inputImageRef = CGImageRetain(inputImage.CGImage); + if (inputImageRef == nil) { + return nil; + } + + CGSize inputSize = inputImage.size; + if (inputSize.width < 1 || + inputSize.height < 1) { + CGImageRelease(inputImageRef); + return nil; + } + +#if PIN_TARGET_IOS + CGFloat imageScale = inputImage.scale; +#elif PIN_TARGET_MAC + // TODO: What scale factor should be used here? + CGFloat imageScale = [[NSScreen mainScreen] backingScaleFactor]; +#endif + + CGFloat radius = (inputImage.size.width / 25.0) * MAX(0, 1.0 - progress); + radius *= imageScale; + + //we'll round the radius to a whole number below anyway, + if (radius < FLT_EPSILON) { + CGImageRelease(inputImageRef); + return inputImage; + } + + CGContextRef ctx; +#if PIN_TARGET_IOS + UIGraphicsBeginImageContextWithOptions(inputSize, YES, imageScale); + ctx = UIGraphicsGetCurrentContext(); +#elif PIN_TARGET_MAC + ctx = CGBitmapContextCreate(0, inputSize.width, inputSize.height, 8, 0, [NSColorSpace genericRGBColorSpace].CGColorSpace, kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little); +#endif + + if (ctx) { +#if PIN_TARGET_IOS + CGContextScaleCTM(ctx, 1.0, -1.0); + CGContextTranslateCTM(ctx, 0, -inputSize.height); +#endif + + vImage_Buffer effectInBuffer; + vImage_Buffer scratchBuffer; + + vImage_Buffer *inputBuffer; + vImage_Buffer *outputBuffer; + + vImage_CGImageFormat format = { + .bitsPerComponent = 8, + .bitsPerPixel = 32, + .colorSpace = NULL, + // (kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little) + // requests a BGRA buffer. + .bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little, + .version = 0, + .decode = NULL, + .renderingIntent = kCGRenderingIntentDefault + }; + + vImage_Error e = vImageBuffer_InitWithCGImage(&effectInBuffer, &format, NULL, inputImage.CGImage, kvImagePrintDiagnosticsToConsole); + if (e == kvImageNoError) + { + e = vImageBuffer_Init(&scratchBuffer, effectInBuffer.height, effectInBuffer.width, format.bitsPerPixel, kvImageNoFlags); + if (e == kvImageNoError) { + inputBuffer = &effectInBuffer; + outputBuffer = &scratchBuffer; + + // A description of how to compute the box kernel width from the Gaussian + // radius (aka standard deviation) appears in the SVG spec: + // http://www.w3.org/TR/SVG/filters.html#feGaussianBlurElement + // + // For larger values of 's' (s >= 2.0), an approximation can be used: Three + // successive box-blurs build a piece-wise quadratic convolution kernel, which + // approximates the Gaussian kernel to within roughly 3%. + // + // let d = floor(s * 3*sqrt(2*pi)/4 + 0.5) + // + // ... if d is odd, use three box-blurs of size 'd', centered on the output pixel. + // + if (radius - 2. < __FLT_EPSILON__) + radius = 2.; + uint32_t wholeRadius = floor((radius * 3. * sqrt(2 * M_PI) / 4 + 0.5) / 2); + + wholeRadius |= 1; // force wholeRadius to be odd so that the three box-blur methodology works. + + //calculate the size necessary for vImageBoxConvolve_ARGB8888, this does not actually do any operations. + NSInteger tempBufferSize = vImageBoxConvolve_ARGB8888(inputBuffer, outputBuffer, NULL, 0, 0, wholeRadius, wholeRadius, NULL, kvImageGetTempBufferSize | kvImageEdgeExtend); + void *tempBuffer = malloc(tempBufferSize); + + if (tempBuffer) { + //errors can be ignored because we've passed in allocated memory + vImageBoxConvolve_ARGB8888(inputBuffer, outputBuffer, tempBuffer, 0, 0, wholeRadius, wholeRadius, NULL, kvImageEdgeExtend); + vImageBoxConvolve_ARGB8888(outputBuffer, inputBuffer, tempBuffer, 0, 0, wholeRadius, wholeRadius, NULL, kvImageEdgeExtend); + vImageBoxConvolve_ARGB8888(inputBuffer, outputBuffer, tempBuffer, 0, 0, wholeRadius, wholeRadius, NULL, kvImageEdgeExtend); + + free(tempBuffer); + + //switch input and output + vImage_Buffer *temp = inputBuffer; + inputBuffer = outputBuffer; + outputBuffer = temp; + + CGImageRef effectCGImage = vImageCreateCGImageFromBuffer(inputBuffer, &format, &cleanupBuffer, NULL, kvImageNoAllocate, NULL); + if (effectCGImage == NULL) { + //if creating the cgimage failed, the cleanup buffer on input buffer will not be called, we must dealloc ourselves + free(inputBuffer->data); + } else { + // draw effect image + CGContextSaveGState(ctx); + CGContextDrawImage(ctx, CGRectMake(0, 0, inputSize.width, inputSize.height), effectCGImage); + CGContextRestoreGState(ctx); + CGImageRelease(effectCGImage); + } + + // Cleanup + free(outputBuffer->data); +#if PIN_TARGET_IOS + outputImage = UIGraphicsGetImageFromCurrentImageContext(); +#elif PIN_TARGET_MAC + CGImageRef outputImageRef = CGBitmapContextCreateImage(ctx); + outputImage = [[NSImage alloc] initWithCGImage:outputImageRef size:inputSize]; + CFRelease(outputImageRef); +#endif + + } + } else { + if (scratchBuffer.data) { + free(scratchBuffer.data); + } + free(effectInBuffer.data); + } + } else { + if (effectInBuffer.data) { + free(effectInBuffer.data); + } + } + } + +#if PIN_TARGET_IOS + UIGraphicsEndImageContext(); +#endif + + CGImageRelease(inputImageRef); + + return outputImage; +} + +// Helper function to handle deferred cleanup of a buffer. +static void cleanupBuffer(void *userData, void *buf_data) +{ + free(buf_data); +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageBasicCache.h b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageBasicCache.h new file mode 100644 index 0000000..c669c3c --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageBasicCache.h @@ -0,0 +1,19 @@ +// +// PINRemoteImageBasicCache.h +// Pods +// +// Created by Aleksei Shevchenko on 7/28/16. +// +// + +#import +#import "PINRemoteImageCaching.h" + +/** + * Simplistic wrapper based on NSCache. + * + * No data is persisted on disk. The disk cache methods are no-op. + */ +@interface PINRemoteImageBasicCache : NSObject + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageBasicCache.m b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageBasicCache.m new file mode 100644 index 0000000..a3953ce --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageBasicCache.m @@ -0,0 +1,99 @@ +// +// PINRemoteImageBasicCache.m +// Pods +// +// Created by Aleksei Shevchenko on 7/28/16. +// +// + +#import "PINRemoteImageBasicCache.h" + +@interface PINRemoteImageBasicCache() +@property (nonatomic, strong) NSCache *cache; +@end + +@implementation PINRemoteImageBasicCache + +- (instancetype)init +{ + self = [super init]; + if (self) { + self.cache = [[NSCache alloc] init]; + } + return self; +} + +//****************************************************************************************************** +// Memory cache methods +//****************************************************************************************************** +-(nullable id)objectFromMemoryForKey:(NSString *)key +{ + return [self.cache objectForKey:key]; +} + +-(void)setObjectInMemory:(id)object forKey:(NSString *)key withCost:(NSUInteger)cost +{ + [self.cache setObject:object forKey:key cost:cost]; +} + +- (void)removeObjectForKeyFromMemory:(NSString *)key +{ + [self.cache removeObjectForKey:key]; +} + +//****************************************************************************************************** +// Disk cache methods +//****************************************************************************************************** +-(nullable id)objectFromDiskForKey:(NSString *)key +{ + return nil; +} + +-(void)objectFromDiskForKey:(NSString *)key completion:(PINRemoteImageCachingObjectBlock)completion +{ + __weak typeof(self) weakSelf = self; + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + if (completion) { + typeof(self) strongSelf = weakSelf; + completion(strongSelf, key, nil); + } + }); +} + +-(void)setObjectOnDisk:(id)object forKey:(NSString *)key +{ + +} + +//****************************************************************************************************** +// Common methods, should apply to both in-memory and disk storage +//****************************************************************************************************** +- (BOOL)objectExistsForKey:(NSString *)key +{ + return [self.cache objectForKey:key] != nil; +} + +- (void)removeObjectForKey:(NSString *)key +{ + [self.cache removeObjectForKey:key]; +} + +- (void)removeObjectForKey:(NSString *)key completion:(PINRemoteImageCachingObjectBlock)completion +{ + __weak typeof(self) weakSelf = self; + id object = [self.cache objectForKey:key]; + [self.cache removeObjectForKey:key]; + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + if (completion) { + typeof(self) strongSelf = weakSelf; + completion(strongSelf, key, object); + } + }); +} + +- (void)removeAllObjects +{ + [self.cache removeAllObjects]; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageCallbacks.h b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageCallbacks.h new file mode 100644 index 0000000..ebfbfc2 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageCallbacks.h @@ -0,0 +1,20 @@ +// +// PINRemoteImageCallbacks.h +// Pods +// +// Created by Garrett Moon on 3/9/15. +// +// + +#import + +#import "PINRemoteImageManager.h" + +@interface PINRemoteImageCallbacks : NSObject + +@property (atomic, strong, nullable) PINRemoteImageManagerImageCompletion completionBlock; +@property (atomic, strong, nullable) PINRemoteImageManagerImageCompletion progressImageBlock; +@property (atomic, strong, nullable) PINRemoteImageManagerProgressDownload progressDownloadBlock; +@property (assign, readonly) CFTimeInterval requestTime; + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageCallbacks.m b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageCallbacks.m new file mode 100644 index 0000000..f64795b --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageCallbacks.m @@ -0,0 +1,21 @@ +// +// PINRemoteImageCallbacks.m +// Pods +// +// Created by Garrett Moon on 3/9/15. +// +// + +#import "PINRemoteImageCallbacks.h" + +@implementation PINRemoteImageCallbacks + +- (instancetype)init +{ + if (self = [super init]) { + _requestTime = CACurrentMediaTime(); + } + return self; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageCategoryManager.m b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageCategoryManager.m new file mode 100644 index 0000000..54635d6 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageCategoryManager.m @@ -0,0 +1,268 @@ +// +// PINRemoteImageCategory.m +// Pods +// +// Created by Garrett Moon on 11/4/14. +// +// + +#import "PINRemoteImageCategoryManager.h" + +#import + +@implementation PINRemoteImageCategoryManager + ++ (void)setImageOnView:(id )view + fromURL:(NSURL *)url +{ + [self setImageOnView:view fromURL:url placeholderImage:nil]; +} + ++ (void)setImageOnView:(id )view + fromURL:(NSURL *)url + placeholderImage:(PINImage *)placeholderImage +{ + [self setImageOnView:view fromURL:url placeholderImage:placeholderImage completion:nil]; +} + ++ (void)setImageOnView:(id )view + fromURL:(NSURL *)url + completion:(PINRemoteImageManagerImageCompletion)completion +{ + [self setImageOnView:view fromURL:url placeholderImage:nil completion:completion]; +} + ++ (void)setImageOnView:(id )view + fromURL:(NSURL *)url + placeholderImage:(PINImage *)placeholderImage + completion:(PINRemoteImageManagerImageCompletion)completion +{ + [self setImageOnView:view + fromURLs:url?@[url]:nil + placeholderImage:placeholderImage + processorKey:nil + processor:nil + completion:completion]; +} + ++ (void)setImageOnView:(id )view + fromURL:(NSURL *)url + processorKey:(NSString *)processorKey + processor:(PINRemoteImageManagerImageProcessor)processor +{ + [self setImageOnView:view + fromURL:url + processorKey:processorKey + processor:processor + completion:nil]; +} + ++ (void)setImageOnView:(id )view + fromURL:(NSURL *)url + placeholderImage:(PINImage *)placeholderImage + processorKey:(NSString *)processorKey + processor:(PINRemoteImageManagerImageProcessor)processor +{ + [self setImageOnView:view + fromURLs:url?@[url]:nil + placeholderImage:placeholderImage + processorKey:processorKey + processor:processor + completion:nil]; +} + ++ (void)setImageOnView:(id )view + fromURL:(NSURL *)url + processorKey:(NSString *)processorKey + processor:(PINRemoteImageManagerImageProcessor)processor + completion:(PINRemoteImageManagerImageCompletion)completion +{ + [self setImageOnView:view + fromURLs:url?@[url]:nil + placeholderImage:nil + processorKey:processorKey + processor:processor + completion:completion]; +} + ++ (void)setImageOnView:(id )view + fromURLs:(NSArray *)urls +{ + [self setImageOnView:view + fromURLs:urls + placeholderImage:nil]; +} + ++ (void)setImageOnView:(id )view + fromURLs:(NSArray *)urls + placeholderImage:(PINImage *)placeholderImage +{ + [self setImageOnView:view + fromURLs:urls + placeholderImage:placeholderImage + completion:nil]; +} + ++ (void)setImageOnView:(id )view + fromURLs:(NSArray *)urls + placeholderImage:(PINImage *)placeholderImage + completion:(PINRemoteImageManagerImageCompletion)completion +{ + return [self setImageOnView:view + fromURLs:urls + placeholderImage:placeholderImage + processorKey:nil + processor:nil + completion:completion]; +} + ++ (NSUUID *)downloadImageOperationUUIDOnView:(id )view +{ + return (NSUUID *)objc_getAssociatedObject(view, @selector(downloadImageOperationUUIDOnView:)); +} + ++ (void)setDownloadImageOperationUUID:(NSUUID *)downloadImageOperationUUID onView:(id )view +{ + objc_setAssociatedObject(view, @selector(downloadImageOperationUUIDOnView:), downloadImageOperationUUID, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + ++ (BOOL)updateWithProgressOnView:(id )view +{ + return [(NSNumber *)objc_getAssociatedObject(view, @selector(updateWithProgressOnView:)) boolValue]; +} + ++ (void)setUpdateWithProgressOnView:(BOOL)updateWithProgress onView:(id )view +{ + objc_setAssociatedObject(view, @selector(updateWithProgressOnView:), [NSNumber numberWithBool:updateWithProgress], OBJC_ASSOCIATION_RETAIN); +} + ++ (void)cancelImageDownloadOnView:(id )view +{ + if ([self downloadImageOperationUUIDOnView:view]) { + [[PINRemoteImageManager sharedImageManager] cancelTaskWithUUID:[self downloadImageOperationUUIDOnView:view]]; + [self setDownloadImageOperationUUID:nil onView:view]; + } +} + ++ (void)setImageOnView:(id )view + fromURLs:(NSArray *)urls + placeholderImage:(PINImage *)placeholderImage + processorKey:(NSString *)processorKey + processor:(PINRemoteImageManagerImageProcessor)processor + completion:(PINRemoteImageManagerImageCompletion)completion +{ + if (![NSThread isMainThread]) { + dispatch_async(dispatch_get_main_queue(), ^{ + [self setImageOnView:view + fromURLs:urls + placeholderImage:placeholderImage + processorKey:processorKey + processor:processor + completion:completion]; + }); + return; + } + + [self cancelImageDownloadOnView:view]; + + if (placeholderImage) { + [view pin_setPlaceholderWithImage:placeholderImage]; + } + + if (urls == nil || urls.count == 0) { + if (!placeholderImage) { + [view pin_clearImages]; + } + return; + } + + PINRemoteImageManagerDownloadOptions options; + if([view respondsToSelector:@selector(pin_defaultOptions)]) { + options = [view pin_defaultOptions]; + } else { + options = PINRemoteImageManagerDownloadOptionsNone; + } + + if ([view pin_ignoreGIFs]) { + options |= PINRemoteImageManagerDisallowAlternateRepresentations; + } + + PINRemoteImageManagerImageCompletion internalProgress = nil; + if ([self updateWithProgressOnView:view] && processorKey.length <= 0 && processor == nil) { + internalProgress = ^(PINRemoteImageManagerResult *result) + { + void (^mainQueue)(void) = ^{ + //if result.UUID is nil, we returned immediately and want this result + NSUUID *currentUUID = [self downloadImageOperationUUIDOnView:view]; + if (![currentUUID isEqual:result.UUID] && result.UUID != nil) { + return; + } + if (result.image) { + [view pin_updateUIWithRemoteImageManagerResult:result]; + + } + }; + if ([NSThread isMainThread]) { + mainQueue(); + } else { + dispatch_async(dispatch_get_main_queue(), ^{ + mainQueue(); + }); + } + }; + } + + PINRemoteImageManagerImageCompletion internalCompletion = ^(PINRemoteImageManagerResult *result) + { + void (^mainQueue)(void) = ^{ + //if result.UUID is nil, we returned immediately and want this result + NSUUID *currentUUID = [self downloadImageOperationUUIDOnView:view]; + if (![currentUUID isEqual:result.UUID] && result.UUID != nil) { + return; + } + [self setDownloadImageOperationUUID:nil onView:view]; + if (result.error) { + if (completion) { + completion(result); + } + return; + } + + [view pin_updateUIWithRemoteImageManagerResult:result]; + + if (completion) { + completion(result); + } + }; + if ([NSThread isMainThread]) { + mainQueue(); + } else { + dispatch_async(dispatch_get_main_queue(), ^{ + mainQueue(); + }); + } + }; + + NSUUID *downloadImageOperationUUID = nil; + if (urls.count > 1) { + downloadImageOperationUUID = [[PINRemoteImageManager sharedImageManager] downloadImageWithURLs:urls + options:options + progressImage:internalProgress + completion:internalCompletion]; + } else if (processorKey.length > 0 && processor) { + downloadImageOperationUUID = [[PINRemoteImageManager sharedImageManager] downloadImageWithURL:urls[0] + options:options + processorKey:processorKey + processor:processor + completion:internalCompletion]; + } else { + downloadImageOperationUUID = [[PINRemoteImageManager sharedImageManager] downloadImageWithURL:urls[0] + options:options + progressImage:internalProgress + completion:internalCompletion]; + } + + [self setDownloadImageOperationUUID:downloadImageOperationUUID onView:view]; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageDownloadQueue.h b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageDownloadQueue.h new file mode 100644 index 0000000..574a280 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageDownloadQueue.h @@ -0,0 +1,47 @@ +// +// PINRemoteImageDownloadQueue.h +// PINRemoteImage +// +// Created by Garrett Moon on 3/1/17. +// Copyright © 2017 Pinterest. All rights reserved. +// + +#import + +#import "PINRemoteImageManager.h" + +@class PINURLSessionManager; + +NS_ASSUME_NONNULL_BEGIN + +typedef void (^PINRemoteImageDownloadCompletion)(NSURLResponse * _Nullable response, NSError *error); + +@interface PINRemoteImageDownloadQueue : NSObject + +@property (atomic, assign) NSUInteger maxNumberOfConcurrentDownloads; + +- (instancetype)init NS_UNAVAILABLE; ++ (PINRemoteImageDownloadQueue *)queueWithMaxConcurrentDownloads:(NSUInteger)maxNumberOfConcurrentDownloads; + +- (NSURLSessionDataTask *)addDownloadWithSessionManager:(PINURLSessionManager *)sessionManager + request:(NSURLRequest *)request + priority:(PINRemoteImageManagerPriority)priority + completionHandler:(PINRemoteImageDownloadCompletion)completionHandler; + +/*** + This prevents a task from being run if it hasn't already started yet. It is the caller's responsibility to cancel + the task if it has already been started. + + @return BOOL Returns YES if the task was in the queue. + */ +- (BOOL)removeDownloadTaskFromQueue:(NSURLSessionDataTask *)downloadTask; + +/* + This sets the tasks priority of execution. It is the caller's responsibility to set the priority on the task itself + for NSURLSessionManager. + */ +- (void)setQueuePriority:(PINRemoteImageManagerPriority)priority forTask:(NSURLSessionDataTask *)downloadTask; + +NS_ASSUME_NONNULL_END + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageDownloadQueue.m b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageDownloadQueue.m new file mode 100644 index 0000000..07e70f2 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageDownloadQueue.m @@ -0,0 +1,177 @@ +// +// PINRemoteImageDownloadQueue.m +// PINRemoteImage +// +// Created by Garrett Moon on 3/1/17. +// Copyright © 2017 Pinterest. All rights reserved. +// + +#import "PINRemoteImageDownloadQueue.h" + +#import "PINURLSessionManager.h" +#import "PINRemoteLock.h" + +@interface PINRemoteImageDownloadQueue () +{ + PINRemoteLock *_lock; + + NSMutableOrderedSet *_highPriorityQueuedOperations; + NSMutableOrderedSet *_defaultPriorityQueuedOperations; + NSMutableOrderedSet *_lowPriorityQueuedOperations; + NSMutableSet *_runningTasks; +} + +@end + +@implementation PINRemoteImageDownloadQueue + +@synthesize maxNumberOfConcurrentDownloads = _maxNumberOfConcurrentDownloads; + ++ (PINRemoteImageDownloadQueue *)queueWithMaxConcurrentDownloads:(NSUInteger)maxNumberOfConcurrentDownloads +{ + return [[PINRemoteImageDownloadQueue alloc] initWithMaxConcurrentDownloads:maxNumberOfConcurrentDownloads]; +} + +- (PINRemoteImageDownloadQueue *)initWithMaxConcurrentDownloads:(NSUInteger)maxNumberOfConcurrentDownloads +{ + if (self = [super init]) { + _maxNumberOfConcurrentDownloads = maxNumberOfConcurrentDownloads; + + _lock = [[PINRemoteLock alloc] initWithName:@"PINRemoteImageDownloadQueue Lock"]; + _highPriorityQueuedOperations = [[NSMutableOrderedSet alloc] init]; + _defaultPriorityQueuedOperations = [[NSMutableOrderedSet alloc] init]; + _lowPriorityQueuedOperations = [[NSMutableOrderedSet alloc] init]; + _runningTasks = [[NSMutableSet alloc] init]; + } + return self; +} + +- (NSUInteger)maxNumberOfConcurrentDownloads +{ + [self lock]; + NSUInteger maxNumberOfConcurrentDownloads = _maxNumberOfConcurrentDownloads; + [self unlock]; + return maxNumberOfConcurrentDownloads; +} + +- (void)setMaxNumberOfConcurrentDownloads:(NSUInteger)maxNumberOfConcurrentDownloads +{ + [self lock]; + _maxNumberOfConcurrentDownloads = maxNumberOfConcurrentDownloads; + [self unlock]; + + [self scheduleDownloadsIfNeeded]; +} + +- (NSURLSessionDataTask *)addDownloadWithSessionManager:(PINURLSessionManager *)sessionManager + request:(NSURLRequest *)request + priority:(PINRemoteImageManagerPriority)priority + completionHandler:(PINRemoteImageDownloadCompletion)completionHandler +{ + NSURLSessionDataTask *dataTask = [sessionManager dataTaskWithRequest:request + priority:priority + completionHandler:^(NSURLSessionTask *task, NSError *error) { + completionHandler(task.response, error); + [self lock]; + [self->_runningTasks removeObject:task]; + [self unlock]; + + [self scheduleDownloadsIfNeeded]; + }]; + + [self setQueuePriority:priority forTask:dataTask addIfNecessary:YES]; + + [self scheduleDownloadsIfNeeded]; + + return dataTask; +} + +- (void)scheduleDownloadsIfNeeded +{ + [self lock]; + while (_runningTasks.count < _maxNumberOfConcurrentDownloads) { + NSMutableOrderedSet *queue = nil; + if (_highPriorityQueuedOperations.count > 0) { + queue = _highPriorityQueuedOperations; + } else if (_defaultPriorityQueuedOperations.count > 0) { + queue = _defaultPriorityQueuedOperations; + } else if (_lowPriorityQueuedOperations.count > 0) { + queue = _lowPriorityQueuedOperations; + } + + if (!queue) { + break; + } + + NSURLSessionDataTask *task = [queue firstObject]; + [queue removeObjectAtIndex:0]; + [task resume]; + + [_runningTasks addObject:task]; + } + [self unlock]; +} + +- (BOOL)removeDownloadTaskFromQueue:(NSURLSessionDataTask *)downloadTask +{ + BOOL containsTask = NO; + [self lock]; + if ([_highPriorityQueuedOperations containsObject:downloadTask]) { + containsTask = YES; + [_highPriorityQueuedOperations removeObject:downloadTask]; + } else if ([_defaultPriorityQueuedOperations containsObject:downloadTask]) { + containsTask = YES; + [_defaultPriorityQueuedOperations removeObject:downloadTask]; + } else if ([_lowPriorityQueuedOperations containsObject:downloadTask]) { + containsTask = YES; + [_lowPriorityQueuedOperations removeObject:downloadTask]; + } + [self unlock]; + return containsTask; +} + +- (void)setQueuePriority:(PINRemoteImageManagerPriority)priority forTask:(NSURLSessionDataTask *)downloadTask +{ + [self setQueuePriority:priority forTask:downloadTask addIfNecessary:NO]; +} + +- (void)setQueuePriority:(PINRemoteImageManagerPriority)priority forTask:(NSURLSessionDataTask *)downloadTask addIfNecessary:(BOOL)addIfNecessary +{ + BOOL containsTask = [self removeDownloadTaskFromQueue:downloadTask]; + + if (containsTask || addIfNecessary) { + NSMutableOrderedSet *queue = nil; + [self lock]; + switch (priority) { + case PINRemoteImageManagerPriorityLow: + queue = _lowPriorityQueuedOperations; + break; + + case PINRemoteImageManagerPriorityDefault: + queue = _defaultPriorityQueuedOperations; + break; + + case PINRemoteImageManagerPriorityHigh: + queue = _highPriorityQueuedOperations; + break; + + default: + NSAssert(NO, @"invalid priority: %tu", priority); + break; + } + [queue addObject:downloadTask]; + [self unlock]; + } +} + +- (void)lock +{ + [_lock lock]; +} + +- (void)unlock +{ + [_lock unlock]; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageDownloadTask.h b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageDownloadTask.h new file mode 100644 index 0000000..bcd434d --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageDownloadTask.h @@ -0,0 +1,37 @@ +// +// PINRemoteImageDownloadTask.h +// Pods +// +// Created by Garrett Moon on 3/9/15. +// +// + +#if SWIFT_PACKAGE +@import PINOperation; +#else +#import +#endif + +#import "PINRemoteImageManager+Private.h" +#import "PINRemoteImageTask.h" +#import "PINProgressiveImage.h" +#import "PINResume.h" + +@interface PINRemoteImageDownloadTask : PINRemoteImageTask + +@property (nonatomic, strong, nullable, readonly) NSURL *URL; +@property (nonatomic, copy, nullable) NSString *ifRange; +@property (nonatomic, copy, readonly, nullable) NSData *data; + +@property (nonatomic, readonly) CFTimeInterval estimatedRemainingTime; + +- (void)scheduleDownloadWithRequest:(nonnull NSURLRequest *)request + resume:(nullable PINResume *)resume + skipRetry:(BOOL)skipRetry + priority:(PINRemoteImageManagerPriority)priority + completionHandler:(nonnull PINRemoteImageManagerDataCompletion)completionHandler; + +- (void)didReceiveData:(nonnull NSData *)data; +- (void)didReceiveResponse:(nonnull NSURLResponse *)response; + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageDownloadTask.m b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageDownloadTask.m new file mode 100644 index 0000000..b1b261d --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageDownloadTask.m @@ -0,0 +1,380 @@ +// +// PINRemoteImageDownloadTask.m +// Pods +// +// Created by Garrett Moon on 3/9/15. +// +// + +#import "PINRemoteImageDownloadTask.h" + +#import "PINRemoteImageTask+Subclassing.h" +#import "PINRemoteImage.h" +#import "PINRemoteImageCallbacks.h" +#import "PINRemoteLock.h" +#import "PINSpeedRecorder.h" + +@interface PINRemoteImageDownloadTask () +{ + PINProgressiveImage *_progressImage; + PINResume *_resume; + id _retryStrategy; +} + +@end + +@implementation PINRemoteImageDownloadTask + +- (instancetype)initWithManager:(PINRemoteImageManager *)manager +{ + if (self = [super initWithManager:manager]) { + _retryStrategy = manager.retryStrategyCreationBlock(); + } + return self; +} + +- (void)callProgressDownload +{ + NSDictionary *callbackBlocks = self.callbackBlocks; + #if PINRemoteImageLogging + NSString *key = self.key; + #endif + + __block int64_t completedBytes; + __block int64_t totalBytes; + + [self.lock lockWithBlock:^{ + completedBytes = self->_progressImage.dataTask.countOfBytesReceived; + totalBytes = self->_progressImage.dataTask.countOfBytesExpectedToReceive; + }]; + + [callbackBlocks enumerateKeysAndObjectsUsingBlock:^(NSUUID *UUID, PINRemoteImageCallbacks *callback, BOOL *stop) { + PINRemoteImageManagerProgressDownload progressDownloadBlock = callback.progressDownloadBlock; + if (progressDownloadBlock != nil) { + PINLog(@"calling progress for UUID: %@ key: %@", UUID, key); + dispatch_async(self.manager.callbackQueue, ^ + { + progressDownloadBlock(completedBytes, totalBytes); + }); + } + }]; +} + +- (void)callProgressImageWithImage:(nonnull PINImage *)image renderedImageQuality:(CGFloat)renderedImageQuality +{ + NSDictionary *callbackBlocks = self.callbackBlocks; +#if PINRemoteImageLogging + NSString *key = self.key; +#endif + + + [callbackBlocks enumerateKeysAndObjectsUsingBlock:^(NSUUID *UUID, PINRemoteImageCallbacks *callback, BOOL *stop) { + PINRemoteImageManagerImageCompletion progressImageBlock = callback.progressImageBlock; + if (progressImageBlock != nil) { + PINLog(@"calling progress for UUID: %@ key: %@", UUID, key); + CFTimeInterval requestTime = callback.requestTime; + + dispatch_async(self.manager.callbackQueue, ^ + { + progressImageBlock([PINRemoteImageManagerResult imageResultWithImage:image + alternativeRepresentation:nil + requestLength:CACurrentMediaTime() - requestTime + resultType:PINRemoteImageResultTypeProgress + UUID:UUID + response:nil + error:nil + renderedImageQuality:renderedImageQuality]); + }); + } + }]; +} + +- (BOOL)cancelWithUUID:(NSUUID *)UUID resume:(PINResume **)resume +{ + __block BOOL noMoreCompletions; + __block PINResume *strongResume; + BOOL hasResume = resume != nil; + [self.lock lockWithBlock:^{ + if (hasResume) { + //consider skipping cancellation if there's a request for resume data and the time to start the connection is greater than + //the time remaining to download. + NSTimeInterval timeToFirstByte = [[PINSpeedRecorder sharedRecorder] weightedTimeToFirstByteForHost:self->_progressImage.dataTask.currentRequest.URL.host]; + if (self->_progressImage.estimatedRemainingTime <= timeToFirstByte) { + noMoreCompletions = NO; + return; + } + } + + noMoreCompletions = [super l_cancelWithUUID:UUID]; + + if (noMoreCompletions) { + [self.manager.urlSessionTaskQueue removeDownloadTaskFromQueue:self->_progressImage.dataTask]; + [self->_progressImage.dataTask cancel]; + + if (hasResume && self->_ifRange && self->_progressImage.dataTask.countOfBytesExpectedToReceive > 0 && self->_progressImage.dataTask.countOfBytesExpectedToReceive != NSURLSessionTransferSizeUnknown) { + NSData *progressData = self->_progressImage.data; + if (progressData.length > 0) { + strongResume = [PINResume resumeData:progressData ifRange:self->_ifRange totalBytes:self->_progressImage.dataTask.countOfBytesExpectedToReceive]; + } + } + + PINLog(@"Canceling download of URL: %@, UUID: %@", _progressImage.dataTask.originalRequest.URL, UUID); + } +#if PINRemoteImageLogging + else { + PINLog(@"Decrementing download of URL: %@, UUID: %@", _progressImage.dataTask.originalRequest.URL, UUID); + } +#endif + }]; + + if (hasResume) { + *resume = strongResume; + } + + return noMoreCompletions; +} + +- (void)setPriority:(PINRemoteImageManagerPriority)priority +{ + [super setPriority:priority]; + if (@available(iOS 8.0, macOS 10.10, tvOS 9.0, watchOS 2.0, *)) { + [self.lock lockWithBlock:^{ + NSURLSessionDataTask *dataTask = self->_progressImage.dataTask; + if (dataTask) { + dataTask.priority = dataTaskPriorityWithImageManagerPriority(priority); + [self.manager.urlSessionTaskQueue setQueuePriority:priority forTask:dataTask]; + } + }]; + } +} + +- (NSURL *)URL +{ + __block NSURL *url; + [self.lock lockWithBlock:^{ + url = self->_progressImage.dataTask.originalRequest.URL; + }]; + return url; +} + +- (nonnull PINRemoteImageManagerResult *)imageResultWithImage:(nullable PINImage *)image + alternativeRepresentation:(nullable id)alternativeRepresentation + requestLength:(NSTimeInterval)requestLength + error:(nullable NSError *)error + resultType:(PINRemoteImageResultType)resultType + UUID:(nullable NSUUID *)UUID + response:(nonnull NSURLResponse *)response +{ + __block NSUInteger bytesSavedByResuming; + [self.lock lockWithBlock:^{ + bytesSavedByResuming = self->_resume.resumeData.length; + }]; + return [PINRemoteImageManagerResult imageResultWithImage:image + alternativeRepresentation:alternativeRepresentation + requestLength:requestLength + resultType:resultType + UUID:UUID + response:response + error:error + bytesSavedByResuming:bytesSavedByResuming]; +} + +- (void)didReceiveData:(NSData *_Nonnull)data +{ + [self callProgressDownload]; + + __block int64_t expectedNumberOfBytes; + [self.lock lockWithBlock:^{ + expectedNumberOfBytes = self->_progressImage.dataTask.countOfBytesExpectedToReceive; + }]; + + [self updateData:data isResume:NO expectedBytes:expectedNumberOfBytes]; +} + +- (void)updateData:(NSData *)data isResume:(BOOL)isResume expectedBytes:(int64_t)expectedBytes +{ + __block PINProgressiveImage *progressImage; + __block BOOL hasProgressBlocks = NO; + [self.lock lockWithBlock:^{ + progressImage = self->_progressImage; + [[self l_callbackBlocks] enumerateKeysAndObjectsUsingBlock:^(NSUUID *UUID, PINRemoteImageCallbacks *callback, BOOL *stop) { + if (callback.progressImageBlock) { + hasProgressBlocks = YES; + *stop = YES; + } + }]; + }]; + + [progressImage updateProgressiveImageWithData:data expectedNumberOfBytes:expectedBytes isResume:isResume]; + + if (hasProgressBlocks) { + if (PINNSOperationSupportsBlur) { + [self.manager.concurrentOperationQueue scheduleOperation:^{ + CGFloat renderedImageQuality = 1.0; + PINImage *image = [progressImage currentImageBlurred:self.manager.shouldBlurProgressive maxProgressiveRenderSize:self.manager.maxProgressiveRenderSize renderedImageQuality:&renderedImageQuality]; + if (image) { + [self callProgressImageWithImage:image renderedImageQuality:renderedImageQuality]; + } + } withPriority:PINOperationQueuePriorityLow]; + } + } +} + +- (void)didReceiveResponse:(nonnull NSURLResponse *)response +{ + if ([response isKindOfClass:[NSHTTPURLResponse class]]) { + NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; + + // Got partial data back for a resume + if (httpResponse.statusCode == 206) { + __block PINResume *resume; + [self.lock lockWithBlock:^{ + resume = self->_resume; + }]; + + [self updateData:resume.resumeData isResume:YES expectedBytes:resume.totalBytes]; + } else { + //Check if there's resume data and we didn't get back a 206, get rid of it + [self.lock lockWithBlock:^{ + self->_resume = nil; + }]; + } + + // Check to see if the server supports resume + if ([[httpResponse allHeaderFields][@"Accept-Ranges"] isEqualToString:@"bytes"]) { + NSString *ifRange = nil; + NSString *etag = nil; + + if ((etag = [httpResponse allHeaderFields][@"ETag"])) { + if ([etag hasPrefix:@"W/"] == NO) { + ifRange = etag; + } + } else { + ifRange = [httpResponse allHeaderFields][@"Last-Modified"]; + } + + if (ifRange.length > 0) { + [self.lock lockWithBlock:^{ + self->_ifRange = ifRange; + }]; + } + } + } +} + +- (void)scheduleDownloadWithRequest:(nonnull NSURLRequest *)request + resume:(nullable PINResume *)resume + skipRetry:(BOOL)skipRetry + priority:(PINRemoteImageManagerPriority)priority + completionHandler:(nonnull PINRemoteImageManagerDataCompletion)completionHandler +{ + [self scheduleDownloadWithRequest:request resume:resume skipRetry:skipRetry priority:priority isRetry:NO completionHandler:completionHandler]; +} + +- (void)scheduleDownloadWithRequest:(NSURLRequest *)request + resume:(PINResume *)resume + skipRetry:(BOOL)skipRetry + priority:(PINRemoteImageManagerPriority)priority + isRetry:(BOOL)isRetry + completionHandler:(PINRemoteImageManagerDataCompletion)completionHandler +{ + [self.lock lockWithBlock:^{ + if (self->_progressImage != nil || [self l_callbackBlocks].count == 0 || (isRetry == NO && self->_retryStrategy.numberOfRetries > 0)) { + return; + } + self->_resume = resume; + + NSURLRequest *adjustedRequest = request; + if (self->_resume) { + NSMutableURLRequest *mutableRequest = [request mutableCopy]; + NSMutableDictionary *headers = [[mutableRequest allHTTPHeaderFields] mutableCopy]; + headers[@"If-Range"] = self->_resume.ifRange; + headers[@"Range"] = [NSString stringWithFormat:@"bytes=%tu-", self->_resume.resumeData.length]; + mutableRequest.allHTTPHeaderFields = headers; + adjustedRequest = mutableRequest; + } + + self->_progressImage = [[PINProgressiveImage alloc] initWithDataTask:[self.manager.urlSessionTaskQueue addDownloadWithSessionManager:self.manager.sessionManager + request:adjustedRequest + priority:priority + completionHandler:^(NSURLResponse * _Nonnull response, NSError * _Nonnull remoteError) + { + [self.manager.concurrentOperationQueue scheduleOperation:^{ + NSError *error = remoteError; +#if PINRemoteImageLogging + if (error && error.code != NSURLErrorCancelled) { + PINLog(@"Failed downloading image: %@ with error: %@", request.URL, error); + } else if (error == nil && response.expectedContentLength == 0) { + PINLog(@"image is empty at URL: %@", request.URL); + } else { + PINLog(@"Finished downloading image: %@", request.URL); + } +#endif + + if (error.code != NSURLErrorCancelled) { + NSData *data = self.progressImage.data; + + if (error == nil && data == nil) { + error = [NSError errorWithDomain:PINRemoteImageManagerErrorDomain + code:PINRemoteImageManagerErrorImageEmpty + userInfo:nil]; + } + + __block BOOL retry = NO; + __block int64_t delay = 0; + [self.lock lockWithBlock:^{ + retry = skipRetry == NO && [self->_retryStrategy shouldRetryWithError:error]; + if (retry) { + // Clear out the existing progress image or else new data from retry will be appended + self->_progressImage = nil; + [self->_retryStrategy incrementRetryCount]; + delay = [self->_retryStrategy nextDelay]; + } + }]; + if (retry) { + PINLog(@"Retrying download of %@ in %lld seconds.", request.URL, delay); + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + [self scheduleDownloadWithRequest:request resume:nil skipRetry:skipRetry priority:priority isRetry:YES completionHandler:completionHandler]; + }); + return; + } + + completionHandler(data, response, error); + } + }]; + }]]; + }]; +} + +- (PINProgressiveImage *)progressImage +{ + __block PINProgressiveImage *progressImage = nil; + [self.lock lockWithBlock:^{ + progressImage = self->_progressImage; + }]; + return progressImage; +} + ++ (BOOL)retriableError:(NSError *)remoteImageError +{ + if ([remoteImageError.domain isEqualToString:PINURLErrorDomain]) { + return remoteImageError.code >= 500; + } else if ([remoteImageError.domain isEqualToString:NSURLErrorDomain] && remoteImageError.code == NSURLErrorUnsupportedURL) { + return NO; + } else if ([remoteImageError.domain isEqualToString:PINRemoteImageManagerErrorDomain]) { + return NO; + } + return YES; +} + +- (CFTimeInterval)estimatedRemainingTime +{ + return self.progressImage.estimatedRemainingTime; +} + +- (NSData *)data +{ + return self.progressImage.data; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageManager+Private.h b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageManager+Private.h new file mode 100644 index 0000000..39550e3 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageManager+Private.h @@ -0,0 +1,30 @@ +// +// PINRemoteImageManager+Private.h +// PINRemoteImage +// +// Created by Garrett Moon on 5/18/17. +// Copyright © 2017 Pinterest. All rights reserved. +// + +#ifndef PINRemoteImageManager_Private_h +#define PINRemoteImageManager_Private_h + +#import "PINRemoteImageDownloadQueue.h" + +typedef void (^PINRemoteImageManagerDataCompletion)(NSData *data, NSURLResponse *response, NSError *error); + +@interface PINRemoteImageManager (PrivateExtension) + +@property (nonatomic, strong, readonly) dispatch_queue_t callbackQueue; +@property (nonatomic, strong, readonly) PINOperationQueue *concurrentOperationQueue; +@property (nonatomic, strong, readonly) PINRemoteImageDownloadQueue *urlSessionTaskQueue; +@property (nonatomic, strong, readonly) PINURLSessionManager *sessionManager; + +@property (nonatomic, readonly) NSArray *progressThresholds; +@property (nonatomic, readonly) NSTimeInterval estimatedRemainingTimeThreshold; +@property (nonatomic, readonly) BOOL shouldBlurProgressive; +@property (nonatomic, readonly) CGSize maxProgressiveRenderSize; + +@end + +#endif /* PINRemoteImageManager_Private_h */ diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageManager.m b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageManager.m new file mode 100644 index 0000000..5ef8ac1 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageManager.m @@ -0,0 +1,1651 @@ +// +// PINRemoteImageManager.m +// Pods +// +// Created by Garrett Moon on 8/17/14. +// +// + +#import "PINRemoteImageManager.h" + +#import + +#if SWIFT_PACKAGE +@import PINOperation; +#else +#import +#endif + +#import + +#import "PINAlternateRepresentationProvider.h" +#import "PINRemoteImage.h" +#import "PINRemoteImageManagerConfiguration.h" +#import "PINRemoteLock.h" +#import "PINProgressiveImage.h" +#import "PINRemoteImageCallbacks.h" +#import "PINRemoteImageTask.h" +#import "PINRemoteImageProcessorTask.h" +#import "PINRemoteImageDownloadTask.h" +#import "PINResume.h" +#import "PINRemoteImageMemoryContainer.h" +#import "PINRemoteImageCaching.h" +#import "PINRequestRetryStrategy.h" +#import "PINRemoteImageDownloadQueue.h" +#import "PINRequestRetryStrategy.h" +#import "PINSpeedRecorder.h" +#import "PINURLSessionManager.h" + +#import "NSData+ImageDetectors.h" +#import "PINImage+DecodedImage.h" +#import "PINImage+ScaledImage.h" +#import "PINRemoteImageManager+Private.h" +#import "NSHTTPURLResponse+MaxAge.h" + +#if USE_PINCACHE +#import "PINCache+PINRemoteImageCaching.h" +#else +#import "PINRemoteImageBasicCache.h" +#endif + + +#define PINRemoteImageManagerDefaultTimeout 30.0 +//A limit of 200 characters is chosen because PINDiskCache +//may expand the length by encoding certain characters +#define PINRemoteImageManagerCacheKeyMaxLength 200 + +PINOperationQueuePriority operationPriorityWithImageManagerPriority(PINRemoteImageManagerPriority priority); +PINOperationQueuePriority operationPriorityWithImageManagerPriority(PINRemoteImageManagerPriority priority) { + switch (priority) { + case PINRemoteImageManagerPriorityLow: + return PINOperationQueuePriorityLow; + break; + + case PINRemoteImageManagerPriorityDefault: + return PINOperationQueuePriorityDefault; + break; + + case PINRemoteImageManagerPriorityHigh: + return PINOperationQueuePriorityHigh; + break; + } +} + +float dataTaskPriorityWithImageManagerPriority(PINRemoteImageManagerPriority priority) { + switch (priority) { + case PINRemoteImageManagerPriorityLow: + return 0.0; + break; + + case PINRemoteImageManagerPriorityDefault: + return 0.5; + break; + + case PINRemoteImageManagerPriorityHigh: + return 1.0; + break; + } +} + +// Reference: https://github.com/TextureGroup/Texture/blob/5dd5611/Source/Private/ASInternalHelpers.m#L60 +BOOL PINRemoteImageManagerSubclassOverridesSelector(Class subclass, SEL selector) +{ + Class superclass = [PINRemoteImageManager class]; + if (superclass == subclass) return NO; // Even if the class implements the selector, it doesn't override itself. + Method superclassMethod = class_getInstanceMethod(superclass, selector); + Method subclassMethod = class_getInstanceMethod(subclass, selector); + return (superclassMethod != subclassMethod); +} + +NSErrorDomain const PINRemoteImageManagerErrorDomain = @"PINRemoteImageManagerErrorDomain"; +NSString * const PINRemoteImageWeakTaskKey = @"PINRemoteImageWeakTaskKey"; +NSString * const PINRemoteImageCacheKeyResumePrefix = @"R-"; +typedef void (^PINRemoteImageManagerDataCompletion)(NSData *data, NSURLResponse *response, NSError *error); + +@interface PINRemoteImageWeakTask : NSObject + +@property (nonatomic, readonly, weak) PINRemoteImageTask *task; + +- (instancetype)initWithTask:(PINRemoteImageTask *)task; + +@end + +@implementation PINRemoteImageWeakTask + +- (instancetype)initWithTask:(PINRemoteImageTask *)task +{ + if (self = [super init]) { + _task = task; + } + return self; +} + +@end + +@interface PINRemoteImageManager () +{ + dispatch_queue_t _callbackQueue; + PINRemoteLock *_lock; + PINOperationQueue *_concurrentOperationQueue; + PINRemoteImageDownloadQueue *_urlSessionTaskQueue; + + // Necessary to have a strong reference to _defaultAlternateRepresentationProvider because _alternateRepProvider is __weak + PINAlternateRepresentationProvider *_defaultAlternateRepresentationProvider; + __weak PINAlternateRepresentationProvider *_alternateRepProvider; + NSURLSessionConfiguration *_sessionConfiguration; + +} + +@property (nonatomic, strong) id cache; +@property (nonatomic, strong) PINURLSessionManager *sessionManager; +@property (nonatomic, strong) NSMutableDictionary *tasks; +@property (nonatomic, strong) NSHashTable *canceledTasks; +@property (nonatomic, strong) NSMapTable *UUIDToTask; +@property (nonatomic, strong) NSArray *progressThresholds; +@property (nonatomic, assign) BOOL shouldBlurProgressive; +@property (nonatomic, assign) CGSize maxProgressiveRenderSize; +@property (nonatomic, assign) NSTimeInterval estimatedRemainingTimeThreshold; +@property (nonatomic, strong) dispatch_queue_t callbackQueue; +@property (nonatomic, strong) PINOperationQueue *concurrentOperationQueue; +@property (nonatomic, strong) PINRemoteImageDownloadQueue *urlSessionTaskQueue; +@property (nonatomic, assign) float highQualityBPSThreshold; +@property (nonatomic, assign) float lowQualityBPSThreshold; +@property (nonatomic, assign) BOOL shouldUpgradeLowQualityImages; +@property (nonatomic, strong) PINRemoteImageManagerMetrics metricsCallback API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0)); +@property (nonatomic, copy) PINRemoteImageManagerAuthenticationChallenge authenticationChallengeHandler; +@property (nonatomic, copy) id (^retryStrategyCreationBlock)(void); +@property (nonatomic, copy) PINRemoteImageManagerRequestConfigurationHandler requestConfigurationHandler; +@property (nonatomic, strong) NSMutableDictionary *httpHeaderFields; +@property (nonatomic, readonly) BOOL diskCacheTTLIsEnabled; +@property (nonatomic, readonly) BOOL memoryCacheTTLIsEnabled; +#if DEBUG +@property (nonatomic, assign) NSUInteger totalDownloads; +#endif + +@end + +#pragma mark PINRemoteImageManager + +@implementation PINRemoteImageManager + +static PINRemoteImageManager *sharedImageManager = nil; +static dispatch_once_t sharedDispatchToken; + ++ (instancetype)sharedImageManager +{ + dispatch_once(&sharedDispatchToken, ^{ + sharedImageManager = [[[self class] alloc] init]; + }); + return sharedImageManager; +} + ++ (void)setSharedImageManagerWithConfiguration:(NSURLSessionConfiguration *)configuration +{ + NSAssert(sharedImageManager == nil, @"sharedImageManager singleton is already configured"); + + dispatch_once(&sharedDispatchToken, ^{ + sharedImageManager = [[[self class] alloc] initWithSessionConfiguration:configuration]; + }); +} + +- (instancetype)init +{ + return [self initWithSessionConfiguration:nil]; +} + +- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)sessionConfiguration +{ + return [self initWithSessionConfiguration:sessionConfiguration alternativeRepresentationProvider:nil]; +} + +- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)sessionConfiguration alternativeRepresentationProvider:(id )alternateRepProvider +{ + return [self initWithSessionConfiguration:sessionConfiguration alternativeRepresentationProvider:alternateRepProvider imageCache:nil managerConfiguration:nil]; +} + +- (nonnull instancetype)initWithSessionConfiguration:(nullable NSURLSessionConfiguration *)sessionConfiguration + alternativeRepresentationProvider:(nullable id )alternateRepDelegate + imageCache:(nullable id)imageCache { + return [self initWithSessionConfiguration:sessionConfiguration alternativeRepresentationProvider:alternateRepDelegate imageCache:imageCache managerConfiguration:nil]; +} + +-(nonnull instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)sessionConfiguration + alternativeRepresentationProvider:(id)alternateRepProvider + imageCache:(id)imageCache + managerConfiguration:(nullable PINRemoteImageManagerConfiguration *)managerConfiguration +{ + if (self = [super init]) { + PINRemoteImageManagerConfiguration *configuration = managerConfiguration; + if (!configuration) { + configuration = [[PINRemoteImageManagerConfiguration alloc] init]; + } + + if (imageCache) { + self.cache = imageCache; + } else if (PINRemoteImageManagerSubclassOverridesSelector([self class], @selector(defaultImageCache))) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + self.cache = [self defaultImageCache]; +#pragma clang diagnostic pop + } else { + self.cache = [[self class] defaultImageCache]; + } + + if ([self.cache respondsToSelector:@selector(setObjectOnDisk:forKey:withAgeLimit:)] && + [self.cache respondsToSelector:@selector(setObjectInMemory:forKey:withCost:withAgeLimit:)] && + [self.cache respondsToSelector:@selector(diskCacheIsTTLCache)] && + [self.cache respondsToSelector:@selector(memoryCacheIsTTLCache)]) { + _diskCacheTTLIsEnabled = [self.cache diskCacheIsTTLCache]; + _memoryCacheTTLIsEnabled = [self.cache memoryCacheIsTTLCache]; + } + + _sessionConfiguration = [sessionConfiguration copy]; + if (!_sessionConfiguration) { + _sessionConfiguration = [NSURLSessionConfiguration ephemeralSessionConfiguration]; + _sessionConfiguration.timeoutIntervalForRequest = PINRemoteImageManagerDefaultTimeout; + _sessionConfiguration.requestCachePolicy = NSURLRequestReloadIgnoringLocalCacheData; + _sessionConfiguration.URLCache = nil; + _sessionConfiguration.HTTPMaximumConnectionsPerHost = PINRemoteImageHTTPMaximumConnectionsPerHost; + } + + _callbackQueue = dispatch_queue_create("PINRemoteImageManagerCallbackQueue", DISPATCH_QUEUE_CONCURRENT); + _lock = [[PINRemoteLock alloc] initWithName:@"PINRemoteImageManager"]; + + _concurrentOperationQueue = [[PINOperationQueue alloc] initWithMaxConcurrentOperations: configuration.maxConcurrentOperations]; + _urlSessionTaskQueue = [PINRemoteImageDownloadQueue queueWithMaxConcurrentDownloads:configuration.maxConcurrentDownloads]; + + self.sessionManager = [[PINURLSessionManager alloc] initWithSessionConfiguration:_sessionConfiguration]; + self.sessionManager.delegate = self; + + self.estimatedRemainingTimeThreshold = configuration.estimatedRemainingTimeThreshold; + + _highQualityBPSThreshold = configuration.highQualityBPSThreshold; + _lowQualityBPSThreshold = configuration.lowQualityBPSThreshold; + _shouldUpgradeLowQualityImages = configuration.shouldUpgradeLowQualityImages; + _shouldBlurProgressive = configuration.shouldBlurProgressive; + _maxProgressiveRenderSize = configuration.maxProgressiveRenderSize; + self.tasks = [[NSMutableDictionary alloc] init]; + self.canceledTasks = [[NSHashTable alloc] initWithOptions:NSHashTableWeakMemory capacity:5]; + self.UUIDToTask = [NSMapTable weakToWeakObjectsMapTable]; + + if (alternateRepProvider == nil) { + _defaultAlternateRepresentationProvider = [[PINAlternateRepresentationProvider alloc] init]; + alternateRepProvider = _defaultAlternateRepresentationProvider; + } + _alternateRepProvider = alternateRepProvider; + __weak typeof(self) weakSelf = self; + _retryStrategyCreationBlock = ^id{ + return [weakSelf defaultRetryStrategy]; + }; + _httpHeaderFields = [[NSMutableDictionary alloc] init]; + } + return self; +} + +- (id)defaultRetryStrategy { + return [[PINRequestExponentialRetryStrategy alloc] initWithRetryMaxCount:3 delayBase:4]; +} + +- (void)dealloc +{ + [self.sessionManager invalidateSessionAndCancelTasks]; +} + +- (id)defaultImageCache { + return [PINRemoteImageManager defaultImageCache]; +} + ++ (id)defaultImageCache { + return [PINRemoteImageManager defaultImageCacheEnablingTtl:NO]; +} + ++ (id)defaultImageTtlCache { + return [PINRemoteImageManager defaultImageCacheEnablingTtl:YES]; +} + ++ (id)defaultImageCacheEnablingTtl:(BOOL)enableTtl +{ +#if USE_PINCACHE + NSString * const kPINRemoteImageDiskCacheName = @"PINRemoteImageManagerCache"; + NSString * const kPINRemoteImageDiskCacheVersionKey = @"kPINRemoteImageDiskCacheVersionKey"; + const NSInteger kPINRemoteImageDiskCacheVersion = 1; + NSUserDefaults *pinDefaults = [[NSUserDefaults alloc] init]; + + NSString *cacheURLRoot = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0]; + + if ([pinDefaults integerForKey:kPINRemoteImageDiskCacheVersionKey] != kPINRemoteImageDiskCacheVersion) { + //remove the old version of the disk cache + NSURL *diskCacheURL = [PINDiskCache cacheURLWithRootPath:cacheURLRoot prefix:PINDiskCachePrefix name:kPINRemoteImageDiskCacheName]; + NSFileManager *fileManager = [NSFileManager defaultManager]; + NSURL *dstURL = [[[NSURL alloc] initFileURLWithPath:NSTemporaryDirectory()] URLByAppendingPathComponent:kPINRemoteImageDiskCacheName]; + [fileManager moveItemAtURL:diskCacheURL toURL:dstURL error:nil]; + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + [fileManager removeItemAtURL:dstURL error:nil]; + }); + [pinDefaults setInteger:kPINRemoteImageDiskCacheVersion forKey:kPINRemoteImageDiskCacheVersionKey]; + } + + PINCache *pinCache = [[PINCache alloc] initWithName:kPINRemoteImageDiskCacheName rootPath:cacheURLRoot serializer:^NSData * _Nonnull(id _Nonnull object, NSString * _Nonnull key) { + id obj = (id )object; + if ([key hasPrefix:PINRemoteImageCacheKeyResumePrefix]) { + return [NSKeyedArchiver archivedDataWithRootObject:obj]; + } + return (NSData *)object; + } deserializer:^id _Nonnull(NSData * _Nonnull data, NSString * _Nonnull key) { + if ([key hasPrefix:PINRemoteImageCacheKeyResumePrefix]) { + return [NSKeyedUnarchiver unarchiveObjectWithData:data]; + } + return data; + } keyEncoder:nil keyDecoder:nil ttlCache:enableTtl]; + + return pinCache; +#else + return [[PINRemoteImageBasicCache alloc] init]; +#endif +} + +- (void)lockOnMainThread +{ +#if !DEBUG + NSAssert(NO, @"lockOnMainThread should only be called for testing on debug builds!"); +#endif + [_lock lock]; +} + +- (void)lock +{ + NSAssert([NSThread isMainThread] == NO, @"lock should not be called from the main thread!"); + [_lock lock]; +} + +- (void)unlock +{ + [_lock unlock]; +} + +- (void)setValue:(nullable NSString *)value forHTTPHeaderField:(nullable NSString *)header { + __weak typeof(self) weakSelf = self; + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + typeof(self) strongSelf = weakSelf; + [strongSelf lock]; + strongSelf.httpHeaderFields[[header copy]] = [value copy]; + [strongSelf unlock]; + }); +} + +- (void)setRequestConfiguration:(PINRemoteImageManagerRequestConfigurationHandler)configurationBlock { + __weak typeof(self) weakSelf = self; + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + typeof(self) strongSelf = weakSelf; + [strongSelf lock]; + strongSelf.requestConfigurationHandler = configurationBlock; + [strongSelf unlock]; + }); +} + +- (void)setAuthenticationChallenge:(PINRemoteImageManagerAuthenticationChallenge)challengeBlock { + __weak typeof(self) weakSelf = self; + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + typeof(self) strongSelf = weakSelf; + [strongSelf lock]; + strongSelf.authenticationChallengeHandler = challengeBlock; + [strongSelf unlock]; + }); +} + +- (void)setMaxNumberOfConcurrentOperations:(NSInteger)maxNumberOfConcurrentOperations completion:(dispatch_block_t)completion +{ + __weak typeof(self) weakSelf = self; + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + typeof(self) strongSelf = weakSelf; + [strongSelf lock]; + strongSelf.concurrentOperationQueue.maxConcurrentOperations = maxNumberOfConcurrentOperations; + [strongSelf unlock]; + if (completion) { + completion(); + } + }); +} + +- (void)setMaxNumberOfConcurrentDownloads:(NSInteger)maxNumberOfConcurrentDownloads completion:(dispatch_block_t)completion +{ + NSAssert(maxNumberOfConcurrentDownloads <= PINRemoteImageHTTPMaximumConnectionsPerHost, @"maxNumberOfConcurrentDownloads must be less than or equal to %d", PINRemoteImageHTTPMaximumConnectionsPerHost); + __weak typeof(self) weakSelf = self; + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + typeof(self) strongSelf = weakSelf; + [strongSelf lock]; + strongSelf.urlSessionTaskQueue.maxNumberOfConcurrentDownloads = maxNumberOfConcurrentDownloads; + [strongSelf unlock]; + if (completion) { + completion(); + } + }); +} + +- (void)setEstimatedRemainingTimeThresholdForProgressiveDownloads:(NSTimeInterval)estimatedRemainingTimeThreshold completion:(dispatch_block_t)completion +{ + __weak typeof(self) weakSelf = self; + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + typeof(self) strongSelf = weakSelf; + [strongSelf lock]; + strongSelf.estimatedRemainingTimeThreshold = estimatedRemainingTimeThreshold; + [strongSelf unlock]; + if (completion) { + completion(); + } + }); +} + +- (void)setProgressThresholds:(NSArray *)progressThresholds completion:(dispatch_block_t)completion +{ + __weak typeof(self) weakSelf = self; + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + typeof(self) strongSelf = weakSelf; + [strongSelf lock]; + strongSelf.progressThresholds = progressThresholds; + [strongSelf unlock]; + if (completion) { + completion(); + } + }); +} + +- (void)setProgressiveRendersShouldBlur:(BOOL)shouldBlur completion:(nullable dispatch_block_t)completion +{ + __weak typeof(self) weakSelf = self; + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + typeof(self) strongSelf = weakSelf; + [strongSelf lock]; + strongSelf.shouldBlurProgressive = shouldBlur; + [strongSelf unlock]; + if (completion) { + completion(); + } + }); +} + +- (void)setProgressiveRendersMaxProgressiveRenderSize:(CGSize)maxProgressiveRenderSize completion:(nullable dispatch_block_t)completion +{ + __weak typeof(self) weakSelf = self; + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + typeof(self) strongSelf = weakSelf; + [strongSelf lock]; + strongSelf.maxProgressiveRenderSize = maxProgressiveRenderSize; + [strongSelf unlock]; + if (completion) { + completion(); + } + }); +} + +- (void)setHighQualityBPSThreshold:(float)highQualityBPSThreshold completion:(dispatch_block_t)completion +{ + __weak typeof(self) weakSelf = self; + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + typeof(self) strongSelf = weakSelf; + [strongSelf lock]; + strongSelf.highQualityBPSThreshold = highQualityBPSThreshold; + [strongSelf unlock]; + if (completion) { + completion(); + } + }); +} + +- (void)setLowQualityBPSThreshold:(float)lowQualityBPSThreshold completion:(dispatch_block_t)completion +{ + __weak typeof(self) weakSelf = self; + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + typeof(self) strongSelf = weakSelf; + [strongSelf lock]; + strongSelf.lowQualityBPSThreshold = lowQualityBPSThreshold; + [strongSelf unlock]; + if (completion) { + completion(); + } + }); +} + +- (void)setShouldUpgradeLowQualityImages:(BOOL)shouldUpgradeLowQualityImages completion:(dispatch_block_t)completion +{ + __weak typeof(self) weakSelf = self; + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + typeof(self) strongSelf = weakSelf; + [strongSelf lock]; + strongSelf.shouldUpgradeLowQualityImages = shouldUpgradeLowQualityImages; + [strongSelf unlock]; + if (completion) { + completion(); + } + }); +} + +- (void)setMetricsCallback:(nullable PINRemoteImageManagerMetrics)metricsCallback completion:(nullable dispatch_block_t)completion +{ + __weak typeof(self) weakSelf = self; + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + typeof(self) strongSelf = weakSelf; + [self lock]; + strongSelf.metricsCallback = metricsCallback; + [self unlock]; + if (completion) { + completion(); + } + }); +} + +- (NSUUID *)downloadImageWithURL:(NSURL *)url + completion:(PINRemoteImageManagerImageCompletion)completion +{ + return [self downloadImageWithURL:url + options:PINRemoteImageManagerDownloadOptionsNone + completion:completion]; +} + +- (NSUUID *)downloadImageWithURL:(NSURL *)url + options:(PINRemoteImageManagerDownloadOptions)options + completion:(PINRemoteImageManagerImageCompletion)completion +{ + return [self downloadImageWithURL:url + options:options + progressImage:nil + completion:completion]; +} + +- (NSUUID *)downloadImageWithURL:(NSURL *)url + options:(PINRemoteImageManagerDownloadOptions)options + progressImage:(PINRemoteImageManagerImageCompletion)progressImage + completion:(PINRemoteImageManagerImageCompletion)completion +{ + return [self downloadImageWithURL:url + options:options + progressImage:progressImage + progressDownload:nil + completion:completion]; +} + +- (NSUUID *)downloadImageWithURL:(NSURL *)url + options:(PINRemoteImageManagerDownloadOptions)options + progressDownload:(PINRemoteImageManagerProgressDownload)progressDownload + completion:(PINRemoteImageManagerImageCompletion)completion +{ + return [self downloadImageWithURL:url + options:options + progressImage:nil + progressDownload:progressDownload + completion:completion]; +} + +- (NSUUID *)downloadImageWithURL:(NSURL *)url + options:(PINRemoteImageManagerDownloadOptions)options + progressImage:(PINRemoteImageManagerImageCompletion)progressImage + progressDownload:(PINRemoteImageManagerProgressDownload)progressDownload + completion:(PINRemoteImageManagerImageCompletion)completion +{ + return [self downloadImageWithURL:url + options:options + priority:PINRemoteImageManagerPriorityDefault + progressImage:progressImage + progressDownload:progressDownload + completion:completion]; +} + +- (nullable NSUUID *)downloadImageWithURL:(nonnull NSURL *)url + options:(PINRemoteImageManagerDownloadOptions)options + priority:(PINRemoteImageManagerPriority)priority + progressImage:(PINRemoteImageManagerImageCompletion)progressImage + progressDownload:(nullable PINRemoteImageManagerProgressDownload)progressDownload + completion:(nullable PINRemoteImageManagerImageCompletion)completion; +{ + return [self downloadImageWithURL:url + options:options + priority:priority + processorKey:nil + processor:nil + progressImage:progressImage + progressDownload:progressDownload + completion:completion + inputUUID:nil]; +} + +- (NSUUID *)downloadImageWithURL:(NSURL *)url + options:(PINRemoteImageManagerDownloadOptions)options + processorKey:(NSString *)processorKey + processor:(PINRemoteImageManagerImageProcessor)processor + completion:(PINRemoteImageManagerImageCompletion)completion +{ + return [self downloadImageWithURL:url + options:options + priority:PINRemoteImageManagerPriorityDefault + processorKey:processorKey + processor:processor + progressImage:nil + progressDownload:nil + completion:completion + inputUUID:nil]; +} + +- (NSUUID *)downloadImageWithURL:(NSURL *)url + options:(PINRemoteImageManagerDownloadOptions)options + processorKey:(NSString *)processorKey + processor:(PINRemoteImageManagerImageProcessor)processor + progressDownload:(PINRemoteImageManagerProgressDownload)progressDownload + completion:(PINRemoteImageManagerImageCompletion)completion +{ + return [self downloadImageWithURL:url + options:options + priority:PINRemoteImageManagerPriorityDefault + processorKey:processorKey + processor:processor + progressImage:nil + progressDownload:progressDownload + completion:completion + inputUUID:nil]; +} + +- (NSUUID *)downloadImageWithURL:(NSURL *)url + options:(PINRemoteImageManagerDownloadOptions)options + priority:(PINRemoteImageManagerPriority)priority + processorKey:(NSString *)processorKey + processor:(PINRemoteImageManagerImageProcessor)processor + progressImage:(PINRemoteImageManagerImageCompletion)progressImage + progressDownload:(PINRemoteImageManagerProgressDownload)progressDownload + completion:(PINRemoteImageManagerImageCompletion)completion + inputUUID:(NSUUID *)UUID +{ + NSAssert((processor != nil && processorKey.length > 0) || (processor == nil && processorKey == nil), @"processor must not be nil and processorKey length must be greater than zero OR processor must be nil and processorKey must be nil"); + + Class taskClass; + if (processor && processorKey.length > 0) { + taskClass = [PINRemoteImageProcessorTask class]; + } else { + taskClass = [PINRemoteImageDownloadTask class]; + } + + NSString *key = [self cacheKeyForURL:url processorKey:processorKey]; + + if (url == nil) { + [self earlyReturnWithOptions:options url:nil key:key object:nil completion:completion]; + return nil; + } + + NSAssert([url isKindOfClass:[NSURL class]], @"url must be of type NSURL, if it's an NSString, we'll try to correct"); + if ([url isKindOfClass:[NSString class]]) { + url = [NSURL URLWithString:(NSString *)url]; + } + + if (UUID == nil) { + UUID = [NSUUID UUID]; + } + + if ((options & PINRemoteImageManagerDownloadOptionsIgnoreCache) == 0) { + //Check to see if the image is in memory cache and we're on the main thread. + //If so, special case this to avoid flashing the UI + id object = [self.cache objectFromMemoryForKey:key]; + if (object) { + if ([self earlyReturnWithOptions:options url:url key:key object:object completion:completion]) { + return nil; + } + } + } + + if ([url.scheme isEqualToString:@"data"]) { + NSData *data = [NSData dataWithContentsOfURL:url]; + if (data) { + if ([self earlyReturnWithOptions:options url:url key:key object:data completion:completion]) { + return nil; + } + } + } + + [_concurrentOperationQueue scheduleOperation:^ + { + [self lock]; + //check canceled tasks first + if ([self.canceledTasks containsObject:UUID]) { + PINLog(@"skipping starting %@ because it was canceled.", UUID); + [self unlock]; + return; + } + + PINRemoteImageTask *task = [self.tasks objectForKey:key]; + BOOL taskExisted = NO; + if (task == nil) { + task = [[taskClass alloc] initWithManager:self]; + PINLog(@"Task does not exist creating with key: %@, URL: %@, UUID: %@, task: %p", key, url, UUID, task); + task.key = key; + } else { + taskExisted = YES; + PINLog(@"Task exists, attaching with key: %@, URL: %@, UUID: %@, task: %@", key, url, UUID, task); + } + [task addCallbacksWithCompletionBlock:completion progressImageBlock:progressImage progressDownloadBlock:progressDownload withUUID:UUID]; + [self.tasks setObject:task forKey:key]; + // Relax :), task retain the UUID for us, it's ok to have a weak reference to UUID here. + [self.UUIDToTask setObject:task forKey:UUID]; + + NSAssert(taskClass == [task class], @"Task class should be the same!"); + [self unlock]; + + if (taskExisted == NO) { + [self.concurrentOperationQueue scheduleOperation:^ + { + [self objectForKey:key options:options completion:^(BOOL found, BOOL valid, PINImage *image, id alternativeRepresentation) { + if (found) { + if (valid) { + [self callCompletionsWithKey:key image:image alternativeRepresentation:alternativeRepresentation cached:YES response:nil error:nil finalized:YES]; + } else { + //Remove completion and try again + [self lock]; + PINRemoteImageTask *task = [self.tasks objectForKey:key]; + [task removeCallbackWithUUID:UUID]; + if (task.callbackBlocks.count == 0) { + [self.tasks removeObjectForKey:key]; + } + [self unlock]; + + //Skip early check + [self downloadImageWithURL:url + options:options | PINRemoteImageManagerDownloadOptionsSkipEarlyCheck + priority:priority + processorKey:processorKey + processor:processor + progressImage:(PINRemoteImageManagerImageCompletion)progressImage + progressDownload:nil + completion:completion + inputUUID:UUID]; + } + } else { + if ([taskClass isSubclassOfClass:[PINRemoteImageProcessorTask class]]) { + //continue processing + [self downloadImageWithURL:url + options:options + priority:priority + key:key + processor:processor + UUID:UUID]; + } else if ([taskClass isSubclassOfClass:[PINRemoteImageDownloadTask class]]) { + //continue downloading + [self downloadImageWithURL:url + options:options + priority:priority + key:key + progressImage:progressImage + UUID:UUID]; + } + } + }]; + } withPriority:operationPriorityWithImageManagerPriority(priority)]; + } + } withPriority:operationPriorityWithImageManagerPriority(priority)]; + + return UUID; +} + +- (void)downloadImageWithURL:(NSURL *)url + options:(PINRemoteImageManagerDownloadOptions)options + priority:(PINRemoteImageManagerPriority)priority + key:(NSString *)key + processor:(PINRemoteImageManagerImageProcessor)processor + UUID:(NSUUID *)UUID +{ + PINRemoteImageProcessorTask *task = nil; + [self lock]; + task = [self.tasks objectForKey:key]; + //check processing task still exists and download hasn't been started for another task + if (task == nil || task.downloadTaskUUID != nil) { + [self unlock]; + return; + } + + __weak typeof(self) weakSelf = self; + NSUUID *downloadTaskUUID = [self downloadImageWithURL:url + options:options | PINRemoteImageManagerDownloadOptionsSkipEarlyCheck + completion:^(PINRemoteImageManagerResult *result) + { + typeof(self) strongSelf = weakSelf; + NSUInteger processCost = 0; + NSError *error = result.error; + PINRemoteImageProcessorTask *task = nil; + [strongSelf lock]; + task = [strongSelf.tasks objectForKey:key]; + [strongSelf unlock]; + //check processing task still exists + if (task == nil) { + return; + } + if (result.image && error == nil) { + //If completionBlocks.count == 0, we've canceled before we were even able to start. + PINImage *image = processor(result, &processCost); + + if (image == nil) { + error = [NSError errorWithDomain:PINRemoteImageManagerErrorDomain + code:PINRemoteImageManagerErrorFailedToProcessImage + userInfo:nil]; + } + [strongSelf callCompletionsWithKey:key image:image alternativeRepresentation:nil cached:NO response:result.response error:error finalized:NO]; + + if (error == nil && image != nil) { + BOOL saveAsJPEG = (options & PINRemoteImageManagerSaveProcessedImageAsJPEG) != 0; + NSData *diskData = nil; + if (saveAsJPEG) { + diskData = PINImageJPEGRepresentation(image, 1.0); + } else { + diskData = PINImagePNGRepresentation(image); + } + + [strongSelf materializeAndCacheObject:image cacheInDisk:diskData additionalCost:processCost url:url key:key options:options outImage:nil outAltRep:nil]; + } + + [strongSelf callCompletionsWithKey:key image:image alternativeRepresentation:nil cached:NO response:result.response error:error finalized:YES]; + } else { + if (error == nil) { + error = [NSError errorWithDomain:PINRemoteImageManagerErrorDomain + code:PINRemoteImageManagerErrorFailedToFetchImageForProcessing + userInfo:nil]; + } + + [strongSelf callCompletionsWithKey:key image:nil alternativeRepresentation:nil cached:NO response:result.response error:error finalized:YES]; + } + }]; + task.downloadTaskUUID = downloadTaskUUID; + [self unlock]; +} + +- (void)downloadImageWithURL:(NSURL *)url + options:(PINRemoteImageManagerDownloadOptions)options + priority:(PINRemoteImageManagerPriority)priority + key:(NSString *)key + progressImage:(PINRemoteImageManagerImageCompletion)progressImage + UUID:(NSUUID *)UUID +{ + PINResume *resume = nil; + if ((options & PINRemoteImageManagerDownloadOptionsIgnoreCache) == NO) { + NSString *resumeKey = [self resumeCacheKeyForURL:url]; + resume = [self.cache objectFromDiskForKey:resumeKey]; + [self.cache removeObjectForKey:resumeKey completion:nil]; + } + + [self lock]; + PINRemoteImageDownloadTask *task = [self.tasks objectForKey:key]; + [self unlock]; + + [task scheduleDownloadWithRequest:[self requestWithURL:url task:task] + resume:resume + skipRetry:(options & PINRemoteImageManagerDownloadOptionsSkipRetry) + priority:priority + completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) + { + [self->_concurrentOperationQueue scheduleOperation:^ + { + NSError *remoteImageError = error; + PINImage *image = nil; + id alternativeRepresentation = nil; + NSNumber *maxAge = nil; + if (remoteImageError == nil) { + BOOL ignoreHeaders = (options & PINRemoteImageManagerDownloadOptionsIgnoreCacheControlHeaders) != 0; + if ((self.diskCacheTTLIsEnabled || self.memoryCacheTTLIsEnabled) && !ignoreHeaders) { + // examine Cache-Control headers (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control) + if ([response isKindOfClass:[NSHTTPURLResponse class]]) { + NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response; + maxAge = [httpResponse findMaxAge]; + } + } + // Stores the object in the cache. + [self materializeAndCacheObject:data cacheInDisk:data additionalCost:0 maxAge:maxAge url:url key:key options:options outImage:&image outAltRep:&alternativeRepresentation]; + } + + if (error == nil && image == nil && alternativeRepresentation == nil) { + remoteImageError = [NSError errorWithDomain:PINRemoteImageManagerErrorDomain + code:PINRemoteImageManagerErrorFailedToDecodeImage + userInfo:nil]; + } + + [self callCompletionsWithKey:key image:image alternativeRepresentation:alternativeRepresentation cached:NO response:response error:remoteImageError finalized:YES]; + } withPriority:operationPriorityWithImageManagerPriority(priority)]; + }]; +} + +-(BOOL)insertImageDataIntoCache:(nonnull NSData*)data + withURL:(nonnull NSURL *)url + processorKey:(nullable NSString *)processorKey + additionalCost:(NSUInteger)additionalCost +{ + + if (url != nil) { + NSString *key = [self cacheKeyForURL:url processorKey:processorKey]; + + PINRemoteImageManagerDownloadOptions options = PINRemoteImageManagerDownloadOptionsSkipDecode | PINRemoteImageManagerDownloadOptionsSkipEarlyCheck; + PINRemoteImageMemoryContainer *container = [[PINRemoteImageMemoryContainer alloc] init]; + container.data = data; + + return [self materializeAndCacheObject:container cacheInDisk:data additionalCost:additionalCost url:url key:key options:options outImage: nil outAltRep: nil]; + } + + return NO; +} + +- (BOOL)earlyReturnWithOptions:(PINRemoteImageManagerDownloadOptions)options url:(NSURL *)url key:(NSString *)key object:(id)object completion:(PINRemoteImageManagerImageCompletion)completion +{ + PINImage *image = nil; + id alternativeRepresentation = nil; + PINRemoteImageResultType resultType = PINRemoteImageResultTypeNone; + + BOOL allowEarlyReturn = !(PINRemoteImageManagerDownloadOptionsSkipEarlyCheck & options); + + if (url != nil && object != nil) { + resultType = PINRemoteImageResultTypeMemoryCache; + [self materializeAndCacheObject:object url:url key:key options:options outImage:&image outAltRep:&alternativeRepresentation]; + } + + if (completion && ((image || alternativeRepresentation) || (url == nil))) { + //If we're on the main thread, special case to call completion immediately + NSError *error = nil; + if (!url) { + error = [NSError errorWithDomain:NSURLErrorDomain + code:NSURLErrorUnsupportedURL + userInfo:@{ NSLocalizedDescriptionKey : @"unsupported URL" }]; + } + PINRemoteImageManagerResult *result = [PINRemoteImageManagerResult imageResultWithImage:image + alternativeRepresentation:alternativeRepresentation + requestLength:0 + resultType:resultType + UUID:nil + response:nil + error:error]; + if (allowEarlyReturn && [NSThread isMainThread]) { + completion(result); + } else { + dispatch_async(self.callbackQueue, ^{ + completion(result); + }); + } + return YES; + } + return NO; +} + +- (NSURLRequest *)requestWithURL:(NSURL *)url task:(PINRemoteImageTask *)task +{ + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; + + NSMutableDictionary *headers = [self.httpHeaderFields mutableCopy]; + + if (headers.count > 0) { + request.allHTTPHeaderFields = headers; + } + + if (_requestConfigurationHandler) { + request = [_requestConfigurationHandler(request) mutableCopy]; + } + + PINRemoteImageWeakTask *weakTask = [[PINRemoteImageWeakTask alloc] initWithTask:task]; + [NSURLProtocol setProperty:weakTask forKey:PINRemoteImageWeakTaskKey inRequest:request]; + + return request; +} + +- (void)callCompletionsWithKey:(NSString *)key + image:(PINImage *)image + alternativeRepresentation:(id)alternativeRepresentation + cached:(BOOL)cached + response:(NSURLResponse *)response + error:(NSError *)error + finalized:(BOOL)finalized +{ + [self lock]; + PINRemoteImageDownloadTask *task = [self.tasks objectForKey:key]; + [task callCompletionsWithImage:image alternativeRepresentation:alternativeRepresentation cached:cached response:response error:error remove:!finalized]; + if (finalized) { + [self.tasks removeObjectForKey:key]; + } + [self unlock]; +} + +#pragma mark - Prefetching + +- (NSArray *)prefetchImagesWithURLs:(NSArray *)urls +{ + return [self prefetchImagesWithURLs:urls options:PINRemoteImageManagerDownloadOptionsNone | PINRemoteImageManagerDownloadOptionsSkipEarlyCheck]; +} + +- (NSArray *)prefetchImagesWithURLs:(NSArray *)urls options:(PINRemoteImageManagerDownloadOptions)options +{ + return [self prefetchImagesWithURLs:urls options:options priority:PINRemoteImageManagerPriorityLow]; +} + +- (NSArray *)prefetchImagesWithURLs:(NSArray *)urls options:(PINRemoteImageManagerDownloadOptions)options priority:(PINRemoteImageManagerPriority)priority +{ + NSMutableArray *tasks = [NSMutableArray arrayWithCapacity:urls.count]; + for (NSURL *url in urls) { + NSUUID *task = [self prefetchImageWithURL:url options:options priority:priority]; + if (task != nil) { + [tasks addObject:task]; + } + } + return tasks; +} + +- (NSUUID *)prefetchImageWithURL:(NSURL *)url +{ + return [self prefetchImageWithURL:url options:PINRemoteImageManagerDownloadOptionsNone | PINRemoteImageManagerDownloadOptionsSkipEarlyCheck]; +} + +- (NSUUID *)prefetchImageWithURL:(NSURL *)url options:(PINRemoteImageManagerDownloadOptions)options +{ + return [self prefetchImageWithURL:url options:options priority:PINRemoteImageManagerPriorityLow]; +} + +- (NSUUID *)prefetchImageWithURL:(NSURL *)url options:(PINRemoteImageManagerDownloadOptions)options priority:(PINRemoteImageManagerPriority)priority +{ + return [self downloadImageWithURL:url + options:options + priority:priority + processorKey:nil + processor:nil + progressImage:nil + progressDownload:nil + completion:nil + inputUUID:nil]; +} + +#pragma mark - Cancellation & Priority + +- (void)cancelTaskWithUUID:(NSUUID *)UUID +{ + [self cancelTaskWithUUID:UUID storeResumeData:NO]; +} + +- (void)cancelTaskWithUUID:(nonnull NSUUID *)UUID storeResumeData:(BOOL)storeResumeData +{ + if (UUID == nil) { + return; + } + PINLog(@"Attempting to cancel UUID: %@", UUID); + [_concurrentOperationQueue scheduleOperation:^{ + PINResume *resume = nil; + [self lock]; + PINRemoteImageTask *taskToEvaluate = [self.UUIDToTask objectForKey:UUID]; + if (taskToEvaluate == nil) { + //maybe task hasn't been added to task list yet, add it to canceled tasks. + //there's no need to ever remove a UUID from canceledTasks because it is weak. + [self.canceledTasks addObject:UUID]; + } + + if ([taskToEvaluate cancelWithUUID:UUID resume:storeResumeData ? &resume : NULL]) { + [self.tasks removeObjectForKey:taskToEvaluate.key]; + } + [self unlock]; + + if (resume) { + //store resume data away, only download tasks currently return resume data + [self storeResumeData:resume forURL:[(PINRemoteImageDownloadTask *)taskToEvaluate URL]]; + } + } withPriority:PINOperationQueuePriorityHigh]; +} + +- (void)cancelAllTasks +{ + [self cancelAllTasksAndStoreResumeData:NO]; +} + +- (void)cancelAllTasksAndStoreResumeData:(BOOL)storeResumeData +{ + [_concurrentOperationQueue scheduleOperation:^{ + [self lock]; + NSMapTable *uuidToTask = [self.UUIDToTask copy]; + [self unlock]; + + for (NSUUID *uuid in uuidToTask) { + [self cancelTaskWithUUID:uuid storeResumeData:storeResumeData]; + } + } withPriority:PINOperationQueuePriorityHigh]; +} + +- (void)setPriority:(PINRemoteImageManagerPriority)priority ofTaskWithUUID:(NSUUID *)UUID +{ + if (UUID == nil) { + return; + } + PINLog(@"Setting priority of UUID: %@ priority: %lu", UUID, (unsigned long)priority); + [_concurrentOperationQueue scheduleOperation:^{ + [self lock]; + PINRemoteImageTask *task = [self.UUIDToTask objectForKey:UUID]; + [task setPriority:priority]; + [self unlock]; + } withPriority:PINOperationQueuePriorityHigh]; +} + +- (void)setProgressImageCallback:(nullable PINRemoteImageManagerImageCompletion)progressImageCallback ofTaskWithUUID:(nonnull NSUUID *)UUID +{ + if (UUID == nil) { + return; + } + + PINLog(@"setting progress block of UUID: %@ progressBlock: %@", UUID, progressImageCallback); + [_concurrentOperationQueue scheduleOperation:^{ + [self lock]; + PINRemoteImageTask *task = [self.UUIDToTask objectForKey:UUID]; + if ([task isKindOfClass:[PINRemoteImageDownloadTask class]]) { + PINRemoteImageCallbacks *callbacks = task.callbackBlocks[UUID]; + callbacks.progressImageBlock = progressImageCallback; + } + [self unlock]; + } withPriority:PINOperationQueuePriorityHigh]; +} + +- (void)setRetryStrategyCreationBlock:(id (^)(void))retryStrategyCreationBlock { + [_concurrentOperationQueue scheduleOperation:^{ + [self lock]; + self->_retryStrategyCreationBlock = retryStrategyCreationBlock; + [self unlock]; + } withPriority:PINOperationQueuePriorityHigh]; +} + +#pragma mark - Caching + +- (void)imageFromCacheWithCacheKey:(NSString *)cacheKey + completion:(PINRemoteImageManagerImageCompletion)completion +{ + [self imageFromCacheWithCacheKey:cacheKey options:PINRemoteImageManagerDownloadOptionsNone completion:completion]; +} + +- (void)imageFromCacheWithCacheKey:(NSString *)cacheKey + options:(PINRemoteImageManagerDownloadOptions)options + completion:(PINRemoteImageManagerImageCompletion)completion +{ + [self imageFromCacheWithURL:nil processorKey:nil cacheKey:cacheKey options:options completion:completion]; +} + +- (void)imageFromCacheWithURL:(nonnull NSURL *)url + processorKey:(nullable NSString *)processorKey + options:(PINRemoteImageManagerDownloadOptions)options + completion:(nonnull PINRemoteImageManagerImageCompletion)completion +{ + [self imageFromCacheWithURL:url processorKey:processorKey cacheKey:nil options:options completion:completion]; +} + +- (void)imageFromCacheWithURL:(NSURL *)url + processorKey:(NSString *)processorKey + cacheKey:(NSString *)cacheKey + options:(PINRemoteImageManagerDownloadOptions)options + completion:(PINRemoteImageManagerImageCompletion)completion +{ + CFTimeInterval requestTime = CACurrentMediaTime(); + + if ((PINRemoteImageManagerDownloadOptionsSkipEarlyCheck & options) == NO && [NSThread isMainThread]) { + PINRemoteImageManagerResult *result = [self synchronousImageFromCacheWithURL:url processorKey:processorKey cacheKey:cacheKey options:options]; + if (result.image && result.error == nil) { + completion((result)); + return; + } + } + + [self objectForURL:url processorKey:processorKey key:cacheKey options:options completion:^(BOOL found, BOOL valid, PINImage *image, id alternativeRepresentation) { + NSError *error = nil; + if (valid == NO) { + error = [NSError errorWithDomain:PINRemoteImageManagerErrorDomain + code:PINRemoteImageManagerErrorInvalidItemInCache + userInfo:nil]; + } + + dispatch_async(self.callbackQueue, ^{ + completion([PINRemoteImageManagerResult imageResultWithImage:image + alternativeRepresentation:alternativeRepresentation + requestLength:CACurrentMediaTime() - requestTime + resultType:PINRemoteImageResultTypeCache + UUID:nil + response:nil + error:error]); + }); + }]; +} + +- (PINRemoteImageManagerResult *)synchronousImageFromCacheWithCacheKey:(NSString *)cacheKey options:(PINRemoteImageManagerDownloadOptions)options +{ + return [self synchronousImageFromCacheWithURL:nil processorKey:nil cacheKey:cacheKey options:options]; +} + +- (nonnull PINRemoteImageManagerResult *)synchronousImageFromCacheWithURL:(NSURL *)url processorKey:(nullable NSString *)processorKey options:(PINRemoteImageManagerDownloadOptions)options +{ + return [self synchronousImageFromCacheWithURL:url processorKey:processorKey cacheKey:nil options:options]; +} + +- (PINRemoteImageManagerResult *)synchronousImageFromCacheWithURL:(NSURL *)url processorKey:(NSString *)processorKey cacheKey:(NSString *)cacheKey options:(PINRemoteImageManagerDownloadOptions)options +{ + CFTimeInterval requestTime = CACurrentMediaTime(); + + if (cacheKey == nil && url == nil) { + return nil; + } + + cacheKey = cacheKey ?: [self cacheKeyForURL:url processorKey:processorKey]; + + id object = [self.cache objectFromMemoryForKey:cacheKey]; + PINImage *image; + id alternativeRepresentation; + NSError *error = nil; + if (object == nil) { + image = nil; + alternativeRepresentation = nil; + } else if ([self materializeAndCacheObject:object url:url key:cacheKey options:options outImage:&image outAltRep:&alternativeRepresentation] == NO) { + error = [NSError errorWithDomain:PINRemoteImageManagerErrorDomain + code:PINRemoteImageManagerErrorInvalidItemInCache + userInfo:nil]; + } + + return [PINRemoteImageManagerResult imageResultWithImage:image + alternativeRepresentation:alternativeRepresentation + requestLength:CACurrentMediaTime() - requestTime + resultType:PINRemoteImageResultTypeMemoryCache + UUID:nil + response:nil + error:error]; +} + +#pragma mark - Session Task Blocks + +- (void)didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge forTask:(NSURLSessionTask *)dataTask completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler { + [self lock]; + if (self.authenticationChallengeHandler) { + self.authenticationChallengeHandler(dataTask, challenge, completionHandler); + } else { + completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil); + } + [self unlock]; +} + +- (void)didReceiveResponse:(nonnull NSURLResponse *)response forTask:(nonnull NSURLSessionTask *)dataTask +{ + [self lock]; + PINRemoteImageWeakTask *weakTask = [NSURLProtocol propertyForKey:PINRemoteImageWeakTaskKey inRequest:dataTask.originalRequest]; + PINRemoteImageDownloadTask *task = (PINRemoteImageDownloadTask *)weakTask.task; + [self unlock]; + [task didReceiveResponse:response]; +} + +- (void)didReceiveData:(NSData *)data forTask:(NSURLSessionTask *)dataTask +{ + [self lock]; + PINRemoteImageWeakTask *weakTask = [NSURLProtocol propertyForKey:PINRemoteImageWeakTaskKey inRequest:dataTask.originalRequest]; + PINRemoteImageDownloadTask *task = (PINRemoteImageDownloadTask *)weakTask.task; + [self unlock]; + [task didReceiveData:data]; +} + +- (void)didCollectMetrics:(nonnull NSURLSessionTaskMetrics *)metrics forURL:(nonnull NSURL *)url API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0)) +{ + [self lock]; + if (self.metricsCallback) { + self.metricsCallback(url, metrics); + } + [self unlock]; +} + +#pragma mark - QOS + +- (NSUUID *)downloadImageWithURLs:(NSArray *)urls + options:(PINRemoteImageManagerDownloadOptions)options + progressImage:(PINRemoteImageManagerImageCompletion)progressImage + completion:(PINRemoteImageManagerImageCompletion)completion +{ + return [self downloadImageWithURLs:urls + options:options + progressImage:progressImage + progressDownload:nil + completion:completion]; +} + +- (nullable NSUUID *)downloadImageWithURLs:(nonnull NSArray *)urls + options:(PINRemoteImageManagerDownloadOptions)options + progressImage:(nullable PINRemoteImageManagerImageCompletion)progressImage + progressDownload:(nullable PINRemoteImageManagerProgressDownload)progressDownload + completion:(nullable PINRemoteImageManagerImageCompletion)completion +{ + NSUUID *UUID = [NSUUID UUID]; + if (urls.count <= 1) { + NSURL *url = [urls firstObject]; + [self downloadImageWithURL:url + options:options + priority:PINRemoteImageManagerPriorityDefault + processorKey:nil + processor:nil + progressImage:progressImage + progressDownload:progressDownload + completion:completion + inputUUID:UUID]; + return UUID; + } + + [self.concurrentOperationQueue scheduleOperation:^{ + __block NSInteger highestQualityDownloadedIdx = -1; + + //check for the highest quality image already in cache. It's possible that an image is in the process of being + //cached when this is being run. In which case two things could happen: + // - If network conditions dictate that a lower quality image should be downloaded than the one that is currently + // being cached, it will be downloaded in addition. This is not ideal behavior, worst case scenario and unlikely. + // - If network conditions dictate that the same quality image should be downloaded as the one being cached, no + // new image will be downloaded as either the caching will have finished by the time we actually request it or + // the task will still exist and our callback will be attached. In this case, no detrimental behavior will have + // occurred. + [urls enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(NSURL *url, NSUInteger idx, BOOL *stop) { + NSAssert([url isKindOfClass:[NSURL class]], @"url must be of type URL"); + NSString *cacheKey = [self cacheKeyForURL:url processorKey:nil]; + + //we don't actually need the object, just need to know it exists so that we can request it later + BOOL hasObject = [self.cache objectExistsForKey:cacheKey]; + + if (hasObject) { + highestQualityDownloadedIdx = idx; + *stop = YES; + } + }]; + + [self lock]; + float highQualityQPSThreshold = [self highQualityBPSThreshold]; + float lowQualityQPSThreshold = [self lowQualityBPSThreshold]; + BOOL shouldUpgradeLowQualityImages = [self shouldUpgradeLowQualityImages]; + [self unlock]; + + NSUInteger desiredImageURLIdx = [PINSpeedRecorder appropriateImageIdxForURLsGivenHistoricalNetworkConditions:urls + lowQualityQPSThreshold:lowQualityQPSThreshold + highQualityQPSThreshold:highQualityQPSThreshold]; + + NSUInteger downloadIdx; + //if the highest quality already downloaded is less than what currentBPS would dictate and shouldUpgrade is + //set, download the new higher quality image. If no image has been cached, download the image dictated by + //current bps + + if ((highestQualityDownloadedIdx < desiredImageURLIdx && shouldUpgradeLowQualityImages) || highestQualityDownloadedIdx == -1) { + downloadIdx = desiredImageURLIdx; + } else { + downloadIdx = highestQualityDownloadedIdx; + } + + NSURL *downloadURL = [urls objectAtIndex:downloadIdx]; + + [self downloadImageWithURL:downloadURL + options:options + priority:PINRemoteImageManagerPriorityDefault + processorKey:nil + processor:nil + progressImage:progressImage + progressDownload:progressDownload + completion:^(PINRemoteImageManagerResult *result) { + //clean out any lower quality images from the cache + for (NSInteger idx = downloadIdx - 1; idx >= 0; idx--) { + [[self cache] removeObjectForKey:[self cacheKeyForURL:[urls objectAtIndex:idx] processorKey:nil]]; + } + + if (completion) { + completion(result); + } + } + inputUUID:UUID]; + } withPriority:PINOperationQueuePriorityDefault]; + return UUID; +} + +#pragma mark - Caching + +- (BOOL)materializeAndCacheObject:(id)object + url:(NSURL *)url + key:(NSString *)key + options:(PINRemoteImageManagerDownloadOptions)options + outImage:(PINImage **)outImage + outAltRep:(id *)outAlternateRepresentation +{ + return [self materializeAndCacheObject:object cacheInDisk:nil additionalCost:0 url:url key:key options:options outImage:outImage outAltRep:outAlternateRepresentation]; +} + +- (BOOL)materializeAndCacheObject:(id)object + cacheInDisk:(NSData *)diskData + additionalCost:(NSUInteger)additionalCost + url:(NSURL *)url + key:(NSString *)key + options:(PINRemoteImageManagerDownloadOptions)options + outImage:(PINImage **)outImage + outAltRep:(id *)outAlternateRepresentation { + return [self materializeAndCacheObject:object cacheInDisk:diskData additionalCost:additionalCost maxAge:nil url:url key:key options:options outImage:outImage outAltRep:outAlternateRepresentation]; +} + +//takes the object from the cache and returns an image or animated image. +//if it's a non-alternative representation and skipDecode is not set it also decompresses the image. +- (BOOL)materializeAndCacheObject:(id)object + cacheInDisk:(NSData *)diskData + additionalCost:(NSUInteger)additionalCost + maxAge:(NSNumber *)maxAge + url:(NSURL *)url + key:(NSString *)key + options:(PINRemoteImageManagerDownloadOptions)options + outImage:(PINImage **)outImage + outAltRep:(id *)outAlternateRepresentation +{ + NSAssert(object != nil, @"Object should not be nil."); + if (object == nil) { + return NO; + } + BOOL alternateRepresentationsAllowed = (PINRemoteImageManagerDisallowAlternateRepresentations & options) == 0; + BOOL skipDecode = (options & PINRemoteImageManagerDownloadOptionsSkipDecode) != 0; + __block id alternateRepresentation = nil; + __block PINImage *image = nil; + __block NSData *data = nil; + __block BOOL updateMemoryCache = NO; + + PINRemoteImageMemoryContainer *container = nil; + if ([object isKindOfClass:[PINRemoteImageMemoryContainer class]]) { + container = (PINRemoteImageMemoryContainer *)object; + [container.lock lockWithBlock:^{ + data = container.data; + }]; + } else { + updateMemoryCache = YES; + + // don't need to lock the container here because we just init it. + container = [[PINRemoteImageMemoryContainer alloc] init]; + + if ([object isKindOfClass:[PINImage class]]) { + data = diskData; + container.image = (PINImage *)object; + } else if ([object isKindOfClass:[NSData class]]) { + data = (NSData *)object; + } else { + //invalid item in cache + updateMemoryCache = NO; + data = nil; + container = nil; + } + + container.data = data; + } + + if (alternateRepresentationsAllowed) { + alternateRepresentation = [_alternateRepProvider alternateRepresentationWithData:data options:options]; + } + + if (alternateRepresentation == nil) { + //we need the image + [container.lock lockWithBlock:^{ + image = container.image; + }]; + if (image == nil && container.data) { + image = [PINImage pin_decodedImageWithData:container.data skipDecodeIfPossible:skipDecode]; + + if (url != nil) { + image = [PINImage pin_scaledImageForImage:image withKey:key]; + } + + if (skipDecode == NO) { + [container.lock lockWithBlock:^{ + updateMemoryCache = YES; + container.image = image; + }]; + } + } + } + + // maxAge set to 0 means that images should not be stored at all. + BOOL doNotCache = (maxAge != nil && [maxAge integerValue] == 0); + + // There is no HTTP header that can be sent to indicate "infinite". However not setting a value at all, which in + // our case is represented by maxAge == nil, effectively means that. + BOOL cacheIndefinitely = (maxAge == nil); + + if (!doNotCache) { + if (updateMemoryCache) { + [container.lock lockWithBlock:^{ + NSUInteger cacheCost = additionalCost; + cacheCost += [container.data length]; + CGImageRef imageRef = container.image.CGImage; + NSAssert(container.image == nil || imageRef != NULL, @"We only cache a decompressed image if we decompressed it ourselves. In that case, it should be backed by a CGImageRef."); + if (imageRef) { + cacheCost += CGImageGetHeight(imageRef) * CGImageGetBytesPerRow(imageRef); + } + if (!self.memoryCacheTTLIsEnabled || cacheIndefinitely) { + [self.cache setObjectInMemory:container forKey:key withCost:cacheCost]; + } else { + [self.cache setObjectInMemory:container forKey:key withCost:cacheCost withAgeLimit:[maxAge integerValue]]; + } + }]; + } + + if (diskData) { + if (!self.diskCacheTTLIsEnabled || cacheIndefinitely) { + // with an unset (nil) maxAge, or a cache that is not _isTtlCache, behave as before (will use cache global behavior) + [self.cache setObjectOnDisk:diskData forKey:key]; + } else { + [self.cache setObjectOnDisk:diskData forKey:key withAgeLimit:[maxAge integerValue]]; + } + } + } + + if (outImage) { + *outImage = image; + } + + if (outAlternateRepresentation) { + *outAlternateRepresentation = alternateRepresentation; + } + + if (image == nil && alternateRepresentation == nil) { + PINLog(@"Invalid item in cache"); + [self.cache removeObjectForKey:key completion:nil]; + return NO; + } + return YES; +} + +- (NSString *)cacheKeyForURL:(NSURL *)url processorKey:(NSString *)processorKey +{ + return [self cacheKeyForURL:url processorKey:processorKey resume:NO]; +} + +- (NSString *)cacheKeyForURL:(NSURL *)url processorKey:(NSString *)processorKey resume:(BOOL)resume +{ + NSString *cacheKey = [url absoluteString]; + NSAssert((processorKey.length == 0 && resume == YES) || resume == NO, @"It doesn't make sense to use resume with processing."); + if (processorKey.length > 0) { + cacheKey = [cacheKey stringByAppendingFormat:@"-<%@>", processorKey]; + } + + //PINDiskCache uses this key as the filename of the file written to disk + //Due to the current filesystem used in Darwin, this name must be limited to 255 chars. + //In case the generated key exceeds PINRemoteImageManagerCacheKeyMaxLength characters, + //we return the hash of it instead. + if (cacheKey.length > PINRemoteImageManagerCacheKeyMaxLength) { + __block CC_MD5_CTX ctx; + CC_MD5_Init(&ctx); + NSData *data = [cacheKey dataUsingEncoding:NSUTF8StringEncoding]; + [data enumerateByteRangesUsingBlock:^(const void * _Nonnull bytes, NSRange byteRange, BOOL * _Nonnull stop) { + CC_MD5_Update(&ctx, bytes, (CC_LONG)byteRange.length); + }]; + + unsigned char digest[CC_MD5_DIGEST_LENGTH]; + CC_MD5_Final(digest, &ctx); + + NSMutableString *hexString = [NSMutableString stringWithCapacity:(CC_MD5_DIGEST_LENGTH * 2)]; + for (int i = 0; i < CC_MD5_DIGEST_LENGTH; i++) { + [hexString appendFormat:@"%02lx", (unsigned long)digest[i]]; + } + cacheKey = [hexString copy]; + } + //The resume key must not be hashed, it is used to decide whether or not to decode from the disk cache. + if (resume) { + cacheKey = [PINRemoteImageCacheKeyResumePrefix stringByAppendingString:cacheKey]; + } + + return cacheKey; +} + +- (void)objectForKey:(NSString *)key options:(PINRemoteImageManagerDownloadOptions)options completion:(void (^)(BOOL found, BOOL valid, PINImage *image, id alternativeRepresentation))completion +{ + return [self objectForURL:nil processorKey:nil key:key options:options completion:completion]; +} + +- (void)objectForURL:(NSURL *)url processorKey:(NSString *)processorKey key:(NSString *)key options:(PINRemoteImageManagerDownloadOptions)options completion:(void (^)(BOOL found, BOOL valid, PINImage *image, id alternativeRepresentation))completion +{ + if ((options & PINRemoteImageManagerDownloadOptionsIgnoreCache) != 0) { + completion(NO, YES, nil, nil); + return; + } + + if (key == nil && url == nil) { + completion(NO, YES, nil, nil); + return; + } + + key = key ?: [self cacheKeyForURL:url processorKey:processorKey]; + + void (^materialize)(id object) = ^(id object) { + PINImage *image = nil; + id alternativeRepresentation = nil; + BOOL valid = [self materializeAndCacheObject:object + url:nil + key:key + options:options + outImage:&image + outAltRep:&alternativeRepresentation]; + + completion(YES, valid, image, alternativeRepresentation); + }; + + PINRemoteImageMemoryContainer *container = [self.cache objectFromMemoryForKey:key]; + if (container) { + materialize(container); + } else { + [self.cache objectFromDiskForKey:key completion:^(id _Nonnull cache, + NSString *_Nonnull key, + id _Nullable object) { + if (object) { + materialize(object); + } else { + completion(NO, YES, nil, nil); + } + }]; + } +} + +#pragma mark - Resume support + +- (NSString *)resumeCacheKeyForURL:(NSURL *)url +{ + return [self cacheKeyForURL:url processorKey:nil resume:YES]; +} + +- (void)storeResumeData:(PINResume *)resume forURL:(NSURL *)URL +{ + NSString *resumeKey = [self resumeCacheKeyForURL:URL]; + [self.cache setObjectOnDisk:resume forKey:resumeKey]; +} + +#if DEBUG +- (NSUInteger)totalDownloads +{ + //hack to avoid main thread assertion since these are only used in testing + [_lock lock]; + NSUInteger totalDownloads = _totalDownloads; + [_lock unlock]; + return totalDownloads; +} +#endif + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageManagerConfiguration.h b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageManagerConfiguration.h new file mode 100644 index 0000000..b150903 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageManagerConfiguration.h @@ -0,0 +1,46 @@ +// +// PINRemoteImageManagerConfiguration.h +// Pods +// +// Created by Ryan Quan on 2/22/19. +// +// + +#import + +#import "PINRemoteImageMacros.h" + +#if PIN_TARGET_IOS +#import +#elif PIN_TARGET_MAC +#import +#endif + +/** A configuration object used to customize a PINRemoteImageManager instance **/ +@interface PINRemoteImageManagerConfiguration : NSObject + +/** The maximum number of concurrent operations. Defaults to NSOperationQueueDefaultMaxConcurrentOperationCount. */ +@property (nonatomic, readwrite, assign) NSUInteger maxConcurrentOperations; + +/** The maximum number of concurrent downloads. Defaults to 10, maximum 65535. */ +@property (nonatomic, readwrite, assign) NSUInteger maxConcurrentDownloads; + +/** The estimated remaining time threshold used to decide to skip progressive rendering. Defaults to 0.1. */ +@property (nonatomic, readwrite, assign) NSTimeInterval estimatedRemainingTimeThreshold; + +/** A bool value indicating whether PINRemoteImage should blur progressive render results */ +@property (nonatomic, readwrite, assign) BOOL shouldBlurProgressive; + +/** A CGSize which indicates the max size PINRemoteImage will render a progressive image. If an image is larger in either dimension, progressive rendering will be skipped */ +@property (nonatomic, readwrite, assign) CGSize maxProgressiveRenderSize; + +/** The minimum BPS to download the highest quality image in a set. */ +@property (nonatomic, readwrite, assign) float highQualityBPSThreshold; + +/** The maximum BPS to download the lowest quality image in a set. */ +@property (nonatomic, readwrite, assign) float lowQualityBPSThreshold; + +/** Whether high quality images should be downloaded when a low quality image is cached if network connectivity has improved. */ +@property (nonatomic, readwrite, assign) BOOL shouldUpgradeLowQualityImages; + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageManagerConfiguration.m b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageManagerConfiguration.m new file mode 100644 index 0000000..ecdad94 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageManagerConfiguration.m @@ -0,0 +1,36 @@ +// +// PINRemoteImageManagerConfiguration.m +// Pods +// +// Created by Ryan Quan on 2/22/19. +// +// + +#import "PINRemoteImageManagerConfiguration.h" + +#import "PINRemoteImageManager.h" + +@implementation PINRemoteImageManagerConfiguration + +- (nonnull instancetype)init { + if (self = [super init]) { + _maxConcurrentOperations = [[NSProcessInfo processInfo] activeProcessorCount] * 2; + _maxConcurrentDownloads = 10; + _estimatedRemainingTimeThreshold = 0.1; + _shouldBlurProgressive = YES; + _maxProgressiveRenderSize = CGSizeMake(1024, 1024); + _highQualityBPSThreshold = 500000; + _lowQualityBPSThreshold = 50000; // approximately edge speed + _shouldUpgradeLowQualityImages = NO; + } + return self; +} + +#pragma mark - Setters + +- (void)setMaxConcurrentDownloads:(NSUInteger)maxConcurrentDownloads { + NSAssert(maxConcurrentDownloads <= PINRemoteImageHTTPMaximumConnectionsPerHost, @"maxNumberOfConcurrentDownloads must be less than or equal to %d", PINRemoteImageHTTPMaximumConnectionsPerHost); + _maxConcurrentDownloads = maxConcurrentDownloads; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageManagerResult.m b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageManagerResult.m new file mode 100644 index 0000000..8076abd --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageManagerResult.m @@ -0,0 +1,118 @@ +// +// PINRemoteImageManagerResult.m +// Pods +// +// Created by Garrett Moon on 3/9/15. +// +// + +#import "PINRemoteImageManagerResult.h" + +@implementation PINRemoteImageManagerResult + ++ (nonnull instancetype)imageResultWithImage:(nullable PINImage *)image + alternativeRepresentation:(nullable id)alternativeRepresentation + requestLength:(NSTimeInterval)requestLength + resultType:(PINRemoteImageResultType)resultType + UUID:(nullable NSUUID *)uuid + response:(nullable NSURLResponse *)response + error:(nullable NSError *)error +{ + return [self imageResultWithImage:image + alternativeRepresentation:alternativeRepresentation + requestLength:requestLength + resultType:resultType + UUID:uuid + response:response + error:error + renderedImageQuality:1.0]; +} + ++ (nonnull instancetype)imageResultWithImage:(nullable PINImage *)image + alternativeRepresentation:(nullable id)alternativeRepresentation + requestLength:(NSTimeInterval)requestLength + resultType:(PINRemoteImageResultType)resultType + UUID:(nullable NSUUID *)uuid + response:(nullable NSURLResponse *)response + error:(nullable NSError *)error + bytesSavedByResuming:(NSUInteger)bytesSavedByResuming +{ + return [[self alloc] initWithImage:image + alternativeRepresentation:alternativeRepresentation + requestLength:requestLength + error:error + resultType:resultType + UUID:uuid + response:response + renderedImageQuality:1.0 + bytesSavedByResuming:bytesSavedByResuming]; +} + ++ (nonnull instancetype)imageResultWithImage:(nullable PINImage *)image + alternativeRepresentation:(nullable id)alternativeRepresentation + requestLength:(NSTimeInterval)requestLength + resultType:(PINRemoteImageResultType)resultType + UUID:(nullable NSUUID *)uuid + response:(nullable NSURLResponse *)response + error:(nullable NSError *)error + renderedImageQuality:(CGFloat)renderedImageQuality +{ + return [[self alloc] initWithImage:image + alternativeRepresentation:alternativeRepresentation + requestLength:requestLength + error:error + resultType:resultType + UUID:uuid + response:response + renderedImageQuality:renderedImageQuality + bytesSavedByResuming:0]; +} + +- (instancetype)initWithImage:(PINImage *)image + alternativeRepresentation:(id)alternativeRepresentation + requestLength:(NSTimeInterval)requestLength + error:(NSError *)error + resultType:(PINRemoteImageResultType)resultType + UUID:(NSUUID *)uuid + response:(NSURLResponse *)response + renderedImageQuality:(CGFloat)renderedImageQuality + bytesSavedByResuming:(NSUInteger)bytesSavedByResuming; +{ + if (self = [super init]) { + _image = image; + _alternativeRepresentation = alternativeRepresentation; + _requestDuration = requestLength; + _error = error; + _resultType = resultType; + _UUID = uuid; + _response = response; + _renderedImageQuality = renderedImageQuality; + _bytesSavedByResuming = bytesSavedByResuming; + } + return self; +} + +- (NSString *)description +{ + NSString *description = [super description]; + description = [description stringByAppendingString:[NSString stringWithFormat:@"image: %@", self.image]]; + description = [description stringByAppendingString:@"\n"]; + description = [description stringByAppendingString:[NSString stringWithFormat:@"alternativeRepresentation: %@", self.alternativeRepresentation]]; + description = [description stringByAppendingString:@"\n"]; + description = [description stringByAppendingString:[NSString stringWithFormat:@"requestDuration: %f", self.requestDuration]]; + description = [description stringByAppendingString:@"\n"]; + description = [description stringByAppendingString:[NSString stringWithFormat:@"error: %@", self.error]]; + description = [description stringByAppendingString:@"\n"]; + description = [description stringByAppendingString:[NSString stringWithFormat:@"resultType: %lu", (unsigned long)self.resultType]]; + description = [description stringByAppendingString:@"\n"]; + description = [description stringByAppendingString:[NSString stringWithFormat:@"UUID: %@", self.UUID]]; + description = [description stringByAppendingString:@"\n"]; + description = [description stringByAppendingString:[NSString stringWithFormat:@"response: %@", self.response]]; + description = [description stringByAppendingString:@"\n"]; + description = [description stringByAppendingString:[NSString stringWithFormat:@"renderedImageQuality: %f", self.renderedImageQuality]]; + description = [description stringByAppendingString:@"\n"]; + description = [description stringByAppendingString:[NSString stringWithFormat:@"bytesSavedByResuming: %lu", (unsigned long)self.bytesSavedByResuming]]; + return description; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageMemoryContainer.h b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageMemoryContainer.h new file mode 100644 index 0000000..2d1d62b --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageMemoryContainer.h @@ -0,0 +1,22 @@ +// +// PINRemoteImageMemoryContainer.h +// Pods +// +// Created by Garrett Moon on 3/17/16. +// +// + +#import + +#import "PINRemoteImageMacros.h" +#import "PINRemoteLock.h" + +@class PINImage; + +@interface PINRemoteImageMemoryContainer : NSObject + +@property (nonatomic, strong) PINImage *image; +@property (nonatomic, strong) NSData *data; +@property (nonatomic, strong) PINRemoteLock *lock; + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageMemoryContainer.m b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageMemoryContainer.m new file mode 100644 index 0000000..3da6b27 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageMemoryContainer.m @@ -0,0 +1,21 @@ +// +// PINRemoteImageMemoryContainer.m +// Pods +// +// Created by Garrett Moon on 3/17/16. +// +// + +#import "PINRemoteImageMemoryContainer.h" + +@implementation PINRemoteImageMemoryContainer + +- (instancetype)init +{ + if (self = [super init]) { + _lock = [[PINRemoteLock alloc] initWithName:@"PINRemoteImageMemoryContainer" lockType:PINRemoteLockTypeNonRecursive]; + } + return self; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageProcessorTask.h b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageProcessorTask.h new file mode 100644 index 0000000..a36fe3a --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageProcessorTask.h @@ -0,0 +1,15 @@ +// +// PINRemoteImageProcessorTask.h +// Pods +// +// Created by Garrett Moon on 3/9/15. +// +// + +#import "PINRemoteImageTask.h" + +@interface PINRemoteImageProcessorTask : PINRemoteImageTask + +@property (nonatomic, strong, nullable) NSUUID *downloadTaskUUID; + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageProcessorTask.m b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageProcessorTask.m new file mode 100644 index 0000000..2f95022 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageProcessorTask.m @@ -0,0 +1,44 @@ +// +// PINRemoteImageProcessorTask.m +// Pods +// +// Created by Garrett Moon on 3/9/15. +// +// + +#import "PINRemoteImageProcessorTask.h" + +@implementation PINRemoteImageProcessorTask + +@synthesize downloadTaskUUID = _downloadTaskUUID; + +- (BOOL)cancelWithUUID:(NSUUID *)UUID resume:(PINResume **)resume +{ + BOOL noMoreCompletions = [super cancelWithUUID:UUID resume:resume]; + [self.lock lockWithBlock:^{ + if (noMoreCompletions && self->_downloadTaskUUID) { + [self.manager cancelTaskWithUUID:self->_downloadTaskUUID]; + self->_downloadTaskUUID = nil; + } + }]; + return noMoreCompletions; +} + +- (void)setDownloadTaskUUID:(NSUUID *)downloadTaskUUID +{ + [self.lock lockWithBlock:^{ + NSAssert(self->_downloadTaskUUID == nil, @"downloadTaskUUID should be nil"); + self->_downloadTaskUUID = downloadTaskUUID; + }]; +} + +- (NSUUID *)downloadTaskUUID +{ + __block NSUUID *downloadTaskUUID; + [self.lock lockWithBlock:^{ + downloadTaskUUID = self->_downloadTaskUUID; + }]; + return downloadTaskUUID; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageTask.h b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageTask.h new file mode 100644 index 0000000..8026bfd --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageTask.h @@ -0,0 +1,65 @@ +// +// PINRemoteImageTask.h +// Pods +// +// Created by Garrett Moon on 3/9/15. +// +// + +#import + +#if SWIFT_PACKAGE +@import PINOperation; +#else +#import +#endif + +#import "PINRemoteImageCallbacks.h" +#import "PINRemoteImageManager.h" +#import "PINRemoteImageMacros.h" +#import "PINRemoteLock.h" +#import "PINResume.h" + +@interface PINRemoteImageTask : NSObject + +@property (nonatomic, strong, readonly, nonnull) PINRemoteLock *lock; + +@property (nonatomic, copy, readonly, nonnull) NSDictionary *callbackBlocks; + +@property (nonatomic, weak, nullable) PINRemoteImageManager *manager; + +@property (nonatomic, strong, nullable) id retryStrategy; + +@property (nonatomic, copy, nullable) NSString *key; + +- (_Nonnull instancetype)init NS_UNAVAILABLE; +- (_Nonnull instancetype)initWithManager:(nonnull PINRemoteImageManager *)manager NS_DESIGNATED_INITIALIZER; + +- (void)addCallbacksWithCompletionBlock:(nonnull PINRemoteImageManagerImageCompletion)completionBlock + progressImageBlock:(nullable PINRemoteImageManagerImageCompletion)progressImageBlock + progressDownloadBlock:(nullable PINRemoteImageManagerProgressDownload)progressDownloadBlock + withUUID:(nonnull NSUUID *)UUID; + +- (void)removeCallbackWithUUID:(nonnull NSUUID *)UUID; + +- (void)callCompletionsWithImage:(nullable PINImage *)image + alternativeRepresentation:(nullable id)alternativeRepresentation + cached:(BOOL)cached + response:(nullable NSURLResponse *)response + error:(nullable NSError *)error + remove:(BOOL)remove; + +//returns YES if no more attached completionBlocks +- (BOOL)cancelWithUUID:(nonnull NSUUID *)UUID resume:(PINResume * _Nullable * _Nullable)resume; + +- (void)setPriority:(PINRemoteImageManagerPriority)priority; + +- (nonnull PINRemoteImageManagerResult *)imageResultWithImage:(nullable PINImage *)image + alternativeRepresentation:(nullable id)alternativeRepresentation + requestLength:(NSTimeInterval)requestLength + resultType:(PINRemoteImageResultType)resultType + UUID:(nullable NSUUID *)uuid + response:(nullable NSURLResponse *)response + error:(nullable NSError *)error; + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageTask.m b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageTask.m new file mode 100644 index 0000000..043a628 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteImageTask.m @@ -0,0 +1,160 @@ +// +// PINRemoteImageTask.m +// Pods +// +// Created by Garrett Moon on 3/9/15. +// +// + +#import "PINRemoteImageTask.h" + +#import "PINRemoteImageCallbacks.h" +#import "PINRemoteImageManager+Private.h" + +@interface PINRemoteImageTask () +{ + NSMutableDictionary *_callbackBlocks; // We need to copy/retain `NSUUID`, because `PINRemoteImageManager` has a weak table `UUIDs` to store all UUIDs. +} + +@end + +@implementation PINRemoteImageTask + +@synthesize lock = _lock; + +- (instancetype)initWithManager:(PINRemoteImageManager *)manager +{ + if (self = [super init]) { + _lock = [[PINRemoteLock alloc] initWithName:@"Task Lock"]; + _manager = manager; + _callbackBlocks = [[NSMutableDictionary alloc] init]; + } + return self; +} + +- (NSString *)description +{ + return [NSString stringWithFormat:@"<%@: %p> completionBlocks: %lu", NSStringFromClass([self class]), self, (unsigned long)self.callbackBlocks.count]; +} + +- (void)addCallbacksWithCompletionBlock:(PINRemoteImageManagerImageCompletion)completionBlock + progressImageBlock:(PINRemoteImageManagerImageCompletion)progressImageBlock + progressDownloadBlock:(PINRemoteImageManagerProgressDownload)progressDownloadBlock + withUUID:(NSUUID *)UUID +{ + PINRemoteImageCallbacks *completion = [[PINRemoteImageCallbacks alloc] init]; + completion.completionBlock = completionBlock; + completion.progressImageBlock = progressImageBlock; + completion.progressDownloadBlock = progressDownloadBlock; + + [self.lock lockWithBlock:^{ + [self->_callbackBlocks setObject:completion forKey:UUID]; + }]; +} + +- (void)removeCallbackWithUUID:(NSUUID *)UUID +{ + [self.lock lockWithBlock:^{ + [self l_removeCallbackWithUUID:UUID]; + }]; +} + +- (void)l_removeCallbackWithUUID:(NSUUID *)UUID +{ + [_callbackBlocks removeObjectForKey:UUID]; +} + +- (NSDictionary *)callbackBlocks +{ + __block NSDictionary *callbackBlocks; + [self.lock lockWithBlock:^{ + callbackBlocks = [self->_callbackBlocks copy]; + }]; + return callbackBlocks; +} + +- (void)callCompletionsWithImage:(PINImage *)image + alternativeRepresentation:(id)alternativeRepresentation + cached:(BOOL)cached + response:(NSURLResponse *)response + error:(NSError *)error + remove:(BOOL)remove; +{ + __weak typeof(self) weakSelf = self; + [self.callbackBlocks enumerateKeysAndObjectsUsingBlock:^(NSUUID *UUID, PINRemoteImageCallbacks *callback, BOOL *stop) { + typeof(self) strongSelf = weakSelf; + PINRemoteImageManagerImageCompletion completionBlock = callback.completionBlock; + if (completionBlock != nil) { + PINLog(@"calling completion for UUID: %@ key: %@", UUID, strongSelf.key); + CFTimeInterval requestTime = callback.requestTime; + + dispatch_async(self.manager.callbackQueue, ^ + { + PINRemoteImageResultType result; + if (image || alternativeRepresentation) { + result = cached ? PINRemoteImageResultTypeCache : PINRemoteImageResultTypeDownload; + } else { + result = PINRemoteImageResultTypeNone; + } + completionBlock([self imageResultWithImage:image + alternativeRepresentation:alternativeRepresentation + requestLength:CACurrentMediaTime() - requestTime + resultType:result + UUID:UUID + response:response + error:error]); + }); + } + if (remove) { + [strongSelf removeCallbackWithUUID:UUID]; + } + }]; +} + +- (BOOL)cancelWithUUID:(NSUUID *)UUID resume:(PINResume **)resume +{ + __block BOOL noMoreCompletions; + [self.lock lockWithBlock:^{ + noMoreCompletions = [self l_cancelWithUUID:UUID]; + }]; + return noMoreCompletions; +} + +- (BOOL)l_cancelWithUUID:(NSUUID *)UUID +{ + BOOL noMoreCompletions = NO; + [self l_removeCallbackWithUUID:UUID]; + if ([_callbackBlocks count] == 0) { + noMoreCompletions = YES; + } + return noMoreCompletions; +} + +- (void)setPriority:(PINRemoteImageManagerPriority)priority +{ + +} + +- (nonnull PINRemoteImageManagerResult *)imageResultWithImage:(nullable PINImage *)image + alternativeRepresentation:(nullable id)alternativeRepresentation + requestLength:(NSTimeInterval)requestLength + resultType:(PINRemoteImageResultType)resultType + UUID:(nullable NSUUID *)UUID + response:(NSURLResponse *)response + error:(nullable NSError *)error +{ + return [PINRemoteImageManagerResult imageResultWithImage:image + alternativeRepresentation:alternativeRepresentation + requestLength:requestLength + resultType:resultType + UUID:UUID + response:response + error:error]; +} + +- (NSMutableDictionary *)l_callbackBlocks +{ + return _callbackBlocks; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteLock.h b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteLock.h new file mode 100644 index 0000000..1ca8488 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteLock.h @@ -0,0 +1,28 @@ +// +// PINRemoteLock.h +// Pods +// +// Created by Garrett Moon on 3/17/16. +// +// + +#import + +/** The type of lock, either recursive or non-recursive */ +typedef NS_ENUM(NSUInteger, PINRemoteLockType) { + /** A non-recursive version of the lock. The default. */ + PINRemoteLockTypeNonRecursive = 0, + /** A recursive version of the lock. More expensive. */ + PINRemoteLockTypeRecursive, +}; + +@interface PINRemoteLock : NSObject + +- (instancetype)initWithName:(NSString *)lockName lockType:(PINRemoteLockType)lockType NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithName:(NSString *)lockName; +- (void)lockWithBlock:(dispatch_block_t)block; + +- (void)lock; +- (void)unlock; + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteLock.m b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteLock.m new file mode 100644 index 0000000..6cf74d6 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteLock.m @@ -0,0 +1,112 @@ +// +// PINRemoteLock.m +// Pods +// +// Created by Garrett Moon on 3/17/16. +// +// + +#import "PINRemoteLock.h" + +#import + +#if !defined(PINREMOTELOCK_DEBUG) && defined(DEBUG) +#define PINREMOTELOCK_DEBUG DEBUG +#endif + +@interface PINRemoteLock () +{ +#if PINREMOTELOCK_DEBUG + NSLock *_lock; + NSRecursiveLock *_recursiveLock; +#else + pthread_mutex_t _lock; +#endif +} + +@end + +@implementation PINRemoteLock + +- (instancetype)init +{ + return [self initWithName:nil]; +} + +- (instancetype)initWithName:(NSString *)lockName lockType:(PINRemoteLockType)lockType +{ + if (self = [super init]) { +#if PINREMOTELOCK_DEBUG + if (lockType == PINRemoteLockTypeNonRecursive) { + _lock = [[NSLock alloc] init]; + } else { + _recursiveLock = [[NSRecursiveLock alloc] init]; + } + + if (lockName) { + [_lock setName:lockName]; + [_recursiveLock setName:lockName]; + } +#else + pthread_mutexattr_t attr; + + pthread_mutexattr_init(&attr); + if (lockType == PINRemoteLockTypeRecursive) { + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + } + pthread_mutex_init(&_lock, &attr); +#endif + } + return self; +} + +- (instancetype)initWithName:(NSString *)lockName +{ + return [self initWithName:lockName lockType:PINRemoteLockTypeNonRecursive]; +} + +#if ! PINREMOTELOCK_DEBUG +- (void)dealloc +{ + pthread_mutex_destroy(&_lock); +} +#endif + +- (void)lockWithBlock:(dispatch_block_t)block +{ +#if PINREMOTELOCK_DEBUG + [_lock lock]; + [_recursiveLock lock]; +#else + pthread_mutex_lock(&_lock); +#endif + block(); +#if PINREMOTELOCK_DEBUG + [_lock unlock]; + [_recursiveLock unlock]; +#else + pthread_mutex_unlock(&_lock); +#endif +} + +- (void)lock +{ +#if PINREMOTELOCK_DEBUG + [_lock lock]; + [_recursiveLock lock]; +#else + pthread_mutex_lock(&_lock); +#endif +} + +- (void)unlock +{ +#if PINREMOTELOCK_DEBUG + [_lock unlock]; + [_recursiveLock unlock]; +#else + pthread_mutex_unlock(&_lock); +#endif +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteWeakProxy.h b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteWeakProxy.h new file mode 100644 index 0000000..b0abc22 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteWeakProxy.h @@ -0,0 +1,16 @@ +// +// PINRemoteWeakProxy.h +// PINRemoteImage +// +// Created by Garrett Moon on 4/24/18. +// Copyright © 2018 Pinterest. All rights reserved. +// + +#import + +@interface PINRemoteWeakProxy : NSProxy + ++ (PINRemoteWeakProxy *)weakProxyWithTarget:(id)target; +- (instancetype)initWithTarget:(id)target; + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteWeakProxy.m b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteWeakProxy.m new file mode 100644 index 0000000..a704932 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRemoteWeakProxy.m @@ -0,0 +1,59 @@ +// +// PINRemoteWeakProxy.m +// PINRemoteImage +// +// Created by Garrett Moon on 4/24/18. +// Copyright © 2018 Pinterest. All rights reserved. +// + +#import "PINRemoteWeakProxy.h" + +@interface PINRemoteWeakProxy () +{ + __weak id _target; + Class _targetClass; +} +@end + +@implementation PINRemoteWeakProxy + ++ (PINRemoteWeakProxy *)weakProxyWithTarget:(id)target +{ + return [[PINRemoteWeakProxy alloc] initWithTarget:target]; +} + +- (instancetype)initWithTarget:(id)target +{ + if (self) { + _target = target; + _targetClass = [target class]; + } + return self; +} + +- (BOOL)respondsToSelector:(SEL)aSelector +{ + return [_target respondsToSelector:aSelector]; +} + +- (BOOL)conformsToProtocol:(Protocol *)aProtocol +{ + return [_target conformsToProtocol:aProtocol]; +} + +- (id)forwardingTargetForSelector:(SEL)aSelector +{ + return _target; +} + +- (void)forwardInvocation:(NSInvocation *)invocation +{ + // Drop it since we shouldn't get here if _target is nil +} + +- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel +{ + return _target ? [_target methodSignatureForSelector:sel] : [_targetClass instanceMethodSignatureForSelector:sel]; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRequestRetryStrategy.m b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRequestRetryStrategy.m new file mode 100644 index 0000000..92d6ecf --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINRequestRetryStrategy.m @@ -0,0 +1,69 @@ +// +// PINRequestRetryStrategy.m +// Pods +// +// Created by Hovhannes Safaryan on 9/24/16. +// +// + +#import "PINRequestRetryStrategy.h" +#import "PINURLSessionManager.h" +#import "PINRemoteImageManager.h" + +@interface PINRequestExponentialRetryStrategy () + +@property (nonatomic, assign) int retryMaxCount; +@property (nonatomic, assign) int retryCount; +@property (nonatomic, assign) int delayBase; + +@end + +@implementation PINRequestExponentialRetryStrategy + +- (instancetype)initWithRetryMaxCount:(int)retryMaxCount delayBase:(int)delayBase +{ + if (self = [super init]) { + _retryCount = 0; + _retryMaxCount = retryMaxCount; + _delayBase = delayBase; + } + return self; +} + +- (int)numberOfRetries +{ + return self.retryCount; +} + +- (BOOL)shouldRetryWithError:(NSError *)error +{ + if (error == nil || ![[self class] retriableError:error] || + self.retryCount >= self.retryMaxCount) { + return NO; + } + return YES; +} + +- (int)nextDelay +{ + return powf(self.delayBase, self.retryCount); +} + +- (void)incrementRetryCount +{ + self.retryCount++; +} + ++ (BOOL)retriableError:(NSError *)remoteImageError +{ + if ([remoteImageError.domain isEqualToString:PINURLErrorDomain]) { + return remoteImageError.code >= 500; + } else if ([remoteImageError.domain isEqualToString:NSURLErrorDomain] && remoteImageError.code == NSURLErrorUnsupportedURL) { + return NO; + } else if ([remoteImageError.domain isEqualToString:PINRemoteImageManagerErrorDomain]) { + return NO; + } + return YES; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINResume.h b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINResume.h new file mode 100644 index 0000000..7ff2b53 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINResume.h @@ -0,0 +1,24 @@ +// +// PINResume.h +// PINRemoteImage +// +// Created by Garrett Moon on 3/10/17. +// Copyright © 2017 Pinterest. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface PINResume : NSObject + +- (id)init NS_UNAVAILABLE; ++ (PINResume *)resumeData:(NSData *)resumeData ifRange:(NSString *)ifRange totalBytes:(long long)totalBytes; + +@property (nonatomic, strong, readonly) NSData *resumeData; +@property (nonatomic, copy, readonly) NSString *ifRange; +@property (nonatomic, assign, readonly) long long totalBytes; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINResume.m b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINResume.m new file mode 100644 index 0000000..cfcbd16 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINResume.m @@ -0,0 +1,51 @@ +// +// PINResume.m +// PINRemoteImage +// +// Created by Garrett Moon on 3/10/17. +// Copyright © 2017 Pinterest. All rights reserved. +// + +#import "PINResume.h" + +@implementation PINResume + +NSString * const kResumeDataKey = @"kResumeDataKey"; +NSString * const kIfRangeKey = @"kIfRangeKey"; +NSString * const kTotalBytesKey = @"kTotalBytesKey"; + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + if (self = [super init]) { + _resumeData = [aDecoder decodeObjectForKey:kResumeDataKey]; + _ifRange = [aDecoder decodeObjectForKey:kIfRangeKey]; + _totalBytes = [aDecoder decodeInt64ForKey:kTotalBytesKey]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeObject:_resumeData forKey:kResumeDataKey]; + [aCoder encodeObject:_ifRange forKey:kIfRangeKey]; + [aCoder encodeInt64:_totalBytes forKey:kTotalBytesKey]; +} + ++ (PINResume *)resumeData:(NSData *)resumeData ifRange:(NSString *)ifRange totalBytes:(long long)totalBytes +{ + PINResume *resume = [[PINResume alloc] initWithResumeData:resumeData ifRange:ifRange totalBytes:totalBytes]; + return resume; +} + +- (PINResume *)initWithResumeData:(NSData *)resumeData ifRange:(NSString *)ifRange totalBytes:(long long)totalBytes +{ + if (self = [super init]) { + NSAssert(resumeData.length > 0 && ifRange.length > 0 && totalBytes > 0, @"PINResume must have all fields non-nil and non-zero length."); + _resumeData = resumeData; + _ifRange = ifRange; + _totalBytes = totalBytes; + } + return self; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINSpeedRecorder.h b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINSpeedRecorder.h new file mode 100644 index 0000000..bee5813 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINSpeedRecorder.h @@ -0,0 +1,51 @@ +// +// PINSpeedRecorder.h +// PINRemoteImage +// +// Created by Garrett Moon on 8/30/17. +// Copyright © 2017 Pinterest. All rights reserved. +// + +#import + +typedef enum : NSUInteger { + PINSpeedRecorderConnectionStatusNotReachable, + PINSpeedRecorderConnectionStatusWWAN, + PINSpeedRecorderConnectionStatusWiFi +} PINSpeedRecorderConnectionStatus; + +@interface PINSpeedRecorder : NSObject + ++ (PINSpeedRecorder *)sharedRecorder; ++ (NSUInteger)appropriateImageIdxForURLsGivenHistoricalNetworkConditions:(NSArray *)urls + lowQualityQPSThreshold:(float)lowQualityQPSThreshold + highQualityQPSThreshold:(float)highQualityQPSThreshold; + +- (void)processMetrics:(NSURLSessionTaskMetrics *)metrics forTask:(NSURLSessionTask *)task API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0)); + +/* + Returns a weighted average of the bytes per second of a transfer with the time to first byte subtracted. + More specifically, we get the time to first byte for every task that completes, + subtract it from the total transfer time, calculate bytes per second + and add it to an existing average using exponential weighted average and adjusting + for starting bias. + This is all done on a per host basis. + */ +- (float)weightedAdjustedBytesPerSecondForHost:(NSString *)host; + +/* + Returns a weighted average of time to first byte for the specified host. + More specifically, we get the time to first byte for every task that completes + and add it to an existing average using exponential weighted average and adjusting + for starting bias. + This is all done on a per host basis. + */ +- (NSTimeInterval)weightedTimeToFirstByteForHost:(NSString *)host; + +- (PINSpeedRecorderConnectionStatus)connectionStatus; + +#if DEBUG +- (void)setCurrentBytesPerSecond:(float)currentBPS; +#endif + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINSpeedRecorder.m b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINSpeedRecorder.m new file mode 100644 index 0000000..8e02fb2 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINSpeedRecorder.m @@ -0,0 +1,265 @@ +// +// PINSpeedRecorder.m +// PINRemoteImage +// +// Created by Garrett Moon on 8/30/17. +// Copyright © 2017 Pinterest. All rights reserved. +// + +#import "PINSpeedRecorder.h" + +#import +#import + +#import "PINRemoteLock.h" + +@interface PINSpeedMeasurement : NSObject + +// Storing the count of each measurement allows for bias adjustment in exponentially +// weighted average. +@property (nonatomic, assign) NSUInteger count; +@property (nonatomic, assign) float bytesPerSecond; +@property (nonatomic, assign) float startAdjustedBytesPerSecond; +@property (nonatomic, assign) CFTimeInterval timeToFirstByte; + +@end + +@interface PINSpeedRecorder () +{ + NSCache *_speedMeasurements; + SCNetworkReachabilityRef _reachability; +#if DEBUG + BOOL _overrideBPS; + float _currentBPS; +#endif +} + +@property (nonatomic, strong) PINRemoteLock *lock; + +@end + +@implementation PINSpeedRecorder + ++ (PINSpeedRecorder *)sharedRecorder +{ + static dispatch_once_t onceToken; + static PINSpeedRecorder *sharedRecorder; + dispatch_once(&onceToken, ^{ + sharedRecorder = [[self alloc] init]; + }); + + return sharedRecorder; +} + +- (instancetype)init +{ + if (self = [super init]) { + _lock = [[PINRemoteLock alloc] initWithName:@"PINSpeedRecorder lock"]; + _speedMeasurements = [[NSCache alloc] init]; + _speedMeasurements.countLimit = 25; + + struct sockaddr_in zeroAddress; + bzero(&zeroAddress, sizeof(zeroAddress)); + zeroAddress.sin_len = sizeof(zeroAddress); + zeroAddress.sin_family = AF_INET; + _reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)&zeroAddress); + } + return self; +} + +- (void)processMetrics:(NSURLSessionTaskMetrics *)metrics forTask:(NSURLSessionTask *)task +{ + NSDate *requestStart = [NSDate distantFuture]; + NSDate *firstByte = [NSDate distantFuture]; + NSDate *requestEnd = [NSDate distantPast]; + int64_t contentLength = task.countOfBytesReceived; + + for (NSURLSessionTaskTransactionMetrics *metric in metrics.transactionMetrics) { + if (metric.requestStartDate == nil || metric.responseStartDate == nil) { + //Only evaluate requests which completed their first byte. + return; + } + + requestStart = [requestStart earlierDate:metric.requestStartDate]; + firstByte = [firstByte earlierDate:metric.responseStartDate]; + requestEnd = [requestEnd laterDate:metric.responseEndDate]; + } + + if ([requestStart isEqual:[NSDate distantFuture]] || [firstByte isEqual:[NSDate distantFuture]] || [requestEnd isEqual:[NSDate distantPast]] || contentLength == 0) { + return; + } + + [self updateSpeedsForHost:task.currentRequest.URL.host + bytesPerSecond:contentLength / [requestEnd timeIntervalSinceDate:requestStart] + startAdjustedBytesPerSecond:contentLength / [requestEnd timeIntervalSinceDate:firstByte] + timeToFirstByte:[firstByte timeIntervalSinceDate:requestStart]]; +} + +- (void)resetMeasurements +{ + [self.lock lockWithBlock:^{ + [self->_speedMeasurements removeAllObjects]; + }]; +} + +- (void)updateSpeedsForHost:(NSString *)host bytesPerSecond:(float)bytesPerSecond startAdjustedBytesPerSecond:(float)startAdjustedBytesPerSecond timeToFirstByte:(float)timeToFirstByte +{ + [self.lock lockWithBlock:^{ + PINSpeedMeasurement *measurement = [self->_speedMeasurements objectForKey:host]; + if (measurement == nil) { + measurement = [[PINSpeedMeasurement alloc] init]; + measurement.count = 0; + measurement.bytesPerSecond = bytesPerSecond; + measurement.startAdjustedBytesPerSecond = startAdjustedBytesPerSecond; + measurement.timeToFirstByte = timeToFirstByte; + [self->_speedMeasurements setObject:measurement forKey:host]; + } else { + const double bpsBeta = 0.8; + const double ttfbBeta = 0.8; + measurement.count++; + measurement.bytesPerSecond = (measurement.bytesPerSecond * bpsBeta) + ((1.0 - bpsBeta) * bytesPerSecond); + measurement.startAdjustedBytesPerSecond = (measurement.startAdjustedBytesPerSecond * bpsBeta) + ((1.0 - bpsBeta) * startAdjustedBytesPerSecond); + measurement.timeToFirstByte = (measurement.timeToFirstByte * ttfbBeta) + ((1.0 - ttfbBeta) * timeToFirstByte); + } + }]; +} + +- (float)weightedAdjustedBytesPerSecondForHost:(NSString *)host +{ + __block float startAdjustedBytesPerSecond = -1; + [self.lock lockWithBlock:^{ +#if DEBUG + if (self->_overrideBPS) { + startAdjustedBytesPerSecond = self->_currentBPS; + return; + } +#endif + PINSpeedMeasurement *measurement = [self->_speedMeasurements objectForKey:host]; + if (measurement == 0) { + startAdjustedBytesPerSecond = -1; + return; + } + startAdjustedBytesPerSecond = measurement.startAdjustedBytesPerSecond; + }]; + return startAdjustedBytesPerSecond; +} + +- (NSTimeInterval)weightedTimeToFirstByteForHost:(NSString *)host +{ + __block NSTimeInterval timeToFirstByte = 0; + [self.lock lockWithBlock:^{ + PINSpeedMeasurement *measurement = [self->_speedMeasurements objectForKey:host]; + timeToFirstByte = measurement.timeToFirstByte; + }]; + return timeToFirstByte; +} + +#if DEBUG +- (void)setCurrentBytesPerSecond:(float)currentBPS +{ + [self.lock lockWithBlock:^{ + if (currentBPS == -1) { + self->_overrideBPS = NO; + } else { + self->_overrideBPS = YES; + } + self->_currentBPS = currentBPS; + }]; +} +#endif + +// Cribbed from Apple's reachability: https://developer.apple.com/library/content/samplecode/Reachability/Listings/Reachability_Reachability_m.html#//apple_ref/doc/uid/DTS40007324-Reachability_Reachability_m-DontLinkElementID_9 + +- (PINSpeedRecorderConnectionStatus)connectionStatus +{ + PINSpeedRecorderConnectionStatus status = PINSpeedRecorderConnectionStatusNotReachable; + SCNetworkReachabilityFlags flags; + + // _reachability is set on init and therefore safe to access outside the lock + if (SCNetworkReachabilityGetFlags(_reachability, &flags)) { + return [self networkStatusForFlags:flags]; + } + return status; +} + +- (PINSpeedRecorderConnectionStatus)networkStatusForFlags:(SCNetworkReachabilityFlags)flags +{ + if ((flags & kSCNetworkReachabilityFlagsReachable) == 0) { + // The target host is not reachable. + return PINSpeedRecorderConnectionStatusNotReachable; + } + + PINSpeedRecorderConnectionStatus connectionStatus = PINSpeedRecorderConnectionStatusNotReachable; + + if ((flags & kSCNetworkReachabilityFlagsConnectionRequired) == 0) { + /* + If the target host is reachable and no connection is required then we'll assume (for now) that you're on Wi-Fi... + */ + connectionStatus = PINSpeedRecorderConnectionStatusWiFi; + } + + if ((((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) || (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0)) { + /* + ... and the connection is on-demand (or on-traffic) if the calling application is using the CFSocketStream or higher APIs... + */ + + if ((flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0) { + /* + ... and no [user] intervention is needed... + */ + connectionStatus = PINSpeedRecorderConnectionStatusWiFi; + } + } + +#if PIN_TARGET_IOS + if ((flags & kSCNetworkReachabilityFlagsIsWWAN) == kSCNetworkReachabilityFlagsIsWWAN) { + /* + ... but WWAN connections are OK if the calling application is using the CFNetwork APIs. + */ + connectionStatus = PINSpeedRecorderConnectionStatusWWAN; + } +#endif + + return connectionStatus; +} + ++ (NSUInteger)appropriateImageIdxForURLsGivenHistoricalNetworkConditions:(NSArray *)urls + lowQualityQPSThreshold:(float)lowQualityQPSThreshold + highQualityQPSThreshold:(float)highQualityQPSThreshold +{ + float currentBytesPerSecond = [[PINSpeedRecorder sharedRecorder] weightedAdjustedBytesPerSecondForHost:[[urls firstObject] host]]; + + NSUInteger desiredImageURLIdx; + + if (currentBytesPerSecond == -1) { + // Base it on reachability + switch ([[PINSpeedRecorder sharedRecorder] connectionStatus]) { + case PINSpeedRecorderConnectionStatusWiFi: + desiredImageURLIdx = urls.count - 1; + break; + + case PINSpeedRecorderConnectionStatusWWAN: + case PINSpeedRecorderConnectionStatusNotReachable: + desiredImageURLIdx = 0; + break; + } + } else { + if (currentBytesPerSecond >= highQualityQPSThreshold) { + desiredImageURLIdx = urls.count - 1; + } else if (currentBytesPerSecond <= lowQualityQPSThreshold) { + desiredImageURLIdx = 0; + } else if (urls.count == 2) { + desiredImageURLIdx = roundf((currentBytesPerSecond - lowQualityQPSThreshold) / ((highQualityQPSThreshold - lowQualityQPSThreshold) / (float)(urls.count - 1))); + } else { + desiredImageURLIdx = ceilf((currentBytesPerSecond - lowQualityQPSThreshold) / ((highQualityQPSThreshold - lowQualityQPSThreshold) / (float)(urls.count - 2))); + } + } + + return desiredImageURLIdx; +} + +@end + +@implementation PINSpeedMeasurement + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINURLSessionManager.m b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINURLSessionManager.m new file mode 100644 index 0000000..e83601c --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/PINURLSessionManager.m @@ -0,0 +1,263 @@ +// +// PINURLSessionManager.m +// Pods +// +// Created by Garrett Moon on 6/26/15. +// +// + +#import "PINURLSessionManager.h" + +#import "PINSpeedRecorder.h" + +NSErrorDomain const PINURLErrorDomain = @"PINURLErrorDomain"; + +@interface PINURLSessionManager () + +@property (nonatomic, strong) NSLock *sessionManagerLock; +@property (nonatomic, strong) NSURLSession *session; +@property (nonatomic, strong) NSOperationQueue *operationQueue; +@property (nonatomic, strong) NSMutableDictionary *delegateQueues; +@property (nonatomic, strong) NSMutableDictionary *completions; + +@end + +@implementation PINURLSessionManager + +- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration +{ + if (self = [super init]) { + self.sessionManagerLock = [[NSLock alloc] init]; + self.sessionManagerLock.name = @"PINURLSessionManager"; + self.operationQueue = [[NSOperationQueue alloc] init]; + self.operationQueue.name = @"PINURLSessionManager Operation Queue"; + + //queue must be serial to ensure proper ordering + [self.operationQueue setMaxConcurrentOperationCount:1]; + self.session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:self.operationQueue]; + self.completions = [[NSMutableDictionary alloc] init]; + self.delegateQueues = [[NSMutableDictionary alloc] init]; + } + return self; +} + +- (void)invalidateSessionAndCancelTasks +{ + [self lock]; + [self.session invalidateAndCancel]; + [self unlock]; +} + +- (nonnull NSURLSessionDataTask *)dataTaskWithRequest:(nonnull NSURLRequest *)request + completionHandler:(nonnull PINURLSessionDataTaskCompletion)completionHandler +{ + return [self dataTaskWithRequest:request + priority:PINRemoteImageManagerPriorityDefault + completionHandler:completionHandler]; +} + +- (nonnull NSURLSessionDataTask *)dataTaskWithRequest:(nonnull NSURLRequest *)request + priority:(PINRemoteImageManagerPriority)priority + completionHandler:(nonnull PINURLSessionDataTaskCompletion)completionHandler +{ + [self lock]; + NSURLSessionDataTask *dataTask = [self.session dataTaskWithRequest:request]; + if (@available(iOS 8.0, macOS 10.10, tvOS 9.0, watchOS 2.0, *)) { + dataTask.priority = dataTaskPriorityWithImageManagerPriority(priority); + } + if (completionHandler) { + [self.completions setObject:completionHandler forKey:@(dataTask.taskIdentifier)]; + } + NSString *queueName = [NSString stringWithFormat:@"PINURLSessionManager delegate queue - %ld", (unsigned long)dataTask.taskIdentifier]; + dispatch_queue_t delegateQueue = dispatch_queue_create([queueName UTF8String], DISPATCH_QUEUE_SERIAL); + [self.delegateQueues setObject:delegateQueue forKey:@(dataTask.taskIdentifier)]; + [self unlock]; + return dataTask; +} + +- (void)lock +{ + [self.sessionManagerLock lock]; +} + +- (void)unlock +{ + [self.sessionManagerLock unlock]; +} + +#pragma mark NSURLSessionDataDelegate + +- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)task didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler +{ + [self lock]; + dispatch_queue_t delegateQueue = self.delegateQueues[@(task.taskIdentifier)]; + [self unlock]; + + NSAssert(delegateQueue != nil, @"There seems to be an issue in iOS 9 where this can be nil. If you can reliably reproduce hitting this, *please* open an issue: https://github.com/pinterest/PINRemoteImage/issues"); + if (delegateQueue == nil) { + return; + } + + __weak typeof(self) weakSelf = self; + dispatch_async(delegateQueue, ^{ + typeof(self) strongSelf = weakSelf; + if ([strongSelf.delegate respondsToSelector:@selector(didReceiveResponse:forTask:)]) { + [strongSelf.delegate didReceiveResponse:response forTask:task]; + } + }); + //Even though this is documented to be non-nil, in the wild it sometimes is. + if (completionHandler) { + completionHandler(NSURLSessionResponseAllow); + } +} + +- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler +{ + if ([self.delegate respondsToSelector:@selector(didReceiveAuthenticationChallenge:forTask:completionHandler:)]) { + [self.delegate didReceiveAuthenticationChallenge:challenge forTask:nil completionHandler:completionHandler]; + } else { + //Even though this is documented to be non-nil, in the wild it sometimes is. + if (completionHandler) { + completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil); + } + } +} + +- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler +{ + [self lock]; + dispatch_queue_t delegateQueue = self.delegateQueues[@(task.taskIdentifier)]; + [self unlock]; + + NSAssert(delegateQueue != nil, @"There seems to be an issue in iOS 9 where this can be nil. If you can reliably reproduce hitting this, *please* open an issue: https://github.com/pinterest/PINRemoteImage/issues"); + if (delegateQueue == nil) { + return; + } + + __weak typeof(self) weakSelf = self; + dispatch_async(delegateQueue, ^{ + typeof(self) strongSelf = weakSelf; + if ([strongSelf.delegate respondsToSelector:@selector(didReceiveAuthenticationChallenge:forTask:completionHandler:)]) { + [strongSelf.delegate didReceiveAuthenticationChallenge:challenge forTask:task completionHandler:completionHandler]; + } else { + //Even though this is documented to be non-nil, in the wild it sometimes is + if (completionHandler) { + completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil); + } + } + }); +} + +- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)task didReceiveData:(NSData *)data +{ + [self lock]; + dispatch_queue_t delegateQueue = self.delegateQueues[@(task.taskIdentifier)]; + [self unlock]; + + NSAssert(delegateQueue != nil, @"There seems to be an issue in iOS 9 where this can be nil. If you can reliably reproduce hitting this, *please* open an issue: https://github.com/pinterest/PINRemoteImage/issues"); + if (delegateQueue == nil) { + return; + } + + __weak typeof(self) weakSelf = self; + dispatch_async(delegateQueue, ^{ + typeof(self) strongSelf = weakSelf; + [strongSelf.delegate didReceiveData:data forTask:task]; + }); +} + +- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error +{ + [self lock]; + dispatch_queue_t delegateQueue = self.delegateQueues[@(task.taskIdentifier)]; + [self unlock]; + + NSAssert(delegateQueue != nil, @"There seems to be an issue in iOS 9 where this can be nil. If you can reliably reproduce hitting this, *please* open an issue: https://github.com/pinterest/PINRemoteImage/issues"); + if (delegateQueue == nil) { + return; + } + + if (!error && [task.response isKindOfClass:[NSHTTPURLResponse class]]) { + NSHTTPURLResponse *response = (NSHTTPURLResponse *)task.response; + NSInteger statusCode = [response statusCode]; + //If a 404 response contains an image, we treat it as a successful request and return the image + BOOL recoverable = [self responseRecoverableFrom404:response]; + if (statusCode >= 400 && recoverable == NO) { + error = [NSError errorWithDomain:PINURLErrorDomain + code:statusCode + userInfo:@{NSLocalizedDescriptionKey : @"HTTP Error Response."}]; + } + } + __weak typeof(self) weakSelf = self; + dispatch_async(delegateQueue, ^{ + typeof(self) strongSelf = weakSelf; + + [strongSelf lock]; + PINURLSessionDataTaskCompletion completionHandler = strongSelf.completions[@(task.taskIdentifier)]; + [strongSelf.completions removeObjectForKey:@(task.taskIdentifier)]; + [strongSelf.delegateQueues removeObjectForKey:@(task.taskIdentifier)]; + [strongSelf unlock]; + + if (completionHandler) { + completionHandler(task, error); + } + + if ([strongSelf.delegate respondsToSelector:@selector(didCompleteTask:withError:)]) { + [strongSelf.delegate didCompleteTask:task withError:error]; + } + }); +} + +#pragma mark - session statistics + +- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didFinishCollectingMetrics:(NSURLSessionTaskMetrics *)metrics +{ + if (@available(iOS 10.0, macOS 10.12, *)) { + [[PINSpeedRecorder sharedRecorder] processMetrics:metrics forTask:task]; + + [self lock]; + dispatch_queue_t delegateQueue = self.delegateQueues[@(task.taskIdentifier)]; + [self unlock]; + + NSAssert(delegateQueue != nil, @"There seems to be an issue in iOS 9 where this can be nil. If you can reliably reproduce hitting this, *please* open an issue: https://github.com/pinterest/PINRemoteImage/issues"); + if (delegateQueue == nil) { + return; + } + + __weak typeof(self) weakSelf = self; + dispatch_async(delegateQueue, ^{ + typeof(self) strongSelf = weakSelf; + if ([strongSelf.delegate respondsToSelector:@selector(didCollectMetrics:forURL:)]) { + [strongSelf.delegate didCollectMetrics:metrics forURL:task.originalRequest.URL]; + } + }); + } +} + +- (BOOL)responseRecoverableFrom404:(NSHTTPURLResponse*)response +{ + return response.statusCode == 404 + && [response.allHeaderFields[@"content-type"] rangeOfString:@"image"].location != NSNotFound; +} + +#if DEBUG +- (void)concurrentDownloads:(void (^_Nullable)(NSUInteger concurrentDownloads))concurrentDownloadsCompletion +{ + if (@available(macos 10.11, iOS 9.0, watchOS 2.0, tvOS 9.0, *)) { + [self.session getAllTasksWithCompletionHandler:^(NSArray<__kindof NSURLSessionTask *> * _Nonnull tasks) { + concurrentDownloadsCompletion(tasks.count); + }]; + } else { + [self.session getTasksWithCompletionHandler:^(NSArray * _Nonnull dataTasks, + NSArray * _Nonnull uploadTasks, + NSArray * _Nonnull downloadTasks) { + NSUInteger total = dataTasks.count + uploadTasks.count + downloadTasks.count; + concurrentDownloadsCompletion(total); + }]; + } +} + +#endif + +@end + diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/NSData+ImageDetectors.h b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/NSData+ImageDetectors.h new file mode 100644 index 0000000..b5d91d9 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/NSData+ImageDetectors.h @@ -0,0 +1,22 @@ +// +// NSData+ImageDetectors.h +// Pods +// +// Created by Garrett Moon on 11/19/14. +// +// + +#import + +#import "PINRemoteImageMacros.h" + +@interface NSData (PINImageDetectors) + +- (BOOL)pin_isGIF; +- (BOOL)pin_isAnimatedGIF; +#if PIN_WEBP +- (BOOL)pin_isWebP; +- (BOOL)pin_isAnimatedWebP; +#endif + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINAlternateRepresentationProvider.h b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINAlternateRepresentationProvider.h new file mode 100644 index 0000000..d350609 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINAlternateRepresentationProvider.h @@ -0,0 +1,25 @@ +// +// PINAlternateRepresentationProvider.h +// Pods +// +// Created by Garrett Moon on 3/17/16. +// +// + +#import + +#import "PINRemoteImageManager.h" + +@protocol PINRemoteImageManagerAlternateRepresentationProvider +@required + +/** + @discussion This method will be called with data off the wire or stored in the cache. Return an object to have it returned as the alternativeRepresentation object in the PINRemoteImageManagerResult. @warning this method can be called on the main thread, be careful of doing expensive work. + */ +- (id)alternateRepresentationWithData:(NSData *)data options:(PINRemoteImageManagerDownloadOptions)options; + +@end + +@interface PINAlternateRepresentationProvider : NSObject + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINAnimatedImage.h b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINAnimatedImage.h new file mode 100644 index 0000000..4f96b15 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINAnimatedImage.h @@ -0,0 +1,165 @@ +// +// PINAnimatedImage.h +// PINRemoteImage +// +// Created by Garrett Moon on 9/17/17. +// Copyright © 2017 Pinterest. All rights reserved. +// + +#import +#import "PINRemoteImageMacros.h" + +#if PIN_TARGET_IOS +#import +#elif PIN_TARGET_MAC +#import +#endif + +NS_ASSUME_NONNULL_BEGIN + +extern NSErrorDomain const kPINAnimatedImageErrorDomain; + +/** + PINAnimatedImage decoding and processing errors. + */ +typedef NS_ERROR_ENUM(kPINAnimatedImageErrorDomain, PINAnimatedImageErrorCode) { + /** No error, yay! */ + PINAnimatedImageErrorNoError = 0, + /** Could not create a necessary file. */ + PINAnimatedImageErrorFileCreationError, + /** Could not get a file handle to the necessary file. */ + PINAnimatedImageErrorFileHandleError, + /** Could not decode the image. */ + PINAnimatedImageErrorImageFrameError, + /** Could not memory map the file. */ + PINAnimatedImageErrorMappingError, + /** File write error */ + PINAnimatedImageErrorFileWrite, +}; + +/** + The processing status of the animated image. + */ +typedef NS_ENUM(NSUInteger, PINAnimatedImageStatus) { + /** No work has been done. */ + PINAnimatedImageStatusUnprocessed = 0, + /** Info about the animated image and the cover image are available. */ + PINAnimatedImageStatusInfoProcessed, + /** At least one set of frames has been decoded to a file. It's safe to start playback. */ + PINAnimatedImageStatusFirstFileProcessed, + /** The entire animated image has been processed. */ + PINAnimatedImageStatusProcessed, + /** Processing was canceled. */ + PINAnimatedImageStatusCanceled, + /** There was an error in processing. */ + PINAnimatedImageStatusError, +}; + +extern const Float32 kPINAnimatedImageDefaultDuration; + +/** + Called when the cover image of an animatedImage is ready. + */ +typedef void(^PINAnimatedImageInfoReady)(PINImage * _Nonnull coverImage); + +@protocol PINAnimatedImage; + +@interface PINAnimatedImage : NSObject + +/** + @abstract The maximum number of frames per second supported. + */ ++ (NSInteger)maximumFramesPerSecond; + +/** + @abstract Return the duration at a given index. + + @warning *Must be overridden by subclass + */ +- (CFTimeInterval)durationAtIndex:(NSUInteger)index; + +/** + @abstract Return the total number of frames in the animated image. + + @warning *Must be overridden by subclass + */ +@property (nonatomic, readonly) size_t frameCount; + +/** + @abstract Return the total duration of the animated image's playback. + */ +@property (nonatomic, readonly) CFTimeInterval totalDuration; + +/** + The number of frames to play per second * display refresh rate (defined as 60 which appears to be true on iOS). You probably want to + set this value on a displayLink. + @warning Access to this property before status == PINAnimatedImageStatusInfoProcessed is undefined. + */ +@property (nonatomic, readonly) NSUInteger frameInterval; + +@end + +@protocol PINCachedAnimatedFrameProvider + +@required + +- (nullable CGImageRef)cachedFrameImageAtIndex:(NSUInteger)index; + +@end + +@protocol PINAnimatedImage + +/** + @abstract the underlying data of the animated image if available. + */ +@property (nonatomic, readonly) NSData *data; + +/** + @abstract the native width of the animated image. + */ +@property (nonatomic, readonly) uint32_t width; + +/** + @abstract the native height of the animated image. + */ +@property (nonatomic, readonly) uint32_t height; + +/** + @abstract number of bytes per frame. + */ +@property (nonatomic, readonly) uint32_t bytesPerFrame; + +/** + @abstract Return the total duration of the animated image's playback. + */ +@property (nonatomic, readonly) CFTimeInterval totalDuration; +/** + @abstract Return the interval at which playback should occur. Will be set to a CADisplayLink's frame interval. + */ +@property (nonatomic, readonly) NSUInteger frameInterval; +/** + @abstract Return the total number of loops the animated image should play or 0 to loop infinitely. + */ +@property (nonatomic, readonly) size_t loopCount; +/** + @abstract Return the total number of frames in the animated image. + */ +@property (nonatomic, readonly) size_t frameCount; +/** + @abstract Return any error that has occurred. Playback will be paused if this returns non-nil. + */ +@property (nonatomic, readonly, nullable) NSError *error; + +/** + @abstract Return the image at a given index. + */ +- (nullable CGImageRef)imageAtIndex:(NSUInteger)index cacheProvider:(nullable id)cacheProvider; +/** + + @abstract Return the duration at a given index. + */ +- (CFTimeInterval)durationAtIndex:(NSUInteger)index; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINAnimatedImageView+PINRemoteImage.h b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINAnimatedImageView+PINRemoteImage.h new file mode 100644 index 0000000..9dbfbc9 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINAnimatedImageView+PINRemoteImage.h @@ -0,0 +1,14 @@ +// +// PINAnimatedImageView+PINRemoteImage.h +// Pods +// +// Created by Garrett Moon on 4/19/18. +// + +#import "PINAnimatedImageView.h" + +#import "PINRemoteImageCategoryManager.h" + +@interface PINAnimatedImageView (PINRemoteImage) + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINAnimatedImageView.h b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINAnimatedImageView.h new file mode 100644 index 0000000..b224f02 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINAnimatedImageView.h @@ -0,0 +1,28 @@ +// +// PINAnimatedImageView.h +// Pods +// +// Created by Garrett Moon on 4/17/18. +// + +#import "PINRemoteImageMacros.h" + +#if PIN_TARGET_IOS +#import +#elif PIN_TARGET_MAC +#import +#endif + +#import "PINCachedAnimatedImage.h" + +@interface PINAnimatedImageView : PINImageView + +- (nonnull instancetype)initWithAnimatedImage:(nonnull PINCachedAnimatedImage *)animatedImage NS_DESIGNATED_INITIALIZER; +- (nonnull instancetype)initWithFrame:(CGRect)frame NS_DESIGNATED_INITIALIZER; +- (nullable instancetype)initWithCoder:(nonnull NSCoder *)aDecoder NS_DESIGNATED_INITIALIZER; + +@property (nullable, nonatomic, strong) PINCachedAnimatedImage *animatedImage; +@property (nullable, nonatomic, strong) NSString *animatedImageRunLoopMode; +@property (nonatomic, assign, getter=isPlaybackPaused) BOOL playbackPaused; + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINButton+PINRemoteImage.h b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINButton+PINRemoteImage.h new file mode 100644 index 0000000..1659db9 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINButton+PINRemoteImage.h @@ -0,0 +1,22 @@ +// +// UIButton+PINRemoteImage.h +// Pods +// +// Created by Garrett Moon on 8/18/14. +// +// + +#import "PINRemoteImageMacros.h" + +#if PIN_TARGET_IOS +#import +#elif PIN_TARGET_MAC +#import +#endif + +#import "PINRemoteImageManager.h" +#import "PINRemoteImageCategoryManager.h" + +@interface PINButton (PINRemoteImage) + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINCache+PINRemoteImageCaching.h b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINCache+PINRemoteImageCaching.h new file mode 100644 index 0000000..f8a5eb1 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINCache+PINRemoteImageCaching.h @@ -0,0 +1,26 @@ +// +// PINCache+PINRemoteImageCaching.h +// Pods +// +// Created by Aleksei Shevchenko on 7/28/16. +// +// + +#if SWIFT_PACKAGE +@import PINCache; +#else +#import +#endif + +#import "PINRemoteImageCaching.h" +#import "PINRemoteImageManager.h" + +@interface PINCache (PINRemoteImageCaching) + +@end + +@interface PINRemoteImageManager (PINCache) + +@property (nonatomic, nullable, readonly) PINCache *pinCache; + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINCachedAnimatedImage.h b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINCachedAnimatedImage.h new file mode 100644 index 0000000..b3b6681 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINCachedAnimatedImage.h @@ -0,0 +1,83 @@ +// +// PINCachedAnimatedImage.h +// PINRemoteImage +// +// Created by Garrett Moon on 9/17/17. +// Copyright © 2017 Pinterest. All rights reserved. +// + +#import + +#import "PINRemoteImageMacros.h" +#import "PINAnimatedImage.h" + +@interface PINCachedAnimatedImage : NSObject + +- (instancetype)init NS_UNAVAILABLE; +- (instancetype)initWithAnimatedImage:(id )animatedImage NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithAnimatedImageData:(NSData *)animatedImageData; + +/** + @abstract A block which receives the cover image. Should be called when the objects cover image is ready. + */ +@property (nonatomic, readwrite) PINAnimatedImageInfoReady coverImageReadyCallback; + +/** + @abstract Return the objects's cover image. + */ +@property (nonatomic, readonly) PINImage *coverImage; +/** + @abstract Return a boolean to indicate that the cover image is ready. + */ +@property (nonatomic, readonly) BOOL coverImageReady; +/** + @abstract Return the total duration of the animated image's playback. + */ +@property (nonatomic, readonly) CFTimeInterval totalDuration; +/** + @abstract Return the interval at which playback should occur. Will be set to a CADisplayLink's frame interval. + */ +@property (nonatomic, readonly) NSUInteger frameInterval; +/** + @abstract Return the size of the underlying animated image. + */ +@property (nonatomic, readonly) CGSize size; +/** + @abstract Return the total number of loops the animated image should play or 0 to loop infinitely. + */ +@property (nonatomic, readonly) size_t loopCount; +/** + @abstract Return the total number of frames in the animated image. + */ +@property (nonatomic, readonly) size_t frameCount; +/** + @abstract Return the underlying data if available; + */ +@property (nonatomic, readonly) NSData *data; +/** + @abstract Return YES when playback is ready to occur. + */ +@property (nonatomic, readonly) BOOL playbackReady; +/** + @abstract Return any error that has occurred. Playback will be paused if this returns non-nil. + */ +@property (nonatomic, readonly) NSError *error; +/** + @abstract Should be called when playback is ready. + */ +@property (nonatomic, readwrite) dispatch_block_t playbackReadyCallback; + +/** + @abstract Return the image at a given index. + */ +- (CGImageRef)imageAtIndex:(NSUInteger)index; +/** + @abstract Return the duration at a given index. + */ +- (CFTimeInterval)durationAtIndex:(NSUInteger)index; +/** + @abstract Clear any cached data. Called when playback is paused. + */ +- (void)clearAnimatedImageCache; + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINGIFAnimatedImage.h b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINGIFAnimatedImage.h new file mode 100644 index 0000000..e106f9e --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINGIFAnimatedImage.h @@ -0,0 +1,18 @@ +// +// PINGIFAnimatedImage.h +// PINRemoteImage +// +// Created by Garrett Moon on 9/17/17. +// Copyright © 2017 Pinterest. All rights reserved. +// + +#import + +#import "PINAnimatedImage.h" + +@interface PINGIFAnimatedImage : PINAnimatedImage + +- (instancetype)init NS_UNAVAILABLE; +- (instancetype)initWithAnimatedImageData:(NSData *)animatedImageData NS_DESIGNATED_INITIALIZER; + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINImageView+PINRemoteImage.h b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINImageView+PINRemoteImage.h new file mode 100644 index 0000000..f5c4b7e --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINImageView+PINRemoteImage.h @@ -0,0 +1,22 @@ +// +// UIImageView+PINRemoteImage.h +// Pods +// +// Created by Garrett Moon on 8/17/14. +// +// + +#import "PINRemoteImageMacros.h" + +#if PIN_TARGET_IOS +#import +#elif PIN_TARGET_MAC +#import +#endif + +#import "PINRemoteImageManager.h" +#import "PINRemoteImageCategoryManager.h" + +@interface PINImageView (PINRemoteImage) + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINProgressiveImage.h b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINProgressiveImage.h new file mode 100644 index 0000000..1c68b1a --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINProgressiveImage.h @@ -0,0 +1,52 @@ +// +// PINProgressiveImage.h +// Pods +// +// Created by Garrett Moon on 2/9/15. +// +// + +#import + +#import "PINRemoteImageMacros.h" + +#if PIN_TARGET_IOS +#import +#elif PIN_TARGET_MAC +#import +#endif + +@class PINRemoteImageDownloadTask; + +/** An object which store the data of a downloading image and vends progressive scans **/ +@interface PINProgressiveImage : NSObject + +@property (atomic, copy, nonnull) NSArray *progressThresholds; +@property (atomic, assign) CFTimeInterval estimatedRemainingTimeThreshold; +@property (nonatomic, strong, readonly, nonnull) NSURLSessionDataTask * dataTask; +@property (nonatomic, readonly) CFTimeInterval estimatedRemainingTime; + +- (nonnull instancetype)init NS_UNAVAILABLE; +- (nonnull instancetype)initWithDataTask:(nonnull NSURLSessionDataTask *)dataTask; + +- (void)updateProgressiveImageWithData:(nonnull NSData *)data expectedNumberOfBytes:(int64_t)expectedNumberOfBytes isResume:(BOOL)isResume; + +/** + Returns the latest image based on thresholds, returns nil if no new image is generated. + + @param blurred A boolean to indicate if the image should be blurred. + @param maxProgressiveRenderSize The maximum dimensions at which to apply a blur. If an image exceeds either the height. + or width of this dimension, the image will *not* be blurred regardless of the blurred parameter. + @param renderedImageQuality Value between 0 and 1. Computed by dividing the received number of bytes by the expected number of bytes. + @return PINImage A progressive scan of the image or nil if a new one has not been generated. + */ +- (nullable PINImage *)currentImageBlurred:(BOOL)blurred maxProgressiveRenderSize:(CGSize)maxProgressiveRenderSize renderedImageQuality:(nonnull out CGFloat *)renderedImageQuality; + +/** + Returns the current data for the image. + + @return NSData The current data for the image. + */ +- (nullable NSData *)data; + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINRemoteImage.h b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINRemoteImage.h new file mode 100644 index 0000000..140e143 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINRemoteImage.h @@ -0,0 +1,25 @@ +// +// PINRemoteImage.h +// +// Created by Garrett Moon on 8/17/14. +// +// + +#import "PINRemoteImageMacros.h" + +#if USE_PINCACHE + #import "PINCache+PINRemoteImageCaching.h" +#endif + +#import "NSData+ImageDetectors.h" +#import "PINAlternateRepresentationProvider.h" +#import "PINCachedAnimatedImage.h" +#import "PINGIFAnimatedImage.h" +#import "PINWebPAnimatedImage.h" +#import "PINRemoteImageManager.h" +#import "PINRemoteImageCategoryManager.h" +#import "PINRemoteImageManagerResult.h" +#import "PINRemoteImageCaching.h" +#import "PINProgressiveImage.h" +#import "PINURLSessionManager.h" +#import "PINRequestRetryStrategy.h" diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINRemoteImageCaching.h b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINRemoteImageCaching.h new file mode 100644 index 0000000..bfafbdf --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINRemoteImageCaching.h @@ -0,0 +1,52 @@ +// +// PINRemoteImageCaching.h +// Pods +// +// Created by Aleksei Shevchenko on 7/25/16. +// +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@protocol PINRemoteImageCaching; +typedef void (^PINRemoteImageCachingObjectBlock)(id cache, NSString *key, id __nullable object); + +/** + * Image Cache is responsible for actual image caching. + */ +@protocol PINRemoteImageCaching + +//****************************************************************************************************** +// Memory cache methods +//****************************************************************************************************** +- (nullable id)objectFromMemoryForKey:(NSString *)key; +- (void)setObjectInMemory:(id)object forKey:(NSString *)key withCost:(NSUInteger)cost; + +//****************************************************************************************************** +// Disk cache methods +//****************************************************************************************************** +- (nullable id)objectFromDiskForKey:(NSString *)key; +- (void)objectFromDiskForKey:(NSString *)key completion:(nullable PINRemoteImageCachingObjectBlock)completion; +- (void)setObjectOnDisk:(id)object forKey:(NSString *)key; + + +- (BOOL)objectExistsForKey:(NSString *)key; + +- (void)removeObjectForKey:(NSString *)key; +- (void)removeObjectForKey:(NSString *)key completion:(nullable PINRemoteImageCachingObjectBlock)completion; +- (void)removeAllObjects; + +@optional + +- (void)removeObjectForKeyFromMemory:(NSString *)key; +- (void)setObjectInMemory:(id)object forKey:(NSString *)key withCost:(NSUInteger)cost withAgeLimit:(NSTimeInterval)ageLimit; +- (void)setObjectOnDisk:(id)object forKey:(NSString *)key withAgeLimit:(NSTimeInterval)ageLimit; +- (BOOL)memoryCacheIsTTLCache; +- (BOOL)diskCacheIsTTLCache; + +@end + + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINRemoteImageCategoryManager.h b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINRemoteImageCategoryManager.h new file mode 100644 index 0000000..ec94124 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINRemoteImageCategoryManager.h @@ -0,0 +1,230 @@ +// +// PINRemoteImageCategory.h +// Pods +// +// Created by Garrett Moon on 11/4/14. +// +// + +#import "PINRemoteImageMacros.h" + +#if PIN_TARGET_IOS +#import +#elif PIN_TARGET_MAC +#import +#endif + +#import "PINRemoteImageManager.h" + +@protocol PINRemoteImageCategory; + +/** + PINRemoteImageCategoryManager is a class that handles subclassing image display classes. PINImageView+PINRemoteImage, UIButton+PINRemoteImage, etc, all delegate their work to this class. If you'd like to create a category to display an image on a view, you should mimic one of the above categories. + */ + +@interface PINRemoteImageCategoryManager : NSObject + ++ (void)setImageOnView:(nonnull id )view + fromURL:(nullable NSURL *)url; + ++ (void)setImageOnView:(nonnull id )view + fromURL:(nullable NSURL *)url + placeholderImage:(nullable PINImage *)placeholderImage; + ++ (void)setImageOnView:(nonnull id )view + fromURL:(nullable NSURL *)url + placeholderImage:(nullable PINImage *)placeholderImage + completion:(nullable PINRemoteImageManagerImageCompletion)completion; + ++ (void)setImageOnView:(nonnull id )view + fromURL:(nullable NSURL *)url + completion:(nullable PINRemoteImageManagerImageCompletion)completion; + ++ (void)setImageOnView:(nonnull id )view + fromURL:(nullable NSURL *)url + processorKey:(nullable NSString *)processorKey + processor:(nullable PINRemoteImageManagerImageProcessor)processor; + ++ (void)setImageOnView:(nonnull id )view + fromURL:(nullable NSURL *)url + placeholderImage:(nullable PINImage *)placeholderImage + processorKey:(nullable NSString *)processorKey + processor:(nullable PINRemoteImageManagerImageProcessor)processor; + ++ (void)setImageOnView:(nonnull id )view + fromURL:(nullable NSURL *)url + processorKey:(nullable NSString *)processorKey + processor:(nullable PINRemoteImageManagerImageProcessor)processor + completion:(nullable PINRemoteImageManagerImageCompletion)completion; + ++ (void)setImageOnView:(nonnull id )view + fromURLs:(nullable NSArray *)urls + placeholderImage:(nullable PINImage *)placeholderImage + processorKey:(nullable NSString *)processorKey + processor:(nullable PINRemoteImageManagerImageProcessor)processor + completion:(nullable PINRemoteImageManagerImageCompletion)completion; + ++ (void)setImageOnView:(nonnull id )view + fromURLs:(nullable NSArray *)urls; + ++ (void)setImageOnView:(nonnull id )view + fromURLs:(nullable NSArray *)urls + placeholderImage:(nullable PINImage *)placeholderImage; + ++ (void)setImageOnView:(nonnull id )view + fromURLs:(nullable NSArray *)urls + placeholderImage:(nullable PINImage *)placeholderImage + completion:(nullable PINRemoteImageManagerImageCompletion)completion; + ++ (void)cancelImageDownloadOnView:(nonnull id )view; + ++ (nullable NSUUID *)downloadImageOperationUUIDOnView:(nonnull id )view; + ++ (void)setDownloadImageOperationUUID:(nullable NSUUID *)downloadImageOperationUUID onView:(nonnull id )view; + ++ (BOOL)updateWithProgressOnView:(nonnull id )view; + ++ (void)setUpdateWithProgressOnView:(BOOL)updateWithProgress onView:(nonnull id )view; + +@end + +/** + Protocol to implement on UIView subclasses to support PINRemoteImage + */ +@protocol PINRemoteImageCategory + +//Call manager + +/** + Set the image from the given URL. + + @param url NSURL to fetch from. + */ +- (void)pin_setImageFromURL:(nullable NSURL *)url; + +/** + Set the image from the given URL and set placeholder image while image at URL is being retrieved. + + @param url NSURL to fetch from. + @param placeholderImage PINImage to set on the view while the image at URL is being retrieved. + */ +- (void)pin_setImageFromURL:(nullable NSURL *)url placeholderImage:(nullable PINImage *)placeholderImage; + +/** + Set the image from the given URL and call completion when finished. + + @param url NSURL to fetch from. + @param completion Called when url has been retrieved and set on view. + */ +- (void)pin_setImageFromURL:(nullable NSURL *)url completion:(nullable PINRemoteImageManagerImageCompletion)completion; + +/** + Set the image from the given URL, set placeholder while image at url is being retrieved and call completion when finished. + + @param url NSURL to fetch from. + @param placeholderImage PINImage to set on the view while the image at URL is being retrieved. + @param completion Called when url has been retrieved and set on view. + */ +- (void)pin_setImageFromURL:(nullable NSURL *)url placeholderImage:(nullable PINImage *)placeholderImage completion:(nullable PINRemoteImageManagerImageCompletion)completion; + +/** + Retrieve the image from the given URL, process it using the passed in processor block and set result on view. + + @param url NSURL to fetch from. + @param processorKey NSString key to uniquely identify processor. Used in caching. + @param processor PINRemoteImageManagerImageProcessor processor block which should return the processed image. + */ +- (void)pin_setImageFromURL:(nullable NSURL *)url processorKey:(nullable NSString *)processorKey processor:(nullable PINRemoteImageManagerImageProcessor)processor; + +/** + Set placeholder on view and retrieve the image from the given URL, process it using the passed in processor block and set result on view. + + @param url NSURL to fetch from. + @param placeholderImage PINImage to set on the view while the image at URL is being retrieved. + @param processorKey NSString key to uniquely identify processor. Used in caching. + @param processor PINRemoteImageManagerImageProcessor processor block which should return the processed image. + */ +- (void)pin_setImageFromURL:(nullable NSURL *)url placeholderImage:(nullable PINImage *)placeholderImage processorKey:(nullable NSString *)processorKey processor:(nullable PINRemoteImageManagerImageProcessor)processor; + +/** + Retrieve the image from the given URL, process it using the passed in processor block and set result on view. Call completion after image has been fetched, processed and set on view. + + @param url NSURL to fetch from. + @param processorKey NSString key to uniquely identify processor. Used in caching. + @param processor PINRemoteImageManagerImageProcessor processor block which should return the processed image. + @param completion Called when url has been retrieved and set on view. + */ +- (void)pin_setImageFromURL:(nullable NSURL *)url processorKey:(nullable NSString *)processorKey processor:(nullable PINRemoteImageManagerImageProcessor)processor completion:(nullable PINRemoteImageManagerImageCompletion)completion; + +/** + Set placeholder on view and retrieve the image from the given URL, process it using the passed in processor block and set result on view. Call completion after image has been fetched, processed and set on view. + + @param url NSURL to fetch from. + @param placeholderImage PINImage to set on the view while the image at URL is being retrieved. + @param processorKey NSString key to uniquely identify processor. Used in caching. + @param processor PINRemoteImageManagerImageProcessor processor block which should return the processed image. + @param completion Called when url has been retrieved and set on view. + */ +- (void)pin_setImageFromURL:(nullable NSURL *)url placeholderImage:(nullable PINImage *)placeholderImage processorKey:(nullable NSString *)processorKey processor:(nullable PINRemoteImageManagerImageProcessor)processor completion:(nullable PINRemoteImageManagerImageCompletion)completion; + +/** + Retrieve one of the images at the passed in URLs depending on previous network performance and set result on view. + + @param urls NSArray of NSURLs sorted in increasing quality + */ +- (void)pin_setImageFromURLs:(nullable NSArray *)urls; + +/** + Set placeholder on view and retrieve one of the images at the passed in URLs depending on previous network performance and set result on view. + + @param urls NSArray of NSURLs sorted in increasing quality + @param placeholderImage PINImage to set on the view while the image at URL is being retrieved. + */ +- (void)pin_setImageFromURLs:(nullable NSArray *)urls placeholderImage:(nullable PINImage *)placeholderImage; + +/** + Set placeholder on view and retrieve one of the images at the passed in URLs depending on previous network performance and set result on view. Call completion after image has been fetched and set on view. + + @param urls NSArray of NSURLs sorted in increasing quality + @param placeholderImage PINImage to set on the view while the image at URL is being retrieved. + @param completion Called when url has been retrieved and set on view. + */ +- (void)pin_setImageFromURLs:(nullable NSArray *)urls placeholderImage:(nullable PINImage *)placeholderImage completion:(nullable PINRemoteImageManagerImageCompletion)completion; + +/** + Cancels the image download. Guarantees that previous setImage calls will *not* have their results set on the image view after calling this (as opposed to PINRemoteImageManager which does not guarantee cancellation). + */ +- (void)pin_cancelImageDownload; + +/** + Returns the NSUUID associated with any PINRemoteImage task currently running on the view. + + @return NSUUID associated with any PINRemoteImage task currently running on the view. + */ +- (nullable NSUUID *)pin_downloadImageOperationUUID; + +/** + Set the current NSUUID associated with a PINRemoteImage task running on the view. + + @param downloadImageOperationUUID NSUUID associated with a PINRemoteImage task. + */ +- (void)pin_setDownloadImageOperationUUID:(nullable NSUUID *)downloadImageOperationUUID; + +/** + Whether the view should update with progress images (such as those provided by progressive JPEG images). + + @return BOOL value indicating whether the view should update with progress images + */ +@property (nonatomic, assign) BOOL pin_updateWithProgress; + +//Handle +- (void)pin_setPlaceholderWithImage:(nullable PINImage *)image; +- (void)pin_updateUIWithRemoteImageManagerResult:(nonnull PINRemoteImageManagerResult *)result; +- (void)pin_clearImages; +- (BOOL)pin_ignoreGIFs; + +@optional + +- (PINRemoteImageManagerDownloadOptions)pin_defaultOptions; + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINRemoteImageMacros.h b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINRemoteImageMacros.h new file mode 100644 index 0000000..5f5dd8e --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINRemoteImageMacros.h @@ -0,0 +1,70 @@ +// +// PINRemoteImageMacros.h +// PINRemoteImage +// + +#import + +#ifndef PINRemoteImageMacros_h +#define PINRemoteImageMacros_h + +#define PIN_TARGET_IOS (TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR || TARGET_OS_TV) +#define PIN_TARGET_MAC TARGET_OS_OSX + +#define PINRemoteImageLogging 0 +#if PINRemoteImageLogging +#define PINLog(args...) NSLog(args) +#else +#define PINLog(args...) +#endif + +#ifndef USE_PINCACHE + #if __has_include() || __has_include("PINCache.h") + #define USE_PINCACHE 1 + #else + #define USE_PINCACHE 0 + #endif +#endif + +#ifndef PIN_WEBP + #if __has_include() || __has_include() + #define PIN_WEBP 1 + #else + #define PIN_WEBP 0 + #endif +#endif + +#if PIN_TARGET_IOS +#define PINImage UIImage +#define PINImageView UIImageView +#define PINButton UIButton +#define PINNSOperationSupportsBlur (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_8_0) +#elif PIN_TARGET_MAC +#define PINImage NSImage +#define PINImageView NSImageView +#define PINButton NSButton +#define PINNSOperationSupportsBlur (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber10_10) +#define PINNSURLSessionTaskSupportsPriority (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber10_10) +#endif + +#define PINWeakify(var) __weak typeof(var) PINWeak_##var = var; + +#define PINStrongify(var) \ +_Pragma("clang diagnostic push") _Pragma("clang diagnostic ignored \"-Wshadow\"") __strong typeof(var) var = \ +PINWeak_##var; \ +_Pragma("clang diagnostic pop") + +#define BlockAssert(condition, desc, ...) \ +do { \ +__PRAGMA_PUSH_NO_EXTRA_ARG_WARNINGS \ +if (!(condition)) { \ +[[NSAssertionHandler currentHandler] handleFailureInMethod:_cmd \ +object:strongSelf file:[NSString stringWithUTF8String:__FILE__] \ +lineNumber:__LINE__ description:(desc), ##__VA_ARGS__]; \ +} \ +__PRAGMA_POP_NO_EXTRA_ARG_WARNINGS \ +} while(0); + +#define PINAssertMain() NSAssert([NSThread isMainThread], @"Expected to be on the main thread."); + +#endif /* PINRemoteImageMacros_h */ diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINRemoteImageManager.h b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINRemoteImageManager.h new file mode 100644 index 0000000..8390707 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINRemoteImageManager.h @@ -0,0 +1,703 @@ +// +// PINRemoteImageManager.h +// Pods +// +// Created by Garrett Moon on 8/17/14. +// +// + +#import + +#import "PINRemoteImageMacros.h" + +#if PIN_TARGET_IOS +#import +#elif PIN_TARGET_MAC +#import +#endif + +#import "PINRemoteImageManagerResult.h" + +#define PINRemoteImageHTTPMaximumConnectionsPerHost UINT16_MAX + +@protocol PINRemoteImageManagerAlternateRepresentationProvider; +@protocol PINRemoteImageCaching; +@protocol PINRequestRetryStrategy; + +@class PINRemoteImageManagerConfiguration; +@class PINRemoteImageManagerResult; + +extern NSErrorDomain _Nonnull const PINRemoteImageManagerErrorDomain; + +/** + Error codes returned by PINRemoteImage + */ +typedef NS_ERROR_ENUM(PINRemoteImageManagerErrorDomain, PINRemoteImageManagerError) { + /** The image failed to decode */ + PINRemoteImageManagerErrorFailedToDecodeImage = 1, + /** The image could not be downloaded and therefore could not be processed */ + PINRemoteImageManagerErrorFailedToFetchImageForProcessing = 2, + /** The image returned by the processor block was nil */ + PINRemoteImageManagerErrorFailedToProcessImage = 3, + /** The image in the cache was invalid */ + PINRemoteImageManagerErrorInvalidItemInCache = 4, + /** The image at the URL was empty */ + PINRemoteImageManagerErrorImageEmpty = 5, +}; + +/** + Options with which to download and process images + */ +typedef NS_OPTIONS(NSUInteger, PINRemoteImageManagerDownloadOptions) { + /** Download and process with default options (no other options set) */ + PINRemoteImageManagerDownloadOptionsNone = 0, + /** Set to disallow any alternate representations such as Animated Images */ + PINRemoteImageManagerDisallowAlternateRepresentations = 1, + /** Skip decoding the image before returning. This means smaller images returned, but images will be decoded on the main thread when set on an image view */ + PINRemoteImageManagerDownloadOptionsSkipDecode = 1 << 1, + /** Skip the early check of the memory cache */ + PINRemoteImageManagerDownloadOptionsSkipEarlyCheck = 1 << 2, + /** Save processed images as JPEGs in the cache. The default is PNG to support transparency */ + PINRemoteImageManagerSaveProcessedImageAsJPEG = 1 << 3, + /** Ignore cache and force download */ + PINRemoteImageManagerDownloadOptionsIgnoreCache = 1 << 4, + /** Skip download retry */ + PINRemoteImageManagerDownloadOptionsSkipRetry = 1 << 5, + /** + * Do not honor HTTP Cache-Control headers + * By default, PINRemoteImage will by default respect 'no-store', 'no-cache', 'max-age', + * 'Expires', and 'must-revalidate'. Set this flag to ignore those headers. + * TODO: Currently PINRemoteImage will re-download images that only must be re-validated. In the + * future this could be improved with revalidation behavior that stores ETag or Last-Modified + * values and only makes HEAD requests to see if these headers are unchanged. + * see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control and + * https://tools.ietf.org/html/rfc7234*/ + PINRemoteImageManagerDownloadOptionsIgnoreCacheControlHeaders = 1 << 6 +}; + +/** + Priority to download and process image at. + */ +typedef NS_ENUM(NSUInteger, PINRemoteImageManagerPriority) { + /** Low priority */ + PINRemoteImageManagerPriorityLow = 0, + /** Default priority */ + PINRemoteImageManagerPriorityDefault, + /** High priority */ + PINRemoteImageManagerPriorityHigh, + PINRemoteImageManagerPriorityVeryHigh DEPRECATED_ATTRIBUTE = PINRemoteImageManagerPriorityHigh, + PINRemoteImageManagerPriorityVeryLow DEPRECATED_ATTRIBUTE = PINRemoteImageManagerPriorityLow, + PINRemoteImageManagerPriorityMedium DEPRECATED_ATTRIBUTE = PINRemoteImageManagerPriorityDefault, +}; + +float dataTaskPriorityWithImageManagerPriority(PINRemoteImageManagerPriority priority); + +/** + Completion called for many PINRemoteImage tasks as well as progress updates. Passed in a PINRemoteImageManagerResult. + + @param result PINRemoteImageManagerResult which contains the downloaded image. + */ +typedef void (^PINRemoteImageManagerImageCompletion)(PINRemoteImageManagerResult * __nonnull result); + +/** + Processor block to post-process a downloaded image. Passed in a PINRemoteImageManagerResult and a pointer to an NSUInteger which can be updated to indicate the cost of processing the image. + + @param result PINRemoteImageManagerResult which contains the downloaded image. + @param cost NSUInteger point which can be modified to indicate the cost of processing the image. This is used when determining which cache objects to evict on memory pressure. + + @return return the processed UIImage + */ +typedef PINImage * _Nullable(^PINRemoteImageManagerImageProcessor)(PINRemoteImageManagerResult * __nonnull result, NSUInteger * __nonnull cost); + +/** + PINRemoteImageManager is the main workhorse of PINRemoteImage. It is unnecessary to access directly if you simply + wish to download images and have them rendered in a UIImageView, UIButton or PINAnimatedImageView. + + However, if you wish to download images directly, this class is your guy / gal. + + You can use this class to download images, postprocess downloaded images, prefetch images, download images progressively, or download one image in a set of images depending on network performance. + */ + +/** + Completion Handler block which will be forwarded to NSURLSessionTaskDelegate's completion handler + + @param disposition One of several constants that describes how the challenge should be handled. + @param credential The credential that should be used for authentication if disposition is NSURLSessionAuthChallengeUseCredential; otherwise, NULL. + */ +typedef void(^PINRemoteImageManagerAuthenticationChallengeCompletionHandler)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * __nullable credential); + +/** + Authentication challenge handler + + @param task The task whose request requires authentication. + @param challenge An object that contains the request for authentication. + @param aHandler A PINRemoteImageManagerAuthenticationChallengeCompletionHandler, see example for further details. + */ +typedef void(^PINRemoteImageManagerAuthenticationChallenge)(NSURLSessionTask * __nullable task, NSURLAuthenticationChallenge * __nonnull challenge, PINRemoteImageManagerAuthenticationChallengeCompletionHandler __nullable aHandler); + + +/** + Request configuration handler. Used to modify a network request before it is executed. + Useful for adding custom, per-request headers. + + @param request The request about to be executed + */ +typedef NSURLRequest * _Nonnull(^PINRemoteImageManagerRequestConfigurationHandler)(NSURLRequest * __nonnull request); + + +/** + Handler called for many PINRemoteImage tasks providing the progress of the download. + + @param completedBytes Amount of bytes that have been downloaded so far. + @param totalBytes Total amount of bytes in the image being downloaded. + */ +typedef void(^PINRemoteImageManagerProgressDownload)(int64_t completedBytes, int64_t totalBytes); + +/** + Reports NSURLSessionTaskMetrics for download requests + + */ +typedef void(^PINRemoteImageManagerMetrics)(NSURL * __nonnull url, NSURLSessionTaskMetrics * __nonnull metrics) API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0)); + +/** An image downloading, processing and caching manager. It uses the concept of download and processing tasks to ensure that even if multiple calls to download or process an image are made, it only occurs one time (unless an item is no longer in the cache). PINRemoteImageManager is backed by GCD and safe to access from multiple threads simultaneously. It ensures that images are decoded off the main thread so that animation performance isn't affected. None of its exposed methods allow for synchronous access. However, it is optimized to call completions on the calling thread if an item is in its memory cache. **/ +@interface PINRemoteImageManager : NSObject + +@property (nonatomic, readonly, nonnull) id cache; + +/** + Create and return a PINRemoteImageManager created with the specified configuration. If configuration is nil, [NSURLSessionConfiguration defaultConfiguration] is used. Specify a custom configuration if you need to configure timeout values, cookie policies, additional HTTP headers, etc. + @param sessionConfiguration The session configuration used to create the PINRemoteImageManager. + @return A PINRemoteImageManager with the specified configuration. + @note If you provide your own `sessionConfiguration`, please attention `HTTPMaximumConnectionsPerHost` property, it may causes timeout when in conjunction with `maxNumberOfConcurrentDownloads` if you set a larger number to `maxNumberOfConcurrentDownloads` but smaller number to `HTTPMaximumConnectionsPerHost`. + */ +- (nonnull instancetype)initWithSessionConfiguration:(nullable NSURLSessionConfiguration *)sessionConfiguration; + +/** + Create and return a PINRemoteImageManager with the specified configuration and alternative representation delegate. If configuration is nil, [NSURLSessionConfiguration defaultConfiguration] is used. Specify a custom configuration if you need to configure timeout values, cookie policies, additional HTTP headers, etc. If alternativeRepresentationProvider is nil, the default is used (and supports PINAnimatedImageView). + @param sessionConfiguration The session configuration used to create the PINRemoteImageManager. + @param alternativeRepresentationProvider a delegate which conforms to the PINRemoteImageManagerAlternateRepresentationProvider protocol. Provide a delegate if you want to have non image results. The manager maintains a weak reference to the delegate. @see PINRemoteImageManagerAlternateRepresentationProvider for an example. + @return A PINRemoteImageManager with the specified configuration. + @note If you provide your own `sessionConfiguration`, please attention `HTTPMaximumConnectionsPerHost` property, it may causes timeout when in conjunction with `maxNumberOfConcurrentDownloads` if you set a larger number to `maxNumberOfConcurrentDownloads` but smaller number to `HTTPMaximumConnectionsPerHost`. + */ +- (nonnull instancetype)initWithSessionConfiguration:(nullable NSURLSessionConfiguration *)sessionConfiguration + alternativeRepresentationProvider:(nullable id )alternativeRepresentationProvider; + +/** + Create and return a PINRemoteImageManager with the specified configuration and alternative representation delegate. If configuration is nil, [NSURLSessionConfiguration defaultConfiguration] is used. Specify a custom configuration if you need to configure timeout values, cookie policies, additional HTTP headers, etc. If alternativeRepresentationProvider is nil, the default is used (and supports PINAnimatedImageView). + @param sessionConfiguration The session configuration used to create the PINRemoteImageManager. + @param alternateRepDelegate a delegate which conforms to the PINRemoteImageManagerAlternateRepresentationProvider protocol. Provide a delegate if you want to have non image results. The manager maintains a weak reference to the delegate. @see PINRemoteImageManagerAlternateRepresentationProvider for an example. + @param imageCache Optional delegate which conforms to the PINRemoteImageCaching protocol. Provide a delegate if you want to control image caching. By default, image manager will use most appropriate implementation available (based on PINCache or NSCache, depending on subspec)@see PINRemoteImageBasicCache for an example. + @return A PINRemoteImageManager with the specified configuration. + @note If you provide your own `sessionConfiguration`, please attention `HTTPMaximumConnectionsPerHost` property, it may causes timeout when in conjunction with `maxNumberOfConcurrentDownloads` if you set a larger number to `maxNumberOfConcurrentDownloads` but smaller number to `HTTPMaximumConnectionsPerHost`. + */ +- (nonnull instancetype)initWithSessionConfiguration:(nullable NSURLSessionConfiguration *)sessionConfiguration + alternativeRepresentationProvider:(nullable id )alternateRepDelegate + imageCache:(nullable id)imageCache; + +/** + Create and return a PINRemoteImageManager with the specified configuration and alternative representation delegate. If configuration is nil, [NSURLSessionConfiguration defaultConfiguration] is used. Specify a custom configuration if you need to configure timeout values, cookie policies, additional HTTP headers, etc. If alternativeRepresentationProvider is nil, the default is used (and supports PINAnimatedImageView). + @param sessionConfiguration The session configuration used to create the PINRemoteImageManager. + @param alternateRepDelegate a delegate which conforms to the PINRemoteImageManagerAlternateRepresentationProvider protocol. Provide a delegate if you want to have non image results. The manager maintains a weak reference to the delegate. @see PINRemoteImageManagerAlternateRepresentationProvider for an example. + @param imageCache Optional delegate which conforms to the PINRemoteImageCaching protocol. Provide a delegate if you want to control image caching. By default, image manager will use most appropriate implementation available (based on PINCache or NSCache, depending on subspec)@see PINRemoteImageBasicCache for an example. + @param managerConfiguration The configuration used to create the PINRemoteImageManager. + @return A PINRemoteImageManager with the specified configuration. + @note If you provide your own `sessionConfiguration`, please attention `HTTPMaximumConnectionsPerHost` property, it may causes timeout when in conjunction with `maxNumberOfConcurrentDownloads` if you set a larger number to `maxNumberOfConcurrentDownloads` but smaller number to `HTTPMaximumConnectionsPerHost`. + */ +- (nonnull instancetype)initWithSessionConfiguration:(nullable NSURLSessionConfiguration *)sessionConfiguration + alternativeRepresentationProvider:(nullable id )alternateRepDelegate + imageCache:(nullable id)imageCache + managerConfiguration:(nullable PINRemoteImageManagerConfiguration *)managerConfiguration NS_DESIGNATED_INITIALIZER; + +/** + Get the shared instance of PINRemoteImageManager + + @return Shared instance of PINRemoteImageManager + */ ++ (nonnull instancetype)sharedImageManager; + +/** + Sets the shared instance of PINRemoteImageManager to an instance with the supplied configuration. If configuration is nil, [NSURLSessionConfiguration ephemeralSessionConfiguration] is used. You specify a custom configuration if you need to configure timeout values, cookie policies, additional HTTP headers, etc. This method should not be used if the shared instance has already been created. + + @param configuration The configuration used to create the PINRemoteImageManager. + @note If you provide your own `sessionConfiguration`, please attention `HTTPMaximumConnectionsPerHost` property, it may causes timeout when in conjunction with `maxNumberOfConcurrentDownloads` if you set a larger number to `maxNumberOfConcurrentDownloads` but smaller number to `HTTPMaximumConnectionsPerHost`. + */ ++ (void)setSharedImageManagerWithConfiguration:(nullable NSURLSessionConfiguration *)configuration; + + +/** + The result of this method is assigned to self.cache in init. If you wish to provide a customized cache to the manager you can subclass PINRemoteImageManager and return a custom object, implementing PINRemoteImageCaching protocol from this method. Same effect could be achieved by using initWithSessionConfiguration:alternativeRepresentationProvider:imageCache: initializer. + @deprecated Use the class method +defaultImageCache + @warning This method is meant only for override. It will be called *once* by an instance of PINRemoteImageManager. The default implementation creates a new cache on every call. If you're looking to access the cache being used by an instance of PINRemoteImageManager, @c cache. + @return An instance of a object, implementing PINRemoteImageCaching protocol. + */ +- (nonnull id)defaultImageCache __attribute__((deprecated)); + +/** + The result of this method is assigned to self.cache in init. If you wish to provide a customized cache to the manager you can subclass PINRemoteImageManager and return a custom object, implementing PINRemoteImageCaching protocol from this method. Same effect could be achieved by using initWithSessionConfiguration:alternativeRepresentationProvider:imageCache: initializer. + @warning This method is meant only for override. It will be called *once* by an instance of PINRemoteImageManager. The default implementation creates a new cache on every call. If you're looking to access the cache being used by an instance of PINRemoteImageManager, @c cache. + @return An instance of a object, implementing PINRemoteImageCaching protocol. + */ ++ (nonnull id)defaultImageCache; + +/** + * If you want a Ttl (maxAge) image cache, you can pass the result of this method explicitly into the initWithSessionConfiguration:alternativeRepresentationProvider:imageCache: initializer. + * @return An instance of a object, implementing PINRemoteImageCaching protocol. + */ ++ (nonnull id)defaultImageTtlCache; + +/** + * Sets a custom header to be included in every request. Headers set from this method will override any header from NSURLSessionConfiguration. + * + * @deprecated Use NSURLSessionConfiguration.HTTPAdditionalHeaders instead + * @param value A value for the header. Pass in nil to remove a previously set value. + * @param header A string field for header. + + */ +- (void)setValue:(nullable NSString *)value forHTTPHeaderField:(nullable NSString *)header __attribute__((deprecated)); + +/** + Sets the Request Configuration Block. + + @param configurationBlock A PINRemoteImageManagerRequestConfigurationHandler block. + */ +- (void)setRequestConfiguration:(nullable PINRemoteImageManagerRequestConfigurationHandler)configurationBlock; + +/** + Set the Authentication Challenge Block. + + @param challengeBlock A PINRemoteImageManagerAuthenticationChallenge block. + */ +- (void)setAuthenticationChallenge:(nullable PINRemoteImageManagerAuthenticationChallenge)challengeBlock; + +/** + Set the minimum BPS to download the highest quality image in a set. + @see downloadImageWithURLs:options:progressImage:completion: + + @param highQualityBPSThreshold bytes per second minimum. Defaults to 500000. + @param completion Completion to be called once highQualityBPSThreshold has been set. + */ +- (void)setHighQualityBPSThreshold:(float)highQualityBPSThreshold completion:(nullable dispatch_block_t)completion; + +/** + Set the maximum BPS to download the lowest quality image in a set. + @see downloadImageWithURLs:options:progressImage:completion: + + @param lowQualityBPSThreshold bytes per second maximum. Defaults to 50000. + @param completion Completion to be called once lowQualityBPSThreshold has been set. + */ +- (void)setLowQualityBPSThreshold:(float)lowQualityBPSThreshold + completion:(nullable dispatch_block_t)completion; + +/** + Set whether high quality images should be downloaded when a low quality image is cached if network connectivity has improved. + @see downloadImageWithURLs:options:progressImage:completion: + + @param shouldUpgradeLowQualityImages if YES, low quality images will be 'upgraded'. + @param completion Completion to be called once shouldUpgradeLowQualityImages has been set. + */ +- (void)setShouldUpgradeLowQualityImages:(BOOL)shouldUpgradeLowQualityImages + completion:(nullable dispatch_block_t)completion; + +/** + Set the maximum number of concurrent operations (decompressing images, creating gifs, etc). + + @param maxNumberOfConcurrentOperations The maximum number of concurrent operations. Defaults to NSOperationQueueDefaultMaxConcurrentOperationCount. + @param completion Completion to be called once maxNumberOfConcurrentOperations is set. + */ +- (void)setMaxNumberOfConcurrentOperations:(NSInteger)maxNumberOfConcurrentOperations + completion:(nullable dispatch_block_t)completion; + +/** + Set the maximum number of concurrent downloads. + + @param maxNumberOfConcurrentDownloads The maximum number of concurrent downloads. Defaults to 10, maximum 65535. + @param completion Completion to be called once maxNumberOfConcurrentDownloads is set. + */ +- (void)setMaxNumberOfConcurrentDownloads:(NSInteger)maxNumberOfConcurrentDownloads + completion:(nullable dispatch_block_t)completion; + +/** + Set the estimated time remaining to download threshold at which to generate progressive images. Progressive images previews will only be generated if the estimated remaining time on a download is greater than estimatedTimeRemainingThreshold. If estimatedTimeRemainingThreshold is less than or equal to zero, this check is skipped. + + @param estimatedRemainingTimeThreshold The estimated remaining time threshold used to decide to skip progressive rendering. Defaults to 0.1. + @param completion Completion to be called once estimatedTimeRemainingTimeThreshold is set. + */ +- (void)setEstimatedRemainingTimeThresholdForProgressiveDownloads:(NSTimeInterval)estimatedRemainingTimeThreshold + completion:(nullable dispatch_block_t)completion; + +/** + Sets the progress at which progressive images are generated. By default this is @[@0.00, @0.35, @0.65] which generates at most, 3 progressive images. The first progressive image will only be generated when at least one scan has been completed (so you never see half an image). + + @param progressThresholds an array of progress thresholds at which to generate progressive images. progress thresholds should range from 0.00 - 1.00. Defaults to @[@0.00, @0.35, @0.65] + @param completion Completion to be called once progressThresholds is set. + */ +- (void)setProgressThresholds:(nonnull NSArray *)progressThresholds + completion:(nullable dispatch_block_t)completion; + +/** + Sets whether PINRemoteImage should blur progressive render results + + @param shouldBlur A bool value indicating whether PINRemoteImage should blur progressive render results + @param completion Completion to be called once progressThresholds is set. + */ +- (void)setProgressiveRendersShouldBlur:(BOOL)shouldBlur + completion:(nullable dispatch_block_t)completion; + +/** + Sets the maximum size of an image that PINRemoteImage will render progessively. If the image is too large, progressive rendering is skipped. + + @param maxProgressiveRenderSize A CGSize which indicates the max size PINRemoteImage will render a progressive image. If an image is larger in either dimension, progressive rendering will be skipped + @param completion Completion to be called once maxProgressiveRenderSize is set. + */ +- (void)setProgressiveRendersMaxProgressiveRenderSize:(CGSize)maxProgressiveRenderSize + completion:(nullable dispatch_block_t)completion; + +/** + Sets a metrics callback block to be called when NSURLSessionTaskMetrics are reported for downloads. + + @warning PINRemoteImageManager will hold a strong reference to metricsCallback. Avoid retain cycles by using weak references in the block! + */ +- (void)setMetricsCallback:(nullable PINRemoteImageManagerMetrics)metricsCallback + completion:(nullable dispatch_block_t)completion API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0)); + +/** + Prefetch an image at the given URL. + + @param url NSURL where the image to prefetch resides. + */ +- (nullable NSUUID *)prefetchImageWithURL:(nonnull NSURL *)url; + +/** + Prefetch an image at the given URL with given options. + + @param url NSURL where the image to prefetch resides. + @param options PINRemoteImageManagerDownloadOptions options with which to pefetch the image. + */ +- (nullable NSUUID *)prefetchImageWithURL:(nonnull NSURL *)url options:(PINRemoteImageManagerDownloadOptions)options; + +/** + Prefetch an image at the given URL with given options. + + @param url NSURL where the image to prefetch resides. + @param options PINRemoteImageManagerDownloadOptions options with which to prefetch the image. + @param priority PINRemoteImageManagerPriority priority of the operation when to prefetch the image. + */ +- (nullable NSUUID *)prefetchImageWithURL:(nonnull NSURL *)url options:(PINRemoteImageManagerDownloadOptions)options priority:(PINRemoteImageManagerPriority)priority; + +/** + Prefetch images at the given URLs. + + @param urls An array of NSURLs where the images to prefetch reside. + */ +- (nonnull NSArray *)prefetchImagesWithURLs:(nonnull NSArray *)urls; + +/** + Prefetch images at the given URLs with given options. + + @param urls An array of NSURLs where the images to prefetch reside. + @param options PINRemoteImageManagerDownloadOptions options with which to pefetch the image. + */ +- (nonnull NSArray *)prefetchImagesWithURLs:(nonnull NSArray *)urls options:(PINRemoteImageManagerDownloadOptions)options; + +/** + Prefetch images at the given URLs with given options. + + @param urls An array of NSURLs where the images to prefetch reside. + @param options PINRemoteImageManagerDownloadOptions options with which to pefetch the image. + @param priority PINRemoteImageManagerPriority priority of the operation to prefetch the image. + */ +- (nonnull NSArray *)prefetchImagesWithURLs:(nonnull NSArray *)urls options:(PINRemoteImageManagerDownloadOptions)options priority:(PINRemoteImageManagerPriority)priority; + +/** + Download or retrieve from cache the image found at the url. All completions are called on an arbitrary callback queue unless called on the main thread and the result is in the memory cache (this is an optimization to allow synchronous results for the UI when an object is cached in memory). + + @param url NSURL where the image to download resides. + @param completion PINRemoteImageManagerImageCompletion block to call when image has been fetched from the cache or downloaded. + @return An NSUUID which uniquely identifies this request. To be used for canceling requests and verifying that the callback is for the request you expect (see categories for example). + */ +- (nullable NSUUID *)downloadImageWithURL:(nonnull NSURL *)url completion:(nullable PINRemoteImageManagerImageCompletion)completion; + +/** + Download or retrieve from cache the image found at the url. All completions are called on an arbitrary callback queue unless called on the main thread and the result is in the memory cache (this is an optimization to allow synchronous results for the UI when an object is cached in memory). + + @param url NSURL where the image to download resides. + @param options PINRemoteImageManagerDownloadOptions options with which to fetch the image. + @param completion PINRemoteImageManagerImageCompletion block to call when image has been fetched from the cache or downloaded. + @return An NSUUID which uniquely identifies this request. To be used for canceling requests and verifying that the callback is for the request you expect (see categories for example). + */ +- (nullable NSUUID *)downloadImageWithURL:(nonnull NSURL *)url + options:(PINRemoteImageManagerDownloadOptions)options + completion:(nullable PINRemoteImageManagerImageCompletion)completion; + +/** + Download or retrieve from cache the image found at the url. All completions are called on an arbitrary callback queue unless called on the main thread and the result is in the memory cache (this is an optimization to allow synchronous results for the UI when an object is cached in memory). + + @param url NSURL where the image to download resides. + @param options PINRemoteImageManagerDownloadOptions options with which to fetch the image. + @param progressImage PINRemoteImageManagerImageCompletion block which will be called to update progress of the image download. + @param completion PINRemoteImageManagerImageCompletion block to call when image has been fetched from the cache or downloaded. + + @return An NSUUID which uniquely identifies this request. To be used for canceling requests and verifying that the callback is for the request you expect (see categories for example). + */ +- (nullable NSUUID *)downloadImageWithURL:(nonnull NSURL *)url + options:(PINRemoteImageManagerDownloadOptions)options + progressImage:(nullable PINRemoteImageManagerImageCompletion)progressImage + completion:(nullable PINRemoteImageManagerImageCompletion)completion; + +/** + Download or retrieve from cache the image found at the url. All completions are called on an arbitrary callback queue unless called on the main thread and the result is in the memory cache (this is an optimization to allow synchronous results for the UI when an object is cached in memory). + + @param url NSURL where the image to download resides. + @param options PINRemoteImageManagerDownloadOptions options with which to fetch the image. + @param progressDownload PINRemoteImageManagerDownloadProgress block which will be called to update progress in bytes of the image download. NOTE: For performance reasons, this block is not called on the main thread every time, if you need to update your UI ensure that you dispatch to the main thread first. + @param completion PINRemoteImageManagerImageCompletion block to call when image has been fetched from the cache or downloaded. + + @return An NSUUID which uniquely identifies this request. To be used for canceling requests and verifying that the callback is for the request you expect (see categories for example). + */ +- (nullable NSUUID *)downloadImageWithURL:(nonnull NSURL *)url + options:(PINRemoteImageManagerDownloadOptions)options + progressDownload:(nullable PINRemoteImageManagerProgressDownload)progressDownload + completion:(nullable PINRemoteImageManagerImageCompletion)completion; + +/** + Download or retrieve from cache the image found at the url. All completions are called on an arbitrary callback queue unless called on the main thread and the result is in the memory cache (this is an optimization to allow synchronous results for the UI when an object is cached in memory). + + @param url NSURL where the image to download resides. + @param options PINRemoteImageManagerDownloadOptions options with which to fetch the image. + @param progressImage PINRemoteImageManagerImageCompletion block which will be called to update progress of the image download. + @param progressDownload PINRemoteImageManagerDownloadProgress block which will be called to update progress in bytes of the image download. NOTE: For performance reasons, this block is not called on the main thread every time, if you need to update your UI ensure that you dispatch to the main thread first. + @param completion PINRemoteImageManagerImageCompletion block to call when image has been fetched from the cache or downloaded. + + @return An NSUUID which uniquely identifies this request. To be used for canceling requests and verifying that the callback is for the request you expect (see categories for example). + */ +- (nullable NSUUID *)downloadImageWithURL:(nonnull NSURL *)url + options:(PINRemoteImageManagerDownloadOptions)options + progressImage:(nullable PINRemoteImageManagerImageCompletion)progressImage + progressDownload:(nullable PINRemoteImageManagerProgressDownload)progressDownload + completion:(nullable PINRemoteImageManagerImageCompletion)completion; + +/** + Download or retrieve from cache the image found at the url and process it before calling completion. All completions are called on an arbitrary callback queue unless called on the main thread and the result is in the memory cache (this is an optimization to allow synchronous results for the UI when an object is cached in memory). + + @param url NSURL where the image to download resides. + @param options PINRemoteImageManagerDownloadOptions options with which to fetch the image. + @param priority PINRemoteImageManagerPriority which indicates the priority of the download task. + @param progressImage PINRemoteImageManagerImageCompletion block which will be called to update progress of the image download. + @param progressDownload PINRemoteImageManagerDownloadProgress block which will be called to update progress in bytes of the image download. NOTE: For performance reasons, this block is not called on the main thread every time, if you need to update your UI ensure that you dispatch to the main thread first. + @param completion PINRemoteImageManagerImageCompletion block to call when image has been fetched from the cache or downloaded. + + @return An NSUUID which uniquely identifies this request. To be used for canceling requests and verifying that the callback is for the request you expect (see categories for example). + */ +- (nullable NSUUID *)downloadImageWithURL:(nonnull NSURL *)url + options:(PINRemoteImageManagerDownloadOptions)options + priority:(PINRemoteImageManagerPriority)priority + progressImage:(nullable PINRemoteImageManagerImageCompletion)progressImage + progressDownload:(nullable PINRemoteImageManagerProgressDownload)progressDownload + completion:(nullable PINRemoteImageManagerImageCompletion)completion; + +/** + Download or retrieve from cache the image found at the url and process it before calling completion. All completions are called on an arbitrary callback queue unless called on the main thread and the result is in the memory cache (this is an optimization to allow synchronous results for the UI when an object is cached in memory). + + @param url NSURL where the image to download resides. + @param options PINRemoteImageManagerDownloadOptions options with which to fetch the image. + @param processorKey NSString key to uniquely identify processor and process. Will be used for caching processed images. + @param processor PINRemoteImageManagerImageProcessor block which will be called to post-process downloaded image. + @param completion PINRemoteImageManagerImageCompletion block to call when image has been fetched from the cache or downloaded. + + @return An NSUUID which uniquely identifies this request. To be used for canceling requests and verifying that the callback is for the request you expect (see categories for example). + */ +- (nullable NSUUID *)downloadImageWithURL:(nonnull NSURL *)url + options:(PINRemoteImageManagerDownloadOptions)options + processorKey:(nullable NSString *)processorKey + processor:(nullable PINRemoteImageManagerImageProcessor)processor + completion:(nullable PINRemoteImageManagerImageCompletion)completion; + +/** + Download or retrieve from cache the image found at the url and process it before calling completion. All completions are called on an arbitrary callback queue unless called on the main thread and the result is in the memory cache (this is an optimization to allow synchronous results for the UI when an object is cached in memory). + + @param url NSURL where the image to download resides. + @param options PINRemoteImageManagerDownloadOptions options with which to fetch the image. + @param processorKey NSString key to uniquely identify processor and process. Will be used for caching processed images. + @param progressDownload PINRemoteImageManagerDownloadProgress block which will be called to update progress in bytes of the image download. NOTE: For performance reasons, this block is not called on the main thread every time, if you need to update your UI ensure that you dispatch to the main thread first. + @param processor PINRemoteImageManagerImageProcessor block which will be called to post-process downloaded image. + @param completion PINRemoteImageManagerImageCompletion block to call when image has been fetched from the cache or downloaded. + + @return An NSUUID which uniquely identifies this request. To be used for canceling requests and verifying that the callback is for the request you expect (see categories for example). + */ +- (nullable NSUUID *)downloadImageWithURL:(nonnull NSURL *)url + options:(PINRemoteImageManagerDownloadOptions)options + processorKey:(nullable NSString *)processorKey + processor:(nullable PINRemoteImageManagerImageProcessor)processor + progressDownload:(nullable PINRemoteImageManagerProgressDownload)progressDownload + completion:(nullable PINRemoteImageManagerImageCompletion)completion; + +/** + Download or retrieve from cache one of the images found at the urls in the passed in array based on current network performance. URLs should be sorted from lowest quality image URL to highest. All completions are called on an arbitrary callback queue unless called on the main thread and the result is in the memory cache (this is an optimization to allow synchronous results for the UI when an object is cached in memory). + + Unless setShouldUpgradeLowQualityImages is set to YES, this method checks the cache for all URLs and returns the highest quality version stored. It is possible though unlikely for a cached image to not be returned if it is still being cached while a call is made to this method and if network conditions have changed. See source for more details. + + @param urls An array of NSURLs of increasing cost to download. + @param options PINRemoteImageManagerDownloadOptions options with which to fetch the image. + @param progressImage PINRemoteImageManagerImageCompletion block which will be called to update progress of the image download. + @param completion PINRemoteImageManagerImageCompletion block to call when image has been fetched from the cache or downloaded. + + @return An NSUUID which uniquely identifies this request. To be used for canceling requests and verifying that the callback is for the request you expect (see categories for example). + */ +- (nullable NSUUID *)downloadImageWithURLs:(nonnull NSArray *)urls + options:(PINRemoteImageManagerDownloadOptions)options + progressImage:(nullable PINRemoteImageManagerImageCompletion)progressImage + completion:(nullable PINRemoteImageManagerImageCompletion)completion; + +/** + Download or retrieve from cache one of the images found at the urls in the passed in array based on current network performance. URLs should be sorted from lowest quality image URL to highest. All completions are called on an arbitrary callback queue unless called on the main thread and the result is in the memory cache (this is an optimization to allow synchronous results for the UI when an object is cached in memory). + + Unless setShouldUpgradeLowQualityImages is set to YES, this method checks the cache for all URLs and returns the highest quality version stored. It is possible though unlikely for a cached image to not be returned if it is still being cached while a call is made to this method and if network conditions have changed. See source for more details. + + @param urls An array of NSURLs of increasing cost to download. + @param options PINRemoteImageManagerDownloadOptions options with which to fetch the image. + @param progressImage PINRemoteImageManagerImageCompletion block which will be called to update progress of the image download. + @param progressDownload PINRemoteImageManagerDownloadProgress block which will be called to update progress in bytes of the image download. NOTE: For performance reasons, this block is not called on the main thread every time, if you need to update your UI ensure that you dispatch to the main thread first. + @param completion PINRemoteImageManagerImageCompletion block to call when image has been fetched from the cache or downloaded. + + @return An NSUUID which uniquely identifies this request. To be used for canceling requests and verifying that the callback is for the request you expect (see categories for example). + */ +- (nullable NSUUID *)downloadImageWithURLs:(nonnull NSArray *)urls + options:(PINRemoteImageManagerDownloadOptions)options + progressImage:(nullable PINRemoteImageManagerImageCompletion)progressImage + progressDownload:(nullable PINRemoteImageManagerProgressDownload)progressDownload + completion:(nullable PINRemoteImageManagerImageCompletion)completion; + + +/** + Adds an image manually into the memory and disk cache. + + @param data NSData with the raw image data. + @param url NSURL where the image resides. + @param processorKey NSString key to uniquely identify processor and process. Will be used for caching processed images. + @param additionalCost NSUInteger the additional cost (for cache eviction purposes) to generate the processed image + + @return A BOOL indicating if the image was successfully added to the cache. + */ +- (BOOL) insertImageDataIntoCache:(nonnull NSData*)data + withURL:(nonnull NSURL *)url + processorKey:(nullable NSString *)processorKey + additionalCost:(NSUInteger)additionalCost; + +/** + Returns the cacheKey for a given URL and processorKey. Exposed to be overridden if necessary or to be used with imageFromCacheWithCacheKey + @see imageFromCacheWithCacheKey:completion: + + @param url NSURL that was used to download image + @param processorKey An optional key to uniquely identify the processor used to post-process the downloaded image. + + @return returns an NSString which is the key used for caching. + */ +- (nonnull NSString *)cacheKeyForURL:(nonnull NSURL *)url processorKey:(nullable NSString *)processorKey; + +/** + @see imageFromCacheWithURL:processorKey:options:completion: + @deprecated + + @param cacheKey NSString key to look up image in the cache. + @param completion PINRemoteImageManagerImageCompletion block to call when image has been fetched from the cache. + */ +- (void)imageFromCacheWithCacheKey:(nonnull NSString *)cacheKey completion:(nonnull PINRemoteImageManagerImageCompletion)completion __attribute__((deprecated)); + +/** + @see imageFromCacheWithURL:processorKey:options:completion: + @deprecated + + @param cacheKey NSString key to look up image in the cache. + @param options options will be used to determine if the cached image should be decompressed or PINCachedAnimatedImages should be returned. + @param completion PINRemoteImageManagerImageCompletion block to call when image has been fetched from the cache. + */ +- (void)imageFromCacheWithCacheKey:(nonnull NSString *)cacheKey options:(PINRemoteImageManagerDownloadOptions)options completion:(nonnull PINRemoteImageManagerImageCompletion)completion __attribute__((deprecated)); + +/** + Directly get an image from the underlying cache. + + @param url NSURL that was used to download image + @param processorKey An optional key to uniquely identify the processor used to post-process the downloaded image. + @param options options will be used to determine if the cached image should be decompressed or PINCachedAnimatedImages should be returned. + @param completion PINRemoteImageManagerImageCompletion block to call when image has been fetched from the cache. + */ +- (void)imageFromCacheWithURL:(nonnull NSURL *)url processorKey:(nullable NSString *)processorKey options:(PINRemoteImageManagerDownloadOptions)options completion:(nonnull PINRemoteImageManagerImageCompletion)completion; + +/** + @deprecated + @see synchronousImageFromCacheWithURL:processorKey:options: + + @param cacheKey NSString obtained from @c cacheKeyForURL:processorKey + @param options options will be used to determine if the cached image should be decompressed or PINCachedAnimatedImages should be returned. + + @return A PINRemoteImageManagerResult + */ +- (nonnull PINRemoteImageManagerResult *)synchronousImageFromCacheWithCacheKey:(nonnull NSString *)cacheKey options:(PINRemoteImageManagerDownloadOptions)options __attribute__((deprecated)); + +/** + Directly get an image from the underlying memory cache synchronously. + + @param url NSURL that was used to download image + @param processorKey An optional key to uniquely identify the processor used to post-process the downloaded image. + @param options options will be used to determine if the cached image should be decompressed or PINCachedAnimatedImages should be returned. + + @return A PINRemoteImageManagerResult + */ +- (nonnull PINRemoteImageManagerResult *)synchronousImageFromCacheWithURL:(nonnull NSURL *)url processorKey:(nullable NSString *)processorKey options:(PINRemoteImageManagerDownloadOptions)options; + +/** + Cancel a download. Canceling will only cancel the download if all other downloads are also canceled with their associated UUIDs. + Canceling *does not* guarantee that your completion will not be called. You can use the UUID provided on the result object to verify + the completion you want called is being called. + @see PINRemoteImageCategoryManager + + @param UUID NSUUID of the task to cancel. + */ +- (void)cancelTaskWithUUID:(nonnull NSUUID *)UUID; + +/** + Cancel a download. Canceling will only cancel the download if all other downloads are also canceled with their associated UUIDs. + Canceling *does not* guarantee that your completion will not be called. You can use the UUID provided on the result object to verify + the completion you want called is being called. + @param storeResumeData if YES and the server indicates it supports resuming downloads, downloaded data will be stored in the disk + cache and used to resume the download if the same URL is attempted to be downloaded in the future. + PINRemoteImageBasicCache does not support disk caching, use PINCache. + */ +- (void)cancelTaskWithUUID:(nonnull NSUUID *)UUID storeResumeData:(BOOL)storeResumeData; + +/** + Cancel all tasks. + */ +- (void)cancelAllTasks; + +/** + Cancel all tasks and store resume data if any task has. + */ +- (void)cancelAllTasksAndStoreResumeData:(BOOL)storeResumeData; + +/** + Set the priority of a download task. Since there is only one task per download, the priority of the download task will always be the last priority this method was called with. + + @param priority priority to set on the task. + @param UUID NSUUID of the task to set the priority on. + */ +- (void)setPriority:(PINRemoteImageManagerPriority)priority ofTaskWithUUID:(nonnull NSUUID *)UUID; + +/** + * @abstract set the progress callback on a download task. You can use this to add progress callbacks or remove them for in flight downloads + * + * @param progressImageCallback a PINRemoteImageManagerImageCompletion block to be called with a progress update + * @param UUID NSUUID of the task to set the priority on. + */ +- (void)setProgressImageCallback:(nullable PINRemoteImageManagerImageCompletion)progressImageCallback ofTaskWithUUID:(nonnull NSUUID *)UUID; + +/** + Set retry strategy for all requests + + @param retryStrategyCreationBlock a block which should create new instance of PINRequestRetryStrategy when called. + */ +- (void)setRetryStrategyCreationBlock:(_Nonnull id (^_Nonnull)(void))retryStrategyCreationBlock; + +@property (nonatomic, readonly) _Nonnull id (^_Nonnull retryStrategyCreationBlock)(void); + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINRemoteImageManagerResult.h b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINRemoteImageManagerResult.h new file mode 100644 index 0000000..41ff8c5 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINRemoteImageManagerResult.h @@ -0,0 +1,71 @@ +// +// PINRemoteImageManagerResult.h +// Pods +// +// Created by Garrett Moon on 3/9/15. +// +// + +#import + +#import "PINRemoteImageMacros.h" + +#if PIN_TARGET_IOS +#import +#elif PIN_TARGET_MAC +#import +#endif + +/** How the image was fetched. */ +typedef NS_ENUM(NSUInteger, PINRemoteImageResultType) { + /** Returned if no image is returned */ + PINRemoteImageResultTypeNone = 0, + /** Image was fetched from the memory cache */ + PINRemoteImageResultTypeMemoryCache, + /** Image was fetched from the disk cache */ + PINRemoteImageResultTypeCache, + /** Image was downloaded */ + PINRemoteImageResultTypeDownload, + /** Image is progress */ + PINRemoteImageResultTypeProgress, +}; + +@interface PINRemoteImageManagerResult : NSObject + +@property (nonatomic, readonly, strong, nullable) PINImage *image; +@property (nonatomic, readonly, strong, nullable) id alternativeRepresentation; +@property (nonatomic, readonly, assign) NSTimeInterval requestDuration; +@property (nonatomic, readonly, strong, nullable) NSError *error; +@property (nonatomic, readonly, assign) PINRemoteImageResultType resultType; +@property (nonatomic, readonly, strong, nullable) NSUUID *UUID; +@property (nonatomic, readonly, assign) CGFloat renderedImageQuality; +@property (nonatomic, readonly, assign) NSUInteger bytesSavedByResuming; +@property (nonatomic, readonly, strong, nullable) NSURLResponse *response; + ++ (nonnull instancetype)imageResultWithImage:(nullable PINImage *)image + alternativeRepresentation:(nullable id)alternativeRepresentation + requestLength:(NSTimeInterval)requestLength + resultType:(PINRemoteImageResultType)resultType + UUID:(nullable NSUUID *)uuid + response:(nullable NSURLResponse *)response + error:(nullable NSError *)error; + ++ (nonnull instancetype)imageResultWithImage:(nullable PINImage *)image + alternativeRepresentation:(nullable id)alternativeRepresentation + requestLength:(NSTimeInterval)requestLength + resultType:(PINRemoteImageResultType)resultType + UUID:(nullable NSUUID *)uuid + response:(nullable NSURLResponse *)response + error:(nullable NSError *)error + bytesSavedByResuming:(NSUInteger)bytesSavedByResuming; + ++ (nonnull instancetype)imageResultWithImage:(nullable PINImage *)image + alternativeRepresentation:(nullable id)alternativeRepresentation + requestLength:(NSTimeInterval)requestLength + resultType:(PINRemoteImageResultType)resultType + UUID:(nullable NSUUID *)uuid + response:(nullable NSURLResponse *)response + error:(nullable NSError *)error + renderedImageQuality:(CGFloat)renderedImageQuality; + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINRequestRetryStrategy.h b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINRequestRetryStrategy.h new file mode 100644 index 0000000..448ec8d --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINRequestRetryStrategy.h @@ -0,0 +1,28 @@ +// +// PINRequestRetryStrategy.h +// Pods +// +// Created by Hovhannes Safaryan on 9/24/16. +// +// + +#import + +/* + Decide whether request should be retried based on the error. + **/ + +@protocol PINRequestRetryStrategy + +- (BOOL)shouldRetryWithError:(NSError *)error; +- (int)nextDelay; +- (void)incrementRetryCount; +- (int)numberOfRetries; + +@end + +@interface PINRequestExponentialRetryStrategy : NSObject + +- (instancetype)initWithRetryMaxCount:(int)retryMaxCount delayBase:(int)delayBase; + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINURLSessionManager.h b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINURLSessionManager.h new file mode 100644 index 0000000..5b25ff1 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINURLSessionManager.h @@ -0,0 +1,51 @@ +// +// PINURLSessionManager.h +// Pods +// +// Created by Garrett Moon on 6/26/15. +// +// + +#import + +#import "PINRemoteImageManager.h" + +extern NSErrorDomain _Nonnull const PINURLErrorDomain; + +@protocol PINURLSessionManagerDelegate + +@required +- (void)didReceiveData:(nonnull NSData *)data forTask:(nonnull NSURLSessionTask *)task; + +@optional +- (void)didCollectMetrics:(nonnull NSURLSessionTaskMetrics *)metrics forURL:(nonnull NSURL *)url API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0)); +- (void)didReceiveResponse:(nonnull NSURLResponse *)response forTask:(nonnull NSURLSessionTask *)task; +- (void)didReceiveAuthenticationChallenge:(nonnull NSURLAuthenticationChallenge *)challenge forTask:(nullable NSURLSessionTask *)task completionHandler:(nonnull void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler; +- (void)didCompleteTask:(nonnull NSURLSessionTask *)task withError:(nullable NSError *)error; + +@end + +typedef void (^PINURLSessionDataTaskCompletion)(NSURLSessionTask * _Nonnull task, NSError * _Nullable error); + +@interface PINURLSessionManager : NSObject + +- (nonnull instancetype)initWithSessionConfiguration:(nullable NSURLSessionConfiguration *)configuration; + +- (nonnull NSURLSessionDataTask *)dataTaskWithRequest:(nonnull NSURLRequest *)request + completionHandler:(nonnull PINURLSessionDataTaskCompletion)completionHandler; + +- (nonnull NSURLSessionDataTask *)dataTaskWithRequest:(nonnull NSURLRequest *)request + priority:(PINRemoteImageManagerPriority)priority + completionHandler:(nonnull PINURLSessionDataTaskCompletion)completionHandler; + +- (void)invalidateSessionAndCancelTasks; + +- (void)URLSession:(nonnull NSURLSession *)session task:(nonnull NSURLSessionTask *)task didFinishCollectingMetrics:(nonnull NSURLSessionTaskMetrics *)metrics API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0)); + +@property (atomic, weak, nullable) id delegate; + +#if DEBUG +- (void)concurrentDownloads:(void (^_Nullable)(NSUInteger concurrentDownloads))concurrentDownloadsCompletion; +#endif + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINWebPAnimatedImage.h b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINWebPAnimatedImage.h new file mode 100644 index 0000000..6bad969 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/PINRemoteImage/Source/Classes/include/PINWebPAnimatedImage.h @@ -0,0 +1,19 @@ +// +// PINWebPAnimatedImage.h +// PINRemoteImage +// +// Created by Garrett Moon on 9/14/17. +// Copyright © 2017 Pinterest. All rights reserved. +// + +#import +#import + +#import "PINAnimatedImage.h" + +@interface PINWebPAnimatedImage : PINAnimatedImage + +- (instancetype)init NS_UNAVAILABLE; +- (instancetype)initWithAnimatedImageData:(NSData *)animatedImageData NS_DESIGNATED_INITIALIZER; + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Pods.xcodeproj/project.pbxproj b/Starbucks_Clone_TexBrother/Pods/Pods.xcodeproj/project.pbxproj new file mode 100644 index 0000000..8506176 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Pods.xcodeproj/project.pbxproj @@ -0,0 +1,3237 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + 02131FDD47626B3DCE579DFFC4E44CC2 /* PINRemoteImageProcessorTask.h in Headers */ = {isa = PBXBuildFile; fileRef = 2D08D914160D3368A237E541232421E1 /* PINRemoteImageProcessorTask.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0284D979FE5030B1BBE20318DFF83407 /* ASAsciiArtBoxCreator.mm in Sources */ = {isa = PBXBuildFile; fileRef = 201D42F70CB101B7CC761B365CB0B2F5 /* ASAsciiArtBoxCreator.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 03177DA0581846B708D9C1B4C69AF5B4 /* ASConfiguration.mm in Sources */ = {isa = PBXBuildFile; fileRef = C58EA3FBFBA1476CBC65553056DC50C2 /* ASConfiguration.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 0368A0C1EE4DCCF63368E227AABF90FA /* _ASHierarchyChangeSet.h in Headers */ = {isa = PBXBuildFile; fileRef = 8BC18D4DB7AA56688D0AF9BDDE7BE8F5 /* _ASHierarchyChangeSet.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 04543BFEF8F8F32EE2B4A3304471A5F4 /* PINRemoteImageManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 8FA479C38E875642DC6D0F70C22C53E2 /* PINRemoteImageManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 047062E4C55AF0E9668F0D8A5210D4FA /* PINRemoteImageProcessorTask.m in Sources */ = {isa = PBXBuildFile; fileRef = 033CCEDAB42EDF1F79E201309B0557B6 /* PINRemoteImageProcessorTask.m */; }; + 048B9EDF57D0234296B8F7C58A17890F /* Pods-Starbucks_Clone_TexBrother-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 07B22A0BED1962C77F9548379F0C548E /* Pods-Starbucks_Clone_TexBrother-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 065EC5AD7B86716183B6C6F10214ECE9 /* ASAbsoluteLayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = E69484760E2878ADDAF63DEB7003DCDC /* ASAbsoluteLayoutSpec.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 0671472257D854CBC8F14A6460CBD70A /* ASMainThreadDeallocation.mm in Sources */ = {isa = PBXBuildFile; fileRef = 74E4E43E31FFF2CF7046E6A3658D79E5 /* ASMainThreadDeallocation.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 070ABBECB4EDE07A98B7B8194C7653F1 /* ASInternalHelpers.h in Headers */ = {isa = PBXBuildFile; fileRef = EC4BC1F8BC97094F5292F0627A230B03 /* ASInternalHelpers.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 0731FF1F4C72E85A9CF8BC8FA403E7CE /* ASBatchFetching.mm in Sources */ = {isa = PBXBuildFile; fileRef = FCEC18E333AD45AB195BE9A624EE1295 /* ASBatchFetching.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 07DA5777E40B8C2671D3103F8E76F969 /* PINProgressiveImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 4FE8339B40FB00918941FBCDC5F30D28 /* PINProgressiveImage.m */; }; + 08E319FC15C276F9F94CE6E16D9F3D06 /* PINOperation-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 741BAE82F2A2B69F81AE5008435BADE3 /* PINOperation-dummy.m */; }; + 095EAC3436164EBB3B0DA2704BEE8D7A /* ASDimension.mm in Sources */ = {isa = PBXBuildFile; fileRef = 7BA62A47FF53155919AA67CBCC0B126A /* ASDimension.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 09650E825B735144A1D66BFD8E55DE4A /* ASImageProtocols.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BADB70D6F6D85A658168553A700DC9F /* ASImageProtocols.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 09D5CC75390536E1FD6F02F5AE3862AE /* ASTwoDimensionalArrayUtils.mm in Sources */ = {isa = PBXBuildFile; fileRef = 070352748F86CF00AF87F8EB9D2DE7E5 /* ASTwoDimensionalArrayUtils.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 0A1A2CA05738BF97386132DC453056D3 /* AsyncDisplayKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 5FBEE9DF8CEE0978987520A5AB6353FD /* AsyncDisplayKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0A8BEE494BB1DFA8A3711444D6CF5728 /* ASDKViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 00A4E1BB5E6F3722E8E36E1169E8D092 /* ASDKViewController.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 0AB37573CCCB26C65FBC953E9DE6E3CA /* ASPendingStateController.h in Headers */ = {isa = PBXBuildFile; fileRef = 5E78560F78CB8BFA4466E9D41BC5176F /* ASPendingStateController.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 0BE5326613B0772676696939B028455F /* ASTextNodeWordKerner.h in Headers */ = {isa = PBXBuildFile; fileRef = AF598AFD0BCBA155CCD60D60E7682662 /* ASTextNodeWordKerner.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 0C8AFCED476D2CA82A33B9A602B7806E /* ASDisplayNode+UIViewBridge.mm in Sources */ = {isa = PBXBuildFile; fileRef = DABECC346A0C5E457DD875465E22622F /* ASDisplayNode+UIViewBridge.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 0D1AFB17D23CD14FEF004D8F753E170F /* PINImageView+PINRemoteImage.h in Headers */ = {isa = PBXBuildFile; fileRef = 72097EDC568749414EDF3AC657BB07C3 /* PINImageView+PINRemoteImage.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0D6962F9248D95D9655C562687D377DD /* PINRemoteImageCategoryManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 42F1BDB74A174F9531FB779E45BC1D50 /* PINRemoteImageCategoryManager.m */; }; + 0DB894195A40459248365D08C82C0BC5 /* ASNodeController+Beta.mm in Sources */ = {isa = PBXBuildFile; fileRef = B3E823C5AED7466242446DADE710BC14 /* ASNodeController+Beta.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 0DCE1ED366ED82607C1A1BE2C822852A /* NSParagraphStyle+ASText.h in Headers */ = {isa = PBXBuildFile; fileRef = 5956F6A0379F1B3E69E48CB79B712D20 /* NSParagraphStyle+ASText.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 0ECB1E4D8FA0F073DC3CF9E5EBCA4370 /* ASTextInput.mm in Sources */ = {isa = PBXBuildFile; fileRef = 376A2AC2E4CDCDE7BDB0A9D99DA37170 /* ASTextInput.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 0FFDB747E5756746172DD89CAC9073A5 /* _ASCollectionGalleryLayoutItem.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6F445AA2EBEFA2D30FF270056C1BC783 /* _ASCollectionGalleryLayoutItem.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 101880381147AEAF37111DAC551649D3 /* ASTextUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = BFA9545ACC55BD7967322ACA3CF6ABDA /* ASTextUtilities.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 12037D088DDA51D83DAED06FB069D79D /* ASLayoutManager.h in Headers */ = {isa = PBXBuildFile; fileRef = A8E85E8B5E162541ED900C4EE3F74124 /* ASLayoutManager.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 128159D60F5F30A14FFA7E2216B689A8 /* ASCollectionLayoutContext+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = D1B206A8F349D1A6BA64F7B57B7CC17A /* ASCollectionLayoutContext+Private.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 12BD7A4EE714789DE378364B25BC681F /* ASCollectionViewFlowLayoutInspector.mm in Sources */ = {isa = PBXBuildFile; fileRef = BD665D50848207C21A9980329097B251 /* ASCollectionViewFlowLayoutInspector.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 1414F0DF56982DFC65C6E01ECE6A4374 /* ASTextUtilities.mm in Sources */ = {isa = PBXBuildFile; fileRef = 19B3CE9F567E5E2BA615C5C6E5E7EB95 /* ASTextUtilities.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 14B7A03552B6697528E765E9DD51F34F /* ASPagerNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = CF4EE8C8467B3FC8306891C7E48F70C3 /* ASPagerNode.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 1563756D23089B763B4317FAC682C06A /* ASDisplayNodeExtras.h in Headers */ = {isa = PBXBuildFile; fileRef = 9E7888C7A1AA9B642609F19D11AF414E /* ASDisplayNodeExtras.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 15AB102788E5371031F010FB3494FDC4 /* ASDisplayNode+Convenience.h in Headers */ = {isa = PBXBuildFile; fileRef = A1666DABB81FF91AB2854A6FFA58794D /* ASDisplayNode+Convenience.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 162A11E39B08B85FD0E61EAB7E1FF9E0 /* ASElementMap.h in Headers */ = {isa = PBXBuildFile; fileRef = D18CF219AFB6A8D799381B345E84B2CD /* ASElementMap.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 16ABB0CB811745D54DC052D21166EF81 /* ASVisibilityProtocols.h in Headers */ = {isa = PBXBuildFile; fileRef = 87065E0D5E1E5ED7C51879E8AE1C6F41 /* ASVisibilityProtocols.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 17514581F08C4029877D4B19982B1AB0 /* ASControlNode+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 2D67B7BE32F2F7AC2A24618DF273D975 /* ASControlNode+Private.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 179E80830E414D876BAE96A5E9D033B1 /* ASTextKitComponents.h in Headers */ = {isa = PBXBuildFile; fileRef = B8AA8DB0A48C9AC29F30CC40A2173133 /* ASTextKitComponents.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 17ADD23D4835CB144BC6CD354844FF50 /* ASPagerNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 607EDA20ACB1995FE512E34150F222F1 /* ASPagerNode.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 17EE7FE8DDF79CAF395E4A3080D7B035 /* ASTextKitCoreTextAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 90AF45C081400459AA7B398DDFAE38FB /* ASTextKitCoreTextAdditions.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 18395D9552702EEA995F3EBDE3DDF009 /* ASWeakMap.h in Headers */ = {isa = PBXBuildFile; fileRef = C8EEA80259BB6314337EE232E2AF34F7 /* ASWeakMap.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 1B05E3D885BC93FAC2F5D2007A888386 /* ASTextKitRenderer+TextChecking.h in Headers */ = {isa = PBXBuildFile; fileRef = A3035FDC9B0F6D11945C34895FCBF6E6 /* ASTextKitRenderer+TextChecking.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 1B264109896A02DB437B7C2FF68DF711 /* PINResume.m in Sources */ = {isa = PBXBuildFile; fileRef = 73AA3A23C9F3D8C94C70E9CD5CAB902C /* PINResume.m */; }; + 1B2A0A4681935DC95834481F21759220 /* _ASAsyncTransaction.mm in Sources */ = {isa = PBXBuildFile; fileRef = E32EFE7E28F3D30B3ADE45D68C01B4E5 /* _ASAsyncTransaction.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 1B3BD47E66BBC5F931984D74A3E0047A /* ASMapNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = 098ADDB3AB7C97CB73062B784B1F66FA /* ASMapNode.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 1B7F2F9ABEC6E2EF28D4200AAA0EA967 /* _ASPendingState.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A8DF6DD2A4E0F0933CDAC8EBDA67FE3 /* _ASPendingState.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 1BC79FE7F10E5A56EBBB646BCA6A2974 /* ASExperimentalFeatures.h in Headers */ = {isa = PBXBuildFile; fileRef = 62310F0C73D1A8CA99D47886AE688B73 /* ASExperimentalFeatures.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 1C085C8ED0685A8CE4B5B5735C7F7EF6 /* UICollectionViewLayout+ASConvenience.h in Headers */ = {isa = PBXBuildFile; fileRef = E690EC656DC50FF9A5210F6CC30AE500 /* UICollectionViewLayout+ASConvenience.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 1C8C87AA0418AAB89B67CEB269819488 /* ASInsetLayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = 74086F595373CAC88B0BF99A2E21A0D5 /* ASInsetLayoutSpec.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 1D0CD1F6962F538225CE1E3DD8275C0D /* ASCollectionViewLayoutInspector.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1F16EC75D41F03D3E305917F4D03604C /* ASCollectionViewLayoutInspector.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 1D31956E47A1667E4ED78EDBCF63657F /* ASDataController.h in Headers */ = {isa = PBXBuildFile; fileRef = DCFAB58A9BA38E8EE152DE448425B204 /* ASDataController.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 1D503DFE241AA255DA1D05DB8E0DAEFD /* ASLayoutElementExtensibility.h in Headers */ = {isa = PBXBuildFile; fileRef = 694E56C9CFBEB7AE6FFD9B45B846993E /* ASLayoutElementExtensibility.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 1E5042D00F802CC758E27A3DFFA27413 /* ASBasicImageDownloader.mm in Sources */ = {isa = PBXBuildFile; fileRef = 74ED204CF8F96D2B3B75CB7545B09E1B /* ASBasicImageDownloader.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 1F51595178FA581C9FA84C59AAC0ADF2 /* ASDelegateProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = FF69CF2A36974F2ED05A1A2EAE1F39C3 /* ASDelegateProxy.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 1FF802E6BAC65A931C00BE18248620DC /* ASIGListAdapterBasedDataSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 54765F44027C7F1F4A283E5D8F1F57E8 /* ASIGListAdapterBasedDataSource.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 2045F583C05F550162C635C56DB9F44E /* ASTextNode.h in Headers */ = {isa = PBXBuildFile; fileRef = FFDEB71BA6A8CFC522FAF3AE658CC98C /* ASTextNode.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 238353CE094AADDB351A5AE7D730F8F0 /* ASPagerFlowLayout.mm in Sources */ = {isa = PBXBuildFile; fileRef = 76F307F7E0F3AB64C1D9E973EEFC055A /* ASPagerFlowLayout.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 23E0F25169AB9190135651F5D06E8AFE /* PINWebPAnimatedImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D673E12DB0B97C4702DCA57BD907DCD /* PINWebPAnimatedImage.m */; }; + 23E59691FBD46D8ACF53E02745593028 /* PINRemoteImageMemoryContainer.h in Headers */ = {isa = PBXBuildFile; fileRef = C9C4E917C6DD24E5A1A14EF3D1471B07 /* PINRemoteImageMemoryContainer.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2490B901F64B7A71103C5BACBBC14195 /* ASRangeController.mm in Sources */ = {isa = PBXBuildFile; fileRef = D4A2BD0267EFCC67E4D6E293788CBD3B /* ASRangeController.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 24E9D62BB1AEE5FC221F92CA8AFCBEC4 /* ASSupplementaryNodeSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 274DE44949B99702678B3A0AD3A3FD1A /* ASSupplementaryNodeSource.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 25153085368DA21ADFDAAD6DA0EFCCC9 /* ASTableView.h in Headers */ = {isa = PBXBuildFile; fileRef = 8C20EDFA6F12C47AF4639E4CB4A75E8B /* ASTableView.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 25283CF364080FF8D8C86F0B9D4936DE /* ASScrollNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 2500B92E1BDED42BF2C222E4683F6452 /* ASScrollNode.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 25950726ACAF3C2B3EF3B24499F05921 /* ASButtonNode+Yoga.mm in Sources */ = {isa = PBXBuildFile; fileRef = 07E4F833BCFBD185B878CF4D918216C2 /* ASButtonNode+Yoga.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 25B46F5BF00AFAE8A15CC01AE4E5A643 /* ASPageTable.h in Headers */ = {isa = PBXBuildFile; fileRef = E8739155AFF4384DA7B122A73A82B396 /* ASPageTable.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 25BDEB129E29246C4BB4044B849B90D6 /* ASPageTable.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1FF8C31089C5323E97CBF2EE23C48598 /* ASPageTable.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 25D87433F8998220C56DEB0CDDA167CA /* PINURLSessionManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 51E4828747ABF0F4B0D5210073FD2269 /* PINURLSessionManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 25F7C6715BCCB73CAA6C20FAE292CF9B /* ASWeakMap.mm in Sources */ = {isa = PBXBuildFile; fileRef = DD7D7ECC692E89521F0E4656EC06ADA5 /* ASWeakMap.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 26880973B4644E87C380BBB5E861951E /* ASCollectionViewLayoutController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9E3FC7BE9409137DCC7A948ECD5695F8 /* ASCollectionViewLayoutController.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 26B3AD7B5C5ED0A014DEFA3AA5CBD94A /* ASLayoutSpecPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = A8F705E5590C234FD939D87CED55C5ED /* ASLayoutSpecPrivate.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 26CC1A88B18BCEFF176989DDF566FEF2 /* ASDispatch.mm in Sources */ = {isa = PBXBuildFile; fileRef = 11B6DB33ACFDC65734A7C2E1F760BD60 /* ASDispatch.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 2718C849A663ADF0E69B40FFC1C5443E /* ASCollectionNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = BD92916D1ECF51A2D58AC7BF0A89F357 /* ASCollectionNode.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 2730E2AB0DF38DB833EF6AD02E788C2D /* _ASCollectionGalleryLayoutItem.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A01E0F76B1D746C5AB7D11D9AE6CC77 /* _ASCollectionGalleryLayoutItem.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 2799C7F1375B432E35F44F6B2011315E /* PINImage+ScaledImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 23B7B3467E387075BB087F9DF9D8EDF5 /* PINImage+ScaledImage.m */; }; + 285F918F7A4896FA40D130FD5F6955EA /* PINCache+PINRemoteImageCaching.m in Sources */ = {isa = PBXBuildFile; fileRef = 09018DF2292D2A0DB7CD1AE16D80E0C5 /* PINCache+PINRemoteImageCaching.m */; }; + 28A9C06F97CB780536F2C2405DF1E4CA /* ASBackgroundLayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0F5C33BA4E88B0B326DFFE9C5340A1E7 /* ASBackgroundLayoutSpec.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 28F61DD0EBC89532FF28A6F967B0DBC7 /* _ASCollectionGalleryLayoutInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 3398193884C73127717DAD2DD429D1DD /* _ASCollectionGalleryLayoutInfo.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 292C3F25A497C5764E033ABE3491999B /* ASImageContainerProtocolCategories.h in Headers */ = {isa = PBXBuildFile; fileRef = 60ECE226961E3317572472B9C7866939 /* ASImageContainerProtocolCategories.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2984AD51F86779E093DBAF2C5DA6CB67 /* PINAnimatedImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = C0F24E93C735911A21D52FCA96DE90EF /* PINAnimatedImageView.m */; }; + 29CAF11CD62CCBDC701E051CBF424C23 /* ASNetworkImageNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4932F584069CBF2BCED5E998CD893E9E /* ASNetworkImageNode.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 2BC2B38EA553A533961723CECFC08DA2 /* PINImage+WebP.m in Sources */ = {isa = PBXBuildFile; fileRef = 2AEE11DDE18165E6A0EE511B797F4CD4 /* PINImage+WebP.m */; }; + 2BEBC2DFEDD5845C56E21A6463822B63 /* ASLayoutElementStylePrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F909487B055776AE89A8FBB70923EBF /* ASLayoutElementStylePrivate.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 2BF1C444606DA73A783706F748051F54 /* ASTextNode+Beta.h in Headers */ = {isa = PBXBuildFile; fileRef = 6C9A4DBE7D41A766936D3801A3B99271 /* ASTextNode+Beta.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2C3EE7C1BE5D6BDB615ABD6589CC1A1F /* ASLayoutSpec+Subclasses.mm in Sources */ = {isa = PBXBuildFile; fileRef = 55BE5B0739B7FB893F77CCAF0101D9D7 /* ASLayoutSpec+Subclasses.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 2D9E8318C4E63140067B52E62DFF4B5B /* ASRecursiveUnfairLock.h in Headers */ = {isa = PBXBuildFile; fileRef = 97E7FC319F436F0B423EE720BD4114C4 /* ASRecursiveUnfairLock.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2E1B3B900BECDFD697A94397EB3208A8 /* ASAbsoluteLayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = FB549CF897CE658795874AA1539B3E00 /* ASAbsoluteLayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2E4E16EEF78B38EA1F4977CF1EC63511 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CD8F721F21C26F26C2A6B469454DB51F /* Foundation.framework */; }; + 2E7753DD0798320C098D6F97215F4EDA /* ASDisplayNodeTipState.mm in Sources */ = {isa = PBXBuildFile; fileRef = D25B4227847397852787D1EF3F3C6FC6 /* ASDisplayNodeTipState.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 2E8CD8DDE7442B3C033B24194762F66B /* PINRemoteImageDownloadTask.h in Headers */ = {isa = PBXBuildFile; fileRef = 1A77A2AEAA733E58FDCD4CAA0922B1A8 /* PINRemoteImageDownloadTask.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2EF0DBBD698C43C660D5C52D387762C3 /* ASImageNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A13BF4119E0C2327A37ADD306A7F280 /* ASImageNode.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2FB492957D10701BDFECCA77C563479B /* ASTipProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = A87349F24336B636FD7357BBD1C1F074 /* ASTipProvider.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 2FD93AFDC2A9F0BE1EEF74E89B5AD84B /* ASMutableAttributedStringBuilder.mm in Sources */ = {isa = PBXBuildFile; fileRef = C9771B7F3EA9E4860D1D432EC3FF9820 /* ASMutableAttributedStringBuilder.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 3013DDB20CC86DBA1FBD4BF107C9EE76 /* ASTip.h in Headers */ = {isa = PBXBuildFile; fileRef = B1025114DDE3DD7D1CA463AA1CA551BD /* ASTip.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 30B53366BF7622BC324BBC3286D51EDA /* ASPINRemoteImageDownloader.h in Headers */ = {isa = PBXBuildFile; fileRef = 7A84C6F510AF68CC4BBA2FC5DDE5E0B2 /* ASPINRemoteImageDownloader.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 30E321C7F2B2528CC0DEFA81FEE5020E /* ASHashing.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8ECD3CA83D8BA42AF9306FEC62CA06BD /* ASHashing.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 311EEE0E05EAB3E5D744F65588D8FDDB /* UIImage+ASConvenience.h in Headers */ = {isa = PBXBuildFile; fileRef = 623F24CFE1B1D4FFEDB792BA41976749 /* UIImage+ASConvenience.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 31E23F58653E80531A771415273BECDC /* PINMemoryCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 0964567655AE549BDF757E8DF816FBFF /* PINMemoryCache.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 326E08CB331794AD3AC0332E7B4F5DC0 /* ASDelegateProxy.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3F292EB56A7A5F9E8775B851D97743C0 /* ASDelegateProxy.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 329730AC1D8EB74370375033D89CAA04 /* PINRemoteImage-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 0B6ED2BB64A6A7DA8BC93530E3F55366 /* PINRemoteImage-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 32E39D4168AA61D28735332214FD7A68 /* PINRemoteImageCaching.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C8BC130248DB0AD806FA743C70A99B4 /* PINRemoteImageCaching.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 32F74F879E223DC924F2D377B0E0438A /* ASControlNode.h in Headers */ = {isa = PBXBuildFile; fileRef = D386EE9C7174CAC93AE4EFAFD2F8D52B /* ASControlNode.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 331FEDE9B0C941A741D0076745ECE9F4 /* PINImage+WebP.h in Headers */ = {isa = PBXBuildFile; fileRef = E2BF0999ECBBCF0288C66B2D84F59B64 /* PINImage+WebP.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 3364AEA7B40321356FE24B945F836C81 /* PINCache.m in Sources */ = {isa = PBXBuildFile; fileRef = CEA150E52F46238642DC87EC38BD0E6D /* PINCache.m */; }; + 33A58AB76D5143C6639B180900E8A4AB /* PINRemoteImageManagerConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = D7AEF2BB9296AC6AD078D6A403286D8F /* PINRemoteImageManagerConfiguration.m */; }; + 33AE74DEA0304E22B4B020C037BB4E8D /* ASBlockTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 5E2C06029EF723FC9CB33F253B588801 /* ASBlockTypes.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 33AFC02D1DDFBFD1EEC47AFD8134C5F2 /* ASMutableAttributedStringBuilder.h in Headers */ = {isa = PBXBuildFile; fileRef = B16AA9D7833946948169CE0EAA7B4B5D /* ASMutableAttributedStringBuilder.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 33EBCAF7E4942B6C1E978213D1046FAF /* ASLayoutElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 679CF8A5C03B3322E8CADABB7DF4748B /* ASLayoutElement.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 34854853C570C98D6E3ACCAA4C7DD1DE /* ASRelativeLayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = 443107AB83C34C89719D607406B3DE57 /* ASRelativeLayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 34ADECC6CF4DB77F274F6ED5178FA3FF /* ASResponderChainEnumerator.mm in Sources */ = {isa = PBXBuildFile; fileRef = 266F28006F8FB035EF18766D7D1D46B8 /* ASResponderChainEnumerator.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 350592582D2BEEAD29989D3C406D296E /* ASHighlightOverlayLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 0360DEFA13C4B085B68FB9C58F5C351D /* ASHighlightOverlayLayer.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 354D5F302A89E5FA56123389EE9EF229 /* PINCacheObjectSubscripting.h in Headers */ = {isa = PBXBuildFile; fileRef = 4AD14C8259CFDAA23540FB84371A8C91 /* PINCacheObjectSubscripting.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 35F73FFF5EC9D38FFF161D8CC64E5218 /* ASTextKitTruncating.h in Headers */ = {isa = PBXBuildFile; fileRef = FA09E94B1CC54AF0EAE0D3CD263B0812 /* ASTextKitTruncating.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 36C8E40A9B88DEE0CDE6738E6CE9B613 /* PINRemoteImageDownloadQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = 3C8F71BC92C3939CC05FD410E84FF05C /* PINRemoteImageDownloadQueue.m */; }; + 37163A6F77A6128EA54B197A1B6E2AF3 /* _ASCoreAnimationExtras.h in Headers */ = {isa = PBXBuildFile; fileRef = A93DD47CE966A545A55866BCC8AD2658 /* _ASCoreAnimationExtras.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 379C8AD0CBCD05F0AC0AAD83E05058A5 /* ASSection.mm in Sources */ = {isa = PBXBuildFile; fileRef = C3748C4FDDDB81BD2D1F20D30668477D /* ASSection.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 37BBB02A7D765725890D32944453EB98 /* _ASAsyncTransactionGroup.h in Headers */ = {isa = PBXBuildFile; fileRef = B50CED0243E158B6FB3AF02FA0EED408 /* _ASAsyncTransactionGroup.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 383FAE52A9550C9936D6237F7A959AEB /* ASScrollDirection.h in Headers */ = {isa = PBXBuildFile; fileRef = 5CF0B8B8B3B7611BFE4FACBD177FAFE9 /* ASScrollDirection.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 3843A5FD95EEDC8984B03B4AB0872139 /* ASTextNodeTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = C0491CD11ADA6A67C64D323A6BB11078 /* ASTextNodeTypes.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 386D4195994F9806D03777081DAEADC4 /* PINRemoteImageBasicCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 25241914808C5FF6BE5F927F84792A40 /* PINRemoteImageBasicCache.m */; }; + 38B66488888B06F1D215DD4E907C8613 /* ASTextAttribute.mm in Sources */ = {isa = PBXBuildFile; fileRef = 965D92DE5A7AB53870ABDDD9BB71EA89 /* ASTextAttribute.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 39441BD56324DADDA1A7387A039B11B4 /* ASAsciiArtBoxCreator.h in Headers */ = {isa = PBXBuildFile; fileRef = E152F08E24DF7356E972205CE1FE4D24 /* ASAsciiArtBoxCreator.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 3B4BDFF8FC2D4FC9050E5A18F96634F2 /* PINRemoteImageDownloadTask.m in Sources */ = {isa = PBXBuildFile; fileRef = 2C0A55D5092394CDBE41B7565F27BD76 /* PINRemoteImageDownloadTask.m */; }; + 3BC85D01305C01A02D674354EA81231E /* ASAssert.h in Headers */ = {isa = PBXBuildFile; fileRef = 1C818C0167A1B3B83CE80B6B6A31AB4A /* ASAssert.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 3C31CF98F49970F19686DB9430000254 /* NSAttributedString+ASText.h in Headers */ = {isa = PBXBuildFile; fileRef = E732EDFE66778C370EDB3A951172CBE2 /* NSAttributedString+ASText.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 3C3358F8891B4D0EEC5DBF01934EE2F4 /* ASLayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = 09C578D28C9AD6E160C72FF044636EBA /* ASLayoutSpec.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 3C5A9FE3D7C7FE22A922753CA96E5C12 /* ASDefaultPlaybackButton.mm in Sources */ = {isa = PBXBuildFile; fileRef = C3D08FDB734E574133896549B7B0E4F9 /* ASDefaultPlaybackButton.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 3DD4626268615851397E19A6D6F5E48A /* ASScrollDirection.mm in Sources */ = {isa = PBXBuildFile; fileRef = D78004E3854FF269F6C7E6E386DA49C4 /* ASScrollDirection.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 3E0ADF4FFBF1AEE2AFF6ADACAEFE3FA2 /* ASDKViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = ECE920DFD35FE6C0A2D054EC3E972D12 /* ASDKViewController.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 3E0B594F11B1375A55CC7FB1BE1E7DA7 /* ASTextAttribute.h in Headers */ = {isa = PBXBuildFile; fileRef = C75D246C46658AF6540D5F980D67A83C /* ASTextAttribute.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 3E66AC6AE52437F25DB9C77B405BF072 /* _ASAsyncTransactionContainer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4564E9382AAB98E6E981F0E4F84CA037 /* _ASAsyncTransactionContainer.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 3EDE54BDA1749D9AA3F967EEE4C819A4 /* ASTableNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = B757AC50585C6BAFCEB05ED2E0A2262D /* ASTableNode.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 3F032BD5B92DB1386DD5F553C335D2AF /* PINAnimatedImageView.h in Headers */ = {isa = PBXBuildFile; fileRef = D13CF7125B69CA994A1A19ADF69164F8 /* PINAnimatedImageView.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 3FA2661EC4B4764F8A0BAA3B6911CE07 /* _ASDisplayView.mm in Sources */ = {isa = PBXBuildFile; fileRef = DE5B18F6F58E950EFC83A61B4A825175 /* _ASDisplayView.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 3FF4BD5B4A904AB6FDC3BFA4A77764EA /* ASControlNode+tvOS.mm in Sources */ = {isa = PBXBuildFile; fileRef = D8697D8727A9A3453A287EBBD21C6AF1 /* ASControlNode+tvOS.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 40AE1EFE7405EB78CE80A1AAF989AA95 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CD8F721F21C26F26C2A6B469454DB51F /* Foundation.framework */; }; + 40F7A9A1246007960D1C10A328562B85 /* ASTextKitShadower.mm in Sources */ = {isa = PBXBuildFile; fileRef = E4D42106BAAABAE5142D533B57E44149 /* ASTextKitShadower.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 4107EE649411835E2A9403AA3E75395E /* ASCollectionLayoutState.h in Headers */ = {isa = PBXBuildFile; fileRef = 29BED58535AE1ADA682A705FB50653CC /* ASCollectionLayoutState.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 41572627C786146E95259AC42812892C /* ASTextKitContext.h in Headers */ = {isa = PBXBuildFile; fileRef = 99F1BC718C4DBB8C83F06F0D5E20460F /* ASTextKitContext.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 42C788E3094CBAAA03EC39561D7181DF /* ASLayout+IGListDiffKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 07BAF72F98DCBECFFC2B7F3A0FB2D752 /* ASLayout+IGListDiffKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4404103D44CA3218F025DA315D800010 /* ASRecursiveUnfairLock.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0F524724237EF9AD3AEF9016F5121229 /* ASRecursiveUnfairLock.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 44231872CA5D99230474B02A713D193C /* PINRemoteImageManagerConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DBBE6C78D2A10AE3C8E91AC0E6C3244 /* PINRemoteImageManagerConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 445A2C4091D5EB91003D00CA744F69C8 /* ASAssert.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3FD8FFBA2AD3C6F7C490C86420605F17 /* ASAssert.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 44F5E8EAAFC519B827079C21092A3110 /* _ASPendingState.mm in Sources */ = {isa = PBXBuildFile; fileRef = B67FEF8CB94D5E7658A7016E7DE658F4 /* _ASPendingState.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 45051BE690707C36628E0A3162A6655C /* ASDefaultPlaybackButton.h in Headers */ = {isa = PBXBuildFile; fileRef = 40738CE7558B636E714D9A7DA06CAE94 /* ASDefaultPlaybackButton.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 458959AD13E6AC3374E81F6487F13B21 /* ASDisplayNodeCornerLayerDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 0CA558CE62A460B44AFC3565ADFCAEE5 /* ASDisplayNodeCornerLayerDelegate.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 45F21A0B56EC7B9577BC185A9ED007C4 /* PINResume.h in Headers */ = {isa = PBXBuildFile; fileRef = A7B540D3D7D6A9543C97F678E925A099 /* PINResume.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 46602671A46883556573D357D8D58179 /* ASCollectionLayoutDefines.mm in Sources */ = {isa = PBXBuildFile; fileRef = AC2577D0AE6AFBA55F004F4BE81ADCCF /* ASCollectionLayoutDefines.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 46A6E2A05E106239C6D1B2429E6363CE /* ASBasicImageDownloader.h in Headers */ = {isa = PBXBuildFile; fileRef = 8099386989773A568E77145C42CD586F /* ASBasicImageDownloader.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4775D60B018E3B4BA0C456FCC93A3489 /* ASStackUnpositionedLayout.mm in Sources */ = {isa = PBXBuildFile; fileRef = B5621F42157B08B48920F0A50B3222AF /* ASStackUnpositionedLayout.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 4863D9CFD8DF00F75537FC81AC2C1B9A /* ASBatchContext.mm in Sources */ = {isa = PBXBuildFile; fileRef = A56D0ECA9EBC62BE680E0D42D7FAB7AF /* ASBatchContext.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 48AB83373950DF31804839DF7317AEB2 /* ASDisplayNode+DebugTiming.mm in Sources */ = {isa = PBXBuildFile; fileRef = B22CAC88C961169FA20051DE8BDF0B27 /* ASDisplayNode+DebugTiming.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 48FB2FA1183990E558E429461CDF77F4 /* ASImageNode+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 65E45EB810D4BBF9D33169EC48A815F5 /* ASImageNode+Private.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 4916347FD174C3002BE32C86D01422E3 /* ASNavigationController.h in Headers */ = {isa = PBXBuildFile; fileRef = 0706408BB07355DB8AE83E0ED628FE32 /* ASNavigationController.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 49175285DAE0F984B215CA4FE269EB09 /* ASMutableElementMap.h in Headers */ = {isa = PBXBuildFile; fileRef = 5BDA6758A4F44A2423F3319C96BBB2EC /* ASMutableElementMap.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 4A5D639EB330216C5E0486C9CA40550A /* ASEditableTextNode.h in Headers */ = {isa = PBXBuildFile; fileRef = BC54F1AC79DB2F89235AD3AAAE4E8794 /* ASEditableTextNode.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4A7C54E9E3062AE28473631F67C4A6A3 /* ASTextInput.h in Headers */ = {isa = PBXBuildFile; fileRef = FEF5907A23C18CFE5915CE25B0CC96D8 /* ASTextInput.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 4B16631D2EC2D5D065CBB55DC8881D0D /* ASCollectionLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = B7E5025469DC385B5D9CB4C22BE358CF /* ASCollectionLayout.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 4B8E788E590ABA95BD8CC4573D0EBFC0 /* ASLayout.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0127E215BAC572610DF54FD21EBFB99E /* ASLayout.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 4D1738F72E511D8788C448223DFF74C7 /* ASCollectionFlowLayoutDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = B377086AE4B7CBACB4F4B5FF68BCDBC7 /* ASCollectionFlowLayoutDelegate.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 4D6326702D2E5445867EDF0CB51981B7 /* Texture-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 6FEDA9949DABCA4821DDDD0414408B22 /* Texture-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4FEC6809913A309C1BB3AE70C9CD1856 /* ASCollections.mm in Sources */ = {isa = PBXBuildFile; fileRef = D389403EE0D912CAB00411EEEBBF5284 /* ASCollections.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 50EDFDEBF7240CDF4671942722897557 /* _ASCoreAnimationExtras.mm in Sources */ = {isa = PBXBuildFile; fileRef = EA00CE5CCA312A7A77C7D868AA3AC38B /* _ASCoreAnimationExtras.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 50EEFB8254EAF126BDAF9F4BB2F41EC1 /* ASObjectDescriptionHelpers.mm in Sources */ = {isa = PBXBuildFile; fileRef = CC9924E9040C5454AC24BC42A84D6E24 /* ASObjectDescriptionHelpers.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 51B91F5F6C099C23A0C0BADC9B435BC7 /* ASVideoNode.h in Headers */ = {isa = PBXBuildFile; fileRef = EA2BDC742161E6067502856B9E2FDAFF /* ASVideoNode.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 533DDBAE6B86634DC5AC964324AFBE5A /* ASCollectionGalleryLayoutDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 99B6328B4EE826FA54866D56A088FE88 /* ASCollectionGalleryLayoutDelegate.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 5367E1DE5A0A1BAB05B32E3C6BD4B9F2 /* ASWeakProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = C92F34A09B8C59BACD85A16B2D11AAF6 /* ASWeakProxy.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 53C40C404A29C9D36C39EFC2A41CE743 /* ASTableViewInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 832F7E0ACF60B901E15D5BA77F9D1DAD /* ASTableViewInternal.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 5415B24BFD37F00BB5D405BCD49555EA /* ASLayerBackingTipProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = DE1EEC4327E7157183B75E4178082C91 /* ASLayerBackingTipProvider.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 55489F9E2C84ABA1DF343520666B7980 /* ASDisplayNode+Ancestry.h in Headers */ = {isa = PBXBuildFile; fileRef = 843FA0564BEA1819D72A52497B191159 /* ASDisplayNode+Ancestry.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 55DA22DDE15CDE86C6A4157789744B72 /* ASTextKitRenderer+Positioning.h in Headers */ = {isa = PBXBuildFile; fileRef = 12CA065AACA31E8F43AE72E67FCB0FFC /* ASTextKitRenderer+Positioning.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 55F66909D00D87259EB4D88697ADF17F /* ASDisplayNode+LayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0628D2361BC4A2D2C737966B84D1338B /* ASDisplayNode+LayoutSpec.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 5614B5D3B2DD1F30555A9233B65A2393 /* ASLayoutRangeType.h in Headers */ = {isa = PBXBuildFile; fileRef = 2B52E76F0416EEE47A995D0B1AB82ADD /* ASLayoutRangeType.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 58392A4A15A03DD118C936B690AA5CA0 /* PINOperation-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = A74625036C72AB1D11BF828EF40AF42E /* PINOperation-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 588F5E132A531BF1B16C0FE24359FDA2 /* PINRemoteImageTask+Subclassing.h in Headers */ = {isa = PBXBuildFile; fileRef = 1E6AFB718C24B2377E5DF0CCDA306C55 /* PINRemoteImageTask+Subclassing.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 5934C09B5C2A178ADD7D0D585AD36037 /* ASDisplayNode+Yoga.mm in Sources */ = {isa = PBXBuildFile; fileRef = 49577A6CB89BA0AB509E6CF764ABDE61 /* ASDisplayNode+Yoga.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 59ACDCDC7DDCA5D20DB4A8F2BB1D132B /* ASDisplayNode+FrameworkPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = D3F73B5CE261EAA709DE2073A342D910 /* ASDisplayNode+FrameworkPrivate.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 5A5D5F0390336EF5FB4E0C7E3A250AA0 /* ASYogaUtilities.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6DBB50E059AE567A69A18F010E0C93C5 /* ASYogaUtilities.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 5AA521195197A5269FE3C52EE1F612FC /* PINDisplayLink.h in Headers */ = {isa = PBXBuildFile; fileRef = 442AD1A685AA2C1A7EDB840685B29CCB /* PINDisplayLink.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 5B2D3DF94C9F82AB1317E69DB48E8372 /* ASTextKitRenderer+TextChecking.mm in Sources */ = {isa = PBXBuildFile; fileRef = 00E907AFDB4D6D82FD9F4128D2C4B8DE /* ASTextKitRenderer+TextChecking.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 5BFC4E12BD19E6A80D098D6A1288A43D /* PINSpeedRecorder.h in Headers */ = {isa = PBXBuildFile; fileRef = F12B84DDA3761BCCFFC101F6185D46A9 /* PINSpeedRecorder.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 5C4DD97A03493777545E355D09A8903E /* ASCollectionView.mm in Sources */ = {isa = PBXBuildFile; fileRef = A17F214DF82A4422342061B698A3F309 /* ASCollectionView.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 5D1C395E767F467B00623F3B9EF05B22 /* ASIntegerMap.h in Headers */ = {isa = PBXBuildFile; fileRef = 1BFFEC4682ED9A6C582741ABB22FFFD2 /* ASIntegerMap.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 5D596E9F8BA6D87E1ADD712A13F131CF /* PINRemoteImageTask.m in Sources */ = {isa = PBXBuildFile; fileRef = 0FEA06CB098370189C1103854CA5077F /* PINRemoteImageTask.m */; }; + 5E6DD54DC1FCC7FA938B7BDBFA6653AA /* ASMainSerialQueue.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5D04F1BC6559A047934F57FEE0D842D0 /* ASMainSerialQueue.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 5F0259FB38017633D98701913A2EF502 /* ASDisplayNode+Convenience.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2EF7DC57329ABA4B85EDFE5DC03BC0A8 /* ASDisplayNode+Convenience.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 5F4662B7E3B0E1B0B9F9C445EFE5FDEF /* ASCollectionLayoutCache.mm in Sources */ = {isa = PBXBuildFile; fileRef = 98B0E4F19691D2F4131FF50AFA1071E1 /* ASCollectionLayoutCache.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 5F4B683A5C8770F9092E0F839DC279B8 /* ASButtonNode+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 21BC3B3B36ADEE1506968B5996F97C34 /* ASButtonNode+Private.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 5F8115BA11746D390E7FE2DA8B4E195F /* ASLog.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6276DF790DF6B5D12814A3A4942DEDD9 /* ASLog.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 625D5BDB7DC6971A04C2F0CC04E78316 /* ASDisplayNodeInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 127F7C3D321A6445E2628FC920109B38 /* ASDisplayNodeInternal.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 62642C50110C9B6D29E7CEBAD2B33BBC /* ASCornerLayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = 7022AFFE25E02BB59DF601176BCECFAB /* ASCornerLayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 63585E8148886E80CB182E3920C51A11 /* PINRemoteImageDownloadQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = D9C64EBA02032F27282C40FD7220273C /* PINRemoteImageDownloadQueue.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 641B4519AF9A061FA679FD64B3CB984B /* AsyncDisplayKit+Tips.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3A0E979A776D8F70B347CC8B258C6B39 /* AsyncDisplayKit+Tips.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 6549266D127F7CF008B8120024AD1826 /* UIImage+ASConvenience.mm in Sources */ = {isa = PBXBuildFile; fileRef = CA4D8229EE753A846B1A324FF59E6F24 /* UIImage+ASConvenience.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 65EFFF712403A158D18F39A945B66278 /* ASNetworkImageLoadInfo+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = E1A4DEAF97B792E35B974180DAB5AAEE /* ASNetworkImageLoadInfo+Private.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 665AFE433398FCE3DEF4DA527D3835EF /* AsyncDisplayKit+IGListKitMethods.h in Headers */ = {isa = PBXBuildFile; fileRef = DB92B37C19A5B459CB704598D51575B9 /* AsyncDisplayKit+IGListKitMethods.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 667DE3D4FCA166D984EB80E3BD949F8E /* ASImageNode+CGExtras.h in Headers */ = {isa = PBXBuildFile; fileRef = D9518F8951D785FB5E83392FF5EEF900 /* ASImageNode+CGExtras.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 66B9FA66EDE6CD8FD3B1C5823155167C /* ASCellNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = F724F08CB726907AD6A2696B44D3A849 /* ASCellNode.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 66F58E36376855ADDBF863373C4D15A9 /* ASTabBarController.h in Headers */ = {isa = PBXBuildFile; fileRef = 0CB053AFA33FB3BE65B1DDD1F6796D07 /* ASTabBarController.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 66FECA84C9846535E165C9ADB214A4F3 /* PINOperationQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = F786D2FE67301111EA52EC486B239B2B /* PINOperationQueue.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 675811F894E8250FD70813BE06DF5E80 /* ASTextKitContext.mm in Sources */ = {isa = PBXBuildFile; fileRef = BA640A1E51676DE0AC8A2AE86C35FDB2 /* ASTextKitContext.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 681352C165A0A0DE357DFC95EF35D7ED /* PINCaching.h in Headers */ = {isa = PBXBuildFile; fileRef = F3A05A320A0B5986D46D8E46AB8F56DB /* PINCaching.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 68158F620B989CD7611B3AEA782A5EEC /* ASGraphicsContext.h in Headers */ = {isa = PBXBuildFile; fileRef = C8C2EE7A63C346D06A35520A77DE19AC /* ASGraphicsContext.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 6851E1AB040073EBF7F89AE04AF025F4 /* IGListAdapter+AsyncDisplayKit.mm in Sources */ = {isa = PBXBuildFile; fileRef = D61846E07A8D0904AF857A84BDD7C878 /* IGListAdapter+AsyncDisplayKit.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 68C7FCDF3699122D51BBB8D181CE2A4F /* ASCollectionFlowLayoutDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 5F272F44E17A59928BA3C19A72C1CE96 /* ASCollectionFlowLayoutDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 690EAE385194D53B290B805C46BA4EF6 /* ASCellNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 643EE8E9B40DC4CB13D65B7243E995B2 /* ASCellNode.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 693AD7C903CD68AA67FAD2DF0D3F676A /* ASTextNode2.h in Headers */ = {isa = PBXBuildFile; fileRef = 1403D90CF17962F5D5818587DA0652FE /* ASTextNode2.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 6A4E81CDAE9421C4389B34E66572AEAA /* ASDisplayNode+DebugTiming.h in Headers */ = {isa = PBXBuildFile; fileRef = 3E1A5494636B0CDA8A4709C8682A52C1 /* ASDisplayNode+DebugTiming.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 6A837AB4E8BB2D3161A42C4BE819519E /* ASTextNodeCommon.h in Headers */ = {isa = PBXBuildFile; fileRef = 5297B2854BA055337B6A353F4581E97B /* ASTextNodeCommon.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 6AFFA660CBFFBF81A6DC1F34AC2B9E39 /* _ASCollectionReusableView.h in Headers */ = {isa = PBXBuildFile; fileRef = DEC0BF308170B3621109986E67AEFA82 /* _ASCollectionReusableView.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 6C83627E6FAA6773A8F7CF351CAFB99F /* ASAbstractLayoutController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5201496769F9BC60F76B9A95B07E8C98 /* ASAbstractLayoutController.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 6D342FAAF97311745F7FF709965F8273 /* ASLocking.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F518543A74B628F00ECD15DB5F7445D /* ASLocking.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 6D6BB69C4FAC4FC632BA75AD97D7D2AD /* ASLayoutElementPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = E834741D474E0658F5747909301B10F7 /* ASLayoutElementPrivate.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 6DA1B9D0492B013012EC140F453A9C89 /* ASCollectionElement.h in Headers */ = {isa = PBXBuildFile; fileRef = FB4422D6FE213AD611123E8A5347E60E /* ASCollectionElement.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 6E1760E30C9461F00BEA1F9E0C678A81 /* UIResponder+AsyncDisplayKit.h in Headers */ = {isa = PBXBuildFile; fileRef = C8D491217E9C139BB7B03BDB3EC9AE93 /* UIResponder+AsyncDisplayKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 6E4E5512E8EC803F48F6D04561EA0A2B /* PINOperation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA1D941B55196700D1C09F1BBF76A710 /* PINOperation.framework */; }; + 6EE514FBAD36F14E9DB7670A1A371419 /* ASResponderChainEnumerator.h in Headers */ = {isa = PBXBuildFile; fileRef = 7D2C56C4B3966B04388D70C04AFA6A27 /* ASResponderChainEnumerator.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 6F0D7C379DAA9767920055697FA3B94C /* ASDisplayNode+AsyncDisplay.mm in Sources */ = {isa = PBXBuildFile; fileRef = 747E4F88A2242E485C0C2A1D600F72CD /* ASDisplayNode+AsyncDisplay.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 6F310E7B5C2D8AF7297A422F050BFDB9 /* ASYogaUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 49E8771A769680B47A2ADB440EDDEDA1 /* ASYogaUtilities.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 70FFE5752D7735FF6DA612721FAF1AD4 /* ASThread.h in Headers */ = {isa = PBXBuildFile; fileRef = 58FA6C5A9C4C19E011EB63F340365513 /* ASThread.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 7123670E11E91E1F2D878B4F8F2AE716 /* PINOperationMacros.h in Headers */ = {isa = PBXBuildFile; fileRef = 6FA8B88434F89A38DB5CEC77891BEAC7 /* PINOperationMacros.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 713DAEE94040383AD0A64FE5E1C6E0D0 /* PINRemoteImage.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A563C19F22250BA10FAAF9CAA214C6C8 /* PINRemoteImage.framework */; }; + 715A56F9131273D746C866720B84F7FF /* NSArray+Diffing.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8146280FD73EB05DF4423A718F6888D0 /* NSArray+Diffing.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 7185AD0B6E50617EB5B063D049F7381E /* ASNetworkImageLoadInfo.mm in Sources */ = {isa = PBXBuildFile; fileRef = 7EC860805746F5FCE75F3EDC2AAAA287 /* ASNetworkImageLoadInfo.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 725A23DC02BF12DAEFD17D9DD1C19537 /* ASDefaultPlayButton.mm in Sources */ = {isa = PBXBuildFile; fileRef = E903B227DE544FEBB3869D8A7D5D3F64 /* ASDefaultPlayButton.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 7367521E016788B60D3B821852CC1D49 /* ASControlNode+Subclasses.h in Headers */ = {isa = PBXBuildFile; fileRef = 75268E97C57C22192F3B263283CA2C79 /* ASControlNode+Subclasses.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 74242DB7DEB36F897B59CC95AED2BDAE /* PINImageView+PINRemoteImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 790FD97BCBD0110961780552C759DF98 /* PINImageView+PINRemoteImage.m */; }; + 743030AC14DFBE803BDE5EC37D25C31C /* PINCache.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F715418A114688F1318388F954C148DE /* PINCache.framework */; }; + 744967C02A56404990BC6E88C01BA548 /* PINAnimatedImageView+PINRemoteImage.m in Sources */ = {isa = PBXBuildFile; fileRef = F6E67D14993A7B56B97A9C6D49B944FC /* PINAnimatedImageView+PINRemoteImage.m */; }; + 7485A8427B78F4D0D38AF63015919793 /* ASElementMap.mm in Sources */ = {isa = PBXBuildFile; fileRef = C2037A0F5BAE21EEAF3BF0C23B362004 /* ASElementMap.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 74E1593D7726C6250595B3640FC6908B /* ASTextKitEntityAttribute.mm in Sources */ = {isa = PBXBuildFile; fileRef = C0464695A56E068F9EB148E0B4515AA0 /* ASTextKitEntityAttribute.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 7615B92004681A1F0CE6E7F61389AC91 /* PINImage+ScaledImage.h in Headers */ = {isa = PBXBuildFile; fileRef = 3C5A6AEC41EFC3AD25534536494FF0B1 /* PINImage+ScaledImage.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 7662C10817C380C0792B383089502CAE /* PINDiskCache.m in Sources */ = {isa = PBXBuildFile; fileRef = CDB9905DE100C45E321FF681B0BE5D74 /* PINDiskCache.m */; settings = {COMPILER_FLAGS = "-fobjc-arc-exceptions"; }; }; + 766AFE24E8D800787DBAAF4EFA1C86D8 /* ASVideoPlayerNode.h in Headers */ = {isa = PBXBuildFile; fileRef = A9AE994038F0C350DEBB06A25EADFC8E /* ASVideoPlayerNode.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 7682DDBF21A55C03731203EDCBDFF27B /* ASCollectionView+Undeprecated.h in Headers */ = {isa = PBXBuildFile; fileRef = D95B3764BC290B9DB81E76736CDC7766 /* ASCollectionView+Undeprecated.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 781DA851FF46FF8A30B48C5E8115FE1B /* ASTextRunDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 039E32F34E01B17F98C04D3C94E2E219 /* ASTextRunDelegate.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 78832930615717444B99AF06239A0497 /* ASTipsController.mm in Sources */ = {isa = PBXBuildFile; fileRef = E51E2FEACF14FE9C9B1FCAC9532F4D3E /* ASTipsController.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 789C7B17F586529CFB16874F11D0612E /* ASTipProvider.mm in Sources */ = {isa = PBXBuildFile; fileRef = ABBC5A4E8EBEC559EF2D0DBE16D3C66F /* ASTipProvider.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 78EA95A196E301CABCA9B8EF56972FE5 /* PINRemoteImageMemoryContainer.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B76C13871113610B790E5634FD8E4B7 /* PINRemoteImageMemoryContainer.m */; }; + 792C265DC6709DF747E801A984353998 /* PINRemoteImageCallbacks.h in Headers */ = {isa = PBXBuildFile; fileRef = 930163D057DF4C17CDACDDB653AD3C46 /* PINRemoteImageCallbacks.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 79D79C15A67C285A4FE840C164A61EAD /* ASPINRemoteImageDownloader.mm in Sources */ = {isa = PBXBuildFile; fileRef = 53F5101FDA12B83E1AF2EE12B127639E /* ASPINRemoteImageDownloader.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 79DEAF233C0FDF9487841F3A5226FE89 /* PINRemoteImageCategoryManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 2138392302088734C71C953F6E8BE31F /* PINRemoteImageCategoryManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 7A541079985175ED988475511FE5F2DC /* PINCache-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 274269F974F0098D974E13CADE4FC16A /* PINCache-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 7B226EB7B088CECD2ECA145103392D56 /* ASControlNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3874B0234F310FC6D5CC02FC07D177D7 /* ASControlNode.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 7BAB8B3B6369EDFA0C7D13F60FBC6794 /* _ASCollectionViewCell.h in Headers */ = {isa = PBXBuildFile; fileRef = C741926C4CC8BF0802B8539AC2697545 /* _ASCollectionViewCell.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 7C9889D2309D0F679BEFAEF035704D5B /* PINRequestRetryStrategy.m in Sources */ = {isa = PBXBuildFile; fileRef = 1394E4E8CCCBB0C8068E295304334637 /* PINRequestRetryStrategy.m */; }; + 7DF36B3FA296898C2CE708F7A547929A /* NSIndexSet+ASHelpers.mm in Sources */ = {isa = PBXBuildFile; fileRef = FFE3A850EF05FC7A29E7138C236D6AE0 /* NSIndexSet+ASHelpers.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 7FD4280D0F3552F62182C2797740B7FA /* ASConfigurationInternal.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3B40FBAEC3D9F250BE499DB7C96477FC /* ASConfigurationInternal.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 80C24E482ED0EC7870E04616ECCD439A /* ASTextKitTailTruncater.mm in Sources */ = {isa = PBXBuildFile; fileRef = F9ADEDE2E6CB82FA634260F9B98A6B6C /* ASTextKitTailTruncater.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 82E0F28243DC81433820662B2BEA008C /* ASTextKitFontSizeAdjuster.mm in Sources */ = {isa = PBXBuildFile; fileRef = 29E0253CCA000E3426448E4D79278C91 /* ASTextKitFontSizeAdjuster.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 835F0D17D2FD72B5C75AB3C73665B952 /* _ASAsyncTransactionGroup.mm in Sources */ = {isa = PBXBuildFile; fileRef = E6AA6582F46D7A6021F34A9D7019F1AF /* _ASAsyncTransactionGroup.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 85CA625022EC17DE0631D5B55F084FB7 /* ASConfigurationInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = CECE4ED7AF67F941DEB6F0EEFDE8052D /* ASConfigurationInternal.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 85F06D91CFD3730D46459D65E5BABCE8 /* UIView+ASConvenience.h in Headers */ = {isa = PBXBuildFile; fileRef = 996D138EA53F8172C888E4E58C2BA094 /* UIView+ASConvenience.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 861967ACB77A0D10A198A15478A9FE5F /* PINButton+PINRemoteImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 9FAB346055C63B3162B59F5DE8235DF0 /* PINButton+PINRemoteImage.m */; }; + 869A0AFC6E446D9559F81B9DE55339BB /* ASCollections.h in Headers */ = {isa = PBXBuildFile; fileRef = FBD8D238CA37E2247A47BEC575B4E643 /* ASCollections.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 882C68A56DB9AAA11032882286153B46 /* ASDisplayNodeExtras.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1D23AC0B7A0FABCDD58FEABD77522221 /* ASDisplayNodeExtras.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 89C8791BCA6D98034A61A54E5FD9D4AE /* ASDisplayNode+InterfaceState.h in Headers */ = {isa = PBXBuildFile; fileRef = 1A150AD21C6C6AEB743E18176E75D78F /* ASDisplayNode+InterfaceState.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 89CCF29068794758917D5BC53196B619 /* ASButtonNode+Yoga.h in Headers */ = {isa = PBXBuildFile; fileRef = 56EDE5D7E4A5C38FBC7FE5EC9D8A1FA4 /* ASButtonNode+Yoga.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 8A2E0B626887AF013A7F84103DA0A836 /* ASDimension.h in Headers */ = {isa = PBXBuildFile; fileRef = FC18FC5D497DA4363C26D1C856C7CCC6 /* ASDimension.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 8AB6AEE4DF780E7CAA39A698FA4E7634 /* CoreMedia.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 461F1ADCB188A45C15C5FFE080FB729A /* CoreMedia.framework */; }; + 8B886D15BFD525FDF4FC0FD05C4E352F /* ASCollectionLayoutState.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA4FF234AD32C005D1BC2803290984C7 /* ASCollectionLayoutState.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 8BB520FB3735FF2ED6F041EB6BD6EE0D /* ASStackPositionedLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = A1C208163829F8088B47F961509583E3 /* ASStackPositionedLayout.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 8BE298408DCAF8348629CC2FD95B8883 /* ImageIO.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7A2B2E2EE493CFA9A7944BBC109C1107 /* ImageIO.framework */; }; + 8C31797394C8EC3AA2597206372419D9 /* ASCollectionLayoutContext.h in Headers */ = {isa = PBXBuildFile; fileRef = A03C7EAEEBD03FBE27B384992167A313 /* ASCollectionLayoutContext.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 8C59DA4CCC93CE415C2A15348CBFC83D /* PINRemoteImageBasicCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 85DC3A0BDC8F3003550567F705BC3C4A /* PINRemoteImageBasicCache.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 8C871044D0661199D892CAA1A56975A8 /* ASImageNode+AnimatedImage.mm in Sources */ = {isa = PBXBuildFile; fileRef = B1B5C512945B8108FC01D7902FB0888F /* ASImageNode+AnimatedImage.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 8CF2DBA98444B46D088E8FC193E26B9D /* ASAbstractLayoutController+FrameworkPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 788A4CA5542DDF6AEDF0958D1F65FA8D /* ASAbstractLayoutController+FrameworkPrivate.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 8D1C38CA2C15929280CC6482BFB46216 /* ASWeakSet.mm in Sources */ = {isa = PBXBuildFile; fileRef = 7F01B58EC031BB0AC720C371201D7E15 /* ASWeakSet.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 8D5273A0FC56233E209A44A5FB5FC15B /* ASCollectionViewLayoutFacilitatorProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 078A70C4529DBC3C4A754417C5FBDBBE /* ASCollectionViewLayoutFacilitatorProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 8D5C3CB94A4EDD4BD08D8A18A9BD7356 /* PINImage+DecodedImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 7E77840DF764C7052517CF2ED7FF71A3 /* PINImage+DecodedImage.m */; }; + 8D8DB918AAA7B2598B2A1633502471E2 /* ASTextKitEntityAttribute.h in Headers */ = {isa = PBXBuildFile; fileRef = 4D40CE701750D8006DA724FDDD8DF487 /* ASTextKitEntityAttribute.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 8DC6B9827D1C470510DC8F6E2F8B7086 /* PINAnimatedImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 29F590A258A736639A4D10C302391831 /* PINAnimatedImage.m */; }; + 8DE9CA21A7082FC2A937E3DBF78E3A0F /* _ASAsyncTransactionContainer+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = E24B74CBEFA4CF9264A81572FD614BD2 /* _ASAsyncTransactionContainer+Private.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 8DF538949C4DC97E959103D2208F3C69 /* ASCollectionViewFlowLayoutInspector.h in Headers */ = {isa = PBXBuildFile; fileRef = 267B1C8B013F93279B71315BB29360E2 /* ASCollectionViewFlowLayoutInspector.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 8E2EEA6890AA83F0C1689F5273B37B8F /* ASMultiplexImageNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = CA2861F5E52D93186A2AADFF0825C4ED /* ASMultiplexImageNode.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 8E50095CB420ADE04A0C30481174C03B /* ASMapNode.h in Headers */ = {isa = PBXBuildFile; fileRef = AFB06A665A86D968044ED349FC6E2817 /* ASMapNode.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 8EDB472A1578F25B749BE234A65CAB68 /* ASEqualityHelpers.h in Headers */ = {isa = PBXBuildFile; fileRef = 0C18C9A1EB2B7BEFDEF4D696077F511A /* ASEqualityHelpers.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 8F3296AA6F7126C161055219C1D70548 /* ASTextKitCoreTextAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 563AA9E4BC576EF280FB0C0F91212620 /* ASTextKitCoreTextAdditions.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 8FB8E71EDEEC0BAF253697B14D1A562D /* ASTipsWindow.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2DF50D3C1F52EB1D87263AFEF84D691D /* ASTipsWindow.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 901059B0F79E50EA1BDACFD9B1A9D5CF /* PINDiskCache.h in Headers */ = {isa = PBXBuildFile; fileRef = CA8AEF5A3D9FB6C2D3EDA71FC15F7D74 /* PINDiskCache.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 905B6202C6ACB27DA5B734CD0EF6923B /* ASObjectDescriptionHelpers.h in Headers */ = {isa = PBXBuildFile; fileRef = 57CFF13E6A7FCF5F26B96D9E52B50118 /* ASObjectDescriptionHelpers.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 90A83F1084AF8045B4B8EB567B188C4A /* ASScrollNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = 349D854652C9440A9E0A24C485F4ACA4 /* ASScrollNode.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 912138EAA142AB646C0AB9E4CDDFC505 /* ASLog.h in Headers */ = {isa = PBXBuildFile; fileRef = 621CFE6D1995E55EC068DC672E84F361 /* ASLog.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 914D6D51178B6E7DC511BBFBCE5B8C60 /* ASTextLine.h in Headers */ = {isa = PBXBuildFile; fileRef = 045B4CB141E1430CE34D42817E360277 /* ASTextLine.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 918B6F04B2ED6C34247F3355BB118462 /* PINRemoteImageMacros.h in Headers */ = {isa = PBXBuildFile; fileRef = B804EAE3C4FD585B6054924F31638531 /* PINRemoteImageMacros.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 92030EC136552B4DBF56BADD013C1304 /* Pods-Starbucks_Clone_TexBrother-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = FB1326EC0457FCC01BFCF7447226FF78 /* Pods-Starbucks_Clone_TexBrother-dummy.m */; }; + 9234C6A8A3D029A8D124CEFB260B8157 /* PINOperationGroup.m in Sources */ = {isa = PBXBuildFile; fileRef = ABC2257CCA051191378319330151F0D4 /* PINOperationGroup.m */; }; + 925401BD47D7A9AB180C1B3630BC0980 /* ASRangeController.h in Headers */ = {isa = PBXBuildFile; fileRef = BA7E4F3BDA84380743FAF975B1665227 /* ASRangeController.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9268301826042884BD37C81E02D6AA0C /* ASTextNodeWordKerner.mm in Sources */ = {isa = PBXBuildFile; fileRef = 67E87CE9FECF81BF0881DD56B7DF780A /* ASTextNodeWordKerner.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 92B7317ED317260E96CC6D658EFAA5F0 /* ASCollectionLayoutContext.mm in Sources */ = {isa = PBXBuildFile; fileRef = 32EAD7F4F3087C11BE2869340DFF33EC /* ASCollectionLayoutContext.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 92DD534C4085F25142F0ACFD4B930719 /* ASMainSerialQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 1001B956AE4E0107CB49144235F7263C /* ASMainSerialQueue.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 938190A76B28D4FBC6FBD6EE39D3D21B /* PINAnimatedImageView+PINRemoteImage.h in Headers */ = {isa = PBXBuildFile; fileRef = 0DBC478814C98DC8DF9A700BA2AFB12E /* PINAnimatedImageView+PINRemoteImage.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 93B61C99B25B247897629D42186F02AB /* ASPhotosFrameworkImageRequest.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A99F1EE1BA8F78FE1D179D4F10990E9 /* ASPhotosFrameworkImageRequest.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 9427254447A9175C3462D1716E78628E /* PINOperationGroup.h in Headers */ = {isa = PBXBuildFile; fileRef = A6648655AE61CC658E7AE86A6804AD60 /* PINOperationGroup.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9451E51841289C96853640629ED47FE7 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CD8F721F21C26F26C2A6B469454DB51F /* Foundation.framework */; }; + 9481A617FEFD1628356CBB00160C2FC0 /* _ASDisplayLayer.mm in Sources */ = {isa = PBXBuildFile; fileRef = ABB03AA3087EA9FCC208E03B28E09935 /* _ASDisplayLayer.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 94E0E3DE8E35A8EE40E20D8A0B253561 /* PINOperation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA1D941B55196700D1C09F1BBF76A710 /* PINOperation.framework */; }; + 9572408CB866579D8EEFA59F946EC4C5 /* ASLayoutTransition.h in Headers */ = {isa = PBXBuildFile; fileRef = 5BC73143B044118E42D111C5B89F8746 /* ASLayoutTransition.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 95B92DA06C709481C8E996BE378BAF05 /* ASTableNode.h in Headers */ = {isa = PBXBuildFile; fileRef = D6820D90344C7012FD9DAF1CE99185E8 /* ASTableNode.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 95D49A06D8AAB718A13FFF1A29B6F6FE /* ASImageNode+CGExtras.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9B3C2A862F2283BFC0EABB764DF58298 /* ASImageNode+CGExtras.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 982632D85E47F99F7181D8F921276FC8 /* ASWeakProxy.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4774FBDD20637C9AC144C7E70D1057A5 /* ASWeakProxy.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 98DF10C729D708E8B23CC3DE227AC5BE /* ASImageContainerProtocolCategories.mm in Sources */ = {isa = PBXBuildFile; fileRef = EDDFB203A3FA480E8F255B7E49928E6B /* ASImageContainerProtocolCategories.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 9A7E5CC0B063C1F1B56B4864E262C508 /* ASTextRunDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = DC9BA969B57D2D800557EE536EB1DC14 /* ASTextRunDelegate.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 9A7F2B5821CB7EA9AD72F2FA5B67D152 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CD8F721F21C26F26C2A6B469454DB51F /* Foundation.framework */; }; + 9ABA4A11DE428613126DA887698C5770 /* ASDefaultPlayButton.h in Headers */ = {isa = PBXBuildFile; fileRef = 01E0794C0798D247DA040090A445797A /* ASDefaultPlayButton.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 9AD75CD02E114BE9834EF4E9DCF8FD35 /* _ASCollectionGalleryLayoutInfo.mm in Sources */ = {isa = PBXBuildFile; fileRef = F9BACA46ECE4EB7466790ED17B5796D4 /* _ASCollectionGalleryLayoutInfo.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 9BB40B9E5A27F0D228FDA2654B90A0C6 /* ASCenterLayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = 792A0180DFD7045FE65D98BB0892B376 /* ASCenterLayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9BB8B7D153A17A4341454EB34059F2D9 /* PINRemoteImageManagerResult.m in Sources */ = {isa = PBXBuildFile; fileRef = 39469547584715DC15C17FD3249DB5F3 /* PINRemoteImageManagerResult.m */; }; + 9BED2B15E515CF1FC8DA1779587024AC /* ASPagerNode+Beta.h in Headers */ = {isa = PBXBuildFile; fileRef = 1E2AF6899A8D865AE83A80BE3854525A /* ASPagerNode+Beta.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9D2156F8882790A1A11AB12D4CE15B63 /* ASPhotosFrameworkImageRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 8BC32B2CA262B735E3055D0089917569 /* ASPhotosFrameworkImageRequest.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9D3FF8DEE22B8C7BFB7A66C333F45B92 /* ASImageNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9F1507A2B71A82D68CA7D8D4B300BB40 /* ASImageNode.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 9DB3E03C376FC722E70B094C2DF85DC2 /* ASStackPositionedLayout.mm in Sources */ = {isa = PBXBuildFile; fileRef = B11E2C1306253F769EADB97E211BF625 /* ASStackPositionedLayout.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 9DDDBA392098C8E92D303321246E4A15 /* IGListAdapter+AsyncDisplayKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 6D84304A4C65F07E602CBAB724FF9D07 /* IGListAdapter+AsyncDisplayKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9DF12A4EA63E693047BC33E9E9FB3458 /* ASLayoutSpecUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 372E3752496C2C26F7D26610EACB5069 /* ASLayoutSpecUtilities.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 9E03D7C1EB8441DA4301C7E83D9A81FC /* ASStackLayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = 78EB5CB83EA4EACE5C49BA7F2950ECA9 /* ASStackLayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9E3882ED3E01A3E7A88E9667C0A0636E /* ASTextLine.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9318386970E3A7089D8AE292D4EEDE6E /* ASTextLine.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + 9E6C79BB56B41FFA1665D1E22479ADA2 /* ASMainThreadDeallocation.h in Headers */ = {isa = PBXBuildFile; fileRef = A95018FAB6DF136289250129CA16934B /* ASMainThreadDeallocation.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9ED8020DA12D0C5DBB23C41412BC265B /* ASDisplayNode.h in Headers */ = {isa = PBXBuildFile; fileRef = D6832FF8EE7BE44EEE48E868181CF012 /* ASDisplayNode.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9F8BD0381058FDEDA5F0671FA6B3A02B /* ASStackLayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2680C9F6C531597EADA4A0063E0F309C /* ASStackLayoutSpec.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + A050FC9032EB0721F865B1DC6AACECE7 /* ASNetworkImageLoadInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 9006140792ABAA03CFF2A94DD61C060C /* ASNetworkImageLoadInfo.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A1879D2E926B3B4D0434F59C3CFEA7E4 /* _ASCollectionViewCell.mm in Sources */ = {isa = PBXBuildFile; fileRef = E61F401F1879DA3A4E7851A9673CD3D1 /* _ASCollectionViewCell.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + A23A686BD3E5A1A45732474F086E5108 /* ASExperimentalFeatures.mm in Sources */ = {isa = PBXBuildFile; fileRef = 968B6A7ECF2BBF3568943F0D1F239684 /* ASExperimentalFeatures.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + A247EE1D278372734D41DA53BCC96820 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 465C7CD0B951D0E0D97FC9851987095F /* UIKit.framework */; }; + A248291B4CDBC90A07C1BCD8726C6CBC /* ASCollectionViewLayoutController.h in Headers */ = {isa = PBXBuildFile; fileRef = B6477497D4580620434C98CDF8C40318 /* ASCollectionViewLayoutController.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A27474180106AAAB41F2871304AC64CC /* UIResponder+AsyncDisplayKit.mm in Sources */ = {isa = PBXBuildFile; fileRef = 460688F46B0349193D03A8CD6459F014 /* UIResponder+AsyncDisplayKit.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + A418F40185DE480BA18D77831E3C9FCB /* ASTextKitRenderer.h in Headers */ = {isa = PBXBuildFile; fileRef = 60A4C17A948E6BF765E2CD491916B631 /* ASTextKitRenderer.h */; settings = {ATTRIBUTES = (Project, ); }; }; + A42EC5DE809DA43B92C0A1FB1591A823 /* ASTextLayout.mm in Sources */ = {isa = PBXBuildFile; fileRef = 18AC72A19500327987AC0AD7EE4F486A /* ASTextLayout.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + A43B3297E738E64DFE93D4CE2C81D24A /* ASIGListAdapterBasedDataSource.mm in Sources */ = {isa = PBXBuildFile; fileRef = F3E3F88611CECB60D3DE4ABAE592D898 /* ASIGListAdapterBasedDataSource.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + A468E4B8A47DCEC32494D8A69DAD4F2D /* PINCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 189BBBEF314F5632F32DEBAB3119AA42 /* PINCache.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A616785B7772BEEE063DDDCC1A5C06B6 /* PINButton+PINRemoteImage.h in Headers */ = {isa = PBXBuildFile; fileRef = 1CD84E09F5FA855D090FB8AA27C039C2 /* PINButton+PINRemoteImage.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A62703EF376B85BECA85A817C34DAEF3 /* ASTipNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = EF0D927D178E9A955F76E23A8D12530C /* ASTipNode.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + A655F6C0A8C3CE5A1554C2CCDE7AC866 /* ASLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 3F391532C2485B229B7BDE74FF398D41 /* ASLayout.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A6DFC86F9D280E7B7F2E025EFCE9EC91 /* ASStackLayoutElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 685D1E26BFDFC90E7B8DDC3D5A043BE8 /* ASStackLayoutElement.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A7319DA9AD51B4B7EA19086A54DD05DB /* NSMutableAttributedString+TextKitAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3645176D480EBDC746EAB7706EC0FE2C /* NSMutableAttributedString+TextKitAdditions.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + A731B930111DDE6D8923FBB8664602BE /* ASTextKitAttributes.h in Headers */ = {isa = PBXBuildFile; fileRef = 569D8D9DDDC854B8EC673ADB1025D26A /* ASTextKitAttributes.h */; settings = {ATTRIBUTES = (Project, ); }; }; + A7D8E709A23130883C176E7E6572C392 /* ASRelativeLayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = C04A40D87995160C194B1F420E82E654 /* ASRelativeLayoutSpec.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + A86B4CF5B6E0E90E2002EFD6ECA2E74A /* PINDisplayLink.m in Sources */ = {isa = PBXBuildFile; fileRef = BB800E39017591858E237D0FF17509B6 /* PINDisplayLink.m */; }; + A8DEB1E7C5738E83B9EB996DF3B6B780 /* _ASTransitionContext.mm in Sources */ = {isa = PBXBuildFile; fileRef = B9B56E35637395BD7F69E4B7F8F55DBB /* _ASTransitionContext.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + A93E5D1247E109BF74D6E0E0C56A1CA2 /* ASAvailability.h in Headers */ = {isa = PBXBuildFile; fileRef = 52DA18796B6E521C4C76474566901974 /* ASAvailability.h */; settings = {ATTRIBUTES = (Public, ); }; }; + AA08E3A01738ABED66294CFED9B6232E /* NSIndexSet+ASHelpers.h in Headers */ = {isa = PBXBuildFile; fileRef = A06BA14EEE45FB041AC986113251A89C /* NSIndexSet+ASHelpers.h */; settings = {ATTRIBUTES = (Public, ); }; }; + AA177091C108492974D400BDB1A1CD62 /* ASCellNode+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 6CF7BC3A95218CA6803E27F02C16AA10 /* ASCellNode+Internal.h */; settings = {ATTRIBUTES = (Project, ); }; }; + AA38AD6092975ECEEE385AE0E53A830C /* ASImageNode+AnimatedImagePrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = A4C06B180E1A14EA1EC52F498B086A88 /* ASImageNode+AnimatedImagePrivate.h */; settings = {ATTRIBUTES = (Project, ); }; }; + AB100DDB742EE0376A45715B42344509 /* _ASScopeTimer.h in Headers */ = {isa = PBXBuildFile; fileRef = 752F0516FE00425919A33CB5BD264BD3 /* _ASScopeTimer.h */; settings = {ATTRIBUTES = (Project, ); }; }; + AB48ADC61226FB5C1BC8D659EAF750F6 /* ASNetworkImageNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 15B25876E76F0B07FDF260C40B4307AA /* ASNetworkImageNode.h */; settings = {ATTRIBUTES = (Public, ); }; }; + ABEA1AED8D9E7FE95885384AAF7786A6 /* ASIntegerMap.mm in Sources */ = {isa = PBXBuildFile; fileRef = 619DC215E1E24600C9B8C65AE0F6C571 /* ASIntegerMap.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + AC2E392B90F75B9B0B877513955A123D /* ASDimensionInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 7EB194E21CA3FFC96FFF3C80B1C91A86 /* ASDimensionInternal.h */; settings = {ATTRIBUTES = (Public, ); }; }; + AE00B7F987BD71B5EFD51D405455D667 /* ASCollectionLayoutDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = D5A164E3FB065BB3632339162B9844A4 /* ASCollectionLayoutDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; + AF55C78E39FBB0158FDD1F04874978C2 /* ASAbsoluteLayoutElement.h in Headers */ = {isa = PBXBuildFile; fileRef = AC3B95E05FDC42AC4D0011ED60ED02AB /* ASAbsoluteLayoutElement.h */; settings = {ATTRIBUTES = (Public, ); }; }; + B04D1648CEB49BF9A215EB68F782E66F /* ASDisplayNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = 921D71A9998F7251DCD498356DAFC992 /* ASDisplayNode.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + B0917E444B1352AF7285C09EF420028D /* _ASAsyncTransaction.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E5DAB6AE58136BEE1249F6A61EA5D30 /* _ASAsyncTransaction.h */; settings = {ATTRIBUTES = (Public, ); }; }; + B0E599A3B72EBA636823B6322F4CBF36 /* ASTipsWindow.h in Headers */ = {isa = PBXBuildFile; fileRef = 63178711D4323569A1B61319677AA577 /* ASTipsWindow.h */; settings = {ATTRIBUTES = (Project, ); }; }; + B1103DF2D4C2767291DD23DFCEA33024 /* Photos.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8C7A366CC3B6A0716315FAE4CC40827B /* Photos.framework */; }; + B12E5975BB9E7CB69C0C0E40564ABAFB /* ASCornerLayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = 32B6100A963D38523A8CA0A26B5F86B9 /* ASCornerLayoutSpec.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + B137969B8A651DA1148DEF66C0E26F73 /* ASHashing.h in Headers */ = {isa = PBXBuildFile; fileRef = 1C169176950FBC99FAAA05DFEBAE3D6F /* ASHashing.h */; settings = {ATTRIBUTES = (Public, ); }; }; + B1EEE6908F5B5C8DF426C37C012EA033 /* ASTableLayoutController.h in Headers */ = {isa = PBXBuildFile; fileRef = 25E255B9F966777064C6C1EE2E74C72E /* ASTableLayoutController.h */; settings = {ATTRIBUTES = (Public, ); }; }; + B205161CC6B478855EF36E244F0B2DA8 /* ASContextTransitioning.h in Headers */ = {isa = PBXBuildFile; fileRef = 14F32F69FDA6EE64E812A50AA22499CC /* ASContextTransitioning.h */; settings = {ATTRIBUTES = (Public, ); }; }; + B4419404D375692A51B0D095CAB19B67 /* ASBatchFetching.h in Headers */ = {isa = PBXBuildFile; fileRef = F1CE60DA44D9B7BDC09E8B75F5D86A7A /* ASBatchFetching.h */; settings = {ATTRIBUTES = (Project, ); }; }; + B487EEBAEBECA362C0EC6EE162A99EC3 /* ASControlTargetAction.mm in Sources */ = {isa = PBXBuildFile; fileRef = 29D339A04F28E55C76040CCF288282BB /* ASControlTargetAction.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + B53BAD4FEA26F027B02C817B0D56B03F /* ASCollectionLayoutCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 1035A7ACAFD6A65A4A3A01D8EC90E0B1 /* ASCollectionLayoutCache.h */; settings = {ATTRIBUTES = (Project, ); }; }; + B5563B5BB14EBE69B0684B4C5A96F51A /* PINRemoteWeakProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = 474F334721D6E818D56CC8B9B8BD4076 /* PINRemoteWeakProxy.h */; settings = {ATTRIBUTES = (Public, ); }; }; + B6FCFB22B26A8AD8B89DFAEAC29E0712 /* ASStackLayoutSpecUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = EF12EEA02B309EF99CDA1C4BB3D8D136 /* ASStackLayoutSpecUtilities.h */; settings = {ATTRIBUTES = (Project, ); }; }; + B70B74682AA34996E8CB2D683505E107 /* ASTableNode+Beta.h in Headers */ = {isa = PBXBuildFile; fileRef = D47F9321D282478772005EA04CBDB68C /* ASTableNode+Beta.h */; settings = {ATTRIBUTES = (Public, ); }; }; + B74E2B68A0C6A8E73F27F0EB5B7695D1 /* ASButtonNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6B20C8FA45310FF28361CEA25BCB0737 /* ASButtonNode.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + B77EF1662C95CF840BD1498F309E8FFC /* ASDisplayNode+Subclasses.h in Headers */ = {isa = PBXBuildFile; fileRef = 93851EFA588262FF22F2E4C428862442 /* ASDisplayNode+Subclasses.h */; settings = {ATTRIBUTES = (Public, ); }; }; + B878FFB40DC743FA707E282E2F66C060 /* _ASDisplayLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = A84EC65C2CB86EEA0D6C180F5830CBB5 /* _ASDisplayLayer.h */; settings = {ATTRIBUTES = (Public, ); }; }; + B9D87848BB35AC1F9280D3035D0E2BA4 /* ASCollectionNode+Beta.h in Headers */ = {isa = PBXBuildFile; fileRef = 48FFF4989571E03EB9AAD6897C4BCB4F /* ASCollectionNode+Beta.h */; settings = {ATTRIBUTES = (Public, ); }; }; + BE96104ACD0169003A3A222039106C33 /* ASVisibilityProtocols.mm in Sources */ = {isa = PBXBuildFile; fileRef = D3C2B5E2A1BCEF6ED01C9280FD3A6C30 /* ASVisibilityProtocols.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + BE9C538F9315DBDF8511D7B1C143276F /* ASBasicImageDownloaderInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = BDDB697D7302D7165178BFC76A053780 /* ASBasicImageDownloaderInternal.h */; settings = {ATTRIBUTES = (Project, ); }; }; + BEB3FA36F7395B27F36C4784927C3CFE /* ASBaseDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = 72A548F3BBC7390DE95BCD4F2A6954DD /* ASBaseDefines.h */; settings = {ATTRIBUTES = (Public, ); }; }; + BF2296DF6290E9C557FF126C8EBBAA7F /* ASTableView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37A923F0EEA3DE37175030DA432C7E9D /* ASTableView.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + BF6DF4769ECC4026C46285CB77452357 /* PINGIFAnimatedImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 5446BF8F6EE5445BB8095CED507BA6E0 /* PINGIFAnimatedImage.m */; }; + BF793A7E0F25CED19582524BD92EA327 /* ASInternalHelpers.mm in Sources */ = {isa = PBXBuildFile; fileRef = 288A70F6746F2D6740CA394D3B1AC339 /* ASInternalHelpers.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + C0A4A044D33E54CF070FA523D6FFA5AC /* ASDisplayNode+Beta.h in Headers */ = {isa = PBXBuildFile; fileRef = 672BD6EE10498EAC204A79FF07840EAE /* ASDisplayNode+Beta.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C1D9DF1192508FC8B1DB84C55D1E7E98 /* ASTextDebugOption.h in Headers */ = {isa = PBXBuildFile; fileRef = EB36743EBAA4B10A57C67D256B097261 /* ASTextDebugOption.h */; settings = {ATTRIBUTES = (Project, ); }; }; + C1E1845DA59006D300466D68C4A6F56F /* Texture-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 805F38459176CFBF7BFE070EAE17FF09 /* Texture-dummy.m */; }; + C25457E2BE17FD066A98C816D2752261 /* ASStackUnpositionedLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 1EB5C446879B3933F3EAB546FE597E8F /* ASStackUnpositionedLayout.h */; settings = {ATTRIBUTES = (Project, ); }; }; + C2B195B9AEF986D33CFF546944B10CDA /* PINRequestRetryStrategy.h in Headers */ = {isa = PBXBuildFile; fileRef = BDBDCD408FDA7E7CFEE56C22BE3A64D4 /* PINRequestRetryStrategy.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C2C213FC002E05563E7A5E1688008092 /* ASTip.mm in Sources */ = {isa = PBXBuildFile; fileRef = 53E2113FBB2C3C1025FA111A09D9705D /* ASTip.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + C48CA75F831753B49D4987E98DCAD48E /* ASTraitCollection.h in Headers */ = {isa = PBXBuildFile; fileRef = AA33C1BA9625FB1059A64D05E85E1075 /* ASTraitCollection.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C499B5868927409608468028A8408838 /* ASWeakSet.h in Headers */ = {isa = PBXBuildFile; fileRef = 2ECB89B68E304B7BCC55443D36FF5ACB /* ASWeakSet.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C49B74B05E2ECF40545BE26D82908D69 /* NSArray+Diffing.h in Headers */ = {isa = PBXBuildFile; fileRef = B4AC4FD5B9DD6F7746F71563A59BE136 /* NSArray+Diffing.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C54D1790AEDE01C590686294934A1E13 /* ASRatioLayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B6A6B6008B8473CDF79B3962055B2B5 /* ASRatioLayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C5F8FA0CB5BB90D56C13A44180EE8329 /* NSParagraphStyle+ASText.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9AAB840C4B625AB58BBC77E40F7BF770 /* NSParagraphStyle+ASText.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + C5F9B33244F7F718765BD17232268474 /* PINMemoryCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 9E12D4B3FB7683F25D5A629A2F5CF4F5 /* PINMemoryCache.m */; }; + C6280E10614BA6CF600C8A80BC1C426E /* ASVideoNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3E57C58291CEC254C5C79E173BBAA35F /* ASVideoNode.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + C75C96A4CB5497109200277BC3B826AC /* ASTextKitAttributes.mm in Sources */ = {isa = PBXBuildFile; fileRef = D51AF6284504A2AF4A3CCCAEABC19559 /* ASTextKitAttributes.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + C779ACC80C0E385B487DD2AF73C7B629 /* ASDisplayNode+Ancestry.mm in Sources */ = {isa = PBXBuildFile; fileRef = 12E1DD8F58F53738EFB3B9BEA7E0CB09 /* ASDisplayNode+Ancestry.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + C782835223517924A26F0EBFEA0C00DF /* AssetsLibrary.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 831337E1D6E0B5DD90702FBF69CA4A97 /* AssetsLibrary.framework */; }; + C7B9FC1B5CFDD47203D8019533E52F0B /* ASPendingStateController.mm in Sources */ = {isa = PBXBuildFile; fileRef = AFEEAA3B4F1296937A0BEE268BAF05EF /* ASPendingStateController.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + C819EBDDE5B4938A862427E81C9B66E9 /* ASCollectionInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 1A499BBBC19F5F5CEDF35EB494657568 /* ASCollectionInternal.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C8CF437ABF283BBC35D39F66A92F7895 /* ASLayerBackingTipProvider.mm in Sources */ = {isa = PBXBuildFile; fileRef = 88CCC3ACF89F4533FFBBD74CFC1806D4 /* ASLayerBackingTipProvider.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + C959FB55033C5DE24B6B14315BCD441B /* AsyncDisplayKit+Debug.h in Headers */ = {isa = PBXBuildFile; fileRef = 488883D3A35A3CCB6513EFFD7D6B5F7A /* AsyncDisplayKit+Debug.h */; settings = {ATTRIBUTES = (Public, ); }; }; + CA5853666CACFF05F38A33B2E5DA1949 /* ASOverlayLayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = F975825068D5F553799FAEC02F007206 /* ASOverlayLayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; }; + CB6EF8043EBA5C2100A53E6547E7FBD5 /* ASDisplayNodeLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 291A0CC83913FFE45EFDE20041E3B489 /* ASDisplayNodeLayout.h */; settings = {ATTRIBUTES = (Project, ); }; }; + CB74C471FCDF99CE778F9F12A435D480 /* ASTextLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 99139531ABD4081B9313BB193B8F3C82 /* ASTextLayout.h */; settings = {ATTRIBUTES = (Project, ); }; }; + CBEBD4CC5E49C32325142632AF551934 /* PINRemoteImageTask.h in Headers */ = {isa = PBXBuildFile; fileRef = 9E57E56ED96B01EFFB474D13F8F2E736 /* PINRemoteImageTask.h */; settings = {ATTRIBUTES = (Public, ); }; }; + CC2E10F3DE5A2E0DEF91BF948FE2E06C /* _ASAsyncTransactionContainer.h in Headers */ = {isa = PBXBuildFile; fileRef = 23917528E76A1002E1E03A86CA321667 /* _ASAsyncTransactionContainer.h */; settings = {ATTRIBUTES = (Public, ); }; }; + CC40F97A1370E500B10B5536F9149E88 /* ASRangeManagingNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 61864DC4D9246D85E128A5C4310F92FA /* ASRangeManagingNode.h */; settings = {ATTRIBUTES = (Public, ); }; }; + CCF02EB012ED66CDE4DCEFE3C6731517 /* AsyncDisplayKit+Debug.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3312506CD352A7FD094C3802FCB31C0B /* AsyncDisplayKit+Debug.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + CD2A3263FA9B3629F7D373E5D898B530 /* ASOverlayLayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1DDEB70D394FE11270864A3A83BD36BB /* ASOverlayLayoutSpec.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + CD3FBB0BBE384A5810065C4D88D035A4 /* ASLayoutTransition.mm in Sources */ = {isa = PBXBuildFile; fileRef = A80CA8D6A0D34301B629FB11D8686CEA /* ASLayoutTransition.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + CD68FB8439266630F7BEB187F7449BA3 /* ASStackLayoutDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = 4FC69BD5F886F0FD2ACA5CE367FA5C9A /* ASStackLayoutDefines.h */; settings = {ATTRIBUTES = (Public, ); }; }; + CD731F1BB5013903E2DF3A6BFF044E3B /* ASTextKitFontSizeAdjuster.h in Headers */ = {isa = PBXBuildFile; fileRef = 443390523E8A73440A7191BA0094872A /* ASTextKitFontSizeAdjuster.h */; settings = {ATTRIBUTES = (Project, ); }; }; + CE2FBCFCF10B33851635AE494E68C29D /* ASMutableElementMap.mm in Sources */ = {isa = PBXBuildFile; fileRef = B3A200415E395C61CA67F56E33FC809D /* ASMutableElementMap.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + CF887DB488D89F9D6C22E641C34E37E5 /* ASCollectionViewProtocols.h in Headers */ = {isa = PBXBuildFile; fileRef = B82A5D010BD4DA75A6D18C51A793149F /* ASCollectionViewProtocols.h */; settings = {ATTRIBUTES = (Public, ); }; }; + CFC2BE7DC7C763B8A173C5151E646630 /* PINOperationQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = E4A4E77985D62E4AE0F17F553E7EB919 /* PINOperationQueue.m */; }; + D0D844293600CC08BA0440D086DF6F31 /* PINRemoteLock.m in Sources */ = {isa = PBXBuildFile; fileRef = 70907139FDD30DCEEA7A9190BA24A9D8 /* PINRemoteLock.m */; }; + D20712489CC62AD1D69DE3D4F067CC6D /* ASDimensionInternal.mm in Sources */ = {isa = PBXBuildFile; fileRef = D84FBBB202FB56A503EA21E03778DED7 /* ASDimensionInternal.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + D22F83E790D592BF6759A38C4001B050 /* PINRemoteLock.h in Headers */ = {isa = PBXBuildFile; fileRef = 28C3E95F4439C05AD7DD1B694E01B06F /* PINRemoteLock.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D263AAA51D764356CED7F375B1536628 /* AsyncDisplayKit+Tips.h in Headers */ = {isa = PBXBuildFile; fileRef = 019B846EC672567C4BD6EF8712E2C6A8 /* AsyncDisplayKit+Tips.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D2663A7660C2B444D8DFD65C341C210D /* ASCollectionView.h in Headers */ = {isa = PBXBuildFile; fileRef = 6C96AD30B4BB4561311AF7FC108E3A40 /* ASCollectionView.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D27BEF1A0F0747FE98EF2B4AC76297BA /* PINGIFAnimatedImage.h in Headers */ = {isa = PBXBuildFile; fileRef = 736C046DCA265EE81A58FD5F89BF6102 /* PINGIFAnimatedImage.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D4DAB9BDA1A61E059E30DF61CDF7DEA7 /* PINOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = EECCA4A8A8683035F019428C5499815C /* PINOperation.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D511B3184C8CA22900E8CF81946472FA /* ASCollectionGalleryLayoutDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 01BB7760D1A5D00C917D4DA5CDCCAA59 /* ASCollectionGalleryLayoutDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D55227D66C8BD0B9EF66806624057C53 /* PINImage+DecodedImage.h in Headers */ = {isa = PBXBuildFile; fileRef = 451F8219FEECBEF1D8D5576A706A6259 /* PINImage+DecodedImage.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D567696E69F612CCD04A8B52C6AE69FD /* ASLayoutElement.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2061A3DD3D85D7601069EE51F296B793 /* ASLayoutElement.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + D5741B35FAD5625327DB7740816490EF /* ASGraphicsContext.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2D1C7EFB62BD6E8103714DB24F4ED389 /* ASGraphicsContext.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + D58F5F325475C2FA76B7A2C6D6DEBA08 /* _ASCollectionReusableView.mm in Sources */ = {isa = PBXBuildFile; fileRef = A36A6CCC10752056AAA5047945986152 /* _ASCollectionReusableView.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + D5C26BE5B56878BE80E4E358D01505BF /* PINProgressiveImage.h in Headers */ = {isa = PBXBuildFile; fileRef = F4EDFDD960862C423CFEE9683653342B /* PINProgressiveImage.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D5F9094AB618823BE2D258D83450ACF8 /* ASRatioLayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9BB90969D06357DC3F9419D00171EFDD /* ASRatioLayoutSpec.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + D65BE49AD19B4ED6FCCA1987CDFEAA81 /* ASRunLoopQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 87944CB6253052129AC860AB6A056454 /* ASRunLoopQueue.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D6FBD69E5AC966E364870F3BC89AE40B /* ASDisplayNodeCornerLayerDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = CAE35B3FA8348440C30B17D645FADAB6 /* ASDisplayNodeCornerLayerDelegate.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + D792C77237A52B9B995A8A2E9FD39B2C /* PINRemoteImageManagerResult.h in Headers */ = {isa = PBXBuildFile; fileRef = 013E1DDF851F0FD096C42D0EAC0480E2 /* PINRemoteImageManagerResult.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D7B3E1598EAA524245035481CFA22A9A /* ASNodeController+Beta.h in Headers */ = {isa = PBXBuildFile; fileRef = 0000FA49664A808C778E0664EAEBF541 /* ASNodeController+Beta.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D83ADCEE529736987678A5E5678C3EF4 /* PINCache+PINRemoteImageCaching.h in Headers */ = {isa = PBXBuildFile; fileRef = 60E2A9C368D8B4DB87B8786A1150005A /* PINCache+PINRemoteImageCaching.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D8B254AD7AE2D4AD795D0F0D24A9DC63 /* ASConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = B30F52EC317262D450BB71ABDF384A75 /* ASConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D8CDE83FC5F7BB9CBDDEF662EA434A76 /* ASHighlightOverlayLayer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 58139A95B4BA8249B925855915F54E9E /* ASHighlightOverlayLayer.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + D9E389EF92E5B549EA600E659E8CD045 /* ASTipsController.h in Headers */ = {isa = PBXBuildFile; fileRef = E801FD290C13CFBB6EECB86EAEDD14A2 /* ASTipsController.h */; settings = {ATTRIBUTES = (Project, ); }; }; + DADB3AD2B89E46C6A114C733FCCFFCCF /* PINRemoteImageManager+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = D7429861868D4103F11B9E94C19522A9 /* PINRemoteImageManager+Private.h */; settings = {ATTRIBUTES = (Public, ); }; }; + DC9252B91D454E933573D0F02CD34A48 /* ASDisplayNodeTipState.h in Headers */ = {isa = PBXBuildFile; fileRef = F1807082404C3DD7326F3A2E7E75B56E /* ASDisplayNodeTipState.h */; settings = {ATTRIBUTES = (Project, ); }; }; + DCA67BCF11BB08C4CC754BAB7DE9700F /* ASDisplayNode+Layout.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3BAB9C702191043B5AD931A92E4B8B03 /* ASDisplayNode+Layout.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + DCBB463A435F0BA2AA998D91A821BD63 /* ASDisplayNode+Yoga.h in Headers */ = {isa = PBXBuildFile; fileRef = 486B3CCA03BCC69D86F973E81F23F54B /* ASDisplayNode+Yoga.h */; settings = {ATTRIBUTES = (Public, ); }; }; + DCC0959800E3EC695210D0372765FBFA /* NSData+ImageDetectors.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F5D034DB1ED346FED8B825B2C4FACAE /* NSData+ImageDetectors.m */; }; + DD25CE53DC082769F959AC1EA39B47E1 /* ASSection.h in Headers */ = {isa = PBXBuildFile; fileRef = 22E5CA9609D7B764C9AD5E3E29A99198 /* ASSection.h */; settings = {ATTRIBUTES = (Project, ); }; }; + DD519256677113580497A433745E2AA5 /* PINCachedAnimatedImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 130371D9D979BBE4A21D3985FF5C0CBA /* PINCachedAnimatedImage.m */; }; + DDABD6DDE99C201E518E277995C4C3F7 /* ASTableViewProtocols.h in Headers */ = {isa = PBXBuildFile; fileRef = 6D14125F3A9F2F63DAD6311079217C87 /* ASTableViewProtocols.h */; settings = {ATTRIBUTES = (Public, ); }; }; + DDBE0B9C33CFF36A689113FB2148B8ED /* MapKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D6AF8F32499937E401433036C62354C2 /* MapKit.framework */; }; + DE1FBD64E727449DFEB41D8CC0B764E4 /* PINCacheMacros.h in Headers */ = {isa = PBXBuildFile; fileRef = D55B5D9D10346E310CC066CE345EF883 /* PINCacheMacros.h */; settings = {ATTRIBUTES = (Public, ); }; }; + DE43FB61D54CA4D4683A2976E2C4B17A /* ASBatchContext.h in Headers */ = {isa = PBXBuildFile; fileRef = 4E127E8EB3DF48F7A91A9E5934571535 /* ASBatchContext.h */; settings = {ATTRIBUTES = (Public, ); }; }; + DE51CF9E4A5474D3988D1BB66EF7618D /* PINRemoteWeakProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 9DA8213BEA83150926293674AE1852A9 /* PINRemoteWeakProxy.m */; }; + DEB64B74E02E6B5A3942813C4B0AA43A /* PINAnimatedImage.h in Headers */ = {isa = PBXBuildFile; fileRef = 966FD881395FF4683060BDF1A2C1AFE7 /* PINAnimatedImage.h */; settings = {ATTRIBUTES = (Public, ); }; }; + DF3EC03998606F8C6B59E9B8FA250DE1 /* NSData+ImageDetectors.h in Headers */ = {isa = PBXBuildFile; fileRef = DF03059CFC2CF8C308FEB24EB69C2BC1 /* NSData+ImageDetectors.h */; settings = {ATTRIBUTES = (Public, ); }; }; + DFB3623B8303E1732A2DE7866457BFA5 /* ASRunLoopQueue.mm in Sources */ = {isa = PBXBuildFile; fileRef = FC599DCB7E17E08FE32BF7AA8EE02251 /* ASRunLoopQueue.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + DFCF371994649A26AB2793A1539FDE27 /* CoreGraphics+ASConvenience.h in Headers */ = {isa = PBXBuildFile; fileRef = 602F988DE92671CC4096ADCC733A5930 /* CoreGraphics+ASConvenience.h */; settings = {ATTRIBUTES = (Public, ); }; }; + DFEDDB04D68B8A713D24FAB88F55988B /* _ASDisplayViewAccessiblity.h in Headers */ = {isa = PBXBuildFile; fileRef = CD86773D0E02DC18396836AFE7DC3918 /* _ASDisplayViewAccessiblity.h */; settings = {ATTRIBUTES = (Public, ); }; }; + E028DD17CB226563694AD3E644DA0E8A /* ASCollectionLayoutState+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C54EEEA6CDF354C39D11F1217C41EF7 /* ASCollectionLayoutState+Private.h */; settings = {ATTRIBUTES = (Project, ); }; }; + E061922DF3A631DE569DEC0B02A0E55C /* _ASDisplayView.h in Headers */ = {isa = PBXBuildFile; fileRef = EFEF3AB75FB8E5BA99A80A1B87EF0E3C /* _ASDisplayView.h */; settings = {ATTRIBUTES = (Public, ); }; }; + E0B5B77C3B206FBB576000E18BF9C56A /* ASVideoPlayerNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5C206ACAAB3B78082D7C88805B4938C1 /* ASVideoPlayerNode.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + E13C6028A12C4D85EBAAAA04C96FF536 /* ASTableLayoutController.mm in Sources */ = {isa = PBXBuildFile; fileRef = EAFE043D342B5CB9C48DEF9F7A994F87 /* ASTableLayoutController.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + E192EDBF272B1EF298F8FE2FA6DDAF25 /* NSAttributedString+ASText.mm in Sources */ = {isa = PBXBuildFile; fileRef = DDB932802CAA60747F82694A454F50F3 /* NSAttributedString+ASText.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + E2580439ED32A4AC27DF6CD05B69B472 /* ASMultiplexImageNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 75CD9ED9F22C2799B7BCA77BCFA90398 /* ASMultiplexImageNode.h */; settings = {ATTRIBUTES = (Public, ); }; }; + E3B60613FB36F520EAA02E7C405B4B10 /* ASTextKitRenderer+Positioning.mm in Sources */ = {isa = PBXBuildFile; fileRef = 00869DEB91DC50D648121592BAF044C1 /* ASTextKitRenderer+Positioning.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + E3DBA9B2AD2B13F98ABC7894743C5FF1 /* _ASTransitionContext.h in Headers */ = {isa = PBXBuildFile; fileRef = 775C7D276B9BD925498D2CC333EA1BC5 /* _ASTransitionContext.h */; settings = {ATTRIBUTES = (Public, ); }; }; + E49A788AD2790861B385BA2E084BFEF2 /* ASDataController.mm in Sources */ = {isa = PBXBuildFile; fileRef = B385F44C418B5E657B7816193A169A10 /* ASDataController.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + E4AFCBB6059FC3C56FCE281FE6B88A14 /* ASLayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = F6FEF6FE7DB7A8E54D5AE8F933CF1464 /* ASLayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; }; + E667CB4EFFBE95EA701598B0F79A3D29 /* PINOperationTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 556D557F23FCD191B665690B58AC0B95 /* PINOperationTypes.h */; settings = {ATTRIBUTES = (Public, ); }; }; + E66C56441574428F0E6319DD42712F00 /* NSHTTPURLResponse+MaxAge.h in Headers */ = {isa = PBXBuildFile; fileRef = FB32E2461EA09ADDAA167B8226BF68A2 /* NSHTTPURLResponse+MaxAge.h */; settings = {ATTRIBUTES = (Public, ); }; }; + E74676FCA35F49AB90084F9D6A133BEC /* ASEditableTextNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = CD6D8F0076FAC6559F82C7EC55D8786C /* ASEditableTextNode.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + E7F75D0551F60AC1E0FAAE389E0E48EE /* ASLayoutManager.mm in Sources */ = {isa = PBXBuildFile; fileRef = A7F0BEBBD6016E8CEB75E14D708BC006 /* ASLayoutManager.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + E80626C7D668DB07B848BEC6AAE711A4 /* ASControlTargetAction.h in Headers */ = {isa = PBXBuildFile; fileRef = D6FC0AB66F5BC4327C141E396044BC18 /* ASControlTargetAction.h */; settings = {ATTRIBUTES = (Project, ); }; }; + E81BA6B935A20C3C5AF436FD1097A607 /* ASCollectionElement.mm in Sources */ = {isa = PBXBuildFile; fileRef = A76D4E87369EBDA42913A7AAEFBF00D6 /* ASCollectionElement.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + E8261A6238D988688A11ECD8B35A479C /* ASDisplayNode+LayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = 16E3E7B6DDAAEE84D6A482759EDC819C /* ASDisplayNode+LayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; }; + E83BD30C134DC77082910C65567F529D /* NSHTTPURLResponse+MaxAge.m in Sources */ = {isa = PBXBuildFile; fileRef = D643AB237A68D3EA33E130BE941C23A2 /* NSHTTPURLResponse+MaxAge.m */; }; + E85CBF5F58F5FF76148975C11E38F27E /* ASButtonNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 5629107AFA4448F2FFFA6ABEDA650A40 /* ASButtonNode.h */; settings = {ATTRIBUTES = (Public, ); }; }; + E977832761EAFD3D2EF100FB74A57717 /* UICollectionViewLayout+ASConvenience.mm in Sources */ = {isa = PBXBuildFile; fileRef = 96BDF2482DF865D7F8FF0B07AEE55F7B /* UICollectionViewLayout+ASConvenience.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + E97E10031318F97C13605A4E7C6C241D /* ASCollectionLayout.mm in Sources */ = {isa = PBXBuildFile; fileRef = F084236284192AB0CB2F19F765DD52D1 /* ASCollectionLayout.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + E99888C1284FD4BA652BFCCBA42A69D0 /* AsyncDisplayKit+IGListKitMethods.mm in Sources */ = {isa = PBXBuildFile; fileRef = C6BA4FA2A0064B482B835FC3FCA51761 /* AsyncDisplayKit+IGListKitMethods.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + E9BF9E31DBD24F528DC4BE7EC406F7F1 /* ASPagerFlowLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = CFD1BD03E8A08531D5ED8002B0FE1376 /* ASPagerFlowLayout.h */; settings = {ATTRIBUTES = (Public, ); }; }; + EA86861420C58AAFFBDD4FE8B05A90F8 /* _ASDisplayViewAccessiblity.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5660A8B0F1FF8E3463CBC35A8A848AED /* _ASDisplayViewAccessiblity.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + EAAACE6B4C88D9F0E4E8EC5CD698212C /* PINWebPAnimatedImage.h in Headers */ = {isa = PBXBuildFile; fileRef = 626548E82C01FE272CD114C9F7F07556 /* PINWebPAnimatedImage.h */; settings = {ATTRIBUTES = (Public, ); }; }; + EB472359A2A67364B3578253C8C4ABC3 /* CoreLocation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6A8D376391747B8A795CD26BEAD779B /* CoreLocation.framework */; }; + EB5CB15BE4021AEEE889E49C99751359 /* ASTraitCollection.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0931C3BD6812CC5847438D3AB46BED25 /* ASTraitCollection.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + EBB22015243AF51E4A657BEB9377011D /* PINRemoteImageCallbacks.m in Sources */ = {isa = PBXBuildFile; fileRef = 1722A238A2D68ECA7C85A592D48C54E5 /* PINRemoteImageCallbacks.m */; }; + EBB98BADBF34A5832DC9812C4D45C02F /* ASBatchFetchingDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = E94C2962BB22E3F85E5F03567F0FE0CF /* ASBatchFetchingDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; + EC5DD146A3413EF265E41C662ACFEA4B /* PINCache-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F1D0CDE40B7ADECFF3A55527DA6DDC0 /* PINCache-dummy.m */; }; + EC8A4029FDCEAEEB3810CF75685F7878 /* PINAlternateRepresentationProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 2F1876604918F7E85C8C8C00692D7E31 /* PINAlternateRepresentationProvider.m */; }; + ECB48648B7061F5DA13D61BF35E9E675 /* PINRemoteImageManager.m in Sources */ = {isa = PBXBuildFile; fileRef = EE2940E42584F168B39AE1ADCDAF32AA /* PINRemoteImageManager.m */; }; + ED7E794ADD340BEFFC80FC75837CFBBC /* PINURLSessionManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 6B20999B345220EA81A18684BC483B2D /* PINURLSessionManager.m */; }; + EE30A346E8E2989D4D8A1E2A60608C5D /* _ASHierarchyChangeSet.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0C9E1840C6E647023E453E49A57E0363 /* _ASHierarchyChangeSet.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + EF68CE5C31FA5D353DFA4536D85E7067 /* ASTextKitRenderer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 26987C59CE8FC7CBB385300C0F6BDA5A /* ASTextKitRenderer.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + EFA0B7147CDDF2C33019191E9E492CDB /* ASTableView+Undeprecated.h in Headers */ = {isa = PBXBuildFile; fileRef = BB705D337F09F90307592D19183A1EFF /* ASTableView+Undeprecated.h */; settings = {ATTRIBUTES = (Project, ); }; }; + EFEBFE9D117F31C3EFBCFF03E4F374B8 /* ASImageNode+tvOS.mm in Sources */ = {isa = PBXBuildFile; fileRef = 047916BEFC3829F9505A8CE2D37A5155 /* ASImageNode+tvOS.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + F01A56AD8FC4D13459E7A4088437DD60 /* ASTextNode2.mm in Sources */ = {isa = PBXBuildFile; fileRef = 99443CE1B364268627439CDB10A3ACE7 /* ASTextNode2.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + F0CE14EDC70238AE475B95C1E92AB258 /* ASRangeControllerUpdateRangeProtocol+Beta.h in Headers */ = {isa = PBXBuildFile; fileRef = 29374D2518720868B57430A0CD71E8C5 /* ASRangeControllerUpdateRangeProtocol+Beta.h */; settings = {ATTRIBUTES = (Public, ); }; }; + F1852DD8600E8B26A90C21441ED9FAF8 /* ASCollectionNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 1815DDF16A824E46B244423DC105FCC8 /* ASCollectionNode.h */; settings = {ATTRIBUTES = (Public, ); }; }; + F1B69EBD630EA9C41E76FAF9B4878266 /* ASTextNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = 7CEC8210F1769BD7D7B357072E7E3B82 /* ASTextNode.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + F1CD75754D06FFE6667055828402AC19 /* ASCenterLayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8A485B78C571482DA19EFA8BD850DB02 /* ASCenterLayoutSpec.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + F1E44CF8A9BDBF622C310A0A52FC9069 /* ASSectionContext.h in Headers */ = {isa = PBXBuildFile; fileRef = EBE62C6CE8D397ED68FF4E791DA4CB38 /* ASSectionContext.h */; settings = {ATTRIBUTES = (Public, ); }; }; + F28CAD5ABEDD987D9FDBD0C75C1E22D5 /* ASTextKitShadower.h in Headers */ = {isa = PBXBuildFile; fileRef = A0CDEAD7A3000E9E2DD98E6D92AD2D21 /* ASTextKitShadower.h */; settings = {ATTRIBUTES = (Project, ); }; }; + F3457FE949B058184A4FB77C6AE576AE /* ASLayoutController.h in Headers */ = {isa = PBXBuildFile; fileRef = D0C20EDEB4D6EE05F48F1581955AD35A /* ASLayoutController.h */; settings = {ATTRIBUTES = (Public, ); }; }; + F37A13201EAD5DDD51A3D7F7BA82C840 /* ASConfigurationDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 064F70BA9938BBEF7CC08DF883950E3E /* ASConfigurationDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; + F39F51D473548ECD4D257BF5CDF965CD /* ASTipNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 2E3B81403D77240183D2690D35A9E173 /* ASTipNode.h */; settings = {ATTRIBUTES = (Project, ); }; }; + F3E0A225E531BB32262D7D9E6BCCCC43 /* ASInsetLayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = 080BF99D1FB1C1DD10745210E4C324F4 /* ASInsetLayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; }; + F40F8AD0FB1CAA4C367596C50912AF95 /* ASTextDebugOption.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9D63CE7677F2708E6EFB6CCFFAC34952 /* ASTextDebugOption.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + F60818A3F394E384B349C7F3FD8896D1 /* ASTabBarController.mm in Sources */ = {isa = PBXBuildFile; fileRef = BB6C6201DDCE0BC37AB907FDD59EE119 /* ASTabBarController.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + F71109526514F1B3E7A8F233DE070559 /* ASTextKitTailTruncater.h in Headers */ = {isa = PBXBuildFile; fileRef = F90035D014D493146733CA992C815AAF /* ASTextKitTailTruncater.h */; settings = {ATTRIBUTES = (Project, ); }; }; + F7DCF358D078332CEB3F7AD5AAAC4530 /* ASDispatch.h in Headers */ = {isa = PBXBuildFile; fileRef = 162A1D688BC1738E7F0A6348E168A3D5 /* ASDispatch.h */; settings = {ATTRIBUTES = (Project, ); }; }; + F7E577DBB5E622CF4B9BE7D057FB7860 /* ASSignpost.h in Headers */ = {isa = PBXBuildFile; fileRef = D458980EAD9CDDC12A46DBFF63A4EBF5 /* ASSignpost.h */; settings = {ATTRIBUTES = (Public, ); }; }; + F803EDD45DA76E8392546A43BD223D8F /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8417A1FA330D0B15E1EDF7547C84EB6E /* Accelerate.framework */; }; + F806F7F37B84AEC1C0AEE5C7000E0466 /* ASSectionController.h in Headers */ = {isa = PBXBuildFile; fileRef = BCE8409AD4EB33E423BAD5BB2C25137D /* ASSectionController.h */; settings = {ATTRIBUTES = (Public, ); }; }; + F80827ADB4E39BC74D92F18ABD8F92A8 /* NSMutableAttributedString+TextKitAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 11F30CDBA493D7DF5D65CDE41CDDB13D /* NSMutableAttributedString+TextKitAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + F8D6DE5B1A9935A9B5C53769624F1C53 /* PINCachedAnimatedImage.h in Headers */ = {isa = PBXBuildFile; fileRef = 3C69FF5E87A081B57AFD9CF41C19BC10 /* PINCachedAnimatedImage.h */; settings = {ATTRIBUTES = (Public, ); }; }; + FA09956C6A65F04B260B032B19C3C408 /* PINAlternateRepresentationProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = 5316B79F60F907878E80E6789410C62C /* PINAlternateRepresentationProvider.h */; settings = {ATTRIBUTES = (Public, ); }; }; + FA3B40D2C4CB095BE4314E8F1FF7000C /* PINRemoteImage-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 510DCE3B48F67D693EF6D5292014D927 /* PINRemoteImage-dummy.m */; }; + FAACAA3FAADE3A8D89DCE1E374BECEA7 /* ASAbstractLayoutController.h in Headers */ = {isa = PBXBuildFile; fileRef = B458A01741A678DD40054F964CE52EA9 /* ASAbstractLayoutController.h */; settings = {ATTRIBUTES = (Public, ); }; }; + FB849B4B080AD99E6AA582A08D0B136D /* ASLayoutSpec+Subclasses.h in Headers */ = {isa = PBXBuildFile; fileRef = 661EC3CE84810954586C6607786653F5 /* ASLayoutSpec+Subclasses.h */; settings = {ATTRIBUTES = (Public, ); }; }; + FC11F6DB1023A426B9D6DEC5FEAFEA20 /* ASCollectionLayoutDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = 8983A1BC34984FEE903C32F947A4A11E /* ASCollectionLayoutDefines.h */; settings = {ATTRIBUTES = (Project, ); }; }; + FC78F578F793BE2AACE996A785F1C6A5 /* ASCollectionViewLayoutInspector.h in Headers */ = {isa = PBXBuildFile; fileRef = A29BC347D15906796EAF09C899F0BAA9 /* ASCollectionViewLayoutInspector.h */; settings = {ATTRIBUTES = (Public, ); }; }; + FDA5B5348C842BAC45AE14D72B2E2782 /* ASBackgroundLayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = 915A108BCEEDF6DED44581345ADD5FF6 /* ASBackgroundLayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; }; + FE31388AFA377EF837E0D80C74351FD7 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6B050E212264CF3B336ED28484FB5132 /* AVFoundation.framework */; }; + FE4EC78B53C49C251ADA8964979013D8 /* ASTwoDimensionalArrayUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = 327F3986E5C333ACAB05D4743613E811 /* ASTwoDimensionalArrayUtils.h */; settings = {ATTRIBUTES = (Project, ); }; }; + FEC35A85C255EC1E831E29A8A6A3280C /* ASLayout+IGListDiffKit.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2FD43643551686EF122ADF4DF1821D39 /* ASLayout+IGListDiffKit.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + FF156CB786C29B397B3A97686DDDA057 /* PINSpeedRecorder.m in Sources */ = {isa = PBXBuildFile; fileRef = 76660865DC7BA0803ED5AF36FA8E6ECA /* PINSpeedRecorder.m */; }; + FF15EAE598D7123397737DBC37BE77A0 /* ASNavigationController.mm in Sources */ = {isa = PBXBuildFile; fileRef = F5EC4250B2AF97E5233012DE973EC2C6 /* ASNavigationController.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + FF4F84E904D5B54DD1E596BB8BB3C4B2 /* ASTextKitComponents.mm in Sources */ = {isa = PBXBuildFile; fileRef = 44980E879FE0D7AEAF373DE0F2B5BEFA /* ASTextKitComponents.mm */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; }; + FF7AA3C39BBA2E267FE9EA24603BB7C5 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CD8F721F21C26F26C2A6B469454DB51F /* Foundation.framework */; }; + FFA03ECBF831BCB7A9BB9D3AB5168587 /* PINRemoteImage.h in Headers */ = {isa = PBXBuildFile; fileRef = 5688A739BB7B10A17AF1E41094FBDD6B /* PINRemoteImage.h */; settings = {ATTRIBUTES = (Public, ); }; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 0D185FAE68986375EFA80D7E588C36F0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = A927C3CD51C30012080D6CA07959B246; + remoteInfo = PINCache; + }; + 25C016BF0E57B6AE0FB3E0108AD294A3 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 57310B016450E63387C9D64F4933E995; + remoteInfo = PINOperation; + }; + 602227D31B6AB6F04095141DDBF80D9D /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 57310B016450E63387C9D64F4933E995; + remoteInfo = PINOperation; + }; + 6351C529E119A5555556668E09168553 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = DB66E43BC980D741F3C7C5CF3ACEEFA4; + remoteInfo = Texture; + }; + 771B02F0B3D3712E3B8E9ACB4406E929 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = A927C3CD51C30012080D6CA07959B246; + remoteInfo = PINCache; + }; + 87D1D5F03D5166DB556E01B4BBC3A40C /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 57310B016450E63387C9D64F4933E995; + remoteInfo = PINOperation; + }; + 88E412F7FD6AAAEE34058E9863C18479 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = E2A8B2A28D2EAB2E4CCF3B69E6792851; + remoteInfo = PINRemoteImage; + }; + F724C535E2BD3CB7050BFBC52A0D455E /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = E2A8B2A28D2EAB2E4CCF3B69E6792851; + remoteInfo = PINRemoteImage; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 0000FA49664A808C778E0664EAEBF541 /* ASNodeController+Beta.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "ASNodeController+Beta.h"; path = "Source/ASNodeController+Beta.h"; sourceTree = ""; }; + 00869DEB91DC50D648121592BAF044C1 /* ASTextKitRenderer+Positioning.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = "ASTextKitRenderer+Positioning.mm"; path = "Source/TextKit/ASTextKitRenderer+Positioning.mm"; sourceTree = ""; }; + 00A4E1BB5E6F3722E8E36E1169E8D092 /* ASDKViewController.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASDKViewController.mm; path = Source/ASDKViewController.mm; sourceTree = ""; }; + 00E907AFDB4D6D82FD9F4128D2C4B8DE /* ASTextKitRenderer+TextChecking.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = "ASTextKitRenderer+TextChecking.mm"; path = "Source/TextKit/ASTextKitRenderer+TextChecking.mm"; sourceTree = ""; }; + 0127E215BAC572610DF54FD21EBFB99E /* ASLayout.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASLayout.mm; path = Source/Layout/ASLayout.mm; sourceTree = ""; }; + 013E1DDF851F0FD096C42D0EAC0480E2 /* PINRemoteImageManagerResult.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PINRemoteImageManagerResult.h; path = Source/Classes/include/PINRemoteImageManagerResult.h; sourceTree = ""; }; + 019B846EC672567C4BD6EF8712E2C6A8 /* AsyncDisplayKit+Tips.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "AsyncDisplayKit+Tips.h"; path = "Source/Debug/AsyncDisplayKit+Tips.h"; sourceTree = ""; }; + 01BB7760D1A5D00C917D4DA5CDCCAA59 /* ASCollectionGalleryLayoutDelegate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASCollectionGalleryLayoutDelegate.h; path = Source/Details/ASCollectionGalleryLayoutDelegate.h; sourceTree = ""; }; + 01E0794C0798D247DA040090A445797A /* ASDefaultPlayButton.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASDefaultPlayButton.h; path = Source/Private/ASDefaultPlayButton.h; sourceTree = ""; }; + 033CCEDAB42EDF1F79E201309B0557B6 /* PINRemoteImageProcessorTask.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PINRemoteImageProcessorTask.m; path = Source/Classes/PINRemoteImageProcessorTask.m; sourceTree = ""; }; + 0360DEFA13C4B085B68FB9C58F5C351D /* ASHighlightOverlayLayer.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASHighlightOverlayLayer.h; path = Source/Details/ASHighlightOverlayLayer.h; sourceTree = ""; }; + 039E32F34E01B17F98C04D3C94E2E219 /* ASTextRunDelegate.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASTextRunDelegate.mm; path = Source/Private/TextExperiment/String/ASTextRunDelegate.mm; sourceTree = ""; }; + 045B4CB141E1430CE34D42817E360277 /* ASTextLine.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASTextLine.h; path = Source/Private/TextExperiment/Component/ASTextLine.h; sourceTree = ""; }; + 047916BEFC3829F9505A8CE2D37A5155 /* ASImageNode+tvOS.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = "ASImageNode+tvOS.mm"; path = "Source/tvOS/ASImageNode+tvOS.mm"; sourceTree = ""; }; + 0628D2361BC4A2D2C737966B84D1338B /* ASDisplayNode+LayoutSpec.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = "ASDisplayNode+LayoutSpec.mm"; path = "Source/ASDisplayNode+LayoutSpec.mm"; sourceTree = ""; }; + 064F70BA9938BBEF7CC08DF883950E3E /* ASConfigurationDelegate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASConfigurationDelegate.h; path = Source/ASConfigurationDelegate.h; sourceTree = ""; }; + 070352748F86CF00AF87F8EB9D2DE7E5 /* ASTwoDimensionalArrayUtils.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASTwoDimensionalArrayUtils.mm; path = Source/Private/ASTwoDimensionalArrayUtils.mm; sourceTree = ""; }; + 0706408BB07355DB8AE83E0ED628FE32 /* ASNavigationController.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASNavigationController.h; path = Source/ASNavigationController.h; sourceTree = ""; }; + 078A70C4529DBC3C4A754417C5FBDBBE /* ASCollectionViewLayoutFacilitatorProtocol.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASCollectionViewLayoutFacilitatorProtocol.h; path = Source/ASCollectionViewLayoutFacilitatorProtocol.h; sourceTree = ""; }; + 07B22A0BED1962C77F9548379F0C548E /* Pods-Starbucks_Clone_TexBrother-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-Starbucks_Clone_TexBrother-umbrella.h"; sourceTree = ""; }; + 07BAF72F98DCBECFFC2B7F3A0FB2D752 /* ASLayout+IGListDiffKit.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "ASLayout+IGListDiffKit.h"; path = "Source/Layout/ASLayout+IGListDiffKit.h"; sourceTree = ""; }; + 07E4F833BCFBD185B878CF4D918216C2 /* ASButtonNode+Yoga.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = "ASButtonNode+Yoga.mm"; path = "Source/ASButtonNode+Yoga.mm"; sourceTree = ""; }; + 080BF99D1FB1C1DD10745210E4C324F4 /* ASInsetLayoutSpec.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASInsetLayoutSpec.h; path = Source/Layout/ASInsetLayoutSpec.h; sourceTree = ""; }; + 09018DF2292D2A0DB7CD1AE16D80E0C5 /* PINCache+PINRemoteImageCaching.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "PINCache+PINRemoteImageCaching.m"; path = "Source/Classes/PINCache/PINCache+PINRemoteImageCaching.m"; sourceTree = ""; }; + 0931C3BD6812CC5847438D3AB46BED25 /* ASTraitCollection.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASTraitCollection.mm; path = Source/Details/ASTraitCollection.mm; sourceTree = ""; }; + 0964567655AE549BDF757E8DF816FBFF /* PINMemoryCache.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PINMemoryCache.h; path = Source/PINMemoryCache.h; sourceTree = ""; }; + 098ADDB3AB7C97CB73062B784B1F66FA /* ASMapNode.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASMapNode.mm; path = Source/ASMapNode.mm; sourceTree = ""; }; + 09C578D28C9AD6E160C72FF044636EBA /* ASLayoutSpec.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASLayoutSpec.mm; path = Source/Layout/ASLayoutSpec.mm; sourceTree = ""; }; + 0B6ED2BB64A6A7DA8BC93530E3F55366 /* PINRemoteImage-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "PINRemoteImage-umbrella.h"; sourceTree = ""; }; + 0C18C9A1EB2B7BEFDEF4D696077F511A /* ASEqualityHelpers.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASEqualityHelpers.h; path = Source/Base/ASEqualityHelpers.h; sourceTree = ""; }; + 0C196203FD400F24E5723D12AB860AB6 /* Pods-Starbucks_Clone_TexBrother-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-Starbucks_Clone_TexBrother-acknowledgements.plist"; sourceTree = ""; }; + 0C2BF8A59FA951CF59C6FA88A1A8895D /* PINOperation.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = PINOperation.modulemap; sourceTree = ""; }; + 0C9E1840C6E647023E453E49A57E0363 /* _ASHierarchyChangeSet.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = _ASHierarchyChangeSet.mm; path = Source/Private/_ASHierarchyChangeSet.mm; sourceTree = ""; }; + 0CA558CE62A460B44AFC3565ADFCAEE5 /* ASDisplayNodeCornerLayerDelegate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASDisplayNodeCornerLayerDelegate.h; path = Source/Private/ASDisplayNodeCornerLayerDelegate.h; sourceTree = ""; }; + 0CB053AFA33FB3BE65B1DDD1F6796D07 /* ASTabBarController.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASTabBarController.h; path = Source/ASTabBarController.h; sourceTree = ""; }; + 0DBC478814C98DC8DF9A700BA2AFB12E /* PINAnimatedImageView+PINRemoteImage.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "PINAnimatedImageView+PINRemoteImage.h"; path = "Source/Classes/include/PINAnimatedImageView+PINRemoteImage.h"; sourceTree = ""; }; + 0E5DAB6AE58136BEE1249F6A61EA5D30 /* _ASAsyncTransaction.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = _ASAsyncTransaction.h; path = Source/Details/Transactions/_ASAsyncTransaction.h; sourceTree = ""; }; + 0ECB2E92B3BD6B8EF0C554FFB94D06B1 /* PINCache.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = PINCache.release.xcconfig; sourceTree = ""; }; + 0F518543A74B628F00ECD15DB5F7445D /* ASLocking.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASLocking.h; path = Source/ASLocking.h; sourceTree = ""; }; + 0F524724237EF9AD3AEF9016F5121229 /* ASRecursiveUnfairLock.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASRecursiveUnfairLock.mm; path = Source/Details/ASRecursiveUnfairLock.mm; sourceTree = ""; }; + 0F5C33BA4E88B0B326DFFE9C5340A1E7 /* ASBackgroundLayoutSpec.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASBackgroundLayoutSpec.mm; path = Source/Layout/ASBackgroundLayoutSpec.mm; sourceTree = ""; }; + 0F909487B055776AE89A8FBB70923EBF /* ASLayoutElementStylePrivate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASLayoutElementStylePrivate.h; path = Source/Private/Layout/ASLayoutElementStylePrivate.h; sourceTree = ""; }; + 0FEA06CB098370189C1103854CA5077F /* PINRemoteImageTask.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PINRemoteImageTask.m; path = Source/Classes/PINRemoteImageTask.m; sourceTree = ""; }; + 1001B956AE4E0107CB49144235F7263C /* ASMainSerialQueue.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASMainSerialQueue.h; path = Source/Details/ASMainSerialQueue.h; sourceTree = ""; }; + 1035A7ACAFD6A65A4A3A01D8EC90E0B1 /* ASCollectionLayoutCache.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASCollectionLayoutCache.h; path = Source/Private/ASCollectionLayoutCache.h; sourceTree = ""; }; + 115A4FCB2A03922EC10B633F15359653 /* PINRemoteImage.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = PINRemoteImage.framework; path = PINRemoteImage.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 11B6DB33ACFDC65734A7C2E1F760BD60 /* ASDispatch.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASDispatch.mm; path = Source/Private/ASDispatch.mm; sourceTree = ""; }; + 11F30CDBA493D7DF5D65CDE41CDDB13D /* NSMutableAttributedString+TextKitAdditions.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSMutableAttributedString+TextKitAdditions.h"; path = "Source/Details/NSMutableAttributedString+TextKitAdditions.h"; sourceTree = ""; }; + 127F7C3D321A6445E2628FC920109B38 /* ASDisplayNodeInternal.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASDisplayNodeInternal.h; path = Source/Private/ASDisplayNodeInternal.h; sourceTree = ""; }; + 12CA065AACA31E8F43AE72E67FCB0FFC /* ASTextKitRenderer+Positioning.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "ASTextKitRenderer+Positioning.h"; path = "Source/TextKit/ASTextKitRenderer+Positioning.h"; sourceTree = ""; }; + 12E1DD8F58F53738EFB3B9BEA7E0CB09 /* ASDisplayNode+Ancestry.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = "ASDisplayNode+Ancestry.mm"; path = "Source/Base/ASDisplayNode+Ancestry.mm"; sourceTree = ""; }; + 130371D9D979BBE4A21D3985FF5C0CBA /* PINCachedAnimatedImage.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PINCachedAnimatedImage.m; path = Source/Classes/AnimatedImages/PINCachedAnimatedImage.m; sourceTree = ""; }; + 1394E4E8CCCBB0C8068E295304334637 /* PINRequestRetryStrategy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PINRequestRetryStrategy.m; path = Source/Classes/PINRequestRetryStrategy.m; sourceTree = ""; }; + 1403D90CF17962F5D5818587DA0652FE /* ASTextNode2.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASTextNode2.h; path = Source/ASTextNode2.h; sourceTree = ""; }; + 14D1789AFF19612903A6DEC3719662F3 /* Texture.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Texture.debug.xcconfig; sourceTree = ""; }; + 14F32F69FDA6EE64E812A50AA22499CC /* ASContextTransitioning.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASContextTransitioning.h; path = Source/ASContextTransitioning.h; sourceTree = ""; }; + 15B25876E76F0B07FDF260C40B4307AA /* ASNetworkImageNode.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASNetworkImageNode.h; path = Source/ASNetworkImageNode.h; sourceTree = ""; }; + 162A1D688BC1738E7F0A6348E168A3D5 /* ASDispatch.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASDispatch.h; path = Source/Private/ASDispatch.h; sourceTree = ""; }; + 16D53A1A64B75F8F2AA4855E40095652 /* Pods-Starbucks_Clone_TexBrother.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = "Pods-Starbucks_Clone_TexBrother.modulemap"; sourceTree = ""; }; + 16E3E7B6DDAAEE84D6A482759EDC819C /* ASDisplayNode+LayoutSpec.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "ASDisplayNode+LayoutSpec.h"; path = "Source/ASDisplayNode+LayoutSpec.h"; sourceTree = ""; }; + 1722A238A2D68ECA7C85A592D48C54E5 /* PINRemoteImageCallbacks.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PINRemoteImageCallbacks.m; path = Source/Classes/PINRemoteImageCallbacks.m; sourceTree = ""; }; + 1815DDF16A824E46B244423DC105FCC8 /* ASCollectionNode.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASCollectionNode.h; path = Source/ASCollectionNode.h; sourceTree = ""; }; + 189BBBEF314F5632F32DEBAB3119AA42 /* PINCache.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PINCache.h; path = Source/PINCache.h; sourceTree = ""; }; + 18AC72A19500327987AC0AD7EE4F486A /* ASTextLayout.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASTextLayout.mm; path = Source/Private/TextExperiment/Component/ASTextLayout.mm; sourceTree = ""; }; + 19B3CE9F567E5E2BA615C5C6E5E7EB95 /* ASTextUtilities.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASTextUtilities.mm; path = Source/Private/TextExperiment/Utility/ASTextUtilities.mm; sourceTree = ""; }; + 1A150AD21C6C6AEB743E18176E75D78F /* ASDisplayNode+InterfaceState.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "ASDisplayNode+InterfaceState.h"; path = "Source/ASDisplayNode+InterfaceState.h"; sourceTree = ""; }; + 1A499BBBC19F5F5CEDF35EB494657568 /* ASCollectionInternal.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASCollectionInternal.h; path = Source/Details/ASCollectionInternal.h; sourceTree = ""; }; + 1A77A2AEAA733E58FDCD4CAA0922B1A8 /* PINRemoteImageDownloadTask.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PINRemoteImageDownloadTask.h; path = Source/Classes/PINRemoteImageDownloadTask.h; sourceTree = ""; }; + 1A99F1EE1BA8F78FE1D179D4F10990E9 /* ASPhotosFrameworkImageRequest.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASPhotosFrameworkImageRequest.mm; path = Source/Details/ASPhotosFrameworkImageRequest.mm; sourceTree = ""; }; + 1BFFEC4682ED9A6C582741ABB22FFFD2 /* ASIntegerMap.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASIntegerMap.h; path = Source/Details/ASIntegerMap.h; sourceTree = ""; }; + 1C169176950FBC99FAAA05DFEBAE3D6F /* ASHashing.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASHashing.h; path = Source/Details/ASHashing.h; sourceTree = ""; }; + 1C818C0167A1B3B83CE80B6B6A31AB4A /* ASAssert.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASAssert.h; path = Source/Base/ASAssert.h; sourceTree = ""; }; + 1CD84E09F5FA855D090FB8AA27C039C2 /* PINButton+PINRemoteImage.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "PINButton+PINRemoteImage.h"; path = "Source/Classes/include/PINButton+PINRemoteImage.h"; sourceTree = ""; }; + 1D23AC0B7A0FABCDD58FEABD77522221 /* ASDisplayNodeExtras.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASDisplayNodeExtras.mm; path = Source/ASDisplayNodeExtras.mm; sourceTree = ""; }; + 1DBBE6C78D2A10AE3C8E91AC0E6C3244 /* PINRemoteImageManagerConfiguration.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PINRemoteImageManagerConfiguration.h; path = Source/Classes/PINRemoteImageManagerConfiguration.h; sourceTree = ""; }; + 1DDEB70D394FE11270864A3A83BD36BB /* ASOverlayLayoutSpec.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASOverlayLayoutSpec.mm; path = Source/Layout/ASOverlayLayoutSpec.mm; sourceTree = ""; }; + 1E2AF6899A8D865AE83A80BE3854525A /* ASPagerNode+Beta.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "ASPagerNode+Beta.h"; path = "Source/ASPagerNode+Beta.h"; sourceTree = ""; }; + 1E54AC7A652759EEEDBFBE49B12F5921 /* Pods-Starbucks_Clone_TexBrother-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-Starbucks_Clone_TexBrother-Info.plist"; sourceTree = ""; }; + 1E6AFB718C24B2377E5DF0CCDA306C55 /* PINRemoteImageTask+Subclassing.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "PINRemoteImageTask+Subclassing.h"; path = "Source/Classes/Categories/PINRemoteImageTask+Subclassing.h"; sourceTree = ""; }; + 1EB5C446879B3933F3EAB546FE597E8F /* ASStackUnpositionedLayout.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASStackUnpositionedLayout.h; path = Source/Private/Layout/ASStackUnpositionedLayout.h; sourceTree = ""; }; + 1F16EC75D41F03D3E305917F4D03604C /* ASCollectionViewLayoutInspector.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASCollectionViewLayoutInspector.mm; path = Source/Details/ASCollectionViewLayoutInspector.mm; sourceTree = ""; }; + 1FF8C31089C5323E97CBF2EE23C48598 /* ASPageTable.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASPageTable.mm; path = Source/Details/ASPageTable.mm; sourceTree = ""; }; + 201D42F70CB101B7CC761B365CB0B2F5 /* ASAsciiArtBoxCreator.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASAsciiArtBoxCreator.mm; path = Source/Layout/ASAsciiArtBoxCreator.mm; sourceTree = ""; }; + 2061A3DD3D85D7601069EE51F296B793 /* ASLayoutElement.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASLayoutElement.mm; path = Source/Layout/ASLayoutElement.mm; sourceTree = ""; }; + 2138392302088734C71C953F6E8BE31F /* PINRemoteImageCategoryManager.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PINRemoteImageCategoryManager.h; path = Source/Classes/include/PINRemoteImageCategoryManager.h; sourceTree = ""; }; + 21BC3B3B36ADEE1506968B5996F97C34 /* ASButtonNode+Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "ASButtonNode+Private.h"; path = "Source/ASButtonNode+Private.h"; sourceTree = ""; }; + 22C347595B1851A1E5B0A3762BB18EC9 /* PINOperation.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = PINOperation.framework; path = PINOperation.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 22E5CA9609D7B764C9AD5E3E29A99198 /* ASSection.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASSection.h; path = Source/Private/ASSection.h; sourceTree = ""; }; + 23917528E76A1002E1E03A86CA321667 /* _ASAsyncTransactionContainer.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = _ASAsyncTransactionContainer.h; path = Source/Details/Transactions/_ASAsyncTransactionContainer.h; sourceTree = ""; }; + 23B7B3467E387075BB087F9DF9D8EDF5 /* PINImage+ScaledImage.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "PINImage+ScaledImage.m"; path = "Source/Classes/Categories/PINImage+ScaledImage.m"; sourceTree = ""; }; + 2500B92E1BDED42BF2C222E4683F6452 /* ASScrollNode.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASScrollNode.h; path = Source/ASScrollNode.h; sourceTree = ""; }; + 25241914808C5FF6BE5F927F84792A40 /* PINRemoteImageBasicCache.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PINRemoteImageBasicCache.m; path = Source/Classes/PINRemoteImageBasicCache.m; sourceTree = ""; }; + 25E255B9F966777064C6C1EE2E74C72E /* ASTableLayoutController.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASTableLayoutController.h; path = Source/Details/ASTableLayoutController.h; sourceTree = ""; }; + 266F28006F8FB035EF18766D7D1D46B8 /* ASResponderChainEnumerator.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASResponderChainEnumerator.mm; path = Source/Private/ASResponderChainEnumerator.mm; sourceTree = ""; }; + 267B1C8B013F93279B71315BB29360E2 /* ASCollectionViewFlowLayoutInspector.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASCollectionViewFlowLayoutInspector.h; path = Source/Private/ASCollectionViewFlowLayoutInspector.h; sourceTree = ""; }; + 2680C9F6C531597EADA4A0063E0F309C /* ASStackLayoutSpec.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASStackLayoutSpec.mm; path = Source/Layout/ASStackLayoutSpec.mm; sourceTree = ""; }; + 26987C59CE8FC7CBB385300C0F6BDA5A /* ASTextKitRenderer.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASTextKitRenderer.mm; path = Source/TextKit/ASTextKitRenderer.mm; sourceTree = ""; }; + 274269F974F0098D974E13CADE4FC16A /* PINCache-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "PINCache-umbrella.h"; sourceTree = ""; }; + 274DE44949B99702678B3A0AD3A3FD1A /* ASSupplementaryNodeSource.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASSupplementaryNodeSource.h; path = Source/ASSupplementaryNodeSource.h; sourceTree = ""; }; + 288A70F6746F2D6740CA394D3B1AC339 /* ASInternalHelpers.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASInternalHelpers.mm; path = Source/Private/ASInternalHelpers.mm; sourceTree = ""; }; + 28C3E95F4439C05AD7DD1B694E01B06F /* PINRemoteLock.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PINRemoteLock.h; path = Source/Classes/PINRemoteLock.h; sourceTree = ""; }; + 291A0CC83913FFE45EFDE20041E3B489 /* ASDisplayNodeLayout.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASDisplayNodeLayout.h; path = Source/Private/ASDisplayNodeLayout.h; sourceTree = ""; }; + 29374D2518720868B57430A0CD71E8C5 /* ASRangeControllerUpdateRangeProtocol+Beta.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "ASRangeControllerUpdateRangeProtocol+Beta.h"; path = "Source/Details/ASRangeControllerUpdateRangeProtocol+Beta.h"; sourceTree = ""; }; + 29BED58535AE1ADA682A705FB50653CC /* ASCollectionLayoutState.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASCollectionLayoutState.h; path = Source/Details/ASCollectionLayoutState.h; sourceTree = ""; }; + 29D339A04F28E55C76040CCF288282BB /* ASControlTargetAction.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASControlTargetAction.mm; path = Source/Private/ASControlTargetAction.mm; sourceTree = ""; }; + 29E0253CCA000E3426448E4D79278C91 /* ASTextKitFontSizeAdjuster.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASTextKitFontSizeAdjuster.mm; path = Source/TextKit/ASTextKitFontSizeAdjuster.mm; sourceTree = ""; }; + 29F590A258A736639A4D10C302391831 /* PINAnimatedImage.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PINAnimatedImage.m; path = Source/Classes/AnimatedImages/PINAnimatedImage.m; sourceTree = ""; }; + 2AEE11DDE18165E6A0EE511B797F4CD4 /* PINImage+WebP.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "PINImage+WebP.m"; path = "Source/Classes/Categories/PINImage+WebP.m"; sourceTree = ""; }; + 2B52E76F0416EEE47A995D0B1AB82ADD /* ASLayoutRangeType.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASLayoutRangeType.h; path = Source/Details/ASLayoutRangeType.h; sourceTree = ""; }; + 2C0A55D5092394CDBE41B7565F27BD76 /* PINRemoteImageDownloadTask.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PINRemoteImageDownloadTask.m; path = Source/Classes/PINRemoteImageDownloadTask.m; sourceTree = ""; }; + 2C41CB510A0394AE786A47727010CFF4 /* PINOperation.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = PINOperation.release.xcconfig; sourceTree = ""; }; + 2D08D914160D3368A237E541232421E1 /* PINRemoteImageProcessorTask.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PINRemoteImageProcessorTask.h; path = Source/Classes/PINRemoteImageProcessorTask.h; sourceTree = ""; }; + 2D1C7EFB62BD6E8103714DB24F4ED389 /* ASGraphicsContext.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASGraphicsContext.mm; path = Source/Details/ASGraphicsContext.mm; sourceTree = ""; }; + 2D67B7BE32F2F7AC2A24618DF273D975 /* ASControlNode+Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "ASControlNode+Private.h"; path = "Source/Private/ASControlNode+Private.h"; sourceTree = ""; }; + 2DF50D3C1F52EB1D87263AFEF84D691D /* ASTipsWindow.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASTipsWindow.mm; path = Source/Private/ASTipsWindow.mm; sourceTree = ""; }; + 2E3B81403D77240183D2690D35A9E173 /* ASTipNode.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASTipNode.h; path = Source/Private/ASTipNode.h; sourceTree = ""; }; + 2ECB89B68E304B7BCC55443D36FF5ACB /* ASWeakSet.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASWeakSet.h; path = Source/Details/ASWeakSet.h; sourceTree = ""; }; + 2EF7DC57329ABA4B85EDFE5DC03BC0A8 /* ASDisplayNode+Convenience.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = "ASDisplayNode+Convenience.mm"; path = "Source/ASDisplayNode+Convenience.mm"; sourceTree = ""; }; + 2F1876604918F7E85C8C8C00692D7E31 /* PINAlternateRepresentationProvider.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PINAlternateRepresentationProvider.m; path = Source/Classes/PINAlternateRepresentationProvider.m; sourceTree = ""; }; + 2FD43643551686EF122ADF4DF1821D39 /* ASLayout+IGListDiffKit.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = "ASLayout+IGListDiffKit.mm"; path = "Source/Layout/ASLayout+IGListDiffKit.mm"; sourceTree = ""; }; + 327F3986E5C333ACAB05D4743613E811 /* ASTwoDimensionalArrayUtils.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASTwoDimensionalArrayUtils.h; path = Source/Private/ASTwoDimensionalArrayUtils.h; sourceTree = ""; }; + 32B6100A963D38523A8CA0A26B5F86B9 /* ASCornerLayoutSpec.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASCornerLayoutSpec.mm; path = Source/Layout/ASCornerLayoutSpec.mm; sourceTree = ""; }; + 32EAD7F4F3087C11BE2869340DFF33EC /* ASCollectionLayoutContext.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASCollectionLayoutContext.mm; path = Source/Details/ASCollectionLayoutContext.mm; sourceTree = ""; }; + 3312506CD352A7FD094C3802FCB31C0B /* AsyncDisplayKit+Debug.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = "AsyncDisplayKit+Debug.mm"; path = "Source/Debug/AsyncDisplayKit+Debug.mm"; sourceTree = ""; }; + 3398193884C73127717DAD2DD429D1DD /* _ASCollectionGalleryLayoutInfo.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = _ASCollectionGalleryLayoutInfo.h; path = Source/Private/_ASCollectionGalleryLayoutInfo.h; sourceTree = ""; }; + 349D854652C9440A9E0A24C485F4ACA4 /* ASScrollNode.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASScrollNode.mm; path = Source/ASScrollNode.mm; sourceTree = ""; }; + 3645176D480EBDC746EAB7706EC0FE2C /* NSMutableAttributedString+TextKitAdditions.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = "NSMutableAttributedString+TextKitAdditions.mm"; path = "Source/Details/NSMutableAttributedString+TextKitAdditions.mm"; sourceTree = ""; }; + 372E3752496C2C26F7D26610EACB5069 /* ASLayoutSpecUtilities.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASLayoutSpecUtilities.h; path = Source/Private/Layout/ASLayoutSpecUtilities.h; sourceTree = ""; }; + 376A2AC2E4CDCDE7BDB0A9D99DA37170 /* ASTextInput.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASTextInput.mm; path = Source/Private/TextExperiment/Component/ASTextInput.mm; sourceTree = ""; }; + 37A923F0EEA3DE37175030DA432C7E9D /* ASTableView.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASTableView.mm; path = Source/ASTableView.mm; sourceTree = ""; }; + 3874B0234F310FC6D5CC02FC07D177D7 /* ASControlNode.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASControlNode.mm; path = Source/ASControlNode.mm; sourceTree = ""; }; + 39469547584715DC15C17FD3249DB5F3 /* PINRemoteImageManagerResult.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PINRemoteImageManagerResult.m; path = Source/Classes/PINRemoteImageManagerResult.m; sourceTree = ""; }; + 3A01E0F76B1D746C5AB7D11D9AE6CC77 /* _ASCollectionGalleryLayoutItem.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = _ASCollectionGalleryLayoutItem.h; path = Source/Private/_ASCollectionGalleryLayoutItem.h; sourceTree = ""; }; + 3A0E979A776D8F70B347CC8B258C6B39 /* AsyncDisplayKit+Tips.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = "AsyncDisplayKit+Tips.mm"; path = "Source/Debug/AsyncDisplayKit+Tips.mm"; sourceTree = ""; }; + 3B40FBAEC3D9F250BE499DB7C96477FC /* ASConfigurationInternal.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASConfigurationInternal.mm; path = Source/ASConfigurationInternal.mm; sourceTree = ""; }; + 3B76C13871113610B790E5634FD8E4B7 /* PINRemoteImageMemoryContainer.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PINRemoteImageMemoryContainer.m; path = Source/Classes/PINRemoteImageMemoryContainer.m; sourceTree = ""; }; + 3BAB9C702191043B5AD931A92E4B8B03 /* ASDisplayNode+Layout.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = "ASDisplayNode+Layout.mm"; path = "Source/ASDisplayNode+Layout.mm"; sourceTree = ""; }; + 3C5A6AEC41EFC3AD25534536494FF0B1 /* PINImage+ScaledImage.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "PINImage+ScaledImage.h"; path = "Source/Classes/Categories/PINImage+ScaledImage.h"; sourceTree = ""; }; + 3C69FF5E87A081B57AFD9CF41C19BC10 /* PINCachedAnimatedImage.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PINCachedAnimatedImage.h; path = Source/Classes/include/PINCachedAnimatedImage.h; sourceTree = ""; }; + 3C8F71BC92C3939CC05FD410E84FF05C /* PINRemoteImageDownloadQueue.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PINRemoteImageDownloadQueue.m; path = Source/Classes/PINRemoteImageDownloadQueue.m; sourceTree = ""; }; + 3D673E12DB0B97C4702DCA57BD907DCD /* PINWebPAnimatedImage.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PINWebPAnimatedImage.m; path = Source/Classes/AnimatedImages/PINWebPAnimatedImage.m; sourceTree = ""; }; + 3E1A5494636B0CDA8A4709C8682A52C1 /* ASDisplayNode+DebugTiming.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "ASDisplayNode+DebugTiming.h"; path = "Source/Private/ASDisplayNode+DebugTiming.h"; sourceTree = ""; }; + 3E57C58291CEC254C5C79E173BBAA35F /* ASVideoNode.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASVideoNode.mm; path = Source/ASVideoNode.mm; sourceTree = ""; }; + 3F292EB56A7A5F9E8775B851D97743C0 /* ASDelegateProxy.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASDelegateProxy.mm; path = Source/Details/ASDelegateProxy.mm; sourceTree = ""; }; + 3F391532C2485B229B7BDE74FF398D41 /* ASLayout.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASLayout.h; path = Source/Layout/ASLayout.h; sourceTree = ""; }; + 3F5A299D3E06C987FD257CCE00A8C1C2 /* Pods-Starbucks_Clone_TexBrother.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-Starbucks_Clone_TexBrother.debug.xcconfig"; sourceTree = ""; }; + 3FD8FFBA2AD3C6F7C490C86420605F17 /* ASAssert.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASAssert.mm; path = Source/Base/ASAssert.mm; sourceTree = ""; }; + 40738CE7558B636E714D9A7DA06CAE94 /* ASDefaultPlaybackButton.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASDefaultPlaybackButton.h; path = Source/Private/ASDefaultPlaybackButton.h; sourceTree = ""; }; + 42F1BDB74A174F9531FB779E45BC1D50 /* PINRemoteImageCategoryManager.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PINRemoteImageCategoryManager.m; path = Source/Classes/PINRemoteImageCategoryManager.m; sourceTree = ""; }; + 442AD1A685AA2C1A7EDB840685B29CCB /* PINDisplayLink.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PINDisplayLink.h; path = Source/Classes/PINDisplayLink.h; sourceTree = ""; }; + 443107AB83C34C89719D607406B3DE57 /* ASRelativeLayoutSpec.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASRelativeLayoutSpec.h; path = Source/Layout/ASRelativeLayoutSpec.h; sourceTree = ""; }; + 443390523E8A73440A7191BA0094872A /* ASTextKitFontSizeAdjuster.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASTextKitFontSizeAdjuster.h; path = Source/TextKit/ASTextKitFontSizeAdjuster.h; sourceTree = ""; }; + 44980E879FE0D7AEAF373DE0F2B5BEFA /* ASTextKitComponents.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASTextKitComponents.mm; path = Source/TextKit/ASTextKitComponents.mm; sourceTree = ""; }; + 451F8219FEECBEF1D8D5576A706A6259 /* PINImage+DecodedImage.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "PINImage+DecodedImage.h"; path = "Source/Classes/Categories/PINImage+DecodedImage.h"; sourceTree = ""; }; + 4564E9382AAB98E6E981F0E4F84CA037 /* _ASAsyncTransactionContainer.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = _ASAsyncTransactionContainer.mm; path = Source/Details/Transactions/_ASAsyncTransactionContainer.mm; sourceTree = ""; }; + 460688F46B0349193D03A8CD6459F014 /* UIResponder+AsyncDisplayKit.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = "UIResponder+AsyncDisplayKit.mm"; path = "Source/UIResponder+AsyncDisplayKit.mm"; sourceTree = ""; }; + 461F1ADCB188A45C15C5FFE080FB729A /* CoreMedia.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreMedia.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS14.0.sdk/System/Library/Frameworks/CoreMedia.framework; sourceTree = DEVELOPER_DIR; }; + 465C7CD0B951D0E0D97FC9851987095F /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS14.0.sdk/System/Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; }; + 474F334721D6E818D56CC8B9B8BD4076 /* PINRemoteWeakProxy.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PINRemoteWeakProxy.h; path = Source/Classes/PINRemoteWeakProxy.h; sourceTree = ""; }; + 4774FBDD20637C9AC144C7E70D1057A5 /* ASWeakProxy.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASWeakProxy.mm; path = Source/Details/ASWeakProxy.mm; sourceTree = ""; }; + 486B3CCA03BCC69D86F973E81F23F54B /* ASDisplayNode+Yoga.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "ASDisplayNode+Yoga.h"; path = "Source/ASDisplayNode+Yoga.h"; sourceTree = ""; }; + 488883D3A35A3CCB6513EFFD7D6B5F7A /* AsyncDisplayKit+Debug.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "AsyncDisplayKit+Debug.h"; path = "Source/Debug/AsyncDisplayKit+Debug.h"; sourceTree = ""; }; + 48FFF4989571E03EB9AAD6897C4BCB4F /* ASCollectionNode+Beta.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "ASCollectionNode+Beta.h"; path = "Source/ASCollectionNode+Beta.h"; sourceTree = ""; }; + 4932F584069CBF2BCED5E998CD893E9E /* ASNetworkImageNode.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASNetworkImageNode.mm; path = Source/ASNetworkImageNode.mm; sourceTree = ""; }; + 49577A6CB89BA0AB509E6CF764ABDE61 /* ASDisplayNode+Yoga.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = "ASDisplayNode+Yoga.mm"; path = "Source/ASDisplayNode+Yoga.mm"; sourceTree = ""; }; + 49E8771A769680B47A2ADB440EDDEDA1 /* ASYogaUtilities.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASYogaUtilities.h; path = Source/Layout/ASYogaUtilities.h; sourceTree = ""; }; + 49EC90EF95A2BAABAA44D24EA9E6115B /* PINCache.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = PINCache.debug.xcconfig; sourceTree = ""; }; + 4A13BF4119E0C2327A37ADD306A7F280 /* ASImageNode.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASImageNode.h; path = Source/ASImageNode.h; sourceTree = ""; }; + 4A8DF6DD2A4E0F0933CDAC8EBDA67FE3 /* _ASPendingState.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = _ASPendingState.h; path = Source/Private/_ASPendingState.h; sourceTree = ""; }; + 4AD14C8259CFDAA23540FB84371A8C91 /* PINCacheObjectSubscripting.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PINCacheObjectSubscripting.h; path = Source/PINCacheObjectSubscripting.h; sourceTree = ""; }; + 4B6A6B6008B8473CDF79B3962055B2B5 /* ASRatioLayoutSpec.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASRatioLayoutSpec.h; path = Source/Layout/ASRatioLayoutSpec.h; sourceTree = ""; }; + 4BADB70D6F6D85A658168553A700DC9F /* ASImageProtocols.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASImageProtocols.h; path = Source/Details/ASImageProtocols.h; sourceTree = ""; }; + 4C54EEEA6CDF354C39D11F1217C41EF7 /* ASCollectionLayoutState+Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "ASCollectionLayoutState+Private.h"; path = "Source/Private/ASCollectionLayoutState+Private.h"; sourceTree = ""; }; + 4C8BC130248DB0AD806FA743C70A99B4 /* PINRemoteImageCaching.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PINRemoteImageCaching.h; path = Source/Classes/include/PINRemoteImageCaching.h; sourceTree = ""; }; + 4D40CE701750D8006DA724FDDD8DF487 /* ASTextKitEntityAttribute.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASTextKitEntityAttribute.h; path = Source/TextKit/ASTextKitEntityAttribute.h; sourceTree = ""; }; + 4E127E8EB3DF48F7A91A9E5934571535 /* ASBatchContext.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASBatchContext.h; path = Source/Details/ASBatchContext.h; sourceTree = ""; }; + 4FC69BD5F886F0FD2ACA5CE367FA5C9A /* ASStackLayoutDefines.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASStackLayoutDefines.h; path = Source/Layout/ASStackLayoutDefines.h; sourceTree = ""; }; + 4FE8339B40FB00918941FBCDC5F30D28 /* PINProgressiveImage.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PINProgressiveImage.m; path = Source/Classes/PINProgressiveImage.m; sourceTree = ""; }; + 510DCE3B48F67D693EF6D5292014D927 /* PINRemoteImage-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "PINRemoteImage-dummy.m"; sourceTree = ""; }; + 51E4828747ABF0F4B0D5210073FD2269 /* PINURLSessionManager.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PINURLSessionManager.h; path = Source/Classes/include/PINURLSessionManager.h; sourceTree = ""; }; + 5201496769F9BC60F76B9A95B07E8C98 /* ASAbstractLayoutController.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASAbstractLayoutController.mm; path = Source/Details/ASAbstractLayoutController.mm; sourceTree = ""; }; + 5297B2854BA055337B6A353F4581E97B /* ASTextNodeCommon.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASTextNodeCommon.h; path = Source/ASTextNodeCommon.h; sourceTree = ""; }; + 52DA18796B6E521C4C76474566901974 /* ASAvailability.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASAvailability.h; path = Source/Base/ASAvailability.h; sourceTree = ""; }; + 5316B79F60F907878E80E6789410C62C /* PINAlternateRepresentationProvider.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PINAlternateRepresentationProvider.h; path = Source/Classes/include/PINAlternateRepresentationProvider.h; sourceTree = ""; }; + 53E2113FBB2C3C1025FA111A09D9705D /* ASTip.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASTip.mm; path = Source/Private/ASTip.mm; sourceTree = ""; }; + 53F5101FDA12B83E1AF2EE12B127639E /* ASPINRemoteImageDownloader.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASPINRemoteImageDownloader.mm; path = Source/Details/ASPINRemoteImageDownloader.mm; sourceTree = ""; }; + 5446BF8F6EE5445BB8095CED507BA6E0 /* PINGIFAnimatedImage.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PINGIFAnimatedImage.m; path = Source/Classes/AnimatedImages/PINGIFAnimatedImage.m; sourceTree = ""; }; + 54765F44027C7F1F4A283E5D8F1F57E8 /* ASIGListAdapterBasedDataSource.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASIGListAdapterBasedDataSource.h; path = Source/Private/ASIGListAdapterBasedDataSource.h; sourceTree = ""; }; + 556D557F23FCD191B665690B58AC0B95 /* PINOperationTypes.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PINOperationTypes.h; path = Source/PINOperationTypes.h; sourceTree = ""; }; + 55BE5B0739B7FB893F77CCAF0101D9D7 /* ASLayoutSpec+Subclasses.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = "ASLayoutSpec+Subclasses.mm"; path = "Source/Layout/ASLayoutSpec+Subclasses.mm"; sourceTree = ""; }; + 5629107AFA4448F2FFFA6ABEDA650A40 /* ASButtonNode.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASButtonNode.h; path = Source/ASButtonNode.h; sourceTree = ""; }; + 563AA9E4BC576EF280FB0C0F91212620 /* ASTextKitCoreTextAdditions.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASTextKitCoreTextAdditions.h; path = Source/TextKit/ASTextKitCoreTextAdditions.h; sourceTree = ""; }; + 5660A8B0F1FF8E3463CBC35A8A848AED /* _ASDisplayViewAccessiblity.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = _ASDisplayViewAccessiblity.mm; path = Source/Details/_ASDisplayViewAccessiblity.mm; sourceTree = ""; }; + 5688A739BB7B10A17AF1E41094FBDD6B /* PINRemoteImage.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PINRemoteImage.h; path = Source/Classes/include/PINRemoteImage.h; sourceTree = ""; }; + 569D8D9DDDC854B8EC673ADB1025D26A /* ASTextKitAttributes.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASTextKitAttributes.h; path = Source/TextKit/ASTextKitAttributes.h; sourceTree = ""; }; + 56EDE5D7E4A5C38FBC7FE5EC9D8A1FA4 /* ASButtonNode+Yoga.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "ASButtonNode+Yoga.h"; path = "Source/ASButtonNode+Yoga.h"; sourceTree = ""; }; + 57CFF13E6A7FCF5F26B96D9E52B50118 /* ASObjectDescriptionHelpers.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASObjectDescriptionHelpers.h; path = Source/Details/ASObjectDescriptionHelpers.h; sourceTree = ""; }; + 58139A95B4BA8249B925855915F54E9E /* ASHighlightOverlayLayer.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASHighlightOverlayLayer.mm; path = Source/Details/ASHighlightOverlayLayer.mm; sourceTree = ""; }; + 58FA6C5A9C4C19E011EB63F340365513 /* ASThread.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASThread.h; path = Source/Details/ASThread.h; sourceTree = ""; }; + 5956F6A0379F1B3E69E48CB79B712D20 /* NSParagraphStyle+ASText.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSParagraphStyle+ASText.h"; path = "Source/Private/TextExperiment/Utility/NSParagraphStyle+ASText.h"; sourceTree = ""; }; + 5A4F9715AFF365EDE24F67C0C708CB04 /* Pods-Starbucks_Clone_TexBrother-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-Starbucks_Clone_TexBrother-acknowledgements.markdown"; sourceTree = ""; }; + 5BC73143B044118E42D111C5B89F8746 /* ASLayoutTransition.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASLayoutTransition.h; path = Source/Private/ASLayoutTransition.h; sourceTree = ""; }; + 5BDA6758A4F44A2423F3319C96BBB2EC /* ASMutableElementMap.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASMutableElementMap.h; path = Source/Private/ASMutableElementMap.h; sourceTree = ""; }; + 5C206ACAAB3B78082D7C88805B4938C1 /* ASVideoPlayerNode.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASVideoPlayerNode.mm; path = Source/ASVideoPlayerNode.mm; sourceTree = ""; }; + 5C4AA3335C622751A32A744499AF7CE2 /* Texture.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = Texture.modulemap; sourceTree = ""; }; + 5CF0B8B8B3B7611BFE4FACBD177FAFE9 /* ASScrollDirection.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASScrollDirection.h; path = Source/Details/ASScrollDirection.h; sourceTree = ""; }; + 5D04F1BC6559A047934F57FEE0D842D0 /* ASMainSerialQueue.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASMainSerialQueue.mm; path = Source/Details/ASMainSerialQueue.mm; sourceTree = ""; }; + 5E2C06029EF723FC9CB33F253B588801 /* ASBlockTypes.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASBlockTypes.h; path = Source/ASBlockTypes.h; sourceTree = ""; }; + 5E78560F78CB8BFA4466E9D41BC5176F /* ASPendingStateController.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASPendingStateController.h; path = Source/Private/ASPendingStateController.h; sourceTree = ""; }; + 5F272F44E17A59928BA3C19A72C1CE96 /* ASCollectionFlowLayoutDelegate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASCollectionFlowLayoutDelegate.h; path = Source/Details/ASCollectionFlowLayoutDelegate.h; sourceTree = ""; }; + 5F5D034DB1ED346FED8B825B2C4FACAE /* NSData+ImageDetectors.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSData+ImageDetectors.m"; path = "Source/Classes/Categories/NSData+ImageDetectors.m"; sourceTree = ""; }; + 5FBEE9DF8CEE0978987520A5AB6353FD /* AsyncDisplayKit.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = AsyncDisplayKit.h; path = Source/AsyncDisplayKit.h; sourceTree = ""; }; + 602F988DE92671CC4096ADCC733A5930 /* CoreGraphics+ASConvenience.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "CoreGraphics+ASConvenience.h"; path = "Source/Details/CoreGraphics+ASConvenience.h"; sourceTree = ""; }; + 607EDA20ACB1995FE512E34150F222F1 /* ASPagerNode.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASPagerNode.h; path = Source/ASPagerNode.h; sourceTree = ""; }; + 60A4C17A948E6BF765E2CD491916B631 /* ASTextKitRenderer.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASTextKitRenderer.h; path = Source/TextKit/ASTextKitRenderer.h; sourceTree = ""; }; + 60E2A9C368D8B4DB87B8786A1150005A /* PINCache+PINRemoteImageCaching.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "PINCache+PINRemoteImageCaching.h"; path = "Source/Classes/include/PINCache+PINRemoteImageCaching.h"; sourceTree = ""; }; + 60ECE226961E3317572472B9C7866939 /* ASImageContainerProtocolCategories.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASImageContainerProtocolCategories.h; path = Source/Details/ASImageContainerProtocolCategories.h; sourceTree = ""; }; + 61864DC4D9246D85E128A5C4310F92FA /* ASRangeManagingNode.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASRangeManagingNode.h; path = Source/ASRangeManagingNode.h; sourceTree = ""; }; + 619DC215E1E24600C9B8C65AE0F6C571 /* ASIntegerMap.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASIntegerMap.mm; path = Source/Details/ASIntegerMap.mm; sourceTree = ""; }; + 621CFE6D1995E55EC068DC672E84F361 /* ASLog.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASLog.h; path = Source/Base/ASLog.h; sourceTree = ""; }; + 62310F0C73D1A8CA99D47886AE688B73 /* ASExperimentalFeatures.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASExperimentalFeatures.h; path = Source/ASExperimentalFeatures.h; sourceTree = ""; }; + 623F24CFE1B1D4FFEDB792BA41976749 /* UIImage+ASConvenience.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIImage+ASConvenience.h"; path = "Source/UIImage+ASConvenience.h"; sourceTree = ""; }; + 626548E82C01FE272CD114C9F7F07556 /* PINWebPAnimatedImage.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PINWebPAnimatedImage.h; path = Source/Classes/include/PINWebPAnimatedImage.h; sourceTree = ""; }; + 6276DF790DF6B5D12814A3A4942DEDD9 /* ASLog.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASLog.mm; path = Source/Base/ASLog.mm; sourceTree = ""; }; + 63178711D4323569A1B61319677AA577 /* ASTipsWindow.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASTipsWindow.h; path = Source/Private/ASTipsWindow.h; sourceTree = ""; }; + 6320170E5A5A268B11CAE15D4EF6A9E3 /* PINCache.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = PINCache.framework; path = PINCache.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 635A8DB9ABD3EE0FFC5E3308011CDF36 /* Texture-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Texture-prefix.pch"; sourceTree = ""; }; + 64116DD37F68B07B3B0DE77751C883F1 /* PINOperation-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "PINOperation-prefix.pch"; sourceTree = ""; }; + 643EE8E9B40DC4CB13D65B7243E995B2 /* ASCellNode.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASCellNode.h; path = Source/ASCellNode.h; sourceTree = ""; }; + 65E45EB810D4BBF9D33169EC48A815F5 /* ASImageNode+Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "ASImageNode+Private.h"; path = "Source/Private/ASImageNode+Private.h"; sourceTree = ""; }; + 661EC3CE84810954586C6607786653F5 /* ASLayoutSpec+Subclasses.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "ASLayoutSpec+Subclasses.h"; path = "Source/Layout/ASLayoutSpec+Subclasses.h"; sourceTree = ""; }; + 6721E5BEB6FCC93087D899B00F020D5A /* PINRemoteImage-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "PINRemoteImage-Info.plist"; sourceTree = ""; }; + 672BD6EE10498EAC204A79FF07840EAE /* ASDisplayNode+Beta.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "ASDisplayNode+Beta.h"; path = "Source/ASDisplayNode+Beta.h"; sourceTree = ""; }; + 679CF8A5C03B3322E8CADABB7DF4748B /* ASLayoutElement.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASLayoutElement.h; path = Source/Layout/ASLayoutElement.h; sourceTree = ""; }; + 67E87CE9FECF81BF0881DD56B7DF780A /* ASTextNodeWordKerner.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASTextNodeWordKerner.mm; path = Source/TextKit/ASTextNodeWordKerner.mm; sourceTree = ""; }; + 685D1E26BFDFC90E7B8DDC3D5A043BE8 /* ASStackLayoutElement.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASStackLayoutElement.h; path = Source/Layout/ASStackLayoutElement.h; sourceTree = ""; }; + 694E56C9CFBEB7AE6FFD9B45B846993E /* ASLayoutElementExtensibility.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASLayoutElementExtensibility.h; path = Source/Layout/ASLayoutElementExtensibility.h; sourceTree = ""; }; + 6B050E212264CF3B336ED28484FB5132 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS14.0.sdk/System/Library/Frameworks/AVFoundation.framework; sourceTree = DEVELOPER_DIR; }; + 6B20999B345220EA81A18684BC483B2D /* PINURLSessionManager.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PINURLSessionManager.m; path = Source/Classes/PINURLSessionManager.m; sourceTree = ""; }; + 6B20C8FA45310FF28361CEA25BCB0737 /* ASButtonNode.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASButtonNode.mm; path = Source/ASButtonNode.mm; sourceTree = ""; }; + 6C96AD30B4BB4561311AF7FC108E3A40 /* ASCollectionView.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASCollectionView.h; path = Source/ASCollectionView.h; sourceTree = ""; }; + 6C9A4DBE7D41A766936D3801A3B99271 /* ASTextNode+Beta.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "ASTextNode+Beta.h"; path = "Source/ASTextNode+Beta.h"; sourceTree = ""; }; + 6CF7BC3A95218CA6803E27F02C16AA10 /* ASCellNode+Internal.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "ASCellNode+Internal.h"; path = "Source/Private/ASCellNode+Internal.h"; sourceTree = ""; }; + 6D14125F3A9F2F63DAD6311079217C87 /* ASTableViewProtocols.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASTableViewProtocols.h; path = Source/ASTableViewProtocols.h; sourceTree = ""; }; + 6D84304A4C65F07E602CBAB724FF9D07 /* IGListAdapter+AsyncDisplayKit.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "IGListAdapter+AsyncDisplayKit.h"; path = "Source/IGListAdapter+AsyncDisplayKit.h"; sourceTree = ""; }; + 6DBB50E059AE567A69A18F010E0C93C5 /* ASYogaUtilities.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASYogaUtilities.mm; path = Source/Layout/ASYogaUtilities.mm; sourceTree = ""; }; + 6F1D0CDE40B7ADECFF3A55527DA6DDC0 /* PINCache-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "PINCache-dummy.m"; sourceTree = ""; }; + 6F445AA2EBEFA2D30FF270056C1BC783 /* _ASCollectionGalleryLayoutItem.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = _ASCollectionGalleryLayoutItem.mm; path = Source/Private/_ASCollectionGalleryLayoutItem.mm; sourceTree = ""; }; + 6FA8B88434F89A38DB5CEC77891BEAC7 /* PINOperationMacros.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PINOperationMacros.h; path = Source/PINOperationMacros.h; sourceTree = ""; }; + 6FEDA9949DABCA4821DDDD0414408B22 /* Texture-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Texture-umbrella.h"; sourceTree = ""; }; + 7022AFFE25E02BB59DF601176BCECFAB /* ASCornerLayoutSpec.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASCornerLayoutSpec.h; path = Source/Layout/ASCornerLayoutSpec.h; sourceTree = ""; }; + 70907139FDD30DCEEA7A9190BA24A9D8 /* PINRemoteLock.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PINRemoteLock.m; path = Source/Classes/PINRemoteLock.m; sourceTree = ""; }; + 72097EDC568749414EDF3AC657BB07C3 /* PINImageView+PINRemoteImage.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "PINImageView+PINRemoteImage.h"; path = "Source/Classes/include/PINImageView+PINRemoteImage.h"; sourceTree = ""; }; + 72A548F3BBC7390DE95BCD4F2A6954DD /* ASBaseDefines.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASBaseDefines.h; path = Source/Base/ASBaseDefines.h; sourceTree = ""; }; + 736C046DCA265EE81A58FD5F89BF6102 /* PINGIFAnimatedImage.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PINGIFAnimatedImage.h; path = Source/Classes/include/PINGIFAnimatedImage.h; sourceTree = ""; }; + 73AA3A23C9F3D8C94C70E9CD5CAB902C /* PINResume.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PINResume.m; path = Source/Classes/PINResume.m; sourceTree = ""; }; + 74086F595373CAC88B0BF99A2E21A0D5 /* ASInsetLayoutSpec.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASInsetLayoutSpec.mm; path = Source/Layout/ASInsetLayoutSpec.mm; sourceTree = ""; }; + 741BAE82F2A2B69F81AE5008435BADE3 /* PINOperation-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "PINOperation-dummy.m"; sourceTree = ""; }; + 7428C524EFC7D50242D9CF71007C0A93 /* PINCache-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "PINCache-prefix.pch"; sourceTree = ""; }; + 747E4F88A2242E485C0C2A1D600F72CD /* ASDisplayNode+AsyncDisplay.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = "ASDisplayNode+AsyncDisplay.mm"; path = "Source/Private/ASDisplayNode+AsyncDisplay.mm"; sourceTree = ""; }; + 74E4E43E31FFF2CF7046E6A3658D79E5 /* ASMainThreadDeallocation.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASMainThreadDeallocation.mm; path = Source/ASMainThreadDeallocation.mm; sourceTree = ""; }; + 74ED204CF8F96D2B3B75CB7545B09E1B /* ASBasicImageDownloader.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASBasicImageDownloader.mm; path = Source/Details/ASBasicImageDownloader.mm; sourceTree = ""; }; + 75268E97C57C22192F3B263283CA2C79 /* ASControlNode+Subclasses.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "ASControlNode+Subclasses.h"; path = "Source/ASControlNode+Subclasses.h"; sourceTree = ""; }; + 752F0516FE00425919A33CB5BD264BD3 /* _ASScopeTimer.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = _ASScopeTimer.h; path = Source/Private/_ASScopeTimer.h; sourceTree = ""; }; + 75CD9ED9F22C2799B7BCA77BCFA90398 /* ASMultiplexImageNode.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASMultiplexImageNode.h; path = Source/ASMultiplexImageNode.h; sourceTree = ""; }; + 76660865DC7BA0803ED5AF36FA8E6ECA /* PINSpeedRecorder.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PINSpeedRecorder.m; path = Source/Classes/PINSpeedRecorder.m; sourceTree = ""; }; + 76F307F7E0F3AB64C1D9E973EEFC055A /* ASPagerFlowLayout.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASPagerFlowLayout.mm; path = Source/ASPagerFlowLayout.mm; sourceTree = ""; }; + 773FC3074F118DBE19C9C2A18699AB84 /* PINRemoteImage.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = PINRemoteImage.release.xcconfig; sourceTree = ""; }; + 775C7D276B9BD925498D2CC333EA1BC5 /* _ASTransitionContext.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = _ASTransitionContext.h; path = Source/_ASTransitionContext.h; sourceTree = ""; }; + 788A4CA5542DDF6AEDF0958D1F65FA8D /* ASAbstractLayoutController+FrameworkPrivate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "ASAbstractLayoutController+FrameworkPrivate.h"; path = "Source/Private/ASAbstractLayoutController+FrameworkPrivate.h"; sourceTree = ""; }; + 78EB5CB83EA4EACE5C49BA7F2950ECA9 /* ASStackLayoutSpec.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASStackLayoutSpec.h; path = Source/Layout/ASStackLayoutSpec.h; sourceTree = ""; }; + 790FD97BCBD0110961780552C759DF98 /* PINImageView+PINRemoteImage.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "PINImageView+PINRemoteImage.m"; path = "Source/Classes/ImageCategories/PINImageView+PINRemoteImage.m"; sourceTree = ""; }; + 792A0180DFD7045FE65D98BB0892B376 /* ASCenterLayoutSpec.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASCenterLayoutSpec.h; path = Source/Layout/ASCenterLayoutSpec.h; sourceTree = ""; }; + 7A2B2E2EE493CFA9A7944BBC109C1107 /* ImageIO.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ImageIO.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS14.0.sdk/System/Library/Frameworks/ImageIO.framework; sourceTree = DEVELOPER_DIR; }; + 7A84C6F510AF68CC4BBA2FC5DDE5E0B2 /* ASPINRemoteImageDownloader.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASPINRemoteImageDownloader.h; path = Source/Details/ASPINRemoteImageDownloader.h; sourceTree = ""; }; + 7BA62A47FF53155919AA67CBCC0B126A /* ASDimension.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASDimension.mm; path = Source/Layout/ASDimension.mm; sourceTree = ""; }; + 7CEC8210F1769BD7D7B357072E7E3B82 /* ASTextNode.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASTextNode.mm; path = Source/ASTextNode.mm; sourceTree = ""; }; + 7D2C56C4B3966B04388D70C04AFA6A27 /* ASResponderChainEnumerator.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASResponderChainEnumerator.h; path = Source/Private/ASResponderChainEnumerator.h; sourceTree = ""; }; + 7DBE419CB2F290B559A7011979017C7D /* Texture.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Texture.release.xcconfig; sourceTree = ""; }; + 7E77840DF764C7052517CF2ED7FF71A3 /* PINImage+DecodedImage.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "PINImage+DecodedImage.m"; path = "Source/Classes/Categories/PINImage+DecodedImage.m"; sourceTree = ""; }; + 7EB194E21CA3FFC96FFF3C80B1C91A86 /* ASDimensionInternal.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASDimensionInternal.h; path = Source/Layout/ASDimensionInternal.h; sourceTree = ""; }; + 7EC860805746F5FCE75F3EDC2AAAA287 /* ASNetworkImageLoadInfo.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASNetworkImageLoadInfo.mm; path = Source/ASNetworkImageLoadInfo.mm; sourceTree = ""; }; + 7F01B58EC031BB0AC720C371201D7E15 /* ASWeakSet.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASWeakSet.mm; path = Source/Details/ASWeakSet.mm; sourceTree = ""; }; + 805F38459176CFBF7BFE070EAE17FF09 /* Texture-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Texture-dummy.m"; sourceTree = ""; }; + 8099386989773A568E77145C42CD586F /* ASBasicImageDownloader.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASBasicImageDownloader.h; path = Source/Details/ASBasicImageDownloader.h; sourceTree = ""; }; + 8146280FD73EB05DF4423A718F6888D0 /* NSArray+Diffing.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = "NSArray+Diffing.mm"; path = "Source/Details/NSArray+Diffing.mm"; sourceTree = ""; }; + 831337E1D6E0B5DD90702FBF69CA4A97 /* AssetsLibrary.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AssetsLibrary.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS14.0.sdk/System/Library/Frameworks/AssetsLibrary.framework; sourceTree = DEVELOPER_DIR; }; + 832F7E0ACF60B901E15D5BA77F9D1DAD /* ASTableViewInternal.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASTableViewInternal.h; path = Source/ASTableViewInternal.h; sourceTree = ""; }; + 8417A1FA330D0B15E1EDF7547C84EB6E /* Accelerate.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Accelerate.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS14.0.sdk/System/Library/Frameworks/Accelerate.framework; sourceTree = DEVELOPER_DIR; }; + 843FA0564BEA1819D72A52497B191159 /* ASDisplayNode+Ancestry.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "ASDisplayNode+Ancestry.h"; path = "Source/Base/ASDisplayNode+Ancestry.h"; sourceTree = ""; }; + 85DC3A0BDC8F3003550567F705BC3C4A /* PINRemoteImageBasicCache.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PINRemoteImageBasicCache.h; path = Source/Classes/PINRemoteImageBasicCache.h; sourceTree = ""; }; + 87065E0D5E1E5ED7C51879E8AE1C6F41 /* ASVisibilityProtocols.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASVisibilityProtocols.h; path = Source/ASVisibilityProtocols.h; sourceTree = ""; }; + 87944CB6253052129AC860AB6A056454 /* ASRunLoopQueue.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASRunLoopQueue.h; path = Source/ASRunLoopQueue.h; sourceTree = ""; }; + 88CCC3ACF89F4533FFBBD74CFC1806D4 /* ASLayerBackingTipProvider.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASLayerBackingTipProvider.mm; path = Source/Private/ASLayerBackingTipProvider.mm; sourceTree = ""; }; + 8983A1BC34984FEE903C32F947A4A11E /* ASCollectionLayoutDefines.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASCollectionLayoutDefines.h; path = Source/Private/ASCollectionLayoutDefines.h; sourceTree = ""; }; + 8A485B78C571482DA19EFA8BD850DB02 /* ASCenterLayoutSpec.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASCenterLayoutSpec.mm; path = Source/Layout/ASCenterLayoutSpec.mm; sourceTree = ""; }; + 8BC18D4DB7AA56688D0AF9BDDE7BE8F5 /* _ASHierarchyChangeSet.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = _ASHierarchyChangeSet.h; path = Source/Private/_ASHierarchyChangeSet.h; sourceTree = ""; }; + 8BC32B2CA262B735E3055D0089917569 /* ASPhotosFrameworkImageRequest.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASPhotosFrameworkImageRequest.h; path = Source/Details/ASPhotosFrameworkImageRequest.h; sourceTree = ""; }; + 8C20EDFA6F12C47AF4639E4CB4A75E8B /* ASTableView.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASTableView.h; path = Source/ASTableView.h; sourceTree = ""; }; + 8C7A366CC3B6A0716315FAE4CC40827B /* Photos.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Photos.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS14.0.sdk/System/Library/Frameworks/Photos.framework; sourceTree = DEVELOPER_DIR; }; + 8D7075DBD5BAA1D6ECBF9309579E9541 /* Pods_Starbucks_Clone_TexBrother.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Pods_Starbucks_Clone_TexBrother.framework; path = "Pods-Starbucks_Clone_TexBrother.framework"; sourceTree = BUILT_PRODUCTS_DIR; }; + 8ECD3CA83D8BA42AF9306FEC62CA06BD /* ASHashing.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASHashing.mm; path = Source/Details/ASHashing.mm; sourceTree = ""; }; + 8FA479C38E875642DC6D0F70C22C53E2 /* PINRemoteImageManager.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PINRemoteImageManager.h; path = Source/Classes/include/PINRemoteImageManager.h; sourceTree = ""; }; + 9006140792ABAA03CFF2A94DD61C060C /* ASNetworkImageLoadInfo.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASNetworkImageLoadInfo.h; path = Source/ASNetworkImageLoadInfo.h; sourceTree = ""; }; + 90AF45C081400459AA7B398DDFAE38FB /* ASTextKitCoreTextAdditions.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASTextKitCoreTextAdditions.mm; path = Source/TextKit/ASTextKitCoreTextAdditions.mm; sourceTree = ""; }; + 90D1B54DABB36946B78848BB58EA41DF /* PINOperation-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "PINOperation-Info.plist"; sourceTree = ""; }; + 915A108BCEEDF6DED44581345ADD5FF6 /* ASBackgroundLayoutSpec.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASBackgroundLayoutSpec.h; path = Source/Layout/ASBackgroundLayoutSpec.h; sourceTree = ""; }; + 921D71A9998F7251DCD498356DAFC992 /* ASDisplayNode.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASDisplayNode.mm; path = Source/ASDisplayNode.mm; sourceTree = ""; }; + 930163D057DF4C17CDACDDB653AD3C46 /* PINRemoteImageCallbacks.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PINRemoteImageCallbacks.h; path = Source/Classes/PINRemoteImageCallbacks.h; sourceTree = ""; }; + 9318386970E3A7089D8AE292D4EEDE6E /* ASTextLine.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASTextLine.mm; path = Source/Private/TextExperiment/Component/ASTextLine.mm; sourceTree = ""; }; + 93851EFA588262FF22F2E4C428862442 /* ASDisplayNode+Subclasses.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "ASDisplayNode+Subclasses.h"; path = "Source/ASDisplayNode+Subclasses.h"; sourceTree = ""; }; + 965D92DE5A7AB53870ABDDD9BB71EA89 /* ASTextAttribute.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASTextAttribute.mm; path = Source/Private/TextExperiment/String/ASTextAttribute.mm; sourceTree = ""; }; + 966FD881395FF4683060BDF1A2C1AFE7 /* PINAnimatedImage.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PINAnimatedImage.h; path = Source/Classes/include/PINAnimatedImage.h; sourceTree = ""; }; + 968B6A7ECF2BBF3568943F0D1F239684 /* ASExperimentalFeatures.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASExperimentalFeatures.mm; path = Source/ASExperimentalFeatures.mm; sourceTree = ""; }; + 96BDF2482DF865D7F8FF0B07AEE55F7B /* UICollectionViewLayout+ASConvenience.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = "UICollectionViewLayout+ASConvenience.mm"; path = "Source/Details/UICollectionViewLayout+ASConvenience.mm"; sourceTree = ""; }; + 97CA4CF3A8DAB8BAF80805672B0FC6FF /* Pods-Starbucks_Clone_TexBrother.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-Starbucks_Clone_TexBrother.release.xcconfig"; sourceTree = ""; }; + 97E7FC319F436F0B423EE720BD4114C4 /* ASRecursiveUnfairLock.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASRecursiveUnfairLock.h; path = Source/Details/ASRecursiveUnfairLock.h; sourceTree = ""; }; + 98B0E4F19691D2F4131FF50AFA1071E1 /* ASCollectionLayoutCache.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASCollectionLayoutCache.mm; path = Source/Private/ASCollectionLayoutCache.mm; sourceTree = ""; }; + 99139531ABD4081B9313BB193B8F3C82 /* ASTextLayout.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASTextLayout.h; path = Source/Private/TextExperiment/Component/ASTextLayout.h; sourceTree = ""; }; + 99443CE1B364268627439CDB10A3ACE7 /* ASTextNode2.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASTextNode2.mm; path = Source/ASTextNode2.mm; sourceTree = ""; }; + 996D138EA53F8172C888E4E58C2BA094 /* UIView+ASConvenience.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIView+ASConvenience.h"; path = "Source/Details/UIView+ASConvenience.h"; sourceTree = ""; }; + 99B6328B4EE826FA54866D56A088FE88 /* ASCollectionGalleryLayoutDelegate.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASCollectionGalleryLayoutDelegate.mm; path = Source/Details/ASCollectionGalleryLayoutDelegate.mm; sourceTree = ""; }; + 99F1BC718C4DBB8C83F06F0D5E20460F /* ASTextKitContext.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASTextKitContext.h; path = Source/TextKit/ASTextKitContext.h; sourceTree = ""; }; + 9AAB840C4B625AB58BBC77E40F7BF770 /* NSParagraphStyle+ASText.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = "NSParagraphStyle+ASText.mm"; path = "Source/Private/TextExperiment/Utility/NSParagraphStyle+ASText.mm"; sourceTree = ""; }; + 9B3C2A862F2283BFC0EABB764DF58298 /* ASImageNode+CGExtras.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = "ASImageNode+CGExtras.mm"; path = "Source/Private/ASImageNode+CGExtras.mm"; sourceTree = ""; }; + 9BB90969D06357DC3F9419D00171EFDD /* ASRatioLayoutSpec.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASRatioLayoutSpec.mm; path = Source/Layout/ASRatioLayoutSpec.mm; sourceTree = ""; }; + 9D63CE7677F2708E6EFB6CCFFAC34952 /* ASTextDebugOption.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASTextDebugOption.mm; path = Source/Private/TextExperiment/Component/ASTextDebugOption.mm; sourceTree = ""; }; + 9D940727FF8FB9C785EB98E56350EF41 /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; indentWidth = 2; lastKnownFileType = text; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; tabWidth = 2; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; + 9DA8213BEA83150926293674AE1852A9 /* PINRemoteWeakProxy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PINRemoteWeakProxy.m; path = Source/Classes/PINRemoteWeakProxy.m; sourceTree = ""; }; + 9E12D4B3FB7683F25D5A629A2F5CF4F5 /* PINMemoryCache.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PINMemoryCache.m; path = Source/PINMemoryCache.m; sourceTree = ""; }; + 9E3FC7BE9409137DCC7A948ECD5695F8 /* ASCollectionViewLayoutController.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASCollectionViewLayoutController.mm; path = Source/Details/ASCollectionViewLayoutController.mm; sourceTree = ""; }; + 9E57E56ED96B01EFFB474D13F8F2E736 /* PINRemoteImageTask.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PINRemoteImageTask.h; path = Source/Classes/PINRemoteImageTask.h; sourceTree = ""; }; + 9E7888C7A1AA9B642609F19D11AF414E /* ASDisplayNodeExtras.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASDisplayNodeExtras.h; path = Source/ASDisplayNodeExtras.h; sourceTree = ""; }; + 9F1507A2B71A82D68CA7D8D4B300BB40 /* ASImageNode.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASImageNode.mm; path = Source/ASImageNode.mm; sourceTree = ""; }; + 9FAB346055C63B3162B59F5DE8235DF0 /* PINButton+PINRemoteImage.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "PINButton+PINRemoteImage.m"; path = "Source/Classes/ImageCategories/PINButton+PINRemoteImage.m"; sourceTree = ""; }; + A03C7EAEEBD03FBE27B384992167A313 /* ASCollectionLayoutContext.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASCollectionLayoutContext.h; path = Source/Details/ASCollectionLayoutContext.h; sourceTree = ""; }; + A06BA14EEE45FB041AC986113251A89C /* NSIndexSet+ASHelpers.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSIndexSet+ASHelpers.h"; path = "Source/Details/NSIndexSet+ASHelpers.h"; sourceTree = ""; }; + A0CDEAD7A3000E9E2DD98E6D92AD2D21 /* ASTextKitShadower.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASTextKitShadower.h; path = Source/TextKit/ASTextKitShadower.h; sourceTree = ""; }; + A15B2911935F13326BE8387085853A39 /* Pods-Starbucks_Clone_TexBrother-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-Starbucks_Clone_TexBrother-frameworks.sh"; sourceTree = ""; }; + A1666DABB81FF91AB2854A6FFA58794D /* ASDisplayNode+Convenience.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "ASDisplayNode+Convenience.h"; path = "Source/ASDisplayNode+Convenience.h"; sourceTree = ""; }; + A17F214DF82A4422342061B698A3F309 /* ASCollectionView.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASCollectionView.mm; path = Source/ASCollectionView.mm; sourceTree = ""; }; + A1C208163829F8088B47F961509583E3 /* ASStackPositionedLayout.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASStackPositionedLayout.h; path = Source/Private/Layout/ASStackPositionedLayout.h; sourceTree = ""; }; + A29BC347D15906796EAF09C899F0BAA9 /* ASCollectionViewLayoutInspector.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASCollectionViewLayoutInspector.h; path = Source/Details/ASCollectionViewLayoutInspector.h; sourceTree = ""; }; + A3035FDC9B0F6D11945C34895FCBF6E6 /* ASTextKitRenderer+TextChecking.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "ASTextKitRenderer+TextChecking.h"; path = "Source/TextKit/ASTextKitRenderer+TextChecking.h"; sourceTree = ""; }; + A36A6CCC10752056AAA5047945986152 /* _ASCollectionReusableView.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = _ASCollectionReusableView.mm; path = Source/Details/_ASCollectionReusableView.mm; sourceTree = ""; }; + A43F626612EAF6E6960627B69535B850 /* PINRemoteImage.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = PINRemoteImage.modulemap; sourceTree = ""; }; + A4C06B180E1A14EA1EC52F498B086A88 /* ASImageNode+AnimatedImagePrivate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "ASImageNode+AnimatedImagePrivate.h"; path = "Source/Private/ASImageNode+AnimatedImagePrivate.h"; sourceTree = ""; }; + A563C19F22250BA10FAAF9CAA214C6C8 /* PINRemoteImage.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = PINRemoteImage.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + A56D0ECA9EBC62BE680E0D42D7FAB7AF /* ASBatchContext.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASBatchContext.mm; path = Source/Details/ASBatchContext.mm; sourceTree = ""; }; + A6648655AE61CC658E7AE86A6804AD60 /* PINOperationGroup.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PINOperationGroup.h; path = Source/PINOperationGroup.h; sourceTree = ""; }; + A74625036C72AB1D11BF828EF40AF42E /* PINOperation-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "PINOperation-umbrella.h"; sourceTree = ""; }; + A76D4E87369EBDA42913A7AAEFBF00D6 /* ASCollectionElement.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASCollectionElement.mm; path = Source/Details/ASCollectionElement.mm; sourceTree = ""; }; + A7B540D3D7D6A9543C97F678E925A099 /* PINResume.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PINResume.h; path = Source/Classes/PINResume.h; sourceTree = ""; }; + A7F0BEBBD6016E8CEB75E14D708BC006 /* ASLayoutManager.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASLayoutManager.mm; path = Source/TextKit/ASLayoutManager.mm; sourceTree = ""; }; + A80CA8D6A0D34301B629FB11D8686CEA /* ASLayoutTransition.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASLayoutTransition.mm; path = Source/Private/ASLayoutTransition.mm; sourceTree = ""; }; + A84EC65C2CB86EEA0D6C180F5830CBB5 /* _ASDisplayLayer.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = _ASDisplayLayer.h; path = Source/Details/_ASDisplayLayer.h; sourceTree = ""; }; + A87349F24336B636FD7357BBD1C1F074 /* ASTipProvider.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASTipProvider.h; path = Source/Private/ASTipProvider.h; sourceTree = ""; }; + A8E85E8B5E162541ED900C4EE3F74124 /* ASLayoutManager.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASLayoutManager.h; path = Source/TextKit/ASLayoutManager.h; sourceTree = ""; }; + A8F705E5590C234FD939D87CED55C5ED /* ASLayoutSpecPrivate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASLayoutSpecPrivate.h; path = Source/Private/Layout/ASLayoutSpecPrivate.h; sourceTree = ""; }; + A93DD47CE966A545A55866BCC8AD2658 /* _ASCoreAnimationExtras.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = _ASCoreAnimationExtras.h; path = Source/Private/_ASCoreAnimationExtras.h; sourceTree = ""; }; + A95018FAB6DF136289250129CA16934B /* ASMainThreadDeallocation.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASMainThreadDeallocation.h; path = Source/ASMainThreadDeallocation.h; sourceTree = ""; }; + A9AE994038F0C350DEBB06A25EADFC8E /* ASVideoPlayerNode.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASVideoPlayerNode.h; path = Source/ASVideoPlayerNode.h; sourceTree = ""; }; + AA33C1BA9625FB1059A64D05E85E1075 /* ASTraitCollection.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASTraitCollection.h; path = Source/Details/ASTraitCollection.h; sourceTree = ""; }; + ABB03AA3087EA9FCC208E03B28E09935 /* _ASDisplayLayer.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = _ASDisplayLayer.mm; path = Source/Details/_ASDisplayLayer.mm; sourceTree = ""; }; + ABBC5A4E8EBEC559EF2D0DBE16D3C66F /* ASTipProvider.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASTipProvider.mm; path = Source/Private/ASTipProvider.mm; sourceTree = ""; }; + ABC2257CCA051191378319330151F0D4 /* PINOperationGroup.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PINOperationGroup.m; path = Source/PINOperationGroup.m; sourceTree = ""; }; + AC2577D0AE6AFBA55F004F4BE81ADCCF /* ASCollectionLayoutDefines.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASCollectionLayoutDefines.mm; path = Source/Private/ASCollectionLayoutDefines.mm; sourceTree = ""; }; + AC3B95E05FDC42AC4D0011ED60ED02AB /* ASAbsoluteLayoutElement.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASAbsoluteLayoutElement.h; path = Source/Layout/ASAbsoluteLayoutElement.h; sourceTree = ""; }; + AE482408FCFD063EC12E94BA06BA5CD4 /* PINCache.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = PINCache.modulemap; sourceTree = ""; }; + AF598AFD0BCBA155CCD60D60E7682662 /* ASTextNodeWordKerner.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASTextNodeWordKerner.h; path = Source/TextKit/ASTextNodeWordKerner.h; sourceTree = ""; }; + AFB06A665A86D968044ED349FC6E2817 /* ASMapNode.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASMapNode.h; path = Source/ASMapNode.h; sourceTree = ""; }; + AFEEAA3B4F1296937A0BEE268BAF05EF /* ASPendingStateController.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASPendingStateController.mm; path = Source/Private/ASPendingStateController.mm; sourceTree = ""; }; + B1025114DDE3DD7D1CA463AA1CA551BD /* ASTip.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASTip.h; path = Source/Private/ASTip.h; sourceTree = ""; }; + B11E2C1306253F769EADB97E211BF625 /* ASStackPositionedLayout.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASStackPositionedLayout.mm; path = Source/Private/Layout/ASStackPositionedLayout.mm; sourceTree = ""; }; + B16AA9D7833946948169CE0EAA7B4B5D /* ASMutableAttributedStringBuilder.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASMutableAttributedStringBuilder.h; path = Source/Details/ASMutableAttributedStringBuilder.h; sourceTree = ""; }; + B1B5C512945B8108FC01D7902FB0888F /* ASImageNode+AnimatedImage.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = "ASImageNode+AnimatedImage.mm"; path = "Source/ASImageNode+AnimatedImage.mm"; sourceTree = ""; }; + B22CAC88C961169FA20051DE8BDF0B27 /* ASDisplayNode+DebugTiming.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = "ASDisplayNode+DebugTiming.mm"; path = "Source/Private/ASDisplayNode+DebugTiming.mm"; sourceTree = ""; }; + B30F52EC317262D450BB71ABDF384A75 /* ASConfiguration.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASConfiguration.h; path = Source/ASConfiguration.h; sourceTree = ""; }; + B377086AE4B7CBACB4F4B5FF68BCDBC7 /* ASCollectionFlowLayoutDelegate.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASCollectionFlowLayoutDelegate.mm; path = Source/Details/ASCollectionFlowLayoutDelegate.mm; sourceTree = ""; }; + B385F44C418B5E657B7816193A169A10 /* ASDataController.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASDataController.mm; path = Source/Details/ASDataController.mm; sourceTree = ""; }; + B3A200415E395C61CA67F56E33FC809D /* ASMutableElementMap.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASMutableElementMap.mm; path = Source/Private/ASMutableElementMap.mm; sourceTree = ""; }; + B3E823C5AED7466242446DADE710BC14 /* ASNodeController+Beta.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = "ASNodeController+Beta.mm"; path = "Source/ASNodeController+Beta.mm"; sourceTree = ""; }; + B458A01741A678DD40054F964CE52EA9 /* ASAbstractLayoutController.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASAbstractLayoutController.h; path = Source/Details/ASAbstractLayoutController.h; sourceTree = ""; }; + B4AC4FD5B9DD6F7746F71563A59BE136 /* NSArray+Diffing.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSArray+Diffing.h"; path = "Source/Details/NSArray+Diffing.h"; sourceTree = ""; }; + B50CED0243E158B6FB3AF02FA0EED408 /* _ASAsyncTransactionGroup.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = _ASAsyncTransactionGroup.h; path = Source/Details/Transactions/_ASAsyncTransactionGroup.h; sourceTree = ""; }; + B5621F42157B08B48920F0A50B3222AF /* ASStackUnpositionedLayout.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASStackUnpositionedLayout.mm; path = Source/Private/Layout/ASStackUnpositionedLayout.mm; sourceTree = ""; }; + B6477497D4580620434C98CDF8C40318 /* ASCollectionViewLayoutController.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASCollectionViewLayoutController.h; path = Source/Details/ASCollectionViewLayoutController.h; sourceTree = ""; }; + B67FEF8CB94D5E7658A7016E7DE658F4 /* _ASPendingState.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = _ASPendingState.mm; path = Source/Private/_ASPendingState.mm; sourceTree = ""; }; + B757AC50585C6BAFCEB05ED2E0A2262D /* ASTableNode.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASTableNode.mm; path = Source/ASTableNode.mm; sourceTree = ""; }; + B7E5025469DC385B5D9CB4C22BE358CF /* ASCollectionLayout.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASCollectionLayout.h; path = Source/Private/ASCollectionLayout.h; sourceTree = ""; }; + B804EAE3C4FD585B6054924F31638531 /* PINRemoteImageMacros.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PINRemoteImageMacros.h; path = Source/Classes/include/PINRemoteImageMacros.h; sourceTree = ""; }; + B82A5D010BD4DA75A6D18C51A793149F /* ASCollectionViewProtocols.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASCollectionViewProtocols.h; path = Source/ASCollectionViewProtocols.h; sourceTree = ""; }; + B8AA8DB0A48C9AC29F30CC40A2173133 /* ASTextKitComponents.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASTextKitComponents.h; path = Source/TextKit/ASTextKitComponents.h; sourceTree = ""; }; + B9B56E35637395BD7F69E4B7F8F55DBB /* _ASTransitionContext.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = _ASTransitionContext.mm; path = Source/_ASTransitionContext.mm; sourceTree = ""; }; + BA640A1E51676DE0AC8A2AE86C35FDB2 /* ASTextKitContext.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASTextKitContext.mm; path = Source/TextKit/ASTextKitContext.mm; sourceTree = ""; }; + BA7E4F3BDA84380743FAF975B1665227 /* ASRangeController.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASRangeController.h; path = Source/Details/ASRangeController.h; sourceTree = ""; }; + BAB31548FC8D5AB50A8EB7217A7712E4 /* PINRemoteImage.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = PINRemoteImage.debug.xcconfig; sourceTree = ""; }; + BB6C6201DDCE0BC37AB907FDD59EE119 /* ASTabBarController.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASTabBarController.mm; path = Source/ASTabBarController.mm; sourceTree = ""; }; + BB705D337F09F90307592D19183A1EFF /* ASTableView+Undeprecated.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "ASTableView+Undeprecated.h"; path = "Source/Private/ASTableView+Undeprecated.h"; sourceTree = ""; }; + BB800E39017591858E237D0FF17509B6 /* PINDisplayLink.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PINDisplayLink.m; path = Source/Classes/PINDisplayLink.m; sourceTree = ""; }; + BC54F1AC79DB2F89235AD3AAAE4E8794 /* ASEditableTextNode.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASEditableTextNode.h; path = Source/ASEditableTextNode.h; sourceTree = ""; }; + BCE8409AD4EB33E423BAD5BB2C25137D /* ASSectionController.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASSectionController.h; path = Source/ASSectionController.h; sourceTree = ""; }; + BD665D50848207C21A9980329097B251 /* ASCollectionViewFlowLayoutInspector.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASCollectionViewFlowLayoutInspector.mm; path = Source/Private/ASCollectionViewFlowLayoutInspector.mm; sourceTree = ""; }; + BD92916D1ECF51A2D58AC7BF0A89F357 /* ASCollectionNode.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASCollectionNode.mm; path = Source/ASCollectionNode.mm; sourceTree = ""; }; + BDBDCD408FDA7E7CFEE56C22BE3A64D4 /* PINRequestRetryStrategy.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PINRequestRetryStrategy.h; path = Source/Classes/include/PINRequestRetryStrategy.h; sourceTree = ""; }; + BDDB697D7302D7165178BFC76A053780 /* ASBasicImageDownloaderInternal.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASBasicImageDownloaderInternal.h; path = Source/Private/ASBasicImageDownloaderInternal.h; sourceTree = ""; }; + BE11BB4291FF4EC28259ED221EAED215 /* PINCache-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "PINCache-Info.plist"; sourceTree = ""; }; + BFA9545ACC55BD7967322ACA3CF6ABDA /* ASTextUtilities.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASTextUtilities.h; path = Source/Private/TextExperiment/Utility/ASTextUtilities.h; sourceTree = ""; }; + C0464695A56E068F9EB148E0B4515AA0 /* ASTextKitEntityAttribute.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASTextKitEntityAttribute.mm; path = Source/TextKit/ASTextKitEntityAttribute.mm; sourceTree = ""; }; + C0491CD11ADA6A67C64D323A6BB11078 /* ASTextNodeTypes.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASTextNodeTypes.h; path = Source/TextKit/ASTextNodeTypes.h; sourceTree = ""; }; + C04A40D87995160C194B1F420E82E654 /* ASRelativeLayoutSpec.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASRelativeLayoutSpec.mm; path = Source/Layout/ASRelativeLayoutSpec.mm; sourceTree = ""; }; + C0F24E93C735911A21D52FCA96DE90EF /* PINAnimatedImageView.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PINAnimatedImageView.m; path = Source/Classes/AnimatedImages/PINAnimatedImageView.m; sourceTree = ""; }; + C2037A0F5BAE21EEAF3BF0C23B362004 /* ASElementMap.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASElementMap.mm; path = Source/Details/ASElementMap.mm; sourceTree = ""; }; + C3748C4FDDDB81BD2D1F20D30668477D /* ASSection.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASSection.mm; path = Source/Private/ASSection.mm; sourceTree = ""; }; + C3D08FDB734E574133896549B7B0E4F9 /* ASDefaultPlaybackButton.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASDefaultPlaybackButton.mm; path = Source/Private/ASDefaultPlaybackButton.mm; sourceTree = ""; }; + C58EA3FBFBA1476CBC65553056DC50C2 /* ASConfiguration.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASConfiguration.mm; path = Source/ASConfiguration.mm; sourceTree = ""; }; + C6BA4FA2A0064B482B835FC3FCA51761 /* AsyncDisplayKit+IGListKitMethods.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = "AsyncDisplayKit+IGListKitMethods.mm"; path = "Source/AsyncDisplayKit+IGListKitMethods.mm"; sourceTree = ""; }; + C741926C4CC8BF0802B8539AC2697545 /* _ASCollectionViewCell.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = _ASCollectionViewCell.h; path = Source/Details/_ASCollectionViewCell.h; sourceTree = ""; }; + C75D246C46658AF6540D5F980D67A83C /* ASTextAttribute.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASTextAttribute.h; path = Source/Private/TextExperiment/String/ASTextAttribute.h; sourceTree = ""; }; + C8C2EE7A63C346D06A35520A77DE19AC /* ASGraphicsContext.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASGraphicsContext.h; path = Source/Details/ASGraphicsContext.h; sourceTree = ""; }; + C8D491217E9C139BB7B03BDB3EC9AE93 /* UIResponder+AsyncDisplayKit.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIResponder+AsyncDisplayKit.h"; path = "Source/UIResponder+AsyncDisplayKit.h"; sourceTree = ""; }; + C8EEA80259BB6314337EE232E2AF34F7 /* ASWeakMap.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASWeakMap.h; path = Source/Private/ASWeakMap.h; sourceTree = ""; }; + C92F34A09B8C59BACD85A16B2D11AAF6 /* ASWeakProxy.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASWeakProxy.h; path = Source/Details/ASWeakProxy.h; sourceTree = ""; }; + C9771B7F3EA9E4860D1D432EC3FF9820 /* ASMutableAttributedStringBuilder.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASMutableAttributedStringBuilder.mm; path = Source/Details/ASMutableAttributedStringBuilder.mm; sourceTree = ""; }; + C9C467665994B887574F02A84A9F4800 /* Texture-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Texture-Info.plist"; sourceTree = ""; }; + C9C4E917C6DD24E5A1A14EF3D1471B07 /* PINRemoteImageMemoryContainer.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PINRemoteImageMemoryContainer.h; path = Source/Classes/PINRemoteImageMemoryContainer.h; sourceTree = ""; }; + CA2861F5E52D93186A2AADFF0825C4ED /* ASMultiplexImageNode.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASMultiplexImageNode.mm; path = Source/ASMultiplexImageNode.mm; sourceTree = ""; }; + CA4D8229EE753A846B1A324FF59E6F24 /* UIImage+ASConvenience.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = "UIImage+ASConvenience.mm"; path = "Source/UIImage+ASConvenience.mm"; sourceTree = ""; }; + CA8AEF5A3D9FB6C2D3EDA71FC15F7D74 /* PINDiskCache.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PINDiskCache.h; path = Source/PINDiskCache.h; sourceTree = ""; }; + CAE35B3FA8348440C30B17D645FADAB6 /* ASDisplayNodeCornerLayerDelegate.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASDisplayNodeCornerLayerDelegate.mm; path = Source/Private/ASDisplayNodeCornerLayerDelegate.mm; sourceTree = ""; }; + CC9924E9040C5454AC24BC42A84D6E24 /* ASObjectDescriptionHelpers.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASObjectDescriptionHelpers.mm; path = Source/Details/ASObjectDescriptionHelpers.mm; sourceTree = ""; }; + CD6D8F0076FAC6559F82C7EC55D8786C /* ASEditableTextNode.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASEditableTextNode.mm; path = Source/ASEditableTextNode.mm; sourceTree = ""; }; + CD86773D0E02DC18396836AFE7DC3918 /* _ASDisplayViewAccessiblity.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = _ASDisplayViewAccessiblity.h; path = Source/Details/_ASDisplayViewAccessiblity.h; sourceTree = ""; }; + CD8F721F21C26F26C2A6B469454DB51F /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS14.0.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; + CDB9905DE100C45E321FF681B0BE5D74 /* PINDiskCache.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PINDiskCache.m; path = Source/PINDiskCache.m; sourceTree = ""; }; + CEA150E52F46238642DC87EC38BD0E6D /* PINCache.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PINCache.m; path = Source/PINCache.m; sourceTree = ""; }; + CECE4ED7AF67F941DEB6F0EEFDE8052D /* ASConfigurationInternal.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASConfigurationInternal.h; path = Source/ASConfigurationInternal.h; sourceTree = ""; }; + CF4EE8C8467B3FC8306891C7E48F70C3 /* ASPagerNode.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASPagerNode.mm; path = Source/ASPagerNode.mm; sourceTree = ""; }; + CFD1BD03E8A08531D5ED8002B0FE1376 /* ASPagerFlowLayout.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASPagerFlowLayout.h; path = Source/ASPagerFlowLayout.h; sourceTree = ""; }; + D0C20EDEB4D6EE05F48F1581955AD35A /* ASLayoutController.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASLayoutController.h; path = Source/Details/ASLayoutController.h; sourceTree = ""; }; + D13CF7125B69CA994A1A19ADF69164F8 /* PINAnimatedImageView.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PINAnimatedImageView.h; path = Source/Classes/include/PINAnimatedImageView.h; sourceTree = ""; }; + D18CF219AFB6A8D799381B345E84B2CD /* ASElementMap.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASElementMap.h; path = Source/Details/ASElementMap.h; sourceTree = ""; }; + D1B206A8F349D1A6BA64F7B57B7CC17A /* ASCollectionLayoutContext+Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "ASCollectionLayoutContext+Private.h"; path = "Source/Private/ASCollectionLayoutContext+Private.h"; sourceTree = ""; }; + D25B4227847397852787D1EF3F3C6FC6 /* ASDisplayNodeTipState.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASDisplayNodeTipState.mm; path = Source/Private/ASDisplayNodeTipState.mm; sourceTree = ""; }; + D386EE9C7174CAC93AE4EFAFD2F8D52B /* ASControlNode.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASControlNode.h; path = Source/ASControlNode.h; sourceTree = ""; }; + D389403EE0D912CAB00411EEEBBF5284 /* ASCollections.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASCollections.mm; path = Source/ASCollections.mm; sourceTree = ""; }; + D3C2B5E2A1BCEF6ED01C9280FD3A6C30 /* ASVisibilityProtocols.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASVisibilityProtocols.mm; path = Source/ASVisibilityProtocols.mm; sourceTree = ""; }; + D3F73B5CE261EAA709DE2073A342D910 /* ASDisplayNode+FrameworkPrivate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "ASDisplayNode+FrameworkPrivate.h"; path = "Source/Private/ASDisplayNode+FrameworkPrivate.h"; sourceTree = ""; }; + D458980EAD9CDDC12A46DBFF63A4EBF5 /* ASSignpost.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASSignpost.h; path = Source/Base/ASSignpost.h; sourceTree = ""; }; + D47F9321D282478772005EA04CBDB68C /* ASTableNode+Beta.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "ASTableNode+Beta.h"; path = "Source/ASTableNode+Beta.h"; sourceTree = ""; }; + D4A2BD0267EFCC67E4D6E293788CBD3B /* ASRangeController.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASRangeController.mm; path = Source/Details/ASRangeController.mm; sourceTree = ""; }; + D51AF6284504A2AF4A3CCCAEABC19559 /* ASTextKitAttributes.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASTextKitAttributes.mm; path = Source/TextKit/ASTextKitAttributes.mm; sourceTree = ""; }; + D55B5D9D10346E310CC066CE345EF883 /* PINCacheMacros.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PINCacheMacros.h; path = Source/PINCacheMacros.h; sourceTree = ""; }; + D5A164E3FB065BB3632339162B9844A4 /* ASCollectionLayoutDelegate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASCollectionLayoutDelegate.h; path = Source/Details/ASCollectionLayoutDelegate.h; sourceTree = ""; }; + D61846E07A8D0904AF857A84BDD7C878 /* IGListAdapter+AsyncDisplayKit.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = "IGListAdapter+AsyncDisplayKit.mm"; path = "Source/IGListAdapter+AsyncDisplayKit.mm"; sourceTree = ""; }; + D643AB237A68D3EA33E130BE941C23A2 /* NSHTTPURLResponse+MaxAge.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSHTTPURLResponse+MaxAge.m"; path = "Source/Classes/Categories/NSHTTPURLResponse+MaxAge.m"; sourceTree = ""; }; + D6820D90344C7012FD9DAF1CE99185E8 /* ASTableNode.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASTableNode.h; path = Source/ASTableNode.h; sourceTree = ""; }; + D6832FF8EE7BE44EEE48E868181CF012 /* ASDisplayNode.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASDisplayNode.h; path = Source/ASDisplayNode.h; sourceTree = ""; }; + D6AF8F32499937E401433036C62354C2 /* MapKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MapKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS14.0.sdk/System/Library/Frameworks/MapKit.framework; sourceTree = DEVELOPER_DIR; }; + D6FC0AB66F5BC4327C141E396044BC18 /* ASControlTargetAction.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASControlTargetAction.h; path = Source/Private/ASControlTargetAction.h; sourceTree = ""; }; + D7429861868D4103F11B9E94C19522A9 /* PINRemoteImageManager+Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "PINRemoteImageManager+Private.h"; path = "Source/Classes/PINRemoteImageManager+Private.h"; sourceTree = ""; }; + D78004E3854FF269F6C7E6E386DA49C4 /* ASScrollDirection.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASScrollDirection.mm; path = Source/Details/ASScrollDirection.mm; sourceTree = ""; }; + D7AEF2BB9296AC6AD078D6A403286D8F /* PINRemoteImageManagerConfiguration.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PINRemoteImageManagerConfiguration.m; path = Source/Classes/PINRemoteImageManagerConfiguration.m; sourceTree = ""; }; + D84FBBB202FB56A503EA21E03778DED7 /* ASDimensionInternal.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASDimensionInternal.mm; path = Source/Layout/ASDimensionInternal.mm; sourceTree = ""; }; + D8697D8727A9A3453A287EBBD21C6AF1 /* ASControlNode+tvOS.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = "ASControlNode+tvOS.mm"; path = "Source/tvOS/ASControlNode+tvOS.mm"; sourceTree = ""; }; + D9518F8951D785FB5E83392FF5EEF900 /* ASImageNode+CGExtras.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "ASImageNode+CGExtras.h"; path = "Source/Private/ASImageNode+CGExtras.h"; sourceTree = ""; }; + D95B3764BC290B9DB81E76736CDC7766 /* ASCollectionView+Undeprecated.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "ASCollectionView+Undeprecated.h"; path = "Source/Private/ASCollectionView+Undeprecated.h"; sourceTree = ""; }; + D9C64EBA02032F27282C40FD7220273C /* PINRemoteImageDownloadQueue.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PINRemoteImageDownloadQueue.h; path = Source/Classes/PINRemoteImageDownloadQueue.h; sourceTree = ""; }; + DA1D941B55196700D1C09F1BBF76A710 /* PINOperation.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = PINOperation.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + DA4FF234AD32C005D1BC2803290984C7 /* ASCollectionLayoutState.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASCollectionLayoutState.mm; path = Source/Details/ASCollectionLayoutState.mm; sourceTree = ""; }; + DABECC346A0C5E457DD875465E22622F /* ASDisplayNode+UIViewBridge.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = "ASDisplayNode+UIViewBridge.mm"; path = "Source/Private/ASDisplayNode+UIViewBridge.mm"; sourceTree = ""; }; + DB92B37C19A5B459CB704598D51575B9 /* AsyncDisplayKit+IGListKitMethods.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "AsyncDisplayKit+IGListKitMethods.h"; path = "Source/AsyncDisplayKit+IGListKitMethods.h"; sourceTree = ""; }; + DC9BA969B57D2D800557EE536EB1DC14 /* ASTextRunDelegate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASTextRunDelegate.h; path = Source/Private/TextExperiment/String/ASTextRunDelegate.h; sourceTree = ""; }; + DCFAB58A9BA38E8EE152DE448425B204 /* ASDataController.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASDataController.h; path = Source/Details/ASDataController.h; sourceTree = ""; }; + DD7D7ECC692E89521F0E4656EC06ADA5 /* ASWeakMap.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASWeakMap.mm; path = Source/Private/ASWeakMap.mm; sourceTree = ""; }; + DDB932802CAA60747F82694A454F50F3 /* NSAttributedString+ASText.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = "NSAttributedString+ASText.mm"; path = "Source/Private/TextExperiment/Utility/NSAttributedString+ASText.mm"; sourceTree = ""; }; + DE1EEC4327E7157183B75E4178082C91 /* ASLayerBackingTipProvider.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASLayerBackingTipProvider.h; path = Source/Private/ASLayerBackingTipProvider.h; sourceTree = ""; }; + DE5B18F6F58E950EFC83A61B4A825175 /* _ASDisplayView.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = _ASDisplayView.mm; path = Source/Details/_ASDisplayView.mm; sourceTree = ""; }; + DEC0BF308170B3621109986E67AEFA82 /* _ASCollectionReusableView.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = _ASCollectionReusableView.h; path = Source/Details/_ASCollectionReusableView.h; sourceTree = ""; }; + DF03059CFC2CF8C308FEB24EB69C2BC1 /* NSData+ImageDetectors.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSData+ImageDetectors.h"; path = "Source/Classes/include/NSData+ImageDetectors.h"; sourceTree = ""; }; + E152F08E24DF7356E972205CE1FE4D24 /* ASAsciiArtBoxCreator.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASAsciiArtBoxCreator.h; path = Source/Layout/ASAsciiArtBoxCreator.h; sourceTree = ""; }; + E1A4DEAF97B792E35B974180DAB5AAEE /* ASNetworkImageLoadInfo+Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "ASNetworkImageLoadInfo+Private.h"; path = "Source/Private/ASNetworkImageLoadInfo+Private.h"; sourceTree = ""; }; + E24B74CBEFA4CF9264A81572FD614BD2 /* _ASAsyncTransactionContainer+Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "_ASAsyncTransactionContainer+Private.h"; path = "Source/Details/Transactions/_ASAsyncTransactionContainer+Private.h"; sourceTree = ""; }; + E2BF0999ECBBCF0288C66B2D84F59B64 /* PINImage+WebP.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "PINImage+WebP.h"; path = "Source/Classes/Categories/PINImage+WebP.h"; sourceTree = ""; }; + E32EFE7E28F3D30B3ADE45D68C01B4E5 /* _ASAsyncTransaction.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = _ASAsyncTransaction.mm; path = Source/Details/Transactions/_ASAsyncTransaction.mm; sourceTree = ""; }; + E4A4E77985D62E4AE0F17F553E7EB919 /* PINOperationQueue.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PINOperationQueue.m; path = Source/PINOperationQueue.m; sourceTree = ""; }; + E4D42106BAAABAE5142D533B57E44149 /* ASTextKitShadower.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASTextKitShadower.mm; path = Source/TextKit/ASTextKitShadower.mm; sourceTree = ""; }; + E51E2FEACF14FE9C9B1FCAC9532F4D3E /* ASTipsController.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASTipsController.mm; path = Source/Private/ASTipsController.mm; sourceTree = ""; }; + E61F401F1879DA3A4E7851A9673CD3D1 /* _ASCollectionViewCell.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = _ASCollectionViewCell.mm; path = Source/Details/_ASCollectionViewCell.mm; sourceTree = ""; }; + E690EC656DC50FF9A5210F6CC30AE500 /* UICollectionViewLayout+ASConvenience.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UICollectionViewLayout+ASConvenience.h"; path = "Source/Details/UICollectionViewLayout+ASConvenience.h"; sourceTree = ""; }; + E69484760E2878ADDAF63DEB7003DCDC /* ASAbsoluteLayoutSpec.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASAbsoluteLayoutSpec.mm; path = Source/Layout/ASAbsoluteLayoutSpec.mm; sourceTree = ""; }; + E6AA6582F46D7A6021F34A9D7019F1AF /* _ASAsyncTransactionGroup.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = _ASAsyncTransactionGroup.mm; path = Source/Details/Transactions/_ASAsyncTransactionGroup.mm; sourceTree = ""; }; + E732EDFE66778C370EDB3A951172CBE2 /* NSAttributedString+ASText.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSAttributedString+ASText.h"; path = "Source/Private/TextExperiment/Utility/NSAttributedString+ASText.h"; sourceTree = ""; }; + E801FD290C13CFBB6EECB86EAEDD14A2 /* ASTipsController.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASTipsController.h; path = Source/Private/ASTipsController.h; sourceTree = ""; }; + E834741D474E0658F5747909301B10F7 /* ASLayoutElementPrivate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASLayoutElementPrivate.h; path = Source/Layout/ASLayoutElementPrivate.h; sourceTree = ""; }; + E8739155AFF4384DA7B122A73A82B396 /* ASPageTable.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASPageTable.h; path = Source/Details/ASPageTable.h; sourceTree = ""; }; + E903B227DE544FEBB3869D8A7D5D3F64 /* ASDefaultPlayButton.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASDefaultPlayButton.mm; path = Source/Private/ASDefaultPlayButton.mm; sourceTree = ""; }; + E94C2962BB22E3F85E5F03567F0FE0CF /* ASBatchFetchingDelegate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASBatchFetchingDelegate.h; path = Source/Details/ASBatchFetchingDelegate.h; sourceTree = ""; }; + EA00CE5CCA312A7A77C7D868AA3AC38B /* _ASCoreAnimationExtras.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = _ASCoreAnimationExtras.mm; path = Source/Private/_ASCoreAnimationExtras.mm; sourceTree = ""; }; + EA2BDC742161E6067502856B9E2FDAFF /* ASVideoNode.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASVideoNode.h; path = Source/ASVideoNode.h; sourceTree = ""; }; + EAFE043D342B5CB9C48DEF9F7A994F87 /* ASTableLayoutController.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASTableLayoutController.mm; path = Source/Details/ASTableLayoutController.mm; sourceTree = ""; }; + EB36743EBAA4B10A57C67D256B097261 /* ASTextDebugOption.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASTextDebugOption.h; path = Source/Private/TextExperiment/Component/ASTextDebugOption.h; sourceTree = ""; }; + EBE62C6CE8D397ED68FF4E791DA4CB38 /* ASSectionContext.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASSectionContext.h; path = Source/Details/ASSectionContext.h; sourceTree = ""; }; + EC4BC1F8BC97094F5292F0627A230B03 /* ASInternalHelpers.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASInternalHelpers.h; path = Source/Private/ASInternalHelpers.h; sourceTree = ""; }; + ECE920DFD35FE6C0A2D054EC3E972D12 /* ASDKViewController.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASDKViewController.h; path = Source/ASDKViewController.h; sourceTree = ""; }; + ED201A924E0BC9468579E76F2F2FE7AD /* PINOperation.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = PINOperation.debug.xcconfig; sourceTree = ""; }; + EDDFB203A3FA480E8F255B7E49928E6B /* ASImageContainerProtocolCategories.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASImageContainerProtocolCategories.mm; path = Source/Details/ASImageContainerProtocolCategories.mm; sourceTree = ""; }; + EE2940E42584F168B39AE1ADCDAF32AA /* PINRemoteImageManager.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = PINRemoteImageManager.m; path = Source/Classes/PINRemoteImageManager.m; sourceTree = ""; }; + EECCA4A8A8683035F019428C5499815C /* PINOperation.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PINOperation.h; path = Source/PINOperation.h; sourceTree = ""; }; + EF0D927D178E9A955F76E23A8D12530C /* ASTipNode.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASTipNode.mm; path = Source/Private/ASTipNode.mm; sourceTree = ""; }; + EF12EEA02B309EF99CDA1C4BB3D8D136 /* ASStackLayoutSpecUtilities.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASStackLayoutSpecUtilities.h; path = Source/Private/Layout/ASStackLayoutSpecUtilities.h; sourceTree = ""; }; + EFEF3AB75FB8E5BA99A80A1B87EF0E3C /* _ASDisplayView.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = _ASDisplayView.h; path = Source/Details/_ASDisplayView.h; sourceTree = ""; }; + F084236284192AB0CB2F19F765DD52D1 /* ASCollectionLayout.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASCollectionLayout.mm; path = Source/Private/ASCollectionLayout.mm; sourceTree = ""; }; + F12B84DDA3761BCCFFC101F6185D46A9 /* PINSpeedRecorder.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PINSpeedRecorder.h; path = Source/Classes/PINSpeedRecorder.h; sourceTree = ""; }; + F1807082404C3DD7326F3A2E7E75B56E /* ASDisplayNodeTipState.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASDisplayNodeTipState.h; path = Source/Private/ASDisplayNodeTipState.h; sourceTree = ""; }; + F1CE60DA44D9B7BDC09E8B75F5D86A7A /* ASBatchFetching.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASBatchFetching.h; path = Source/Private/ASBatchFetching.h; sourceTree = ""; }; + F3A05A320A0B5986D46D8E46AB8F56DB /* PINCaching.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PINCaching.h; path = Source/PINCaching.h; sourceTree = ""; }; + F3E3F88611CECB60D3DE4ABAE592D898 /* ASIGListAdapterBasedDataSource.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASIGListAdapterBasedDataSource.mm; path = Source/Private/ASIGListAdapterBasedDataSource.mm; sourceTree = ""; }; + F46831F4FCE3AB140F12577CE0C2AAB3 /* AsyncDisplayKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = AsyncDisplayKit.framework; path = Texture.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + F4EDFDD960862C423CFEE9683653342B /* PINProgressiveImage.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PINProgressiveImage.h; path = Source/Classes/include/PINProgressiveImage.h; sourceTree = ""; }; + F5EC4250B2AF97E5233012DE973EC2C6 /* ASNavigationController.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASNavigationController.mm; path = Source/ASNavigationController.mm; sourceTree = ""; }; + F6A8D376391747B8A795CD26BEAD779B /* CoreLocation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreLocation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS14.0.sdk/System/Library/Frameworks/CoreLocation.framework; sourceTree = DEVELOPER_DIR; }; + F6E67D14993A7B56B97A9C6D49B944FC /* PINAnimatedImageView+PINRemoteImage.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "PINAnimatedImageView+PINRemoteImage.m"; path = "Source/Classes/ImageCategories/PINAnimatedImageView+PINRemoteImage.m"; sourceTree = ""; }; + F6FEF6FE7DB7A8E54D5AE8F933CF1464 /* ASLayoutSpec.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASLayoutSpec.h; path = Source/Layout/ASLayoutSpec.h; sourceTree = ""; }; + F715418A114688F1318388F954C148DE /* PINCache.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = PINCache.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + F724F08CB726907AD6A2696B44D3A849 /* ASCellNode.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASCellNode.mm; path = Source/ASCellNode.mm; sourceTree = ""; }; + F786D2FE67301111EA52EC486B239B2B /* PINOperationQueue.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = PINOperationQueue.h; path = Source/PINOperationQueue.h; sourceTree = ""; }; + F90035D014D493146733CA992C815AAF /* ASTextKitTailTruncater.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASTextKitTailTruncater.h; path = Source/TextKit/ASTextKitTailTruncater.h; sourceTree = ""; }; + F975825068D5F553799FAEC02F007206 /* ASOverlayLayoutSpec.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASOverlayLayoutSpec.h; path = Source/Layout/ASOverlayLayoutSpec.h; sourceTree = ""; }; + F9ADEDE2E6CB82FA634260F9B98A6B6C /* ASTextKitTailTruncater.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASTextKitTailTruncater.mm; path = Source/TextKit/ASTextKitTailTruncater.mm; sourceTree = ""; }; + F9BACA46ECE4EB7466790ED17B5796D4 /* _ASCollectionGalleryLayoutInfo.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = _ASCollectionGalleryLayoutInfo.mm; path = Source/Private/_ASCollectionGalleryLayoutInfo.mm; sourceTree = ""; }; + FA09E94B1CC54AF0EAE0D3CD263B0812 /* ASTextKitTruncating.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASTextKitTruncating.h; path = Source/TextKit/ASTextKitTruncating.h; sourceTree = ""; }; + FB1326EC0457FCC01BFCF7447226FF78 /* Pods-Starbucks_Clone_TexBrother-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-Starbucks_Clone_TexBrother-dummy.m"; sourceTree = ""; }; + FB32E2461EA09ADDAA167B8226BF68A2 /* NSHTTPURLResponse+MaxAge.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSHTTPURLResponse+MaxAge.h"; path = "Source/Classes/Categories/NSHTTPURLResponse+MaxAge.h"; sourceTree = ""; }; + FB4422D6FE213AD611123E8A5347E60E /* ASCollectionElement.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASCollectionElement.h; path = Source/Details/ASCollectionElement.h; sourceTree = ""; }; + FB549CF897CE658795874AA1539B3E00 /* ASAbsoluteLayoutSpec.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASAbsoluteLayoutSpec.h; path = Source/Layout/ASAbsoluteLayoutSpec.h; sourceTree = ""; }; + FBD8D238CA37E2247A47BEC575B4E643 /* ASCollections.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASCollections.h; path = Source/ASCollections.h; sourceTree = ""; }; + FC18FC5D497DA4363C26D1C856C7CCC6 /* ASDimension.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASDimension.h; path = Source/Layout/ASDimension.h; sourceTree = ""; }; + FC599DCB7E17E08FE32BF7AA8EE02251 /* ASRunLoopQueue.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASRunLoopQueue.mm; path = Source/ASRunLoopQueue.mm; sourceTree = ""; }; + FCEC18E333AD45AB195BE9A624EE1295 /* ASBatchFetching.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = ASBatchFetching.mm; path = Source/Private/ASBatchFetching.mm; sourceTree = ""; }; + FEF5907A23C18CFE5915CE25B0CC96D8 /* ASTextInput.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASTextInput.h; path = Source/Private/TextExperiment/Component/ASTextInput.h; sourceTree = ""; }; + FF69CF2A36974F2ED05A1A2EAE1F39C3 /* ASDelegateProxy.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASDelegateProxy.h; path = Source/Details/ASDelegateProxy.h; sourceTree = ""; }; + FFDEB71BA6A8CFC522FAF3AE658CC98C /* ASTextNode.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = ASTextNode.h; path = Source/ASTextNode.h; sourceTree = ""; }; + FFE3A850EF05FC7A29E7138C236D6AE0 /* NSIndexSet+ASHelpers.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = "NSIndexSet+ASHelpers.mm"; path = "Source/Details/NSIndexSet+ASHelpers.mm"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 059E3121D15442F422222D8AD47E0686 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 40AE1EFE7405EB78CE80A1AAF989AA95 /* Foundation.framework in Frameworks */, + 6E4E5512E8EC803F48F6D04561EA0A2B /* PINOperation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4944372286DB7CF00171CB0CC5D55B61 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + C782835223517924A26F0EBFEA0C00DF /* AssetsLibrary.framework in Frameworks */, + FE31388AFA377EF837E0D80C74351FD7 /* AVFoundation.framework in Frameworks */, + EB472359A2A67364B3578253C8C4ABC3 /* CoreLocation.framework in Frameworks */, + 8AB6AEE4DF780E7CAA39A698FA4E7634 /* CoreMedia.framework in Frameworks */, + 2E4E16EEF78B38EA1F4977CF1EC63511 /* Foundation.framework in Frameworks */, + DDBE0B9C33CFF36A689113FB2148B8ED /* MapKit.framework in Frameworks */, + B1103DF2D4C2767291DD23DFCEA33024 /* Photos.framework in Frameworks */, + 713DAEE94040383AD0A64FE5E1C6E0D0 /* PINRemoteImage.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 9939403A7E3F97143E9E5DDC5A2CC843 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 9451E51841289C96853640629ED47FE7 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + C6A2093EFA9C0E4742B6F8AF6DCB412D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + F803EDD45DA76E8392546A43BD223D8F /* Accelerate.framework in Frameworks */, + 9A7F2B5821CB7EA9AD72F2FA5B67D152 /* Foundation.framework in Frameworks */, + 8BE298408DCAF8348629CC2FD95B8883 /* ImageIO.framework in Frameworks */, + 743030AC14DFBE803BDE5EC37D25C31C /* PINCache.framework in Frameworks */, + 94E0E3DE8E35A8EE40E20D8A0B253561 /* PINOperation.framework in Frameworks */, + A247EE1D278372734D41DA53BCC96820 /* UIKit.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D7EAA78B2042C124185E2E361B861E50 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + FF7AA3C39BBA2E267FE9EA24603BB7C5 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 0049678DAC1BCA6333BCF228E121A6EE /* Products */ = { + isa = PBXGroup; + children = ( + F46831F4FCE3AB140F12577CE0C2AAB3 /* AsyncDisplayKit.framework */, + 6320170E5A5A268B11CAE15D4EF6A9E3 /* PINCache.framework */, + 22C347595B1851A1E5B0A3762BB18EC9 /* PINOperation.framework */, + 115A4FCB2A03922EC10B633F15359653 /* PINRemoteImage.framework */, + 8D7075DBD5BAA1D6ECBF9309579E9541 /* Pods_Starbucks_Clone_TexBrother.framework */, + ); + name = Products; + sourceTree = ""; + }; + 0A3D2ABC3AD80561D335EF3444F18408 /* Support Files */ = { + isa = PBXGroup; + children = ( + A43F626612EAF6E6960627B69535B850 /* PINRemoteImage.modulemap */, + 510DCE3B48F67D693EF6D5292014D927 /* PINRemoteImage-dummy.m */, + 6721E5BEB6FCC93087D899B00F020D5A /* PINRemoteImage-Info.plist */, + 0B6ED2BB64A6A7DA8BC93530E3F55366 /* PINRemoteImage-umbrella.h */, + BAB31548FC8D5AB50A8EB7217A7712E4 /* PINRemoteImage.debug.xcconfig */, + 773FC3074F118DBE19C9C2A18699AB84 /* PINRemoteImage.release.xcconfig */, + ); + name = "Support Files"; + path = "../Target Support Files/PINRemoteImage"; + sourceTree = ""; + }; + 0D5E7BF27B539B0913146F9F9E012151 /* PINCache */ = { + isa = PBXGroup; + children = ( + 33AA14BD89980AFD2BBC18731D53D264 /* Arc-exception-safe */, + AF01054A076C894E59C4EAC9EF1D23C7 /* Core */, + 7D9B71082E14B346BD914A790DA23BF3 /* Support Files */, + ); + name = PINCache; + path = PINCache; + sourceTree = ""; + }; + 2B9558268D0B572464D0A335F8ED6FF7 /* Pods-Starbucks_Clone_TexBrother */ = { + isa = PBXGroup; + children = ( + 16D53A1A64B75F8F2AA4855E40095652 /* Pods-Starbucks_Clone_TexBrother.modulemap */, + 5A4F9715AFF365EDE24F67C0C708CB04 /* Pods-Starbucks_Clone_TexBrother-acknowledgements.markdown */, + 0C196203FD400F24E5723D12AB860AB6 /* Pods-Starbucks_Clone_TexBrother-acknowledgements.plist */, + FB1326EC0457FCC01BFCF7447226FF78 /* Pods-Starbucks_Clone_TexBrother-dummy.m */, + A15B2911935F13326BE8387085853A39 /* Pods-Starbucks_Clone_TexBrother-frameworks.sh */, + 1E54AC7A652759EEEDBFBE49B12F5921 /* Pods-Starbucks_Clone_TexBrother-Info.plist */, + 07B22A0BED1962C77F9548379F0C548E /* Pods-Starbucks_Clone_TexBrother-umbrella.h */, + 3F5A299D3E06C987FD257CCE00A8C1C2 /* Pods-Starbucks_Clone_TexBrother.debug.xcconfig */, + 97CA4CF3A8DAB8BAF80805672B0FC6FF /* Pods-Starbucks_Clone_TexBrother.release.xcconfig */, + ); + name = "Pods-Starbucks_Clone_TexBrother"; + path = "Target Support Files/Pods-Starbucks_Clone_TexBrother"; + sourceTree = ""; + }; + 33AA14BD89980AFD2BBC18731D53D264 /* Arc-exception-safe */ = { + isa = PBXGroup; + children = ( + CDB9905DE100C45E321FF681B0BE5D74 /* PINDiskCache.m */, + ); + name = "Arc-exception-safe"; + sourceTree = ""; + }; + 4943E861C9E9B41168E44698FBDDC1F3 /* Texture */ = { + isa = PBXGroup; + children = ( + 6017EA1C33D6C52EF916F53FA143DD2D /* Core */, + 5791E39C24AEAEC8AC6C161D7091624D /* Support Files */, + ); + name = Texture; + path = Texture; + sourceTree = ""; + }; + 5791E39C24AEAEC8AC6C161D7091624D /* Support Files */ = { + isa = PBXGroup; + children = ( + 5C4AA3335C622751A32A744499AF7CE2 /* Texture.modulemap */, + 805F38459176CFBF7BFE070EAE17FF09 /* Texture-dummy.m */, + C9C467665994B887574F02A84A9F4800 /* Texture-Info.plist */, + 635A8DB9ABD3EE0FFC5E3308011CDF36 /* Texture-prefix.pch */, + 6FEDA9949DABCA4821DDDD0414408B22 /* Texture-umbrella.h */, + 14D1789AFF19612903A6DEC3719662F3 /* Texture.debug.xcconfig */, + 7DBE419CB2F290B559A7011979017C7D /* Texture.release.xcconfig */, + ); + name = "Support Files"; + path = "../Target Support Files/Texture"; + sourceTree = ""; + }; + 6017EA1C33D6C52EF916F53FA143DD2D /* Core */ = { + isa = PBXGroup; + children = ( + 0E5DAB6AE58136BEE1249F6A61EA5D30 /* _ASAsyncTransaction.h */, + E32EFE7E28F3D30B3ADE45D68C01B4E5 /* _ASAsyncTransaction.mm */, + 23917528E76A1002E1E03A86CA321667 /* _ASAsyncTransactionContainer.h */, + 4564E9382AAB98E6E981F0E4F84CA037 /* _ASAsyncTransactionContainer.mm */, + E24B74CBEFA4CF9264A81572FD614BD2 /* _ASAsyncTransactionContainer+Private.h */, + B50CED0243E158B6FB3AF02FA0EED408 /* _ASAsyncTransactionGroup.h */, + E6AA6582F46D7A6021F34A9D7019F1AF /* _ASAsyncTransactionGroup.mm */, + 3398193884C73127717DAD2DD429D1DD /* _ASCollectionGalleryLayoutInfo.h */, + F9BACA46ECE4EB7466790ED17B5796D4 /* _ASCollectionGalleryLayoutInfo.mm */, + 3A01E0F76B1D746C5AB7D11D9AE6CC77 /* _ASCollectionGalleryLayoutItem.h */, + 6F445AA2EBEFA2D30FF270056C1BC783 /* _ASCollectionGalleryLayoutItem.mm */, + DEC0BF308170B3621109986E67AEFA82 /* _ASCollectionReusableView.h */, + A36A6CCC10752056AAA5047945986152 /* _ASCollectionReusableView.mm */, + C741926C4CC8BF0802B8539AC2697545 /* _ASCollectionViewCell.h */, + E61F401F1879DA3A4E7851A9673CD3D1 /* _ASCollectionViewCell.mm */, + A93DD47CE966A545A55866BCC8AD2658 /* _ASCoreAnimationExtras.h */, + EA00CE5CCA312A7A77C7D868AA3AC38B /* _ASCoreAnimationExtras.mm */, + A84EC65C2CB86EEA0D6C180F5830CBB5 /* _ASDisplayLayer.h */, + ABB03AA3087EA9FCC208E03B28E09935 /* _ASDisplayLayer.mm */, + EFEF3AB75FB8E5BA99A80A1B87EF0E3C /* _ASDisplayView.h */, + DE5B18F6F58E950EFC83A61B4A825175 /* _ASDisplayView.mm */, + CD86773D0E02DC18396836AFE7DC3918 /* _ASDisplayViewAccessiblity.h */, + 5660A8B0F1FF8E3463CBC35A8A848AED /* _ASDisplayViewAccessiblity.mm */, + 8BC18D4DB7AA56688D0AF9BDDE7BE8F5 /* _ASHierarchyChangeSet.h */, + 0C9E1840C6E647023E453E49A57E0363 /* _ASHierarchyChangeSet.mm */, + 4A8DF6DD2A4E0F0933CDAC8EBDA67FE3 /* _ASPendingState.h */, + B67FEF8CB94D5E7658A7016E7DE658F4 /* _ASPendingState.mm */, + 752F0516FE00425919A33CB5BD264BD3 /* _ASScopeTimer.h */, + 775C7D276B9BD925498D2CC333EA1BC5 /* _ASTransitionContext.h */, + B9B56E35637395BD7F69E4B7F8F55DBB /* _ASTransitionContext.mm */, + AC3B95E05FDC42AC4D0011ED60ED02AB /* ASAbsoluteLayoutElement.h */, + FB549CF897CE658795874AA1539B3E00 /* ASAbsoluteLayoutSpec.h */, + E69484760E2878ADDAF63DEB7003DCDC /* ASAbsoluteLayoutSpec.mm */, + B458A01741A678DD40054F964CE52EA9 /* ASAbstractLayoutController.h */, + 5201496769F9BC60F76B9A95B07E8C98 /* ASAbstractLayoutController.mm */, + 788A4CA5542DDF6AEDF0958D1F65FA8D /* ASAbstractLayoutController+FrameworkPrivate.h */, + E152F08E24DF7356E972205CE1FE4D24 /* ASAsciiArtBoxCreator.h */, + 201D42F70CB101B7CC761B365CB0B2F5 /* ASAsciiArtBoxCreator.mm */, + 1C818C0167A1B3B83CE80B6B6A31AB4A /* ASAssert.h */, + 3FD8FFBA2AD3C6F7C490C86420605F17 /* ASAssert.mm */, + 52DA18796B6E521C4C76474566901974 /* ASAvailability.h */, + 915A108BCEEDF6DED44581345ADD5FF6 /* ASBackgroundLayoutSpec.h */, + 0F5C33BA4E88B0B326DFFE9C5340A1E7 /* ASBackgroundLayoutSpec.mm */, + 72A548F3BBC7390DE95BCD4F2A6954DD /* ASBaseDefines.h */, + 8099386989773A568E77145C42CD586F /* ASBasicImageDownloader.h */, + 74ED204CF8F96D2B3B75CB7545B09E1B /* ASBasicImageDownloader.mm */, + BDDB697D7302D7165178BFC76A053780 /* ASBasicImageDownloaderInternal.h */, + 4E127E8EB3DF48F7A91A9E5934571535 /* ASBatchContext.h */, + A56D0ECA9EBC62BE680E0D42D7FAB7AF /* ASBatchContext.mm */, + F1CE60DA44D9B7BDC09E8B75F5D86A7A /* ASBatchFetching.h */, + FCEC18E333AD45AB195BE9A624EE1295 /* ASBatchFetching.mm */, + E94C2962BB22E3F85E5F03567F0FE0CF /* ASBatchFetchingDelegate.h */, + 5E2C06029EF723FC9CB33F253B588801 /* ASBlockTypes.h */, + 5629107AFA4448F2FFFA6ABEDA650A40 /* ASButtonNode.h */, + 6B20C8FA45310FF28361CEA25BCB0737 /* ASButtonNode.mm */, + 21BC3B3B36ADEE1506968B5996F97C34 /* ASButtonNode+Private.h */, + 56EDE5D7E4A5C38FBC7FE5EC9D8A1FA4 /* ASButtonNode+Yoga.h */, + 07E4F833BCFBD185B878CF4D918216C2 /* ASButtonNode+Yoga.mm */, + 643EE8E9B40DC4CB13D65B7243E995B2 /* ASCellNode.h */, + F724F08CB726907AD6A2696B44D3A849 /* ASCellNode.mm */, + 6CF7BC3A95218CA6803E27F02C16AA10 /* ASCellNode+Internal.h */, + 792A0180DFD7045FE65D98BB0892B376 /* ASCenterLayoutSpec.h */, + 8A485B78C571482DA19EFA8BD850DB02 /* ASCenterLayoutSpec.mm */, + FB4422D6FE213AD611123E8A5347E60E /* ASCollectionElement.h */, + A76D4E87369EBDA42913A7AAEFBF00D6 /* ASCollectionElement.mm */, + 5F272F44E17A59928BA3C19A72C1CE96 /* ASCollectionFlowLayoutDelegate.h */, + B377086AE4B7CBACB4F4B5FF68BCDBC7 /* ASCollectionFlowLayoutDelegate.mm */, + 01BB7760D1A5D00C917D4DA5CDCCAA59 /* ASCollectionGalleryLayoutDelegate.h */, + 99B6328B4EE826FA54866D56A088FE88 /* ASCollectionGalleryLayoutDelegate.mm */, + 1A499BBBC19F5F5CEDF35EB494657568 /* ASCollectionInternal.h */, + B7E5025469DC385B5D9CB4C22BE358CF /* ASCollectionLayout.h */, + F084236284192AB0CB2F19F765DD52D1 /* ASCollectionLayout.mm */, + 1035A7ACAFD6A65A4A3A01D8EC90E0B1 /* ASCollectionLayoutCache.h */, + 98B0E4F19691D2F4131FF50AFA1071E1 /* ASCollectionLayoutCache.mm */, + A03C7EAEEBD03FBE27B384992167A313 /* ASCollectionLayoutContext.h */, + 32EAD7F4F3087C11BE2869340DFF33EC /* ASCollectionLayoutContext.mm */, + D1B206A8F349D1A6BA64F7B57B7CC17A /* ASCollectionLayoutContext+Private.h */, + 8983A1BC34984FEE903C32F947A4A11E /* ASCollectionLayoutDefines.h */, + AC2577D0AE6AFBA55F004F4BE81ADCCF /* ASCollectionLayoutDefines.mm */, + D5A164E3FB065BB3632339162B9844A4 /* ASCollectionLayoutDelegate.h */, + 29BED58535AE1ADA682A705FB50653CC /* ASCollectionLayoutState.h */, + DA4FF234AD32C005D1BC2803290984C7 /* ASCollectionLayoutState.mm */, + 4C54EEEA6CDF354C39D11F1217C41EF7 /* ASCollectionLayoutState+Private.h */, + 1815DDF16A824E46B244423DC105FCC8 /* ASCollectionNode.h */, + BD92916D1ECF51A2D58AC7BF0A89F357 /* ASCollectionNode.mm */, + 48FFF4989571E03EB9AAD6897C4BCB4F /* ASCollectionNode+Beta.h */, + FBD8D238CA37E2247A47BEC575B4E643 /* ASCollections.h */, + D389403EE0D912CAB00411EEEBBF5284 /* ASCollections.mm */, + 6C96AD30B4BB4561311AF7FC108E3A40 /* ASCollectionView.h */, + A17F214DF82A4422342061B698A3F309 /* ASCollectionView.mm */, + D95B3764BC290B9DB81E76736CDC7766 /* ASCollectionView+Undeprecated.h */, + 267B1C8B013F93279B71315BB29360E2 /* ASCollectionViewFlowLayoutInspector.h */, + BD665D50848207C21A9980329097B251 /* ASCollectionViewFlowLayoutInspector.mm */, + B6477497D4580620434C98CDF8C40318 /* ASCollectionViewLayoutController.h */, + 9E3FC7BE9409137DCC7A948ECD5695F8 /* ASCollectionViewLayoutController.mm */, + 078A70C4529DBC3C4A754417C5FBDBBE /* ASCollectionViewLayoutFacilitatorProtocol.h */, + A29BC347D15906796EAF09C899F0BAA9 /* ASCollectionViewLayoutInspector.h */, + 1F16EC75D41F03D3E305917F4D03604C /* ASCollectionViewLayoutInspector.mm */, + B82A5D010BD4DA75A6D18C51A793149F /* ASCollectionViewProtocols.h */, + B30F52EC317262D450BB71ABDF384A75 /* ASConfiguration.h */, + C58EA3FBFBA1476CBC65553056DC50C2 /* ASConfiguration.mm */, + 064F70BA9938BBEF7CC08DF883950E3E /* ASConfigurationDelegate.h */, + CECE4ED7AF67F941DEB6F0EEFDE8052D /* ASConfigurationInternal.h */, + 3B40FBAEC3D9F250BE499DB7C96477FC /* ASConfigurationInternal.mm */, + 14F32F69FDA6EE64E812A50AA22499CC /* ASContextTransitioning.h */, + D386EE9C7174CAC93AE4EFAFD2F8D52B /* ASControlNode.h */, + 3874B0234F310FC6D5CC02FC07D177D7 /* ASControlNode.mm */, + 2D67B7BE32F2F7AC2A24618DF273D975 /* ASControlNode+Private.h */, + 75268E97C57C22192F3B263283CA2C79 /* ASControlNode+Subclasses.h */, + D8697D8727A9A3453A287EBBD21C6AF1 /* ASControlNode+tvOS.mm */, + D6FC0AB66F5BC4327C141E396044BC18 /* ASControlTargetAction.h */, + 29D339A04F28E55C76040CCF288282BB /* ASControlTargetAction.mm */, + 7022AFFE25E02BB59DF601176BCECFAB /* ASCornerLayoutSpec.h */, + 32B6100A963D38523A8CA0A26B5F86B9 /* ASCornerLayoutSpec.mm */, + DCFAB58A9BA38E8EE152DE448425B204 /* ASDataController.h */, + B385F44C418B5E657B7816193A169A10 /* ASDataController.mm */, + 40738CE7558B636E714D9A7DA06CAE94 /* ASDefaultPlaybackButton.h */, + C3D08FDB734E574133896549B7B0E4F9 /* ASDefaultPlaybackButton.mm */, + 01E0794C0798D247DA040090A445797A /* ASDefaultPlayButton.h */, + E903B227DE544FEBB3869D8A7D5D3F64 /* ASDefaultPlayButton.mm */, + FF69CF2A36974F2ED05A1A2EAE1F39C3 /* ASDelegateProxy.h */, + 3F292EB56A7A5F9E8775B851D97743C0 /* ASDelegateProxy.mm */, + FC18FC5D497DA4363C26D1C856C7CCC6 /* ASDimension.h */, + 7BA62A47FF53155919AA67CBCC0B126A /* ASDimension.mm */, + 7EB194E21CA3FFC96FFF3C80B1C91A86 /* ASDimensionInternal.h */, + D84FBBB202FB56A503EA21E03778DED7 /* ASDimensionInternal.mm */, + 162A1D688BC1738E7F0A6348E168A3D5 /* ASDispatch.h */, + 11B6DB33ACFDC65734A7C2E1F760BD60 /* ASDispatch.mm */, + D6832FF8EE7BE44EEE48E868181CF012 /* ASDisplayNode.h */, + 921D71A9998F7251DCD498356DAFC992 /* ASDisplayNode.mm */, + 843FA0564BEA1819D72A52497B191159 /* ASDisplayNode+Ancestry.h */, + 12E1DD8F58F53738EFB3B9BEA7E0CB09 /* ASDisplayNode+Ancestry.mm */, + 747E4F88A2242E485C0C2A1D600F72CD /* ASDisplayNode+AsyncDisplay.mm */, + 672BD6EE10498EAC204A79FF07840EAE /* ASDisplayNode+Beta.h */, + A1666DABB81FF91AB2854A6FFA58794D /* ASDisplayNode+Convenience.h */, + 2EF7DC57329ABA4B85EDFE5DC03BC0A8 /* ASDisplayNode+Convenience.mm */, + 3E1A5494636B0CDA8A4709C8682A52C1 /* ASDisplayNode+DebugTiming.h */, + B22CAC88C961169FA20051DE8BDF0B27 /* ASDisplayNode+DebugTiming.mm */, + D3F73B5CE261EAA709DE2073A342D910 /* ASDisplayNode+FrameworkPrivate.h */, + 1A150AD21C6C6AEB743E18176E75D78F /* ASDisplayNode+InterfaceState.h */, + 3BAB9C702191043B5AD931A92E4B8B03 /* ASDisplayNode+Layout.mm */, + 16E3E7B6DDAAEE84D6A482759EDC819C /* ASDisplayNode+LayoutSpec.h */, + 0628D2361BC4A2D2C737966B84D1338B /* ASDisplayNode+LayoutSpec.mm */, + 93851EFA588262FF22F2E4C428862442 /* ASDisplayNode+Subclasses.h */, + DABECC346A0C5E457DD875465E22622F /* ASDisplayNode+UIViewBridge.mm */, + 486B3CCA03BCC69D86F973E81F23F54B /* ASDisplayNode+Yoga.h */, + 49577A6CB89BA0AB509E6CF764ABDE61 /* ASDisplayNode+Yoga.mm */, + 0CA558CE62A460B44AFC3565ADFCAEE5 /* ASDisplayNodeCornerLayerDelegate.h */, + CAE35B3FA8348440C30B17D645FADAB6 /* ASDisplayNodeCornerLayerDelegate.mm */, + 9E7888C7A1AA9B642609F19D11AF414E /* ASDisplayNodeExtras.h */, + 1D23AC0B7A0FABCDD58FEABD77522221 /* ASDisplayNodeExtras.mm */, + 127F7C3D321A6445E2628FC920109B38 /* ASDisplayNodeInternal.h */, + 291A0CC83913FFE45EFDE20041E3B489 /* ASDisplayNodeLayout.h */, + F1807082404C3DD7326F3A2E7E75B56E /* ASDisplayNodeTipState.h */, + D25B4227847397852787D1EF3F3C6FC6 /* ASDisplayNodeTipState.mm */, + ECE920DFD35FE6C0A2D054EC3E972D12 /* ASDKViewController.h */, + 00A4E1BB5E6F3722E8E36E1169E8D092 /* ASDKViewController.mm */, + BC54F1AC79DB2F89235AD3AAAE4E8794 /* ASEditableTextNode.h */, + CD6D8F0076FAC6559F82C7EC55D8786C /* ASEditableTextNode.mm */, + D18CF219AFB6A8D799381B345E84B2CD /* ASElementMap.h */, + C2037A0F5BAE21EEAF3BF0C23B362004 /* ASElementMap.mm */, + 0C18C9A1EB2B7BEFDEF4D696077F511A /* ASEqualityHelpers.h */, + 62310F0C73D1A8CA99D47886AE688B73 /* ASExperimentalFeatures.h */, + 968B6A7ECF2BBF3568943F0D1F239684 /* ASExperimentalFeatures.mm */, + C8C2EE7A63C346D06A35520A77DE19AC /* ASGraphicsContext.h */, + 2D1C7EFB62BD6E8103714DB24F4ED389 /* ASGraphicsContext.mm */, + 1C169176950FBC99FAAA05DFEBAE3D6F /* ASHashing.h */, + 8ECD3CA83D8BA42AF9306FEC62CA06BD /* ASHashing.mm */, + 0360DEFA13C4B085B68FB9C58F5C351D /* ASHighlightOverlayLayer.h */, + 58139A95B4BA8249B925855915F54E9E /* ASHighlightOverlayLayer.mm */, + 54765F44027C7F1F4A283E5D8F1F57E8 /* ASIGListAdapterBasedDataSource.h */, + F3E3F88611CECB60D3DE4ABAE592D898 /* ASIGListAdapterBasedDataSource.mm */, + 60ECE226961E3317572472B9C7866939 /* ASImageContainerProtocolCategories.h */, + EDDFB203A3FA480E8F255B7E49928E6B /* ASImageContainerProtocolCategories.mm */, + 4A13BF4119E0C2327A37ADD306A7F280 /* ASImageNode.h */, + 9F1507A2B71A82D68CA7D8D4B300BB40 /* ASImageNode.mm */, + B1B5C512945B8108FC01D7902FB0888F /* ASImageNode+AnimatedImage.mm */, + A4C06B180E1A14EA1EC52F498B086A88 /* ASImageNode+AnimatedImagePrivate.h */, + D9518F8951D785FB5E83392FF5EEF900 /* ASImageNode+CGExtras.h */, + 9B3C2A862F2283BFC0EABB764DF58298 /* ASImageNode+CGExtras.mm */, + 65E45EB810D4BBF9D33169EC48A815F5 /* ASImageNode+Private.h */, + 047916BEFC3829F9505A8CE2D37A5155 /* ASImageNode+tvOS.mm */, + 4BADB70D6F6D85A658168553A700DC9F /* ASImageProtocols.h */, + 080BF99D1FB1C1DD10745210E4C324F4 /* ASInsetLayoutSpec.h */, + 74086F595373CAC88B0BF99A2E21A0D5 /* ASInsetLayoutSpec.mm */, + 1BFFEC4682ED9A6C582741ABB22FFFD2 /* ASIntegerMap.h */, + 619DC215E1E24600C9B8C65AE0F6C571 /* ASIntegerMap.mm */, + EC4BC1F8BC97094F5292F0627A230B03 /* ASInternalHelpers.h */, + 288A70F6746F2D6740CA394D3B1AC339 /* ASInternalHelpers.mm */, + DE1EEC4327E7157183B75E4178082C91 /* ASLayerBackingTipProvider.h */, + 88CCC3ACF89F4533FFBBD74CFC1806D4 /* ASLayerBackingTipProvider.mm */, + 3F391532C2485B229B7BDE74FF398D41 /* ASLayout.h */, + 0127E215BAC572610DF54FD21EBFB99E /* ASLayout.mm */, + 07BAF72F98DCBECFFC2B7F3A0FB2D752 /* ASLayout+IGListDiffKit.h */, + 2FD43643551686EF122ADF4DF1821D39 /* ASLayout+IGListDiffKit.mm */, + D0C20EDEB4D6EE05F48F1581955AD35A /* ASLayoutController.h */, + 679CF8A5C03B3322E8CADABB7DF4748B /* ASLayoutElement.h */, + 2061A3DD3D85D7601069EE51F296B793 /* ASLayoutElement.mm */, + 694E56C9CFBEB7AE6FFD9B45B846993E /* ASLayoutElementExtensibility.h */, + E834741D474E0658F5747909301B10F7 /* ASLayoutElementPrivate.h */, + 0F909487B055776AE89A8FBB70923EBF /* ASLayoutElementStylePrivate.h */, + A8E85E8B5E162541ED900C4EE3F74124 /* ASLayoutManager.h */, + A7F0BEBBD6016E8CEB75E14D708BC006 /* ASLayoutManager.mm */, + 2B52E76F0416EEE47A995D0B1AB82ADD /* ASLayoutRangeType.h */, + F6FEF6FE7DB7A8E54D5AE8F933CF1464 /* ASLayoutSpec.h */, + 09C578D28C9AD6E160C72FF044636EBA /* ASLayoutSpec.mm */, + 661EC3CE84810954586C6607786653F5 /* ASLayoutSpec+Subclasses.h */, + 55BE5B0739B7FB893F77CCAF0101D9D7 /* ASLayoutSpec+Subclasses.mm */, + A8F705E5590C234FD939D87CED55C5ED /* ASLayoutSpecPrivate.h */, + 372E3752496C2C26F7D26610EACB5069 /* ASLayoutSpecUtilities.h */, + 5BC73143B044118E42D111C5B89F8746 /* ASLayoutTransition.h */, + A80CA8D6A0D34301B629FB11D8686CEA /* ASLayoutTransition.mm */, + 0F518543A74B628F00ECD15DB5F7445D /* ASLocking.h */, + 621CFE6D1995E55EC068DC672E84F361 /* ASLog.h */, + 6276DF790DF6B5D12814A3A4942DEDD9 /* ASLog.mm */, + 1001B956AE4E0107CB49144235F7263C /* ASMainSerialQueue.h */, + 5D04F1BC6559A047934F57FEE0D842D0 /* ASMainSerialQueue.mm */, + A95018FAB6DF136289250129CA16934B /* ASMainThreadDeallocation.h */, + 74E4E43E31FFF2CF7046E6A3658D79E5 /* ASMainThreadDeallocation.mm */, + AFB06A665A86D968044ED349FC6E2817 /* ASMapNode.h */, + 098ADDB3AB7C97CB73062B784B1F66FA /* ASMapNode.mm */, + 75CD9ED9F22C2799B7BCA77BCFA90398 /* ASMultiplexImageNode.h */, + CA2861F5E52D93186A2AADFF0825C4ED /* ASMultiplexImageNode.mm */, + B16AA9D7833946948169CE0EAA7B4B5D /* ASMutableAttributedStringBuilder.h */, + C9771B7F3EA9E4860D1D432EC3FF9820 /* ASMutableAttributedStringBuilder.mm */, + 5BDA6758A4F44A2423F3319C96BBB2EC /* ASMutableElementMap.h */, + B3A200415E395C61CA67F56E33FC809D /* ASMutableElementMap.mm */, + 0706408BB07355DB8AE83E0ED628FE32 /* ASNavigationController.h */, + F5EC4250B2AF97E5233012DE973EC2C6 /* ASNavigationController.mm */, + 9006140792ABAA03CFF2A94DD61C060C /* ASNetworkImageLoadInfo.h */, + 7EC860805746F5FCE75F3EDC2AAAA287 /* ASNetworkImageLoadInfo.mm */, + E1A4DEAF97B792E35B974180DAB5AAEE /* ASNetworkImageLoadInfo+Private.h */, + 15B25876E76F0B07FDF260C40B4307AA /* ASNetworkImageNode.h */, + 4932F584069CBF2BCED5E998CD893E9E /* ASNetworkImageNode.mm */, + 0000FA49664A808C778E0664EAEBF541 /* ASNodeController+Beta.h */, + B3E823C5AED7466242446DADE710BC14 /* ASNodeController+Beta.mm */, + 57CFF13E6A7FCF5F26B96D9E52B50118 /* ASObjectDescriptionHelpers.h */, + CC9924E9040C5454AC24BC42A84D6E24 /* ASObjectDescriptionHelpers.mm */, + F975825068D5F553799FAEC02F007206 /* ASOverlayLayoutSpec.h */, + 1DDEB70D394FE11270864A3A83BD36BB /* ASOverlayLayoutSpec.mm */, + CFD1BD03E8A08531D5ED8002B0FE1376 /* ASPagerFlowLayout.h */, + 76F307F7E0F3AB64C1D9E973EEFC055A /* ASPagerFlowLayout.mm */, + 607EDA20ACB1995FE512E34150F222F1 /* ASPagerNode.h */, + CF4EE8C8467B3FC8306891C7E48F70C3 /* ASPagerNode.mm */, + 1E2AF6899A8D865AE83A80BE3854525A /* ASPagerNode+Beta.h */, + E8739155AFF4384DA7B122A73A82B396 /* ASPageTable.h */, + 1FF8C31089C5323E97CBF2EE23C48598 /* ASPageTable.mm */, + 5E78560F78CB8BFA4466E9D41BC5176F /* ASPendingStateController.h */, + AFEEAA3B4F1296937A0BEE268BAF05EF /* ASPendingStateController.mm */, + 8BC32B2CA262B735E3055D0089917569 /* ASPhotosFrameworkImageRequest.h */, + 1A99F1EE1BA8F78FE1D179D4F10990E9 /* ASPhotosFrameworkImageRequest.mm */, + 7A84C6F510AF68CC4BBA2FC5DDE5E0B2 /* ASPINRemoteImageDownloader.h */, + 53F5101FDA12B83E1AF2EE12B127639E /* ASPINRemoteImageDownloader.mm */, + BA7E4F3BDA84380743FAF975B1665227 /* ASRangeController.h */, + D4A2BD0267EFCC67E4D6E293788CBD3B /* ASRangeController.mm */, + 29374D2518720868B57430A0CD71E8C5 /* ASRangeControllerUpdateRangeProtocol+Beta.h */, + 61864DC4D9246D85E128A5C4310F92FA /* ASRangeManagingNode.h */, + 4B6A6B6008B8473CDF79B3962055B2B5 /* ASRatioLayoutSpec.h */, + 9BB90969D06357DC3F9419D00171EFDD /* ASRatioLayoutSpec.mm */, + 97E7FC319F436F0B423EE720BD4114C4 /* ASRecursiveUnfairLock.h */, + 0F524724237EF9AD3AEF9016F5121229 /* ASRecursiveUnfairLock.mm */, + 443107AB83C34C89719D607406B3DE57 /* ASRelativeLayoutSpec.h */, + C04A40D87995160C194B1F420E82E654 /* ASRelativeLayoutSpec.mm */, + 7D2C56C4B3966B04388D70C04AFA6A27 /* ASResponderChainEnumerator.h */, + 266F28006F8FB035EF18766D7D1D46B8 /* ASResponderChainEnumerator.mm */, + 87944CB6253052129AC860AB6A056454 /* ASRunLoopQueue.h */, + FC599DCB7E17E08FE32BF7AA8EE02251 /* ASRunLoopQueue.mm */, + 5CF0B8B8B3B7611BFE4FACBD177FAFE9 /* ASScrollDirection.h */, + D78004E3854FF269F6C7E6E386DA49C4 /* ASScrollDirection.mm */, + 2500B92E1BDED42BF2C222E4683F6452 /* ASScrollNode.h */, + 349D854652C9440A9E0A24C485F4ACA4 /* ASScrollNode.mm */, + 22E5CA9609D7B764C9AD5E3E29A99198 /* ASSection.h */, + C3748C4FDDDB81BD2D1F20D30668477D /* ASSection.mm */, + EBE62C6CE8D397ED68FF4E791DA4CB38 /* ASSectionContext.h */, + BCE8409AD4EB33E423BAD5BB2C25137D /* ASSectionController.h */, + D458980EAD9CDDC12A46DBFF63A4EBF5 /* ASSignpost.h */, + 4FC69BD5F886F0FD2ACA5CE367FA5C9A /* ASStackLayoutDefines.h */, + 685D1E26BFDFC90E7B8DDC3D5A043BE8 /* ASStackLayoutElement.h */, + 78EB5CB83EA4EACE5C49BA7F2950ECA9 /* ASStackLayoutSpec.h */, + 2680C9F6C531597EADA4A0063E0F309C /* ASStackLayoutSpec.mm */, + EF12EEA02B309EF99CDA1C4BB3D8D136 /* ASStackLayoutSpecUtilities.h */, + A1C208163829F8088B47F961509583E3 /* ASStackPositionedLayout.h */, + B11E2C1306253F769EADB97E211BF625 /* ASStackPositionedLayout.mm */, + 1EB5C446879B3933F3EAB546FE597E8F /* ASStackUnpositionedLayout.h */, + B5621F42157B08B48920F0A50B3222AF /* ASStackUnpositionedLayout.mm */, + 274DE44949B99702678B3A0AD3A3FD1A /* ASSupplementaryNodeSource.h */, + 0CB053AFA33FB3BE65B1DDD1F6796D07 /* ASTabBarController.h */, + BB6C6201DDCE0BC37AB907FDD59EE119 /* ASTabBarController.mm */, + 25E255B9F966777064C6C1EE2E74C72E /* ASTableLayoutController.h */, + EAFE043D342B5CB9C48DEF9F7A994F87 /* ASTableLayoutController.mm */, + D6820D90344C7012FD9DAF1CE99185E8 /* ASTableNode.h */, + B757AC50585C6BAFCEB05ED2E0A2262D /* ASTableNode.mm */, + D47F9321D282478772005EA04CBDB68C /* ASTableNode+Beta.h */, + 8C20EDFA6F12C47AF4639E4CB4A75E8B /* ASTableView.h */, + 37A923F0EEA3DE37175030DA432C7E9D /* ASTableView.mm */, + BB705D337F09F90307592D19183A1EFF /* ASTableView+Undeprecated.h */, + 832F7E0ACF60B901E15D5BA77F9D1DAD /* ASTableViewInternal.h */, + 6D14125F3A9F2F63DAD6311079217C87 /* ASTableViewProtocols.h */, + C75D246C46658AF6540D5F980D67A83C /* ASTextAttribute.h */, + 965D92DE5A7AB53870ABDDD9BB71EA89 /* ASTextAttribute.mm */, + EB36743EBAA4B10A57C67D256B097261 /* ASTextDebugOption.h */, + 9D63CE7677F2708E6EFB6CCFFAC34952 /* ASTextDebugOption.mm */, + FEF5907A23C18CFE5915CE25B0CC96D8 /* ASTextInput.h */, + 376A2AC2E4CDCDE7BDB0A9D99DA37170 /* ASTextInput.mm */, + 569D8D9DDDC854B8EC673ADB1025D26A /* ASTextKitAttributes.h */, + D51AF6284504A2AF4A3CCCAEABC19559 /* ASTextKitAttributes.mm */, + B8AA8DB0A48C9AC29F30CC40A2173133 /* ASTextKitComponents.h */, + 44980E879FE0D7AEAF373DE0F2B5BEFA /* ASTextKitComponents.mm */, + 99F1BC718C4DBB8C83F06F0D5E20460F /* ASTextKitContext.h */, + BA640A1E51676DE0AC8A2AE86C35FDB2 /* ASTextKitContext.mm */, + 563AA9E4BC576EF280FB0C0F91212620 /* ASTextKitCoreTextAdditions.h */, + 90AF45C081400459AA7B398DDFAE38FB /* ASTextKitCoreTextAdditions.mm */, + 4D40CE701750D8006DA724FDDD8DF487 /* ASTextKitEntityAttribute.h */, + C0464695A56E068F9EB148E0B4515AA0 /* ASTextKitEntityAttribute.mm */, + 443390523E8A73440A7191BA0094872A /* ASTextKitFontSizeAdjuster.h */, + 29E0253CCA000E3426448E4D79278C91 /* ASTextKitFontSizeAdjuster.mm */, + 60A4C17A948E6BF765E2CD491916B631 /* ASTextKitRenderer.h */, + 26987C59CE8FC7CBB385300C0F6BDA5A /* ASTextKitRenderer.mm */, + 12CA065AACA31E8F43AE72E67FCB0FFC /* ASTextKitRenderer+Positioning.h */, + 00869DEB91DC50D648121592BAF044C1 /* ASTextKitRenderer+Positioning.mm */, + A3035FDC9B0F6D11945C34895FCBF6E6 /* ASTextKitRenderer+TextChecking.h */, + 00E907AFDB4D6D82FD9F4128D2C4B8DE /* ASTextKitRenderer+TextChecking.mm */, + A0CDEAD7A3000E9E2DD98E6D92AD2D21 /* ASTextKitShadower.h */, + E4D42106BAAABAE5142D533B57E44149 /* ASTextKitShadower.mm */, + F90035D014D493146733CA992C815AAF /* ASTextKitTailTruncater.h */, + F9ADEDE2E6CB82FA634260F9B98A6B6C /* ASTextKitTailTruncater.mm */, + FA09E94B1CC54AF0EAE0D3CD263B0812 /* ASTextKitTruncating.h */, + 99139531ABD4081B9313BB193B8F3C82 /* ASTextLayout.h */, + 18AC72A19500327987AC0AD7EE4F486A /* ASTextLayout.mm */, + 045B4CB141E1430CE34D42817E360277 /* ASTextLine.h */, + 9318386970E3A7089D8AE292D4EEDE6E /* ASTextLine.mm */, + FFDEB71BA6A8CFC522FAF3AE658CC98C /* ASTextNode.h */, + 7CEC8210F1769BD7D7B357072E7E3B82 /* ASTextNode.mm */, + 6C9A4DBE7D41A766936D3801A3B99271 /* ASTextNode+Beta.h */, + 1403D90CF17962F5D5818587DA0652FE /* ASTextNode2.h */, + 99443CE1B364268627439CDB10A3ACE7 /* ASTextNode2.mm */, + 5297B2854BA055337B6A353F4581E97B /* ASTextNodeCommon.h */, + C0491CD11ADA6A67C64D323A6BB11078 /* ASTextNodeTypes.h */, + AF598AFD0BCBA155CCD60D60E7682662 /* ASTextNodeWordKerner.h */, + 67E87CE9FECF81BF0881DD56B7DF780A /* ASTextNodeWordKerner.mm */, + DC9BA969B57D2D800557EE536EB1DC14 /* ASTextRunDelegate.h */, + 039E32F34E01B17F98C04D3C94E2E219 /* ASTextRunDelegate.mm */, + BFA9545ACC55BD7967322ACA3CF6ABDA /* ASTextUtilities.h */, + 19B3CE9F567E5E2BA615C5C6E5E7EB95 /* ASTextUtilities.mm */, + 58FA6C5A9C4C19E011EB63F340365513 /* ASThread.h */, + B1025114DDE3DD7D1CA463AA1CA551BD /* ASTip.h */, + 53E2113FBB2C3C1025FA111A09D9705D /* ASTip.mm */, + 2E3B81403D77240183D2690D35A9E173 /* ASTipNode.h */, + EF0D927D178E9A955F76E23A8D12530C /* ASTipNode.mm */, + A87349F24336B636FD7357BBD1C1F074 /* ASTipProvider.h */, + ABBC5A4E8EBEC559EF2D0DBE16D3C66F /* ASTipProvider.mm */, + E801FD290C13CFBB6EECB86EAEDD14A2 /* ASTipsController.h */, + E51E2FEACF14FE9C9B1FCAC9532F4D3E /* ASTipsController.mm */, + 63178711D4323569A1B61319677AA577 /* ASTipsWindow.h */, + 2DF50D3C1F52EB1D87263AFEF84D691D /* ASTipsWindow.mm */, + AA33C1BA9625FB1059A64D05E85E1075 /* ASTraitCollection.h */, + 0931C3BD6812CC5847438D3AB46BED25 /* ASTraitCollection.mm */, + 327F3986E5C333ACAB05D4743613E811 /* ASTwoDimensionalArrayUtils.h */, + 070352748F86CF00AF87F8EB9D2DE7E5 /* ASTwoDimensionalArrayUtils.mm */, + EA2BDC742161E6067502856B9E2FDAFF /* ASVideoNode.h */, + 3E57C58291CEC254C5C79E173BBAA35F /* ASVideoNode.mm */, + A9AE994038F0C350DEBB06A25EADFC8E /* ASVideoPlayerNode.h */, + 5C206ACAAB3B78082D7C88805B4938C1 /* ASVideoPlayerNode.mm */, + 87065E0D5E1E5ED7C51879E8AE1C6F41 /* ASVisibilityProtocols.h */, + D3C2B5E2A1BCEF6ED01C9280FD3A6C30 /* ASVisibilityProtocols.mm */, + C8EEA80259BB6314337EE232E2AF34F7 /* ASWeakMap.h */, + DD7D7ECC692E89521F0E4656EC06ADA5 /* ASWeakMap.mm */, + C92F34A09B8C59BACD85A16B2D11AAF6 /* ASWeakProxy.h */, + 4774FBDD20637C9AC144C7E70D1057A5 /* ASWeakProxy.mm */, + 2ECB89B68E304B7BCC55443D36FF5ACB /* ASWeakSet.h */, + 7F01B58EC031BB0AC720C371201D7E15 /* ASWeakSet.mm */, + 5FBEE9DF8CEE0978987520A5AB6353FD /* AsyncDisplayKit.h */, + 488883D3A35A3CCB6513EFFD7D6B5F7A /* AsyncDisplayKit+Debug.h */, + 3312506CD352A7FD094C3802FCB31C0B /* AsyncDisplayKit+Debug.mm */, + DB92B37C19A5B459CB704598D51575B9 /* AsyncDisplayKit+IGListKitMethods.h */, + C6BA4FA2A0064B482B835FC3FCA51761 /* AsyncDisplayKit+IGListKitMethods.mm */, + 019B846EC672567C4BD6EF8712E2C6A8 /* AsyncDisplayKit+Tips.h */, + 3A0E979A776D8F70B347CC8B258C6B39 /* AsyncDisplayKit+Tips.mm */, + 49E8771A769680B47A2ADB440EDDEDA1 /* ASYogaUtilities.h */, + 6DBB50E059AE567A69A18F010E0C93C5 /* ASYogaUtilities.mm */, + 602F988DE92671CC4096ADCC733A5930 /* CoreGraphics+ASConvenience.h */, + 6D84304A4C65F07E602CBAB724FF9D07 /* IGListAdapter+AsyncDisplayKit.h */, + D61846E07A8D0904AF857A84BDD7C878 /* IGListAdapter+AsyncDisplayKit.mm */, + B4AC4FD5B9DD6F7746F71563A59BE136 /* NSArray+Diffing.h */, + 8146280FD73EB05DF4423A718F6888D0 /* NSArray+Diffing.mm */, + E732EDFE66778C370EDB3A951172CBE2 /* NSAttributedString+ASText.h */, + DDB932802CAA60747F82694A454F50F3 /* NSAttributedString+ASText.mm */, + A06BA14EEE45FB041AC986113251A89C /* NSIndexSet+ASHelpers.h */, + FFE3A850EF05FC7A29E7138C236D6AE0 /* NSIndexSet+ASHelpers.mm */, + 11F30CDBA493D7DF5D65CDE41CDDB13D /* NSMutableAttributedString+TextKitAdditions.h */, + 3645176D480EBDC746EAB7706EC0FE2C /* NSMutableAttributedString+TextKitAdditions.mm */, + 5956F6A0379F1B3E69E48CB79B712D20 /* NSParagraphStyle+ASText.h */, + 9AAB840C4B625AB58BBC77E40F7BF770 /* NSParagraphStyle+ASText.mm */, + E690EC656DC50FF9A5210F6CC30AE500 /* UICollectionViewLayout+ASConvenience.h */, + 96BDF2482DF865D7F8FF0B07AEE55F7B /* UICollectionViewLayout+ASConvenience.mm */, + 623F24CFE1B1D4FFEDB792BA41976749 /* UIImage+ASConvenience.h */, + CA4D8229EE753A846B1A324FF59E6F24 /* UIImage+ASConvenience.mm */, + C8D491217E9C139BB7B03BDB3EC9AE93 /* UIResponder+AsyncDisplayKit.h */, + 460688F46B0349193D03A8CD6459F014 /* UIResponder+AsyncDisplayKit.mm */, + 996D138EA53F8172C888E4E58C2BA094 /* UIView+ASConvenience.h */, + ); + name = Core; + sourceTree = ""; + }; + 7D9B71082E14B346BD914A790DA23BF3 /* Support Files */ = { + isa = PBXGroup; + children = ( + AE482408FCFD063EC12E94BA06BA5CD4 /* PINCache.modulemap */, + 6F1D0CDE40B7ADECFF3A55527DA6DDC0 /* PINCache-dummy.m */, + BE11BB4291FF4EC28259ED221EAED215 /* PINCache-Info.plist */, + 7428C524EFC7D50242D9CF71007C0A93 /* PINCache-prefix.pch */, + 274269F974F0098D974E13CADE4FC16A /* PINCache-umbrella.h */, + 49EC90EF95A2BAABAA44D24EA9E6115B /* PINCache.debug.xcconfig */, + 0ECB2E92B3BD6B8EF0C554FFB94D06B1 /* PINCache.release.xcconfig */, + ); + name = "Support Files"; + path = "../Target Support Files/PINCache"; + sourceTree = ""; + }; + 88A0DD5433BF494A46E50E31F6C6A20B /* PINRemoteImage */ = { + isa = PBXGroup; + children = ( + D0C8620CB5D7EE3AF126DA59CE4392D7 /* Core */, + C1E8A3DCAAE3BFCC36929031CC045756 /* PINCache */, + 0A3D2ABC3AD80561D335EF3444F18408 /* Support Files */, + ); + name = PINRemoteImage; + path = PINRemoteImage; + sourceTree = ""; + }; + 8C18976B5DE1BDC7443329C1FB33648D /* iOS */ = { + isa = PBXGroup; + children = ( + 8417A1FA330D0B15E1EDF7547C84EB6E /* Accelerate.framework */, + 831337E1D6E0B5DD90702FBF69CA4A97 /* AssetsLibrary.framework */, + 6B050E212264CF3B336ED28484FB5132 /* AVFoundation.framework */, + F6A8D376391747B8A795CD26BEAD779B /* CoreLocation.framework */, + 461F1ADCB188A45C15C5FFE080FB729A /* CoreMedia.framework */, + CD8F721F21C26F26C2A6B469454DB51F /* Foundation.framework */, + 7A2B2E2EE493CFA9A7944BBC109C1107 /* ImageIO.framework */, + D6AF8F32499937E401433036C62354C2 /* MapKit.framework */, + 8C7A366CC3B6A0716315FAE4CC40827B /* Photos.framework */, + 465C7CD0B951D0E0D97FC9851987095F /* UIKit.framework */, + ); + name = iOS; + sourceTree = ""; + }; + 9D6AB999CF94FAE72B2E5C5F7C60A29F /* PINOperation */ = { + isa = PBXGroup; + children = ( + EECCA4A8A8683035F019428C5499815C /* PINOperation.h */, + A6648655AE61CC658E7AE86A6804AD60 /* PINOperationGroup.h */, + ABC2257CCA051191378319330151F0D4 /* PINOperationGroup.m */, + 6FA8B88434F89A38DB5CEC77891BEAC7 /* PINOperationMacros.h */, + F786D2FE67301111EA52EC486B239B2B /* PINOperationQueue.h */, + E4A4E77985D62E4AE0F17F553E7EB919 /* PINOperationQueue.m */, + 556D557F23FCD191B665690B58AC0B95 /* PINOperationTypes.h */, + DB2E41F787B23A6D94DD0E8BEE86D3DD /* Support Files */, + ); + name = PINOperation; + path = PINOperation; + sourceTree = ""; + }; + AF01054A076C894E59C4EAC9EF1D23C7 /* Core */ = { + isa = PBXGroup; + children = ( + 189BBBEF314F5632F32DEBAB3119AA42 /* PINCache.h */, + CEA150E52F46238642DC87EC38BD0E6D /* PINCache.m */, + D55B5D9D10346E310CC066CE345EF883 /* PINCacheMacros.h */, + 4AD14C8259CFDAA23540FB84371A8C91 /* PINCacheObjectSubscripting.h */, + F3A05A320A0B5986D46D8E46AB8F56DB /* PINCaching.h */, + CA8AEF5A3D9FB6C2D3EDA71FC15F7D74 /* PINDiskCache.h */, + 0964567655AE549BDF757E8DF816FBFF /* PINMemoryCache.h */, + 9E12D4B3FB7683F25D5A629A2F5CF4F5 /* PINMemoryCache.m */, + ); + name = Core; + sourceTree = ""; + }; + C1E8A3DCAAE3BFCC36929031CC045756 /* PINCache */ = { + isa = PBXGroup; + children = ( + 60E2A9C368D8B4DB87B8786A1150005A /* PINCache+PINRemoteImageCaching.h */, + 09018DF2292D2A0DB7CD1AE16D80E0C5 /* PINCache+PINRemoteImageCaching.m */, + ); + name = PINCache; + sourceTree = ""; + }; + CE3809B66CC481149AC20B17A0F0DD9B /* Frameworks */ = { + isa = PBXGroup; + children = ( + F715418A114688F1318388F954C148DE /* PINCache.framework */, + DA1D941B55196700D1C09F1BBF76A710 /* PINOperation.framework */, + A563C19F22250BA10FAAF9CAA214C6C8 /* PINRemoteImage.framework */, + 8C18976B5DE1BDC7443329C1FB33648D /* iOS */, + ); + name = Frameworks; + sourceTree = ""; + }; + CF1408CF629C7361332E53B88F7BD30C = { + isa = PBXGroup; + children = ( + 9D940727FF8FB9C785EB98E56350EF41 /* Podfile */, + CE3809B66CC481149AC20B17A0F0DD9B /* Frameworks */, + FFA46F15410013C35249535D7CB49C7F /* Pods */, + 0049678DAC1BCA6333BCF228E121A6EE /* Products */, + EFD15DA214AF58F11E17BA702F8295DD /* Targets Support Files */, + ); + sourceTree = ""; + }; + D0C8620CB5D7EE3AF126DA59CE4392D7 /* Core */ = { + isa = PBXGroup; + children = ( + DF03059CFC2CF8C308FEB24EB69C2BC1 /* NSData+ImageDetectors.h */, + 5F5D034DB1ED346FED8B825B2C4FACAE /* NSData+ImageDetectors.m */, + FB32E2461EA09ADDAA167B8226BF68A2 /* NSHTTPURLResponse+MaxAge.h */, + D643AB237A68D3EA33E130BE941C23A2 /* NSHTTPURLResponse+MaxAge.m */, + 5316B79F60F907878E80E6789410C62C /* PINAlternateRepresentationProvider.h */, + 2F1876604918F7E85C8C8C00692D7E31 /* PINAlternateRepresentationProvider.m */, + 966FD881395FF4683060BDF1A2C1AFE7 /* PINAnimatedImage.h */, + 29F590A258A736639A4D10C302391831 /* PINAnimatedImage.m */, + D13CF7125B69CA994A1A19ADF69164F8 /* PINAnimatedImageView.h */, + C0F24E93C735911A21D52FCA96DE90EF /* PINAnimatedImageView.m */, + 0DBC478814C98DC8DF9A700BA2AFB12E /* PINAnimatedImageView+PINRemoteImage.h */, + F6E67D14993A7B56B97A9C6D49B944FC /* PINAnimatedImageView+PINRemoteImage.m */, + 1CD84E09F5FA855D090FB8AA27C039C2 /* PINButton+PINRemoteImage.h */, + 9FAB346055C63B3162B59F5DE8235DF0 /* PINButton+PINRemoteImage.m */, + 3C69FF5E87A081B57AFD9CF41C19BC10 /* PINCachedAnimatedImage.h */, + 130371D9D979BBE4A21D3985FF5C0CBA /* PINCachedAnimatedImage.m */, + 442AD1A685AA2C1A7EDB840685B29CCB /* PINDisplayLink.h */, + BB800E39017591858E237D0FF17509B6 /* PINDisplayLink.m */, + 736C046DCA265EE81A58FD5F89BF6102 /* PINGIFAnimatedImage.h */, + 5446BF8F6EE5445BB8095CED507BA6E0 /* PINGIFAnimatedImage.m */, + 451F8219FEECBEF1D8D5576A706A6259 /* PINImage+DecodedImage.h */, + 7E77840DF764C7052517CF2ED7FF71A3 /* PINImage+DecodedImage.m */, + 3C5A6AEC41EFC3AD25534536494FF0B1 /* PINImage+ScaledImage.h */, + 23B7B3467E387075BB087F9DF9D8EDF5 /* PINImage+ScaledImage.m */, + E2BF0999ECBBCF0288C66B2D84F59B64 /* PINImage+WebP.h */, + 2AEE11DDE18165E6A0EE511B797F4CD4 /* PINImage+WebP.m */, + 72097EDC568749414EDF3AC657BB07C3 /* PINImageView+PINRemoteImage.h */, + 790FD97BCBD0110961780552C759DF98 /* PINImageView+PINRemoteImage.m */, + F4EDFDD960862C423CFEE9683653342B /* PINProgressiveImage.h */, + 4FE8339B40FB00918941FBCDC5F30D28 /* PINProgressiveImage.m */, + 5688A739BB7B10A17AF1E41094FBDD6B /* PINRemoteImage.h */, + 85DC3A0BDC8F3003550567F705BC3C4A /* PINRemoteImageBasicCache.h */, + 25241914808C5FF6BE5F927F84792A40 /* PINRemoteImageBasicCache.m */, + 4C8BC130248DB0AD806FA743C70A99B4 /* PINRemoteImageCaching.h */, + 930163D057DF4C17CDACDDB653AD3C46 /* PINRemoteImageCallbacks.h */, + 1722A238A2D68ECA7C85A592D48C54E5 /* PINRemoteImageCallbacks.m */, + 2138392302088734C71C953F6E8BE31F /* PINRemoteImageCategoryManager.h */, + 42F1BDB74A174F9531FB779E45BC1D50 /* PINRemoteImageCategoryManager.m */, + D9C64EBA02032F27282C40FD7220273C /* PINRemoteImageDownloadQueue.h */, + 3C8F71BC92C3939CC05FD410E84FF05C /* PINRemoteImageDownloadQueue.m */, + 1A77A2AEAA733E58FDCD4CAA0922B1A8 /* PINRemoteImageDownloadTask.h */, + 2C0A55D5092394CDBE41B7565F27BD76 /* PINRemoteImageDownloadTask.m */, + B804EAE3C4FD585B6054924F31638531 /* PINRemoteImageMacros.h */, + 8FA479C38E875642DC6D0F70C22C53E2 /* PINRemoteImageManager.h */, + EE2940E42584F168B39AE1ADCDAF32AA /* PINRemoteImageManager.m */, + D7429861868D4103F11B9E94C19522A9 /* PINRemoteImageManager+Private.h */, + 1DBBE6C78D2A10AE3C8E91AC0E6C3244 /* PINRemoteImageManagerConfiguration.h */, + D7AEF2BB9296AC6AD078D6A403286D8F /* PINRemoteImageManagerConfiguration.m */, + 013E1DDF851F0FD096C42D0EAC0480E2 /* PINRemoteImageManagerResult.h */, + 39469547584715DC15C17FD3249DB5F3 /* PINRemoteImageManagerResult.m */, + C9C4E917C6DD24E5A1A14EF3D1471B07 /* PINRemoteImageMemoryContainer.h */, + 3B76C13871113610B790E5634FD8E4B7 /* PINRemoteImageMemoryContainer.m */, + 2D08D914160D3368A237E541232421E1 /* PINRemoteImageProcessorTask.h */, + 033CCEDAB42EDF1F79E201309B0557B6 /* PINRemoteImageProcessorTask.m */, + 9E57E56ED96B01EFFB474D13F8F2E736 /* PINRemoteImageTask.h */, + 0FEA06CB098370189C1103854CA5077F /* PINRemoteImageTask.m */, + 1E6AFB718C24B2377E5DF0CCDA306C55 /* PINRemoteImageTask+Subclassing.h */, + 28C3E95F4439C05AD7DD1B694E01B06F /* PINRemoteLock.h */, + 70907139FDD30DCEEA7A9190BA24A9D8 /* PINRemoteLock.m */, + 474F334721D6E818D56CC8B9B8BD4076 /* PINRemoteWeakProxy.h */, + 9DA8213BEA83150926293674AE1852A9 /* PINRemoteWeakProxy.m */, + BDBDCD408FDA7E7CFEE56C22BE3A64D4 /* PINRequestRetryStrategy.h */, + 1394E4E8CCCBB0C8068E295304334637 /* PINRequestRetryStrategy.m */, + A7B540D3D7D6A9543C97F678E925A099 /* PINResume.h */, + 73AA3A23C9F3D8C94C70E9CD5CAB902C /* PINResume.m */, + F12B84DDA3761BCCFFC101F6185D46A9 /* PINSpeedRecorder.h */, + 76660865DC7BA0803ED5AF36FA8E6ECA /* PINSpeedRecorder.m */, + 51E4828747ABF0F4B0D5210073FD2269 /* PINURLSessionManager.h */, + 6B20999B345220EA81A18684BC483B2D /* PINURLSessionManager.m */, + 626548E82C01FE272CD114C9F7F07556 /* PINWebPAnimatedImage.h */, + 3D673E12DB0B97C4702DCA57BD907DCD /* PINWebPAnimatedImage.m */, + ); + name = Core; + sourceTree = ""; + }; + DB2E41F787B23A6D94DD0E8BEE86D3DD /* Support Files */ = { + isa = PBXGroup; + children = ( + 0C2BF8A59FA951CF59C6FA88A1A8895D /* PINOperation.modulemap */, + 741BAE82F2A2B69F81AE5008435BADE3 /* PINOperation-dummy.m */, + 90D1B54DABB36946B78848BB58EA41DF /* PINOperation-Info.plist */, + 64116DD37F68B07B3B0DE77751C883F1 /* PINOperation-prefix.pch */, + A74625036C72AB1D11BF828EF40AF42E /* PINOperation-umbrella.h */, + ED201A924E0BC9468579E76F2F2FE7AD /* PINOperation.debug.xcconfig */, + 2C41CB510A0394AE786A47727010CFF4 /* PINOperation.release.xcconfig */, + ); + name = "Support Files"; + path = "../Target Support Files/PINOperation"; + sourceTree = ""; + }; + EFD15DA214AF58F11E17BA702F8295DD /* Targets Support Files */ = { + isa = PBXGroup; + children = ( + 2B9558268D0B572464D0A335F8ED6FF7 /* Pods-Starbucks_Clone_TexBrother */, + ); + name = "Targets Support Files"; + sourceTree = ""; + }; + FFA46F15410013C35249535D7CB49C7F /* Pods */ = { + isa = PBXGroup; + children = ( + 0D5E7BF27B539B0913146F9F9E012151 /* PINCache */, + 9D6AB999CF94FAE72B2E5C5F7C60A29F /* PINOperation */, + 88A0DD5433BF494A46E50E31F6C6A20B /* PINRemoteImage */, + 4943E861C9E9B41168E44698FBDDC1F3 /* Texture */, + ); + name = Pods; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 2A105393BCB5FE9278CB202153804F72 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + DF3EC03998606F8C6B59E9B8FA250DE1 /* NSData+ImageDetectors.h in Headers */, + E66C56441574428F0E6319DD42712F00 /* NSHTTPURLResponse+MaxAge.h in Headers */, + FA09956C6A65F04B260B032B19C3C408 /* PINAlternateRepresentationProvider.h in Headers */, + DEB64B74E02E6B5A3942813C4B0AA43A /* PINAnimatedImage.h in Headers */, + 938190A76B28D4FBC6FBD6EE39D3D21B /* PINAnimatedImageView+PINRemoteImage.h in Headers */, + 3F032BD5B92DB1386DD5F553C335D2AF /* PINAnimatedImageView.h in Headers */, + A616785B7772BEEE063DDDCC1A5C06B6 /* PINButton+PINRemoteImage.h in Headers */, + D83ADCEE529736987678A5E5678C3EF4 /* PINCache+PINRemoteImageCaching.h in Headers */, + F8D6DE5B1A9935A9B5C53769624F1C53 /* PINCachedAnimatedImage.h in Headers */, + 5AA521195197A5269FE3C52EE1F612FC /* PINDisplayLink.h in Headers */, + D27BEF1A0F0747FE98EF2B4AC76297BA /* PINGIFAnimatedImage.h in Headers */, + D55227D66C8BD0B9EF66806624057C53 /* PINImage+DecodedImage.h in Headers */, + 7615B92004681A1F0CE6E7F61389AC91 /* PINImage+ScaledImage.h in Headers */, + 331FEDE9B0C941A741D0076745ECE9F4 /* PINImage+WebP.h in Headers */, + 0D1AFB17D23CD14FEF004D8F753E170F /* PINImageView+PINRemoteImage.h in Headers */, + D5C26BE5B56878BE80E4E358D01505BF /* PINProgressiveImage.h in Headers */, + 329730AC1D8EB74370375033D89CAA04 /* PINRemoteImage-umbrella.h in Headers */, + FFA03ECBF831BCB7A9BB9D3AB5168587 /* PINRemoteImage.h in Headers */, + 8C59DA4CCC93CE415C2A15348CBFC83D /* PINRemoteImageBasicCache.h in Headers */, + 32E39D4168AA61D28735332214FD7A68 /* PINRemoteImageCaching.h in Headers */, + 792C265DC6709DF747E801A984353998 /* PINRemoteImageCallbacks.h in Headers */, + 79DEAF233C0FDF9487841F3A5226FE89 /* PINRemoteImageCategoryManager.h in Headers */, + 63585E8148886E80CB182E3920C51A11 /* PINRemoteImageDownloadQueue.h in Headers */, + 2E8CD8DDE7442B3C033B24194762F66B /* PINRemoteImageDownloadTask.h in Headers */, + 918B6F04B2ED6C34247F3355BB118462 /* PINRemoteImageMacros.h in Headers */, + DADB3AD2B89E46C6A114C733FCCFFCCF /* PINRemoteImageManager+Private.h in Headers */, + 04543BFEF8F8F32EE2B4A3304471A5F4 /* PINRemoteImageManager.h in Headers */, + 44231872CA5D99230474B02A713D193C /* PINRemoteImageManagerConfiguration.h in Headers */, + D792C77237A52B9B995A8A2E9FD39B2C /* PINRemoteImageManagerResult.h in Headers */, + 23E59691FBD46D8ACF53E02745593028 /* PINRemoteImageMemoryContainer.h in Headers */, + 02131FDD47626B3DCE579DFFC4E44CC2 /* PINRemoteImageProcessorTask.h in Headers */, + 588F5E132A531BF1B16C0FE24359FDA2 /* PINRemoteImageTask+Subclassing.h in Headers */, + CBEBD4CC5E49C32325142632AF551934 /* PINRemoteImageTask.h in Headers */, + D22F83E790D592BF6759A38C4001B050 /* PINRemoteLock.h in Headers */, + B5563B5BB14EBE69B0684B4C5A96F51A /* PINRemoteWeakProxy.h in Headers */, + C2B195B9AEF986D33CFF546944B10CDA /* PINRequestRetryStrategy.h in Headers */, + 45F21A0B56EC7B9577BC185A9ED007C4 /* PINResume.h in Headers */, + 5BFC4E12BD19E6A80D098D6A1288A43D /* PINSpeedRecorder.h in Headers */, + 25D87433F8998220C56DEB0CDDA167CA /* PINURLSessionManager.h in Headers */, + EAAACE6B4C88D9F0E4E8EC5CD698212C /* PINWebPAnimatedImage.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BA923AD163937D722E40905AF3F28F6F /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 7A541079985175ED988475511FE5F2DC /* PINCache-umbrella.h in Headers */, + A468E4B8A47DCEC32494D8A69DAD4F2D /* PINCache.h in Headers */, + DE1FBD64E727449DFEB41D8CC0B764E4 /* PINCacheMacros.h in Headers */, + 354D5F302A89E5FA56123389EE9EF229 /* PINCacheObjectSubscripting.h in Headers */, + 681352C165A0A0DE357DFC95EF35D7ED /* PINCaching.h in Headers */, + 901059B0F79E50EA1BDACFD9B1A9D5CF /* PINDiskCache.h in Headers */, + 31E23F58653E80531A771415273BECDC /* PINMemoryCache.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + CFDD8E01973A320462A7591B41B1F068 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + B0917E444B1352AF7285C09EF420028D /* _ASAsyncTransaction.h in Headers */, + 8DE9CA21A7082FC2A937E3DBF78E3A0F /* _ASAsyncTransactionContainer+Private.h in Headers */, + CC2E10F3DE5A2E0DEF91BF948FE2E06C /* _ASAsyncTransactionContainer.h in Headers */, + 37BBB02A7D765725890D32944453EB98 /* _ASAsyncTransactionGroup.h in Headers */, + 28F61DD0EBC89532FF28A6F967B0DBC7 /* _ASCollectionGalleryLayoutInfo.h in Headers */, + 2730E2AB0DF38DB833EF6AD02E788C2D /* _ASCollectionGalleryLayoutItem.h in Headers */, + 6AFFA660CBFFBF81A6DC1F34AC2B9E39 /* _ASCollectionReusableView.h in Headers */, + 7BAB8B3B6369EDFA0C7D13F60FBC6794 /* _ASCollectionViewCell.h in Headers */, + 37163A6F77A6128EA54B197A1B6E2AF3 /* _ASCoreAnimationExtras.h in Headers */, + B878FFB40DC743FA707E282E2F66C060 /* _ASDisplayLayer.h in Headers */, + E061922DF3A631DE569DEC0B02A0E55C /* _ASDisplayView.h in Headers */, + DFEDDB04D68B8A713D24FAB88F55988B /* _ASDisplayViewAccessiblity.h in Headers */, + 0368A0C1EE4DCCF63368E227AABF90FA /* _ASHierarchyChangeSet.h in Headers */, + 1B7F2F9ABEC6E2EF28D4200AAA0EA967 /* _ASPendingState.h in Headers */, + AB100DDB742EE0376A45715B42344509 /* _ASScopeTimer.h in Headers */, + E3DBA9B2AD2B13F98ABC7894743C5FF1 /* _ASTransitionContext.h in Headers */, + AF55C78E39FBB0158FDD1F04874978C2 /* ASAbsoluteLayoutElement.h in Headers */, + 2E1B3B900BECDFD697A94397EB3208A8 /* ASAbsoluteLayoutSpec.h in Headers */, + 8CF2DBA98444B46D088E8FC193E26B9D /* ASAbstractLayoutController+FrameworkPrivate.h in Headers */, + FAACAA3FAADE3A8D89DCE1E374BECEA7 /* ASAbstractLayoutController.h in Headers */, + 39441BD56324DADDA1A7387A039B11B4 /* ASAsciiArtBoxCreator.h in Headers */, + 3BC85D01305C01A02D674354EA81231E /* ASAssert.h in Headers */, + A93E5D1247E109BF74D6E0E0C56A1CA2 /* ASAvailability.h in Headers */, + FDA5B5348C842BAC45AE14D72B2E2782 /* ASBackgroundLayoutSpec.h in Headers */, + BEB3FA36F7395B27F36C4784927C3CFE /* ASBaseDefines.h in Headers */, + 46A6E2A05E106239C6D1B2429E6363CE /* ASBasicImageDownloader.h in Headers */, + BE9C538F9315DBDF8511D7B1C143276F /* ASBasicImageDownloaderInternal.h in Headers */, + DE43FB61D54CA4D4683A2976E2C4B17A /* ASBatchContext.h in Headers */, + B4419404D375692A51B0D095CAB19B67 /* ASBatchFetching.h in Headers */, + EBB98BADBF34A5832DC9812C4D45C02F /* ASBatchFetchingDelegate.h in Headers */, + 33AE74DEA0304E22B4B020C037BB4E8D /* ASBlockTypes.h in Headers */, + 5F4B683A5C8770F9092E0F839DC279B8 /* ASButtonNode+Private.h in Headers */, + 89CCF29068794758917D5BC53196B619 /* ASButtonNode+Yoga.h in Headers */, + E85CBF5F58F5FF76148975C11E38F27E /* ASButtonNode.h in Headers */, + AA177091C108492974D400BDB1A1CD62 /* ASCellNode+Internal.h in Headers */, + 690EAE385194D53B290B805C46BA4EF6 /* ASCellNode.h in Headers */, + 9BB40B9E5A27F0D228FDA2654B90A0C6 /* ASCenterLayoutSpec.h in Headers */, + 6DA1B9D0492B013012EC140F453A9C89 /* ASCollectionElement.h in Headers */, + 68C7FCDF3699122D51BBB8D181CE2A4F /* ASCollectionFlowLayoutDelegate.h in Headers */, + D511B3184C8CA22900E8CF81946472FA /* ASCollectionGalleryLayoutDelegate.h in Headers */, + C819EBDDE5B4938A862427E81C9B66E9 /* ASCollectionInternal.h in Headers */, + 4B16631D2EC2D5D065CBB55DC8881D0D /* ASCollectionLayout.h in Headers */, + B53BAD4FEA26F027B02C817B0D56B03F /* ASCollectionLayoutCache.h in Headers */, + 128159D60F5F30A14FFA7E2216B689A8 /* ASCollectionLayoutContext+Private.h in Headers */, + 8C31797394C8EC3AA2597206372419D9 /* ASCollectionLayoutContext.h in Headers */, + FC11F6DB1023A426B9D6DEC5FEAFEA20 /* ASCollectionLayoutDefines.h in Headers */, + AE00B7F987BD71B5EFD51D405455D667 /* ASCollectionLayoutDelegate.h in Headers */, + E028DD17CB226563694AD3E644DA0E8A /* ASCollectionLayoutState+Private.h in Headers */, + 4107EE649411835E2A9403AA3E75395E /* ASCollectionLayoutState.h in Headers */, + B9D87848BB35AC1F9280D3035D0E2BA4 /* ASCollectionNode+Beta.h in Headers */, + F1852DD8600E8B26A90C21441ED9FAF8 /* ASCollectionNode.h in Headers */, + 869A0AFC6E446D9559F81B9DE55339BB /* ASCollections.h in Headers */, + 7682DDBF21A55C03731203EDCBDFF27B /* ASCollectionView+Undeprecated.h in Headers */, + D2663A7660C2B444D8DFD65C341C210D /* ASCollectionView.h in Headers */, + 8DF538949C4DC97E959103D2208F3C69 /* ASCollectionViewFlowLayoutInspector.h in Headers */, + A248291B4CDBC90A07C1BCD8726C6CBC /* ASCollectionViewLayoutController.h in Headers */, + 8D5273A0FC56233E209A44A5FB5FC15B /* ASCollectionViewLayoutFacilitatorProtocol.h in Headers */, + FC78F578F793BE2AACE996A785F1C6A5 /* ASCollectionViewLayoutInspector.h in Headers */, + CF887DB488D89F9D6C22E641C34E37E5 /* ASCollectionViewProtocols.h in Headers */, + D8B254AD7AE2D4AD795D0F0D24A9DC63 /* ASConfiguration.h in Headers */, + F37A13201EAD5DDD51A3D7F7BA82C840 /* ASConfigurationDelegate.h in Headers */, + 85CA625022EC17DE0631D5B55F084FB7 /* ASConfigurationInternal.h in Headers */, + B205161CC6B478855EF36E244F0B2DA8 /* ASContextTransitioning.h in Headers */, + 17514581F08C4029877D4B19982B1AB0 /* ASControlNode+Private.h in Headers */, + 7367521E016788B60D3B821852CC1D49 /* ASControlNode+Subclasses.h in Headers */, + 32F74F879E223DC924F2D377B0E0438A /* ASControlNode.h in Headers */, + E80626C7D668DB07B848BEC6AAE711A4 /* ASControlTargetAction.h in Headers */, + 62642C50110C9B6D29E7CEBAD2B33BBC /* ASCornerLayoutSpec.h in Headers */, + 1D31956E47A1667E4ED78EDBCF63657F /* ASDataController.h in Headers */, + 45051BE690707C36628E0A3162A6655C /* ASDefaultPlaybackButton.h in Headers */, + 9ABA4A11DE428613126DA887698C5770 /* ASDefaultPlayButton.h in Headers */, + 1F51595178FA581C9FA84C59AAC0ADF2 /* ASDelegateProxy.h in Headers */, + 8A2E0B626887AF013A7F84103DA0A836 /* ASDimension.h in Headers */, + AC2E392B90F75B9B0B877513955A123D /* ASDimensionInternal.h in Headers */, + F7DCF358D078332CEB3F7AD5AAAC4530 /* ASDispatch.h in Headers */, + 55489F9E2C84ABA1DF343520666B7980 /* ASDisplayNode+Ancestry.h in Headers */, + C0A4A044D33E54CF070FA523D6FFA5AC /* ASDisplayNode+Beta.h in Headers */, + 15AB102788E5371031F010FB3494FDC4 /* ASDisplayNode+Convenience.h in Headers */, + 6A4E81CDAE9421C4389B34E66572AEAA /* ASDisplayNode+DebugTiming.h in Headers */, + 59ACDCDC7DDCA5D20DB4A8F2BB1D132B /* ASDisplayNode+FrameworkPrivate.h in Headers */, + 89C8791BCA6D98034A61A54E5FD9D4AE /* ASDisplayNode+InterfaceState.h in Headers */, + E8261A6238D988688A11ECD8B35A479C /* ASDisplayNode+LayoutSpec.h in Headers */, + B77EF1662C95CF840BD1498F309E8FFC /* ASDisplayNode+Subclasses.h in Headers */, + DCBB463A435F0BA2AA998D91A821BD63 /* ASDisplayNode+Yoga.h in Headers */, + 9ED8020DA12D0C5DBB23C41412BC265B /* ASDisplayNode.h in Headers */, + 458959AD13E6AC3374E81F6487F13B21 /* ASDisplayNodeCornerLayerDelegate.h in Headers */, + 1563756D23089B763B4317FAC682C06A /* ASDisplayNodeExtras.h in Headers */, + 625D5BDB7DC6971A04C2F0CC04E78316 /* ASDisplayNodeInternal.h in Headers */, + CB6EF8043EBA5C2100A53E6547E7FBD5 /* ASDisplayNodeLayout.h in Headers */, + DC9252B91D454E933573D0F02CD34A48 /* ASDisplayNodeTipState.h in Headers */, + 3E0ADF4FFBF1AEE2AFF6ADACAEFE3FA2 /* ASDKViewController.h in Headers */, + 4A5D639EB330216C5E0486C9CA40550A /* ASEditableTextNode.h in Headers */, + 162A11E39B08B85FD0E61EAB7E1FF9E0 /* ASElementMap.h in Headers */, + 8EDB472A1578F25B749BE234A65CAB68 /* ASEqualityHelpers.h in Headers */, + 1BC79FE7F10E5A56EBBB646BCA6A2974 /* ASExperimentalFeatures.h in Headers */, + 68158F620B989CD7611B3AEA782A5EEC /* ASGraphicsContext.h in Headers */, + B137969B8A651DA1148DEF66C0E26F73 /* ASHashing.h in Headers */, + 350592582D2BEEAD29989D3C406D296E /* ASHighlightOverlayLayer.h in Headers */, + 1FF802E6BAC65A931C00BE18248620DC /* ASIGListAdapterBasedDataSource.h in Headers */, + 292C3F25A497C5764E033ABE3491999B /* ASImageContainerProtocolCategories.h in Headers */, + AA38AD6092975ECEEE385AE0E53A830C /* ASImageNode+AnimatedImagePrivate.h in Headers */, + 667DE3D4FCA166D984EB80E3BD949F8E /* ASImageNode+CGExtras.h in Headers */, + 48FB2FA1183990E558E429461CDF77F4 /* ASImageNode+Private.h in Headers */, + 2EF0DBBD698C43C660D5C52D387762C3 /* ASImageNode.h in Headers */, + 09650E825B735144A1D66BFD8E55DE4A /* ASImageProtocols.h in Headers */, + F3E0A225E531BB32262D7D9E6BCCCC43 /* ASInsetLayoutSpec.h in Headers */, + 5D1C395E767F467B00623F3B9EF05B22 /* ASIntegerMap.h in Headers */, + 070ABBECB4EDE07A98B7B8194C7653F1 /* ASInternalHelpers.h in Headers */, + 5415B24BFD37F00BB5D405BCD49555EA /* ASLayerBackingTipProvider.h in Headers */, + 42C788E3094CBAAA03EC39561D7181DF /* ASLayout+IGListDiffKit.h in Headers */, + A655F6C0A8C3CE5A1554C2CCDE7AC866 /* ASLayout.h in Headers */, + F3457FE949B058184A4FB77C6AE576AE /* ASLayoutController.h in Headers */, + 33EBCAF7E4942B6C1E978213D1046FAF /* ASLayoutElement.h in Headers */, + 1D503DFE241AA255DA1D05DB8E0DAEFD /* ASLayoutElementExtensibility.h in Headers */, + 6D6BB69C4FAC4FC632BA75AD97D7D2AD /* ASLayoutElementPrivate.h in Headers */, + 2BEBC2DFEDD5845C56E21A6463822B63 /* ASLayoutElementStylePrivate.h in Headers */, + 12037D088DDA51D83DAED06FB069D79D /* ASLayoutManager.h in Headers */, + 5614B5D3B2DD1F30555A9233B65A2393 /* ASLayoutRangeType.h in Headers */, + FB849B4B080AD99E6AA582A08D0B136D /* ASLayoutSpec+Subclasses.h in Headers */, + E4AFCBB6059FC3C56FCE281FE6B88A14 /* ASLayoutSpec.h in Headers */, + 26B3AD7B5C5ED0A014DEFA3AA5CBD94A /* ASLayoutSpecPrivate.h in Headers */, + 9DF12A4EA63E693047BC33E9E9FB3458 /* ASLayoutSpecUtilities.h in Headers */, + 9572408CB866579D8EEFA59F946EC4C5 /* ASLayoutTransition.h in Headers */, + 6D342FAAF97311745F7FF709965F8273 /* ASLocking.h in Headers */, + 912138EAA142AB646C0AB9E4CDDFC505 /* ASLog.h in Headers */, + 92DD534C4085F25142F0ACFD4B930719 /* ASMainSerialQueue.h in Headers */, + 9E6C79BB56B41FFA1665D1E22479ADA2 /* ASMainThreadDeallocation.h in Headers */, + 8E50095CB420ADE04A0C30481174C03B /* ASMapNode.h in Headers */, + E2580439ED32A4AC27DF6CD05B69B472 /* ASMultiplexImageNode.h in Headers */, + 33AFC02D1DDFBFD1EEC47AFD8134C5F2 /* ASMutableAttributedStringBuilder.h in Headers */, + 49175285DAE0F984B215CA4FE269EB09 /* ASMutableElementMap.h in Headers */, + 4916347FD174C3002BE32C86D01422E3 /* ASNavigationController.h in Headers */, + 65EFFF712403A158D18F39A945B66278 /* ASNetworkImageLoadInfo+Private.h in Headers */, + A050FC9032EB0721F865B1DC6AACECE7 /* ASNetworkImageLoadInfo.h in Headers */, + AB48ADC61226FB5C1BC8D659EAF750F6 /* ASNetworkImageNode.h in Headers */, + D7B3E1598EAA524245035481CFA22A9A /* ASNodeController+Beta.h in Headers */, + 905B6202C6ACB27DA5B734CD0EF6923B /* ASObjectDescriptionHelpers.h in Headers */, + CA5853666CACFF05F38A33B2E5DA1949 /* ASOverlayLayoutSpec.h in Headers */, + E9BF9E31DBD24F528DC4BE7EC406F7F1 /* ASPagerFlowLayout.h in Headers */, + 9BED2B15E515CF1FC8DA1779587024AC /* ASPagerNode+Beta.h in Headers */, + 17ADD23D4835CB144BC6CD354844FF50 /* ASPagerNode.h in Headers */, + 25B46F5BF00AFAE8A15CC01AE4E5A643 /* ASPageTable.h in Headers */, + 0AB37573CCCB26C65FBC953E9DE6E3CA /* ASPendingStateController.h in Headers */, + 9D2156F8882790A1A11AB12D4CE15B63 /* ASPhotosFrameworkImageRequest.h in Headers */, + 30B53366BF7622BC324BBC3286D51EDA /* ASPINRemoteImageDownloader.h in Headers */, + 925401BD47D7A9AB180C1B3630BC0980 /* ASRangeController.h in Headers */, + F0CE14EDC70238AE475B95C1E92AB258 /* ASRangeControllerUpdateRangeProtocol+Beta.h in Headers */, + CC40F97A1370E500B10B5536F9149E88 /* ASRangeManagingNode.h in Headers */, + C54D1790AEDE01C590686294934A1E13 /* ASRatioLayoutSpec.h in Headers */, + 2D9E8318C4E63140067B52E62DFF4B5B /* ASRecursiveUnfairLock.h in Headers */, + 34854853C570C98D6E3ACCAA4C7DD1DE /* ASRelativeLayoutSpec.h in Headers */, + 6EE514FBAD36F14E9DB7670A1A371419 /* ASResponderChainEnumerator.h in Headers */, + D65BE49AD19B4ED6FCCA1987CDFEAA81 /* ASRunLoopQueue.h in Headers */, + 383FAE52A9550C9936D6237F7A959AEB /* ASScrollDirection.h in Headers */, + 25283CF364080FF8D8C86F0B9D4936DE /* ASScrollNode.h in Headers */, + DD25CE53DC082769F959AC1EA39B47E1 /* ASSection.h in Headers */, + F1E44CF8A9BDBF622C310A0A52FC9069 /* ASSectionContext.h in Headers */, + F806F7F37B84AEC1C0AEE5C7000E0466 /* ASSectionController.h in Headers */, + F7E577DBB5E622CF4B9BE7D057FB7860 /* ASSignpost.h in Headers */, + CD68FB8439266630F7BEB187F7449BA3 /* ASStackLayoutDefines.h in Headers */, + A6DFC86F9D280E7B7F2E025EFCE9EC91 /* ASStackLayoutElement.h in Headers */, + 9E03D7C1EB8441DA4301C7E83D9A81FC /* ASStackLayoutSpec.h in Headers */, + B6FCFB22B26A8AD8B89DFAEAC29E0712 /* ASStackLayoutSpecUtilities.h in Headers */, + 8BB520FB3735FF2ED6F041EB6BD6EE0D /* ASStackPositionedLayout.h in Headers */, + C25457E2BE17FD066A98C816D2752261 /* ASStackUnpositionedLayout.h in Headers */, + 24E9D62BB1AEE5FC221F92CA8AFCBEC4 /* ASSupplementaryNodeSource.h in Headers */, + 66F58E36376855ADDBF863373C4D15A9 /* ASTabBarController.h in Headers */, + B1EEE6908F5B5C8DF426C37C012EA033 /* ASTableLayoutController.h in Headers */, + B70B74682AA34996E8CB2D683505E107 /* ASTableNode+Beta.h in Headers */, + 95B92DA06C709481C8E996BE378BAF05 /* ASTableNode.h in Headers */, + EFA0B7147CDDF2C33019191E9E492CDB /* ASTableView+Undeprecated.h in Headers */, + 25153085368DA21ADFDAAD6DA0EFCCC9 /* ASTableView.h in Headers */, + 53C40C404A29C9D36C39EFC2A41CE743 /* ASTableViewInternal.h in Headers */, + DDABD6DDE99C201E518E277995C4C3F7 /* ASTableViewProtocols.h in Headers */, + 3E0B594F11B1375A55CC7FB1BE1E7DA7 /* ASTextAttribute.h in Headers */, + C1D9DF1192508FC8B1DB84C55D1E7E98 /* ASTextDebugOption.h in Headers */, + 4A7C54E9E3062AE28473631F67C4A6A3 /* ASTextInput.h in Headers */, + A731B930111DDE6D8923FBB8664602BE /* ASTextKitAttributes.h in Headers */, + 179E80830E414D876BAE96A5E9D033B1 /* ASTextKitComponents.h in Headers */, + 41572627C786146E95259AC42812892C /* ASTextKitContext.h in Headers */, + 8F3296AA6F7126C161055219C1D70548 /* ASTextKitCoreTextAdditions.h in Headers */, + 8D8DB918AAA7B2598B2A1633502471E2 /* ASTextKitEntityAttribute.h in Headers */, + CD731F1BB5013903E2DF3A6BFF044E3B /* ASTextKitFontSizeAdjuster.h in Headers */, + 55DA22DDE15CDE86C6A4157789744B72 /* ASTextKitRenderer+Positioning.h in Headers */, + 1B05E3D885BC93FAC2F5D2007A888386 /* ASTextKitRenderer+TextChecking.h in Headers */, + A418F40185DE480BA18D77831E3C9FCB /* ASTextKitRenderer.h in Headers */, + F28CAD5ABEDD987D9FDBD0C75C1E22D5 /* ASTextKitShadower.h in Headers */, + F71109526514F1B3E7A8F233DE070559 /* ASTextKitTailTruncater.h in Headers */, + 35F73FFF5EC9D38FFF161D8CC64E5218 /* ASTextKitTruncating.h in Headers */, + CB74C471FCDF99CE778F9F12A435D480 /* ASTextLayout.h in Headers */, + 914D6D51178B6E7DC511BBFBCE5B8C60 /* ASTextLine.h in Headers */, + 2BF1C444606DA73A783706F748051F54 /* ASTextNode+Beta.h in Headers */, + 2045F583C05F550162C635C56DB9F44E /* ASTextNode.h in Headers */, + 693AD7C903CD68AA67FAD2DF0D3F676A /* ASTextNode2.h in Headers */, + 6A837AB4E8BB2D3161A42C4BE819519E /* ASTextNodeCommon.h in Headers */, + 3843A5FD95EEDC8984B03B4AB0872139 /* ASTextNodeTypes.h in Headers */, + 0BE5326613B0772676696939B028455F /* ASTextNodeWordKerner.h in Headers */, + 9A7E5CC0B063C1F1B56B4864E262C508 /* ASTextRunDelegate.h in Headers */, + 101880381147AEAF37111DAC551649D3 /* ASTextUtilities.h in Headers */, + 70FFE5752D7735FF6DA612721FAF1AD4 /* ASThread.h in Headers */, + 3013DDB20CC86DBA1FBD4BF107C9EE76 /* ASTip.h in Headers */, + F39F51D473548ECD4D257BF5CDF965CD /* ASTipNode.h in Headers */, + 2FB492957D10701BDFECCA77C563479B /* ASTipProvider.h in Headers */, + D9E389EF92E5B549EA600E659E8CD045 /* ASTipsController.h in Headers */, + B0E599A3B72EBA636823B6322F4CBF36 /* ASTipsWindow.h in Headers */, + C48CA75F831753B49D4987E98DCAD48E /* ASTraitCollection.h in Headers */, + FE4EC78B53C49C251ADA8964979013D8 /* ASTwoDimensionalArrayUtils.h in Headers */, + 51B91F5F6C099C23A0C0BADC9B435BC7 /* ASVideoNode.h in Headers */, + 766AFE24E8D800787DBAAF4EFA1C86D8 /* ASVideoPlayerNode.h in Headers */, + 16ABB0CB811745D54DC052D21166EF81 /* ASVisibilityProtocols.h in Headers */, + 18395D9552702EEA995F3EBDE3DDF009 /* ASWeakMap.h in Headers */, + 5367E1DE5A0A1BAB05B32E3C6BD4B9F2 /* ASWeakProxy.h in Headers */, + C499B5868927409608468028A8408838 /* ASWeakSet.h in Headers */, + C959FB55033C5DE24B6B14315BCD441B /* AsyncDisplayKit+Debug.h in Headers */, + 665AFE433398FCE3DEF4DA527D3835EF /* AsyncDisplayKit+IGListKitMethods.h in Headers */, + D263AAA51D764356CED7F375B1536628 /* AsyncDisplayKit+Tips.h in Headers */, + 0A1A2CA05738BF97386132DC453056D3 /* AsyncDisplayKit.h in Headers */, + 6F310E7B5C2D8AF7297A422F050BFDB9 /* ASYogaUtilities.h in Headers */, + DFCF371994649A26AB2793A1539FDE27 /* CoreGraphics+ASConvenience.h in Headers */, + 9DDDBA392098C8E92D303321246E4A15 /* IGListAdapter+AsyncDisplayKit.h in Headers */, + C49B74B05E2ECF40545BE26D82908D69 /* NSArray+Diffing.h in Headers */, + 3C31CF98F49970F19686DB9430000254 /* NSAttributedString+ASText.h in Headers */, + AA08E3A01738ABED66294CFED9B6232E /* NSIndexSet+ASHelpers.h in Headers */, + F80827ADB4E39BC74D92F18ABD8F92A8 /* NSMutableAttributedString+TextKitAdditions.h in Headers */, + 0DCE1ED366ED82607C1A1BE2C822852A /* NSParagraphStyle+ASText.h in Headers */, + 4D6326702D2E5445867EDF0CB51981B7 /* Texture-umbrella.h in Headers */, + 1C085C8ED0685A8CE4B5B5735C7F7EF6 /* UICollectionViewLayout+ASConvenience.h in Headers */, + 311EEE0E05EAB3E5D744F65588D8FDDB /* UIImage+ASConvenience.h in Headers */, + 6E1760E30C9461F00BEA1F9E0C678A81 /* UIResponder+AsyncDisplayKit.h in Headers */, + 85F06D91CFD3730D46459D65E5BABCE8 /* UIView+ASConvenience.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + EEE9449B0C16AF32F933BB8C37B2E1C3 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 048B9EDF57D0234296B8F7C58A17890F /* Pods-Starbucks_Clone_TexBrother-umbrella.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + EF4C0185F9A99C4E8E8859687DE9B88E /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 58392A4A15A03DD118C936B690AA5CA0 /* PINOperation-umbrella.h in Headers */, + D4DAB9BDA1A61E059E30DF61CDF7DEA7 /* PINOperation.h in Headers */, + 9427254447A9175C3462D1716E78628E /* PINOperationGroup.h in Headers */, + 7123670E11E91E1F2D878B4F8F2AE716 /* PINOperationMacros.h in Headers */, + 66FECA84C9846535E165C9ADB214A4F3 /* PINOperationQueue.h in Headers */, + E667CB4EFFBE95EA701598B0F79A3D29 /* PINOperationTypes.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 42249780E41C88D248C3DDD644B63372 /* Pods-Starbucks_Clone_TexBrother */ = { + isa = PBXNativeTarget; + buildConfigurationList = DDC8A7405D8AB8B8486C2A8EAF23232E /* Build configuration list for PBXNativeTarget "Pods-Starbucks_Clone_TexBrother" */; + buildPhases = ( + EEE9449B0C16AF32F933BB8C37B2E1C3 /* Headers */, + DBA8352CA0BCA6D355FEEDBE3A24A570 /* Sources */, + D7EAA78B2042C124185E2E361B861E50 /* Frameworks */, + 73F9883E7CD1C35314A38C2088F36CA0 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 5CF2963FF9F2A40ECFEFFD2CB1B1AAB8 /* PBXTargetDependency */, + DBF8386B95C87D886559CC0035805857 /* PBXTargetDependency */, + 1EEF08635F68867D626CCFE02C2563B0 /* PBXTargetDependency */, + BBE3689CBA0E35DA23F6D6D157F73AE3 /* PBXTargetDependency */, + ); + name = "Pods-Starbucks_Clone_TexBrother"; + productName = "Pods-Starbucks_Clone_TexBrother"; + productReference = 8D7075DBD5BAA1D6ECBF9309579E9541 /* Pods_Starbucks_Clone_TexBrother.framework */; + productType = "com.apple.product-type.framework"; + }; + 57310B016450E63387C9D64F4933E995 /* PINOperation */ = { + isa = PBXNativeTarget; + buildConfigurationList = 5CA1823A92CB850EAF9AD183366ACC9B /* Build configuration list for PBXNativeTarget "PINOperation" */; + buildPhases = ( + EF4C0185F9A99C4E8E8859687DE9B88E /* Headers */, + 370C15A7318C7C373D68B2F03436488F /* Sources */, + 9939403A7E3F97143E9E5DDC5A2CC843 /* Frameworks */, + 357D8058D03E88FE9600D1502570E08C /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = PINOperation; + productName = PINOperation; + productReference = 22C347595B1851A1E5B0A3762BB18EC9 /* PINOperation.framework */; + productType = "com.apple.product-type.framework"; + }; + A927C3CD51C30012080D6CA07959B246 /* PINCache */ = { + isa = PBXNativeTarget; + buildConfigurationList = B794DF6469B3D94E9347E82D67AFAF6E /* Build configuration list for PBXNativeTarget "PINCache" */; + buildPhases = ( + BA923AD163937D722E40905AF3F28F6F /* Headers */, + C1B61E7C7CFBEB67C3AF77D3B185E9FD /* Sources */, + 059E3121D15442F422222D8AD47E0686 /* Frameworks */, + 9C80EE02089BF890DF2D261AEAD04144 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 2E567167E7240D0E74CE1A9CA537C23A /* PBXTargetDependency */, + ); + name = PINCache; + productName = PINCache; + productReference = 6320170E5A5A268B11CAE15D4EF6A9E3 /* PINCache.framework */; + productType = "com.apple.product-type.framework"; + }; + DB66E43BC980D741F3C7C5CF3ACEEFA4 /* Texture */ = { + isa = PBXNativeTarget; + buildConfigurationList = 78DCB2C5EC3B5BA90EEB95A1ACFFDF0E /* Build configuration list for PBXNativeTarget "Texture" */; + buildPhases = ( + CFDD8E01973A320462A7591B41B1F068 /* Headers */, + 205A91CA63B15EDB711FBF192800F91A /* Sources */, + 4944372286DB7CF00171CB0CC5D55B61 /* Frameworks */, + 5932610002C5C8243CDFE673CFF79E62 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + FFF0EDCF2A8250963360132ACEA7C16C /* PBXTargetDependency */, + ); + name = Texture; + productName = Texture; + productReference = F46831F4FCE3AB140F12577CE0C2AAB3 /* AsyncDisplayKit.framework */; + productType = "com.apple.product-type.framework"; + }; + E2A8B2A28D2EAB2E4CCF3B69E6792851 /* PINRemoteImage */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1000880B548699E9BBD74146AFFA3702 /* Build configuration list for PBXNativeTarget "PINRemoteImage" */; + buildPhases = ( + 2A105393BCB5FE9278CB202153804F72 /* Headers */, + 6D126D8E6EF94CE128B535484322F538 /* Sources */, + C6A2093EFA9C0E4742B6F8AF6DCB412D /* Frameworks */, + A627139AA8F807B0BF8DC256470578D9 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 5EF23CBE2A082130F9831C1406C1036F /* PBXTargetDependency */, + 3F6F6A6ADE989E5894E449CC5FC66F7D /* PBXTargetDependency */, + ); + name = PINRemoteImage; + productName = PINRemoteImage; + productReference = 115A4FCB2A03922EC10B633F15359653 /* PINRemoteImage.framework */; + productType = "com.apple.product-type.framework"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + BFDFE7DC352907FC980B868725387E98 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1100; + LastUpgradeCheck = 1100; + }; + buildConfigurationList = 4821239608C13582E20E6DA73FD5F1F9 /* Build configuration list for PBXProject "Pods" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = CF1408CF629C7361332E53B88F7BD30C; + productRefGroup = 0049678DAC1BCA6333BCF228E121A6EE /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + A927C3CD51C30012080D6CA07959B246 /* PINCache */, + 57310B016450E63387C9D64F4933E995 /* PINOperation */, + E2A8B2A28D2EAB2E4CCF3B69E6792851 /* PINRemoteImage */, + 42249780E41C88D248C3DDD644B63372 /* Pods-Starbucks_Clone_TexBrother */, + DB66E43BC980D741F3C7C5CF3ACEEFA4 /* Texture */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 357D8058D03E88FE9600D1502570E08C /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 5932610002C5C8243CDFE673CFF79E62 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 73F9883E7CD1C35314A38C2088F36CA0 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 9C80EE02089BF890DF2D261AEAD04144 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A627139AA8F807B0BF8DC256470578D9 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 205A91CA63B15EDB711FBF192800F91A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 1B2A0A4681935DC95834481F21759220 /* _ASAsyncTransaction.mm in Sources */, + 3E66AC6AE52437F25DB9C77B405BF072 /* _ASAsyncTransactionContainer.mm in Sources */, + 835F0D17D2FD72B5C75AB3C73665B952 /* _ASAsyncTransactionGroup.mm in Sources */, + 9AD75CD02E114BE9834EF4E9DCF8FD35 /* _ASCollectionGalleryLayoutInfo.mm in Sources */, + 0FFDB747E5756746172DD89CAC9073A5 /* _ASCollectionGalleryLayoutItem.mm in Sources */, + D58F5F325475C2FA76B7A2C6D6DEBA08 /* _ASCollectionReusableView.mm in Sources */, + A1879D2E926B3B4D0434F59C3CFEA7E4 /* _ASCollectionViewCell.mm in Sources */, + 50EDFDEBF7240CDF4671942722897557 /* _ASCoreAnimationExtras.mm in Sources */, + 9481A617FEFD1628356CBB00160C2FC0 /* _ASDisplayLayer.mm in Sources */, + 3FA2661EC4B4764F8A0BAA3B6911CE07 /* _ASDisplayView.mm in Sources */, + EA86861420C58AAFFBDD4FE8B05A90F8 /* _ASDisplayViewAccessiblity.mm in Sources */, + EE30A346E8E2989D4D8A1E2A60608C5D /* _ASHierarchyChangeSet.mm in Sources */, + 44F5E8EAAFC519B827079C21092A3110 /* _ASPendingState.mm in Sources */, + A8DEB1E7C5738E83B9EB996DF3B6B780 /* _ASTransitionContext.mm in Sources */, + 065EC5AD7B86716183B6C6F10214ECE9 /* ASAbsoluteLayoutSpec.mm in Sources */, + 6C83627E6FAA6773A8F7CF351CAFB99F /* ASAbstractLayoutController.mm in Sources */, + 0284D979FE5030B1BBE20318DFF83407 /* ASAsciiArtBoxCreator.mm in Sources */, + 445A2C4091D5EB91003D00CA744F69C8 /* ASAssert.mm in Sources */, + 28A9C06F97CB780536F2C2405DF1E4CA /* ASBackgroundLayoutSpec.mm in Sources */, + 1E5042D00F802CC758E27A3DFFA27413 /* ASBasicImageDownloader.mm in Sources */, + 4863D9CFD8DF00F75537FC81AC2C1B9A /* ASBatchContext.mm in Sources */, + 0731FF1F4C72E85A9CF8BC8FA403E7CE /* ASBatchFetching.mm in Sources */, + 25950726ACAF3C2B3EF3B24499F05921 /* ASButtonNode+Yoga.mm in Sources */, + B74E2B68A0C6A8E73F27F0EB5B7695D1 /* ASButtonNode.mm in Sources */, + 66B9FA66EDE6CD8FD3B1C5823155167C /* ASCellNode.mm in Sources */, + F1CD75754D06FFE6667055828402AC19 /* ASCenterLayoutSpec.mm in Sources */, + E81BA6B935A20C3C5AF436FD1097A607 /* ASCollectionElement.mm in Sources */, + 4D1738F72E511D8788C448223DFF74C7 /* ASCollectionFlowLayoutDelegate.mm in Sources */, + 533DDBAE6B86634DC5AC964324AFBE5A /* ASCollectionGalleryLayoutDelegate.mm in Sources */, + E97E10031318F97C13605A4E7C6C241D /* ASCollectionLayout.mm in Sources */, + 5F4662B7E3B0E1B0B9F9C445EFE5FDEF /* ASCollectionLayoutCache.mm in Sources */, + 92B7317ED317260E96CC6D658EFAA5F0 /* ASCollectionLayoutContext.mm in Sources */, + 46602671A46883556573D357D8D58179 /* ASCollectionLayoutDefines.mm in Sources */, + 8B886D15BFD525FDF4FC0FD05C4E352F /* ASCollectionLayoutState.mm in Sources */, + 2718C849A663ADF0E69B40FFC1C5443E /* ASCollectionNode.mm in Sources */, + 4FEC6809913A309C1BB3AE70C9CD1856 /* ASCollections.mm in Sources */, + 5C4DD97A03493777545E355D09A8903E /* ASCollectionView.mm in Sources */, + 12BD7A4EE714789DE378364B25BC681F /* ASCollectionViewFlowLayoutInspector.mm in Sources */, + 26880973B4644E87C380BBB5E861951E /* ASCollectionViewLayoutController.mm in Sources */, + 1D0CD1F6962F538225CE1E3DD8275C0D /* ASCollectionViewLayoutInspector.mm in Sources */, + 03177DA0581846B708D9C1B4C69AF5B4 /* ASConfiguration.mm in Sources */, + 7FD4280D0F3552F62182C2797740B7FA /* ASConfigurationInternal.mm in Sources */, + 3FF4BD5B4A904AB6FDC3BFA4A77764EA /* ASControlNode+tvOS.mm in Sources */, + 7B226EB7B088CECD2ECA145103392D56 /* ASControlNode.mm in Sources */, + B487EEBAEBECA362C0EC6EE162A99EC3 /* ASControlTargetAction.mm in Sources */, + B12E5975BB9E7CB69C0C0E40564ABAFB /* ASCornerLayoutSpec.mm in Sources */, + E49A788AD2790861B385BA2E084BFEF2 /* ASDataController.mm in Sources */, + 3C5A9FE3D7C7FE22A922753CA96E5C12 /* ASDefaultPlaybackButton.mm in Sources */, + 725A23DC02BF12DAEFD17D9DD1C19537 /* ASDefaultPlayButton.mm in Sources */, + 326E08CB331794AD3AC0332E7B4F5DC0 /* ASDelegateProxy.mm in Sources */, + 095EAC3436164EBB3B0DA2704BEE8D7A /* ASDimension.mm in Sources */, + D20712489CC62AD1D69DE3D4F067CC6D /* ASDimensionInternal.mm in Sources */, + 26CC1A88B18BCEFF176989DDF566FEF2 /* ASDispatch.mm in Sources */, + C779ACC80C0E385B487DD2AF73C7B629 /* ASDisplayNode+Ancestry.mm in Sources */, + 6F0D7C379DAA9767920055697FA3B94C /* ASDisplayNode+AsyncDisplay.mm in Sources */, + 5F0259FB38017633D98701913A2EF502 /* ASDisplayNode+Convenience.mm in Sources */, + 48AB83373950DF31804839DF7317AEB2 /* ASDisplayNode+DebugTiming.mm in Sources */, + DCA67BCF11BB08C4CC754BAB7DE9700F /* ASDisplayNode+Layout.mm in Sources */, + 55F66909D00D87259EB4D88697ADF17F /* ASDisplayNode+LayoutSpec.mm in Sources */, + 0C8AFCED476D2CA82A33B9A602B7806E /* ASDisplayNode+UIViewBridge.mm in Sources */, + 5934C09B5C2A178ADD7D0D585AD36037 /* ASDisplayNode+Yoga.mm in Sources */, + B04D1648CEB49BF9A215EB68F782E66F /* ASDisplayNode.mm in Sources */, + D6FBD69E5AC966E364870F3BC89AE40B /* ASDisplayNodeCornerLayerDelegate.mm in Sources */, + 882C68A56DB9AAA11032882286153B46 /* ASDisplayNodeExtras.mm in Sources */, + 2E7753DD0798320C098D6F97215F4EDA /* ASDisplayNodeTipState.mm in Sources */, + 0A8BEE494BB1DFA8A3711444D6CF5728 /* ASDKViewController.mm in Sources */, + E74676FCA35F49AB90084F9D6A133BEC /* ASEditableTextNode.mm in Sources */, + 7485A8427B78F4D0D38AF63015919793 /* ASElementMap.mm in Sources */, + A23A686BD3E5A1A45732474F086E5108 /* ASExperimentalFeatures.mm in Sources */, + D5741B35FAD5625327DB7740816490EF /* ASGraphicsContext.mm in Sources */, + 30E321C7F2B2528CC0DEFA81FEE5020E /* ASHashing.mm in Sources */, + D8CDE83FC5F7BB9CBDDEF662EA434A76 /* ASHighlightOverlayLayer.mm in Sources */, + A43B3297E738E64DFE93D4CE2C81D24A /* ASIGListAdapterBasedDataSource.mm in Sources */, + 98DF10C729D708E8B23CC3DE227AC5BE /* ASImageContainerProtocolCategories.mm in Sources */, + 8C871044D0661199D892CAA1A56975A8 /* ASImageNode+AnimatedImage.mm in Sources */, + 95D49A06D8AAB718A13FFF1A29B6F6FE /* ASImageNode+CGExtras.mm in Sources */, + EFEBFE9D117F31C3EFBCFF03E4F374B8 /* ASImageNode+tvOS.mm in Sources */, + 9D3FF8DEE22B8C7BFB7A66C333F45B92 /* ASImageNode.mm in Sources */, + 1C8C87AA0418AAB89B67CEB269819488 /* ASInsetLayoutSpec.mm in Sources */, + ABEA1AED8D9E7FE95885384AAF7786A6 /* ASIntegerMap.mm in Sources */, + BF793A7E0F25CED19582524BD92EA327 /* ASInternalHelpers.mm in Sources */, + C8CF437ABF283BBC35D39F66A92F7895 /* ASLayerBackingTipProvider.mm in Sources */, + FEC35A85C255EC1E831E29A8A6A3280C /* ASLayout+IGListDiffKit.mm in Sources */, + 4B8E788E590ABA95BD8CC4573D0EBFC0 /* ASLayout.mm in Sources */, + D567696E69F612CCD04A8B52C6AE69FD /* ASLayoutElement.mm in Sources */, + E7F75D0551F60AC1E0FAAE389E0E48EE /* ASLayoutManager.mm in Sources */, + 2C3EE7C1BE5D6BDB615ABD6589CC1A1F /* ASLayoutSpec+Subclasses.mm in Sources */, + 3C3358F8891B4D0EEC5DBF01934EE2F4 /* ASLayoutSpec.mm in Sources */, + CD3FBB0BBE384A5810065C4D88D035A4 /* ASLayoutTransition.mm in Sources */, + 5F8115BA11746D390E7FE2DA8B4E195F /* ASLog.mm in Sources */, + 5E6DD54DC1FCC7FA938B7BDBFA6653AA /* ASMainSerialQueue.mm in Sources */, + 0671472257D854CBC8F14A6460CBD70A /* ASMainThreadDeallocation.mm in Sources */, + 1B3BD47E66BBC5F931984D74A3E0047A /* ASMapNode.mm in Sources */, + 8E2EEA6890AA83F0C1689F5273B37B8F /* ASMultiplexImageNode.mm in Sources */, + 2FD93AFDC2A9F0BE1EEF74E89B5AD84B /* ASMutableAttributedStringBuilder.mm in Sources */, + CE2FBCFCF10B33851635AE494E68C29D /* ASMutableElementMap.mm in Sources */, + FF15EAE598D7123397737DBC37BE77A0 /* ASNavigationController.mm in Sources */, + 7185AD0B6E50617EB5B063D049F7381E /* ASNetworkImageLoadInfo.mm in Sources */, + 29CAF11CD62CCBDC701E051CBF424C23 /* ASNetworkImageNode.mm in Sources */, + 0DB894195A40459248365D08C82C0BC5 /* ASNodeController+Beta.mm in Sources */, + 50EEFB8254EAF126BDAF9F4BB2F41EC1 /* ASObjectDescriptionHelpers.mm in Sources */, + CD2A3263FA9B3629F7D373E5D898B530 /* ASOverlayLayoutSpec.mm in Sources */, + 238353CE094AADDB351A5AE7D730F8F0 /* ASPagerFlowLayout.mm in Sources */, + 14B7A03552B6697528E765E9DD51F34F /* ASPagerNode.mm in Sources */, + 25BDEB129E29246C4BB4044B849B90D6 /* ASPageTable.mm in Sources */, + C7B9FC1B5CFDD47203D8019533E52F0B /* ASPendingStateController.mm in Sources */, + 93B61C99B25B247897629D42186F02AB /* ASPhotosFrameworkImageRequest.mm in Sources */, + 79D79C15A67C285A4FE840C164A61EAD /* ASPINRemoteImageDownloader.mm in Sources */, + 2490B901F64B7A71103C5BACBBC14195 /* ASRangeController.mm in Sources */, + D5F9094AB618823BE2D258D83450ACF8 /* ASRatioLayoutSpec.mm in Sources */, + 4404103D44CA3218F025DA315D800010 /* ASRecursiveUnfairLock.mm in Sources */, + A7D8E709A23130883C176E7E6572C392 /* ASRelativeLayoutSpec.mm in Sources */, + 34ADECC6CF4DB77F274F6ED5178FA3FF /* ASResponderChainEnumerator.mm in Sources */, + DFB3623B8303E1732A2DE7866457BFA5 /* ASRunLoopQueue.mm in Sources */, + 3DD4626268615851397E19A6D6F5E48A /* ASScrollDirection.mm in Sources */, + 90A83F1084AF8045B4B8EB567B188C4A /* ASScrollNode.mm in Sources */, + 379C8AD0CBCD05F0AC0AAD83E05058A5 /* ASSection.mm in Sources */, + 9F8BD0381058FDEDA5F0671FA6B3A02B /* ASStackLayoutSpec.mm in Sources */, + 9DB3E03C376FC722E70B094C2DF85DC2 /* ASStackPositionedLayout.mm in Sources */, + 4775D60B018E3B4BA0C456FCC93A3489 /* ASStackUnpositionedLayout.mm in Sources */, + F60818A3F394E384B349C7F3FD8896D1 /* ASTabBarController.mm in Sources */, + E13C6028A12C4D85EBAAAA04C96FF536 /* ASTableLayoutController.mm in Sources */, + 3EDE54BDA1749D9AA3F967EEE4C819A4 /* ASTableNode.mm in Sources */, + BF2296DF6290E9C557FF126C8EBBAA7F /* ASTableView.mm in Sources */, + 38B66488888B06F1D215DD4E907C8613 /* ASTextAttribute.mm in Sources */, + F40F8AD0FB1CAA4C367596C50912AF95 /* ASTextDebugOption.mm in Sources */, + 0ECB1E4D8FA0F073DC3CF9E5EBCA4370 /* ASTextInput.mm in Sources */, + C75C96A4CB5497109200277BC3B826AC /* ASTextKitAttributes.mm in Sources */, + FF4F84E904D5B54DD1E596BB8BB3C4B2 /* ASTextKitComponents.mm in Sources */, + 675811F894E8250FD70813BE06DF5E80 /* ASTextKitContext.mm in Sources */, + 17EE7FE8DDF79CAF395E4A3080D7B035 /* ASTextKitCoreTextAdditions.mm in Sources */, + 74E1593D7726C6250595B3640FC6908B /* ASTextKitEntityAttribute.mm in Sources */, + 82E0F28243DC81433820662B2BEA008C /* ASTextKitFontSizeAdjuster.mm in Sources */, + E3B60613FB36F520EAA02E7C405B4B10 /* ASTextKitRenderer+Positioning.mm in Sources */, + 5B2D3DF94C9F82AB1317E69DB48E8372 /* ASTextKitRenderer+TextChecking.mm in Sources */, + EF68CE5C31FA5D353DFA4536D85E7067 /* ASTextKitRenderer.mm in Sources */, + 40F7A9A1246007960D1C10A328562B85 /* ASTextKitShadower.mm in Sources */, + 80C24E482ED0EC7870E04616ECCD439A /* ASTextKitTailTruncater.mm in Sources */, + A42EC5DE809DA43B92C0A1FB1591A823 /* ASTextLayout.mm in Sources */, + 9E3882ED3E01A3E7A88E9667C0A0636E /* ASTextLine.mm in Sources */, + F1B69EBD630EA9C41E76FAF9B4878266 /* ASTextNode.mm in Sources */, + F01A56AD8FC4D13459E7A4088437DD60 /* ASTextNode2.mm in Sources */, + 9268301826042884BD37C81E02D6AA0C /* ASTextNodeWordKerner.mm in Sources */, + 781DA851FF46FF8A30B48C5E8115FE1B /* ASTextRunDelegate.mm in Sources */, + 1414F0DF56982DFC65C6E01ECE6A4374 /* ASTextUtilities.mm in Sources */, + C2C213FC002E05563E7A5E1688008092 /* ASTip.mm in Sources */, + A62703EF376B85BECA85A817C34DAEF3 /* ASTipNode.mm in Sources */, + 789C7B17F586529CFB16874F11D0612E /* ASTipProvider.mm in Sources */, + 78832930615717444B99AF06239A0497 /* ASTipsController.mm in Sources */, + 8FB8E71EDEEC0BAF253697B14D1A562D /* ASTipsWindow.mm in Sources */, + EB5CB15BE4021AEEE889E49C99751359 /* ASTraitCollection.mm in Sources */, + 09D5CC75390536E1FD6F02F5AE3862AE /* ASTwoDimensionalArrayUtils.mm in Sources */, + C6280E10614BA6CF600C8A80BC1C426E /* ASVideoNode.mm in Sources */, + E0B5B77C3B206FBB576000E18BF9C56A /* ASVideoPlayerNode.mm in Sources */, + BE96104ACD0169003A3A222039106C33 /* ASVisibilityProtocols.mm in Sources */, + 25F7C6715BCCB73CAA6C20FAE292CF9B /* ASWeakMap.mm in Sources */, + 982632D85E47F99F7181D8F921276FC8 /* ASWeakProxy.mm in Sources */, + 8D1C38CA2C15929280CC6482BFB46216 /* ASWeakSet.mm in Sources */, + CCF02EB012ED66CDE4DCEFE3C6731517 /* AsyncDisplayKit+Debug.mm in Sources */, + E99888C1284FD4BA652BFCCBA42A69D0 /* AsyncDisplayKit+IGListKitMethods.mm in Sources */, + 641B4519AF9A061FA679FD64B3CB984B /* AsyncDisplayKit+Tips.mm in Sources */, + 5A5D5F0390336EF5FB4E0C7E3A250AA0 /* ASYogaUtilities.mm in Sources */, + 6851E1AB040073EBF7F89AE04AF025F4 /* IGListAdapter+AsyncDisplayKit.mm in Sources */, + 715A56F9131273D746C866720B84F7FF /* NSArray+Diffing.mm in Sources */, + E192EDBF272B1EF298F8FE2FA6DDAF25 /* NSAttributedString+ASText.mm in Sources */, + 7DF36B3FA296898C2CE708F7A547929A /* NSIndexSet+ASHelpers.mm in Sources */, + A7319DA9AD51B4B7EA19086A54DD05DB /* NSMutableAttributedString+TextKitAdditions.mm in Sources */, + C5F8FA0CB5BB90D56C13A44180EE8329 /* NSParagraphStyle+ASText.mm in Sources */, + C1E1845DA59006D300466D68C4A6F56F /* Texture-dummy.m in Sources */, + E977832761EAFD3D2EF100FB74A57717 /* UICollectionViewLayout+ASConvenience.mm in Sources */, + 6549266D127F7CF008B8120024AD1826 /* UIImage+ASConvenience.mm in Sources */, + A27474180106AAAB41F2871304AC64CC /* UIResponder+AsyncDisplayKit.mm in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 370C15A7318C7C373D68B2F03436488F /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 08E319FC15C276F9F94CE6E16D9F3D06 /* PINOperation-dummy.m in Sources */, + 9234C6A8A3D029A8D124CEFB260B8157 /* PINOperationGroup.m in Sources */, + CFC2BE7DC7C763B8A173C5151E646630 /* PINOperationQueue.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 6D126D8E6EF94CE128B535484322F538 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + DCC0959800E3EC695210D0372765FBFA /* NSData+ImageDetectors.m in Sources */, + E83BD30C134DC77082910C65567F529D /* NSHTTPURLResponse+MaxAge.m in Sources */, + EC8A4029FDCEAEEB3810CF75685F7878 /* PINAlternateRepresentationProvider.m in Sources */, + 8DC6B9827D1C470510DC8F6E2F8B7086 /* PINAnimatedImage.m in Sources */, + 744967C02A56404990BC6E88C01BA548 /* PINAnimatedImageView+PINRemoteImage.m in Sources */, + 2984AD51F86779E093DBAF2C5DA6CB67 /* PINAnimatedImageView.m in Sources */, + 861967ACB77A0D10A198A15478A9FE5F /* PINButton+PINRemoteImage.m in Sources */, + 285F918F7A4896FA40D130FD5F6955EA /* PINCache+PINRemoteImageCaching.m in Sources */, + DD519256677113580497A433745E2AA5 /* PINCachedAnimatedImage.m in Sources */, + A86B4CF5B6E0E90E2002EFD6ECA2E74A /* PINDisplayLink.m in Sources */, + BF6DF4769ECC4026C46285CB77452357 /* PINGIFAnimatedImage.m in Sources */, + 8D5C3CB94A4EDD4BD08D8A18A9BD7356 /* PINImage+DecodedImage.m in Sources */, + 2799C7F1375B432E35F44F6B2011315E /* PINImage+ScaledImage.m in Sources */, + 2BC2B38EA553A533961723CECFC08DA2 /* PINImage+WebP.m in Sources */, + 74242DB7DEB36F897B59CC95AED2BDAE /* PINImageView+PINRemoteImage.m in Sources */, + 07DA5777E40B8C2671D3103F8E76F969 /* PINProgressiveImage.m in Sources */, + FA3B40D2C4CB095BE4314E8F1FF7000C /* PINRemoteImage-dummy.m in Sources */, + 386D4195994F9806D03777081DAEADC4 /* PINRemoteImageBasicCache.m in Sources */, + EBB22015243AF51E4A657BEB9377011D /* PINRemoteImageCallbacks.m in Sources */, + 0D6962F9248D95D9655C562687D377DD /* PINRemoteImageCategoryManager.m in Sources */, + 36C8E40A9B88DEE0CDE6738E6CE9B613 /* PINRemoteImageDownloadQueue.m in Sources */, + 3B4BDFF8FC2D4FC9050E5A18F96634F2 /* PINRemoteImageDownloadTask.m in Sources */, + ECB48648B7061F5DA13D61BF35E9E675 /* PINRemoteImageManager.m in Sources */, + 33A58AB76D5143C6639B180900E8A4AB /* PINRemoteImageManagerConfiguration.m in Sources */, + 9BB8B7D153A17A4341454EB34059F2D9 /* PINRemoteImageManagerResult.m in Sources */, + 78EA95A196E301CABCA9B8EF56972FE5 /* PINRemoteImageMemoryContainer.m in Sources */, + 047062E4C55AF0E9668F0D8A5210D4FA /* PINRemoteImageProcessorTask.m in Sources */, + 5D596E9F8BA6D87E1ADD712A13F131CF /* PINRemoteImageTask.m in Sources */, + D0D844293600CC08BA0440D086DF6F31 /* PINRemoteLock.m in Sources */, + DE51CF9E4A5474D3988D1BB66EF7618D /* PINRemoteWeakProxy.m in Sources */, + 7C9889D2309D0F679BEFAEF035704D5B /* PINRequestRetryStrategy.m in Sources */, + 1B264109896A02DB437B7C2FF68DF711 /* PINResume.m in Sources */, + FF156CB786C29B397B3A97686DDDA057 /* PINSpeedRecorder.m in Sources */, + ED7E794ADD340BEFFC80FC75837CFBBC /* PINURLSessionManager.m in Sources */, + 23E0F25169AB9190135651F5D06E8AFE /* PINWebPAnimatedImage.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + C1B61E7C7CFBEB67C3AF77D3B185E9FD /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + EC5DD146A3413EF265E41C662ACFEA4B /* PINCache-dummy.m in Sources */, + 3364AEA7B40321356FE24B945F836C81 /* PINCache.m in Sources */, + 7662C10817C380C0792B383089502CAE /* PINDiskCache.m in Sources */, + C5F9B33244F7F718765BD17232268474 /* PINMemoryCache.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DBA8352CA0BCA6D355FEEDBE3A24A570 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 92030EC136552B4DBF56BADD013C1304 /* Pods-Starbucks_Clone_TexBrother-dummy.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 1EEF08635F68867D626CCFE02C2563B0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = PINRemoteImage; + target = E2A8B2A28D2EAB2E4CCF3B69E6792851 /* PINRemoteImage */; + targetProxy = 88E412F7FD6AAAEE34058E9863C18479 /* PBXContainerItemProxy */; + }; + 2E567167E7240D0E74CE1A9CA537C23A /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = PINOperation; + target = 57310B016450E63387C9D64F4933E995 /* PINOperation */; + targetProxy = 25C016BF0E57B6AE0FB3E0108AD294A3 /* PBXContainerItemProxy */; + }; + 3F6F6A6ADE989E5894E449CC5FC66F7D /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = PINOperation; + target = 57310B016450E63387C9D64F4933E995 /* PINOperation */; + targetProxy = 602227D31B6AB6F04095141DDBF80D9D /* PBXContainerItemProxy */; + }; + 5CF2963FF9F2A40ECFEFFD2CB1B1AAB8 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = PINCache; + target = A927C3CD51C30012080D6CA07959B246 /* PINCache */; + targetProxy = 0D185FAE68986375EFA80D7E588C36F0 /* PBXContainerItemProxy */; + }; + 5EF23CBE2A082130F9831C1406C1036F /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = PINCache; + target = A927C3CD51C30012080D6CA07959B246 /* PINCache */; + targetProxy = 771B02F0B3D3712E3B8E9ACB4406E929 /* PBXContainerItemProxy */; + }; + BBE3689CBA0E35DA23F6D6D157F73AE3 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = Texture; + target = DB66E43BC980D741F3C7C5CF3ACEEFA4 /* Texture */; + targetProxy = 6351C529E119A5555556668E09168553 /* PBXContainerItemProxy */; + }; + DBF8386B95C87D886559CC0035805857 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = PINOperation; + target = 57310B016450E63387C9D64F4933E995 /* PINOperation */; + targetProxy = 87D1D5F03D5166DB556E01B4BBC3A40C /* PBXContainerItemProxy */; + }; + FFF0EDCF2A8250963360132ACEA7C16C /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = PINRemoteImage; + target = E2A8B2A28D2EAB2E4CCF3B69E6792851 /* PINRemoteImage */; + targetProxy = F724C535E2BD3CB7050BFBC52A0D455E /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 035FB835B9DD1B51618E10395BAA9EF2 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 49EC90EF95A2BAABAA44D24EA9E6115B /* PINCache.debug.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/PINCache/PINCache-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/PINCache/PINCache-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MODULEMAP_FILE = "Target Support Files/PINCache/PINCache.modulemap"; + PRODUCT_MODULE_NAME = PINCache; + PRODUCT_NAME = PINCache; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 0A505670016E3B81DA99E384DCFC6B16 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = BAB31548FC8D5AB50A8EB7217A7712E4 /* PINRemoteImage.debug.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = "Target Support Files/PINRemoteImage/PINRemoteImage-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MODULEMAP_FILE = "Target Support Files/PINRemoteImage/PINRemoteImage.modulemap"; + PRODUCT_MODULE_NAME = PINRemoteImage; + PRODUCT_NAME = PINRemoteImage; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 3BE5FCEBFCF554FBC3E3735E61D0D162 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 14D1789AFF19612903A6DEC3719662F3 /* Texture.debug.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/Texture/Texture-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/Texture/Texture-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MODULEMAP_FILE = "Target Support Files/Texture/Texture.modulemap"; + PRODUCT_MODULE_NAME = AsyncDisplayKit; + PRODUCT_NAME = AsyncDisplayKit; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 6452ADB9CE2287F29C8A172C038A2C54 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = ED201A924E0BC9468579E76F2F2FE7AD /* PINOperation.debug.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/PINOperation/PINOperation-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/PINOperation/PINOperation-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MODULEMAP_FILE = "Target Support Files/PINOperation/PINOperation.modulemap"; + PRODUCT_MODULE_NAME = PINOperation; + PRODUCT_NAME = PINOperation; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 77CC427B1FD68936D34C723856838086 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7DBE419CB2F290B559A7011979017C7D /* Texture.release.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/Texture/Texture-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/Texture/Texture-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MODULEMAP_FILE = "Target Support Files/Texture/Texture.modulemap"; + PRODUCT_MODULE_NAME = AsyncDisplayKit; + PRODUCT_NAME = AsyncDisplayKit; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 7CC582C364CB71B789A9CA221B981D2F /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 97CA4CF3A8DAB8BAF80805672B0FC6FF /* Pods-Starbucks_Clone_TexBrother.release.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + CLANG_ENABLE_OBJC_WEAK = NO; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = "Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + MODULEMAP_FILE = "Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother.modulemap"; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 8DE5143C03248BB6CD542DE3963D6F3A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "POD_CONFIGURATION_DEBUG=1", + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + STRIP_INSTALLED_PRODUCT = NO; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + SYMROOT = "${SRCROOT}/../build"; + }; + name = Debug; + }; + 934BCAB874D613A787A437914705852C /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 3F5A299D3E06C987FD257CCE00A8C1C2 /* Pods-Starbucks_Clone_TexBrother.debug.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + CLANG_ENABLE_OBJC_WEAK = NO; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = "Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + MODULEMAP_FILE = "Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother.modulemap"; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 9E406C6AAF85E580207CD97B0044DEAB /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "POD_CONFIGURATION_RELEASE=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + STRIP_INSTALLED_PRODUCT = NO; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_VERSION = 5.0; + SYMROOT = "${SRCROOT}/../build"; + }; + name = Release; + }; + BFA5F2335A9CA2C4F9D383AAAFBF6667 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 0ECB2E92B3BD6B8EF0C554FFB94D06B1 /* PINCache.release.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/PINCache/PINCache-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/PINCache/PINCache-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MODULEMAP_FILE = "Target Support Files/PINCache/PINCache.modulemap"; + PRODUCT_MODULE_NAME = PINCache; + PRODUCT_NAME = PINCache; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + D42B906D52A05CD497B2EB2E22FFF55F /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 2C41CB510A0394AE786A47727010CFF4 /* PINOperation.release.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/PINOperation/PINOperation-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/PINOperation/PINOperation-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MODULEMAP_FILE = "Target Support Files/PINOperation/PINOperation.modulemap"; + PRODUCT_MODULE_NAME = PINOperation; + PRODUCT_NAME = PINOperation; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + DA396EDF99868E48276B93BF6AA83B09 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 773FC3074F118DBE19C9C2A18699AB84 /* PINRemoteImage.release.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = "Target Support Files/PINRemoteImage/PINRemoteImage-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MODULEMAP_FILE = "Target Support Files/PINRemoteImage/PINRemoteImage.modulemap"; + PRODUCT_MODULE_NAME = PINRemoteImage; + PRODUCT_NAME = PINRemoteImage; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 1000880B548699E9BBD74146AFFA3702 /* Build configuration list for PBXNativeTarget "PINRemoteImage" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 0A505670016E3B81DA99E384DCFC6B16 /* Debug */, + DA396EDF99868E48276B93BF6AA83B09 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 4821239608C13582E20E6DA73FD5F1F9 /* Build configuration list for PBXProject "Pods" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 8DE5143C03248BB6CD542DE3963D6F3A /* Debug */, + 9E406C6AAF85E580207CD97B0044DEAB /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 5CA1823A92CB850EAF9AD183366ACC9B /* Build configuration list for PBXNativeTarget "PINOperation" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6452ADB9CE2287F29C8A172C038A2C54 /* Debug */, + D42B906D52A05CD497B2EB2E22FFF55F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 78DCB2C5EC3B5BA90EEB95A1ACFFDF0E /* Build configuration list for PBXNativeTarget "Texture" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 3BE5FCEBFCF554FBC3E3735E61D0D162 /* Debug */, + 77CC427B1FD68936D34C723856838086 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + B794DF6469B3D94E9347E82D67AFAF6E /* Build configuration list for PBXNativeTarget "PINCache" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 035FB835B9DD1B51618E10395BAA9EF2 /* Debug */, + BFA5F2335A9CA2C4F9D383AAAFBF6667 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + DDC8A7405D8AB8B8486C2A8EAF23232E /* Build configuration list for PBXNativeTarget "Pods-Starbucks_Clone_TexBrother" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 934BCAB874D613A787A437914705852C /* Debug */, + 7CC582C364CB71B789A9CA221B981D2F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = BFDFE7DC352907FC980B868725387E98 /* Project object */; +} diff --git a/Starbucks_Clone_TexBrother/Pods/Target Support Files/PINCache/PINCache-Info.plist b/Starbucks_Clone_TexBrother/Pods/Target Support Files/PINCache/PINCache-Info.plist new file mode 100644 index 0000000..b3ee339 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Target Support Files/PINCache/PINCache-Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 3.0.3 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/Starbucks_Clone_TexBrother/Pods/Target Support Files/PINCache/PINCache-dummy.m b/Starbucks_Clone_TexBrother/Pods/Target Support Files/PINCache/PINCache-dummy.m new file mode 100644 index 0000000..717c2b7 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Target Support Files/PINCache/PINCache-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_PINCache : NSObject +@end +@implementation PodsDummy_PINCache +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Target Support Files/PINCache/PINCache-prefix.pch b/Starbucks_Clone_TexBrother/Pods/Target Support Files/PINCache/PINCache-prefix.pch new file mode 100644 index 0000000..2b29dbc --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Target Support Files/PINCache/PINCache-prefix.pch @@ -0,0 +1,15 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + +#ifndef TARGET_OS_WATCH + #define TARGET_OS_WATCH 0 +#endif diff --git a/Starbucks_Clone_TexBrother/Pods/Target Support Files/PINCache/PINCache-umbrella.h b/Starbucks_Clone_TexBrother/Pods/Target Support Files/PINCache/PINCache-umbrella.h new file mode 100644 index 0000000..8c5b2c0 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Target Support Files/PINCache/PINCache-umbrella.h @@ -0,0 +1,22 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + +#import "PINCache.h" +#import "PINCacheMacros.h" +#import "PINCacheObjectSubscripting.h" +#import "PINCaching.h" +#import "PINDiskCache.h" +#import "PINMemoryCache.h" + +FOUNDATION_EXPORT double PINCacheVersionNumber; +FOUNDATION_EXPORT const unsigned char PINCacheVersionString[]; + diff --git a/Starbucks_Clone_TexBrother/Pods/Target Support Files/PINCache/PINCache.debug.xcconfig b/Starbucks_Clone_TexBrother/Pods/Target Support Files/PINCache/PINCache.debug.xcconfig new file mode 100644 index 0000000..2aa8e20 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Target Support Files/PINCache/PINCache.debug.xcconfig @@ -0,0 +1,13 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/PINCache +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/PINOperation" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +OTHER_LDFLAGS = $(inherited) -framework "Foundation" -weak_framework "UIKit" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/PINCache +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Starbucks_Clone_TexBrother/Pods/Target Support Files/PINCache/PINCache.modulemap b/Starbucks_Clone_TexBrother/Pods/Target Support Files/PINCache/PINCache.modulemap new file mode 100644 index 0000000..d6699ef --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Target Support Files/PINCache/PINCache.modulemap @@ -0,0 +1,6 @@ +framework module PINCache { + umbrella header "PINCache-umbrella.h" + + export * + module * { export * } +} diff --git a/Starbucks_Clone_TexBrother/Pods/Target Support Files/PINCache/PINCache.release.xcconfig b/Starbucks_Clone_TexBrother/Pods/Target Support Files/PINCache/PINCache.release.xcconfig new file mode 100644 index 0000000..2aa8e20 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Target Support Files/PINCache/PINCache.release.xcconfig @@ -0,0 +1,13 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/PINCache +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/PINOperation" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +OTHER_LDFLAGS = $(inherited) -framework "Foundation" -weak_framework "UIKit" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/PINCache +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Starbucks_Clone_TexBrother/Pods/Target Support Files/PINOperation/PINOperation-Info.plist b/Starbucks_Clone_TexBrother/Pods/Target Support Files/PINOperation/PINOperation-Info.plist new file mode 100644 index 0000000..19f73ea --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Target Support Files/PINOperation/PINOperation-Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.2.1 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/Starbucks_Clone_TexBrother/Pods/Target Support Files/PINOperation/PINOperation-dummy.m b/Starbucks_Clone_TexBrother/Pods/Target Support Files/PINOperation/PINOperation-dummy.m new file mode 100644 index 0000000..209943f --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Target Support Files/PINOperation/PINOperation-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_PINOperation : NSObject +@end +@implementation PodsDummy_PINOperation +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Target Support Files/PINOperation/PINOperation-prefix.pch b/Starbucks_Clone_TexBrother/Pods/Target Support Files/PINOperation/PINOperation-prefix.pch new file mode 100644 index 0000000..2b29dbc --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Target Support Files/PINOperation/PINOperation-prefix.pch @@ -0,0 +1,15 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + +#ifndef TARGET_OS_WATCH + #define TARGET_OS_WATCH 0 +#endif diff --git a/Starbucks_Clone_TexBrother/Pods/Target Support Files/PINOperation/PINOperation-umbrella.h b/Starbucks_Clone_TexBrother/Pods/Target Support Files/PINOperation/PINOperation-umbrella.h new file mode 100644 index 0000000..ea9f6ae --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Target Support Files/PINOperation/PINOperation-umbrella.h @@ -0,0 +1,21 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + +#import "PINOperation.h" +#import "PINOperationGroup.h" +#import "PINOperationMacros.h" +#import "PINOperationQueue.h" +#import "PINOperationTypes.h" + +FOUNDATION_EXPORT double PINOperationVersionNumber; +FOUNDATION_EXPORT const unsigned char PINOperationVersionString[]; + diff --git a/Starbucks_Clone_TexBrother/Pods/Target Support Files/PINOperation/PINOperation.debug.xcconfig b/Starbucks_Clone_TexBrother/Pods/Target Support Files/PINOperation/PINOperation.debug.xcconfig new file mode 100644 index 0000000..53d7629 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Target Support Files/PINOperation/PINOperation.debug.xcconfig @@ -0,0 +1,12 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/PINOperation +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +OTHER_LDFLAGS = $(inherited) -framework "Foundation" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/PINOperation +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Starbucks_Clone_TexBrother/Pods/Target Support Files/PINOperation/PINOperation.modulemap b/Starbucks_Clone_TexBrother/Pods/Target Support Files/PINOperation/PINOperation.modulemap new file mode 100644 index 0000000..653b58f --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Target Support Files/PINOperation/PINOperation.modulemap @@ -0,0 +1,6 @@ +framework module PINOperation { + umbrella header "PINOperation-umbrella.h" + + export * + module * { export * } +} diff --git a/Starbucks_Clone_TexBrother/Pods/Target Support Files/PINOperation/PINOperation.release.xcconfig b/Starbucks_Clone_TexBrother/Pods/Target Support Files/PINOperation/PINOperation.release.xcconfig new file mode 100644 index 0000000..53d7629 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Target Support Files/PINOperation/PINOperation.release.xcconfig @@ -0,0 +1,12 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/PINOperation +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +OTHER_LDFLAGS = $(inherited) -framework "Foundation" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/PINOperation +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Starbucks_Clone_TexBrother/Pods/Target Support Files/PINRemoteImage/PINRemoteImage-Info.plist b/Starbucks_Clone_TexBrother/Pods/Target Support Files/PINRemoteImage/PINRemoteImage-Info.plist new file mode 100644 index 0000000..b3ee339 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Target Support Files/PINRemoteImage/PINRemoteImage-Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 3.0.3 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/Starbucks_Clone_TexBrother/Pods/Target Support Files/PINRemoteImage/PINRemoteImage-dummy.m b/Starbucks_Clone_TexBrother/Pods/Target Support Files/PINRemoteImage/PINRemoteImage-dummy.m new file mode 100644 index 0000000..d122522 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Target Support Files/PINRemoteImage/PINRemoteImage-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_PINRemoteImage : NSObject +@end +@implementation PodsDummy_PINRemoteImage +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Target Support Files/PINRemoteImage/PINRemoteImage-umbrella.h b/Starbucks_Clone_TexBrother/Pods/Target Support Files/PINRemoteImage/PINRemoteImage-umbrella.h new file mode 100644 index 0000000..9fd0f73 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Target Support Files/PINRemoteImage/PINRemoteImage-umbrella.h @@ -0,0 +1,55 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + +#import "NSHTTPURLResponse+MaxAge.h" +#import "PINImage+DecodedImage.h" +#import "PINImage+ScaledImage.h" +#import "PINImage+WebP.h" +#import "PINRemoteImageTask+Subclassing.h" +#import "NSData+ImageDetectors.h" +#import "PINAlternateRepresentationProvider.h" +#import "PINAnimatedImage.h" +#import "PINAnimatedImageView+PINRemoteImage.h" +#import "PINAnimatedImageView.h" +#import "PINButton+PINRemoteImage.h" +#import "PINCachedAnimatedImage.h" +#import "PINGIFAnimatedImage.h" +#import "PINImageView+PINRemoteImage.h" +#import "PINProgressiveImage.h" +#import "PINRemoteImage.h" +#import "PINRemoteImageCaching.h" +#import "PINRemoteImageCategoryManager.h" +#import "PINRemoteImageMacros.h" +#import "PINRemoteImageManager.h" +#import "PINRemoteImageManagerResult.h" +#import "PINRequestRetryStrategy.h" +#import "PINURLSessionManager.h" +#import "PINWebPAnimatedImage.h" +#import "PINDisplayLink.h" +#import "PINRemoteImageBasicCache.h" +#import "PINRemoteImageCallbacks.h" +#import "PINRemoteImageDownloadQueue.h" +#import "PINRemoteImageDownloadTask.h" +#import "PINRemoteImageManager+Private.h" +#import "PINRemoteImageManagerConfiguration.h" +#import "PINRemoteImageMemoryContainer.h" +#import "PINRemoteImageProcessorTask.h" +#import "PINRemoteImageTask.h" +#import "PINRemoteLock.h" +#import "PINRemoteWeakProxy.h" +#import "PINResume.h" +#import "PINSpeedRecorder.h" +#import "PINCache+PINRemoteImageCaching.h" + +FOUNDATION_EXPORT double PINRemoteImageVersionNumber; +FOUNDATION_EXPORT const unsigned char PINRemoteImageVersionString[]; + diff --git a/Starbucks_Clone_TexBrother/Pods/Target Support Files/PINRemoteImage/PINRemoteImage.debug.xcconfig b/Starbucks_Clone_TexBrother/Pods/Target Support Files/PINRemoteImage/PINRemoteImage.debug.xcconfig new file mode 100644 index 0000000..2014576 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Target Support Files/PINRemoteImage/PINRemoteImage.debug.xcconfig @@ -0,0 +1,13 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/PINRemoteImage +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/PINCache" "${PODS_CONFIGURATION_BUILD_DIR}/PINOperation" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +OTHER_LDFLAGS = $(inherited) -framework "Accelerate" -framework "ImageIO" -framework "UIKit" -weak_framework "UIKit" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/PINRemoteImage +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Starbucks_Clone_TexBrother/Pods/Target Support Files/PINRemoteImage/PINRemoteImage.modulemap b/Starbucks_Clone_TexBrother/Pods/Target Support Files/PINRemoteImage/PINRemoteImage.modulemap new file mode 100644 index 0000000..d37f76b --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Target Support Files/PINRemoteImage/PINRemoteImage.modulemap @@ -0,0 +1,6 @@ +framework module PINRemoteImage { + umbrella header "PINRemoteImage-umbrella.h" + + export * + module * { export * } +} diff --git a/Starbucks_Clone_TexBrother/Pods/Target Support Files/PINRemoteImage/PINRemoteImage.release.xcconfig b/Starbucks_Clone_TexBrother/Pods/Target Support Files/PINRemoteImage/PINRemoteImage.release.xcconfig new file mode 100644 index 0000000..2014576 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Target Support Files/PINRemoteImage/PINRemoteImage.release.xcconfig @@ -0,0 +1,13 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/PINRemoteImage +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/PINCache" "${PODS_CONFIGURATION_BUILD_DIR}/PINOperation" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +OTHER_LDFLAGS = $(inherited) -framework "Accelerate" -framework "ImageIO" -framework "UIKit" -weak_framework "UIKit" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/PINRemoteImage +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Starbucks_Clone_TexBrother/Pods/Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother-Info.plist b/Starbucks_Clone_TexBrother/Pods/Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother-Info.plist new file mode 100644 index 0000000..2243fe6 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother-Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0.0 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/Starbucks_Clone_TexBrother/Pods/Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother-acknowledgements.markdown b/Starbucks_Clone_TexBrother/Pods/Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother-acknowledgements.markdown new file mode 100644 index 0000000..3c53a2f --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother-acknowledgements.markdown @@ -0,0 +1,753 @@ +# Acknowledgements +This application makes use of the following third party libraries: + +## PINCache + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [2013] [Tumblr, Inc.] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +## PINOperation + +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + +## PINRemoteImage + +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + +## Texture + +The Texture project was created by Pinterest as a continuation, under a different +name and license, of the AsyncDisplayKit codebase originally developed by Facebook. + +All code in Texture is covered by the Apache License, Version 2.0. + +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + +Generated by CocoaPods - https://cocoapods.org diff --git a/Starbucks_Clone_TexBrother/Pods/Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother-acknowledgements.plist b/Starbucks_Clone_TexBrother/Pods/Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother-acknowledgements.plist new file mode 100644 index 0000000..4d48ec8 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother-acknowledgements.plist @@ -0,0 +1,803 @@ + + + + + PreferenceSpecifiers + + + FooterText + This application makes use of the following third party libraries: + Title + Acknowledgements + Type + PSGroupSpecifier + + + FooterText + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [2013] [Tumblr, Inc.] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + License + Apache 2.0 + Title + PINCache + Type + PSGroupSpecifier + + + FooterText + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + License + Apache 2.0 + Title + PINOperation + Type + PSGroupSpecifier + + + FooterText + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + License + Apache 2.0 + Title + PINRemoteImage + Type + PSGroupSpecifier + + + FooterText + The Texture project was created by Pinterest as a continuation, under a different +name and license, of the AsyncDisplayKit codebase originally developed by Facebook. + +All code in Texture is covered by the Apache License, Version 2.0. + +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + License + Apache 2 + Title + Texture + Type + PSGroupSpecifier + + + FooterText + Generated by CocoaPods - https://cocoapods.org + Title + + Type + PSGroupSpecifier + + + StringsTable + Acknowledgements + Title + Acknowledgements + + diff --git a/Starbucks_Clone_TexBrother/Pods/Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother-dummy.m b/Starbucks_Clone_TexBrother/Pods/Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother-dummy.m new file mode 100644 index 0000000..c7a1af0 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_Pods_Starbucks_Clone_TexBrother : NSObject +@end +@implementation PodsDummy_Pods_Starbucks_Clone_TexBrother +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother-frameworks-Debug-input-files.xcfilelist b/Starbucks_Clone_TexBrother/Pods/Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother-frameworks-Debug-input-files.xcfilelist new file mode 100644 index 0000000..80c704b --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother-frameworks-Debug-input-files.xcfilelist @@ -0,0 +1,5 @@ +${PODS_ROOT}/Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother-frameworks.sh +${BUILT_PRODUCTS_DIR}/PINCache/PINCache.framework +${BUILT_PRODUCTS_DIR}/PINOperation/PINOperation.framework +${BUILT_PRODUCTS_DIR}/PINRemoteImage/PINRemoteImage.framework +${BUILT_PRODUCTS_DIR}/Texture/AsyncDisplayKit.framework \ No newline at end of file diff --git a/Starbucks_Clone_TexBrother/Pods/Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother-frameworks-Debug-output-files.xcfilelist b/Starbucks_Clone_TexBrother/Pods/Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother-frameworks-Debug-output-files.xcfilelist new file mode 100644 index 0000000..8251566 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother-frameworks-Debug-output-files.xcfilelist @@ -0,0 +1,4 @@ +${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/PINCache.framework +${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/PINOperation.framework +${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/PINRemoteImage.framework +${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AsyncDisplayKit.framework \ No newline at end of file diff --git a/Starbucks_Clone_TexBrother/Pods/Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother-frameworks-Release-input-files.xcfilelist b/Starbucks_Clone_TexBrother/Pods/Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother-frameworks-Release-input-files.xcfilelist new file mode 100644 index 0000000..80c704b --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother-frameworks-Release-input-files.xcfilelist @@ -0,0 +1,5 @@ +${PODS_ROOT}/Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother-frameworks.sh +${BUILT_PRODUCTS_DIR}/PINCache/PINCache.framework +${BUILT_PRODUCTS_DIR}/PINOperation/PINOperation.framework +${BUILT_PRODUCTS_DIR}/PINRemoteImage/PINRemoteImage.framework +${BUILT_PRODUCTS_DIR}/Texture/AsyncDisplayKit.framework \ No newline at end of file diff --git a/Starbucks_Clone_TexBrother/Pods/Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother-frameworks-Release-output-files.xcfilelist b/Starbucks_Clone_TexBrother/Pods/Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother-frameworks-Release-output-files.xcfilelist new file mode 100644 index 0000000..8251566 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother-frameworks-Release-output-files.xcfilelist @@ -0,0 +1,4 @@ +${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/PINCache.framework +${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/PINOperation.framework +${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/PINRemoteImage.framework +${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AsyncDisplayKit.framework \ No newline at end of file diff --git a/Starbucks_Clone_TexBrother/Pods/Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother-frameworks.sh b/Starbucks_Clone_TexBrother/Pods/Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother-frameworks.sh new file mode 100755 index 0000000..6a64bfc --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother-frameworks.sh @@ -0,0 +1,191 @@ +#!/bin/sh +set -e +set -u +set -o pipefail + +function on_error { + echo "$(realpath -mq "${0}"):$1: error: Unexpected failure" +} +trap 'on_error $LINENO' ERR + +if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then + # If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy + # frameworks to, so exit 0 (signalling the script phase was successful). + exit 0 +fi + +echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" +mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + +COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}" +SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" +BCSYMBOLMAP_DIR="BCSymbolMaps" + + +# This protects against multiple targets copying the same framework dependency at the same time. The solution +# was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html +RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") + +# Copies and strips a vendored framework +install_framework() +{ + if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then + local source="${BUILT_PRODUCTS_DIR}/$1" + elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then + local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" + elif [ -r "$1" ]; then + local source="$1" + fi + + local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + + if [ -L "${source}" ]; then + echo "Symlinked..." + source="$(readlink "${source}")" + fi + + if [ -d "${source}/${BCSYMBOLMAP_DIR}" ]; then + # Locate and install any .bcsymbolmaps if present, and remove them from the .framework before the framework is copied + find "${source}/${BCSYMBOLMAP_DIR}" -name "*.bcsymbolmap"|while read f; do + echo "Installing $f" + install_bcsymbolmap "$f" "$destination" + rm "$f" + done + rmdir "${source}/${BCSYMBOLMAP_DIR}" + fi + + # Use filter instead of exclude so missing patterns don't throw errors. + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" + + local basename + basename="$(basename -s .framework "$1")" + binary="${destination}/${basename}.framework/${basename}" + + if ! [ -r "$binary" ]; then + binary="${destination}/${basename}" + elif [ -L "${binary}" ]; then + echo "Destination binary is symlinked..." + dirname="$(dirname "${binary}")" + binary="${dirname}/$(readlink "${binary}")" + fi + + # Strip invalid architectures so "fat" simulator / device frameworks work on device + if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then + strip_invalid_archs "$binary" + fi + + # Resign the code if required by the build settings to avoid unstable apps + code_sign_if_enabled "${destination}/$(basename "$1")" + + # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. + if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then + local swift_runtime_libs + swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u) + for lib in $swift_runtime_libs; do + echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" + rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" + code_sign_if_enabled "${destination}/${lib}" + done + fi +} +# Copies and strips a vendored dSYM +install_dsym() { + local source="$1" + warn_missing_arch=${2:-true} + if [ -r "$source" ]; then + # Copy the dSYM into the targets temp dir. + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\"" + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}" + + local basename + basename="$(basename -s .dSYM "$source")" + binary_name="$(ls "$source/Contents/Resources/DWARF")" + binary="${DERIVED_FILES_DIR}/${basename}.dSYM/Contents/Resources/DWARF/${binary_name}" + + # Strip invalid architectures from the dSYM. + if [[ "$(file "$binary")" == *"Mach-O "*"dSYM companion"* ]]; then + strip_invalid_archs "$binary" "$warn_missing_arch" + fi + if [[ $STRIP_BINARY_RETVAL == 0 ]]; then + # Move the stripped file into its final destination. + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\"" + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.dSYM" "${DWARF_DSYM_FOLDER_PATH}" + else + # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing. + touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.dSYM" + fi + fi +} + +# Used as a return value for each invocation of `strip_invalid_archs` function. +STRIP_BINARY_RETVAL=0 + +# Strip invalid architectures +strip_invalid_archs() { + binary="$1" + warn_missing_arch=${2:-true} + # Get architectures for current target binary + binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)" + # Intersect them with the architectures we are building for + intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)" + # If there are no archs supported by this binary then warn the user + if [[ -z "$intersected_archs" ]]; then + if [[ "$warn_missing_arch" == "true" ]]; then + echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." + fi + STRIP_BINARY_RETVAL=1 + return + fi + stripped="" + for arch in $binary_archs; do + if ! [[ "${ARCHS}" == *"$arch"* ]]; then + # Strip non-valid architectures in-place + lipo -remove "$arch" -output "$binary" "$binary" + stripped="$stripped $arch" + fi + done + if [[ "$stripped" ]]; then + echo "Stripped $binary of architectures:$stripped" + fi + STRIP_BINARY_RETVAL=0 +} + +# Copies the bcsymbolmap files of a vendored framework +install_bcsymbolmap() { + local bcsymbolmap_path="$1" + local destination="${BUILT_PRODUCTS_DIR}" + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}"" + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}" +} + +# Signs a framework with the provided identity +code_sign_if_enabled() { + if [ -n "${EXPANDED_CODE_SIGN_IDENTITY:-}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then + # Use the current code_sign_identity + echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" + local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'" + + if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then + code_sign_cmd="$code_sign_cmd &" + fi + echo "$code_sign_cmd" + eval "$code_sign_cmd" + fi +} + +if [[ "$CONFIGURATION" == "Debug" ]]; then + install_framework "${BUILT_PRODUCTS_DIR}/PINCache/PINCache.framework" + install_framework "${BUILT_PRODUCTS_DIR}/PINOperation/PINOperation.framework" + install_framework "${BUILT_PRODUCTS_DIR}/PINRemoteImage/PINRemoteImage.framework" + install_framework "${BUILT_PRODUCTS_DIR}/Texture/AsyncDisplayKit.framework" +fi +if [[ "$CONFIGURATION" == "Release" ]]; then + install_framework "${BUILT_PRODUCTS_DIR}/PINCache/PINCache.framework" + install_framework "${BUILT_PRODUCTS_DIR}/PINOperation/PINOperation.framework" + install_framework "${BUILT_PRODUCTS_DIR}/PINRemoteImage/PINRemoteImage.framework" + install_framework "${BUILT_PRODUCTS_DIR}/Texture/AsyncDisplayKit.framework" +fi +if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then + wait +fi diff --git a/Starbucks_Clone_TexBrother/Pods/Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother-umbrella.h b/Starbucks_Clone_TexBrother/Pods/Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother-umbrella.h new file mode 100644 index 0000000..917fa97 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother-umbrella.h @@ -0,0 +1,16 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + + +FOUNDATION_EXPORT double Pods_Starbucks_Clone_TexBrotherVersionNumber; +FOUNDATION_EXPORT const unsigned char Pods_Starbucks_Clone_TexBrotherVersionString[]; + diff --git a/Starbucks_Clone_TexBrother/Pods/Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother.debug.xcconfig b/Starbucks_Clone_TexBrother/Pods/Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother.debug.xcconfig new file mode 100644 index 0000000..71b480e --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother.debug.xcconfig @@ -0,0 +1,12 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/PINCache" "${PODS_CONFIGURATION_BUILD_DIR}/PINOperation" "${PODS_CONFIGURATION_BUILD_DIR}/PINRemoteImage" "${PODS_CONFIGURATION_BUILD_DIR}/Texture" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 $(inherited) AS_USE_ASSETS_LIBRARY=1 $(inherited) AS_USE_MAPKIT=1 $(inherited) AS_USE_PHOTOS=1 $(inherited) AS_USE_VIDEO=1 +HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/PINCache/PINCache.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/PINOperation/PINOperation.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/PINRemoteImage/PINRemoteImage.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Texture/AsyncDisplayKit.framework/Headers" +LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' +OTHER_LDFLAGS = $(inherited) -l"c++" -framework "AVFoundation" -framework "Accelerate" -framework "AssetsLibrary" -framework "AsyncDisplayKit" -framework "CoreLocation" -framework "CoreMedia" -framework "Foundation" -framework "ImageIO" -framework "MapKit" -framework "PINCache" -framework "PINOperation" -framework "PINRemoteImage" -framework "Photos" -framework "UIKit" -weak_framework "UIKit" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_PODFILE_DIR_PATH = ${SRCROOT}/. +PODS_ROOT = ${SRCROOT}/Pods +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Starbucks_Clone_TexBrother/Pods/Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother.modulemap b/Starbucks_Clone_TexBrother/Pods/Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother.modulemap new file mode 100644 index 0000000..24e8bb5 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother.modulemap @@ -0,0 +1,6 @@ +framework module Pods_Starbucks_Clone_TexBrother { + umbrella header "Pods-Starbucks_Clone_TexBrother-umbrella.h" + + export * + module * { export * } +} diff --git a/Starbucks_Clone_TexBrother/Pods/Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother.release.xcconfig b/Starbucks_Clone_TexBrother/Pods/Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother.release.xcconfig new file mode 100644 index 0000000..71b480e --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother.release.xcconfig @@ -0,0 +1,12 @@ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/PINCache" "${PODS_CONFIGURATION_BUILD_DIR}/PINOperation" "${PODS_CONFIGURATION_BUILD_DIR}/PINRemoteImage" "${PODS_CONFIGURATION_BUILD_DIR}/Texture" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 $(inherited) AS_USE_ASSETS_LIBRARY=1 $(inherited) AS_USE_MAPKIT=1 $(inherited) AS_USE_PHOTOS=1 $(inherited) AS_USE_VIDEO=1 +HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/PINCache/PINCache.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/PINOperation/PINOperation.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/PINRemoteImage/PINRemoteImage.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Texture/AsyncDisplayKit.framework/Headers" +LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' +OTHER_LDFLAGS = $(inherited) -l"c++" -framework "AVFoundation" -framework "Accelerate" -framework "AssetsLibrary" -framework "AsyncDisplayKit" -framework "CoreLocation" -framework "CoreMedia" -framework "Foundation" -framework "ImageIO" -framework "MapKit" -framework "PINCache" -framework "PINOperation" -framework "PINRemoteImage" -framework "Photos" -framework "UIKit" -weak_framework "UIKit" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_PODFILE_DIR_PATH = ${SRCROOT}/. +PODS_ROOT = ${SRCROOT}/Pods +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Starbucks_Clone_TexBrother/Pods/Target Support Files/Texture/Texture-Info.plist b/Starbucks_Clone_TexBrother/Pods/Target Support Files/Texture/Texture-Info.plist new file mode 100644 index 0000000..4522675 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Target Support Files/Texture/Texture-Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 3.0.0 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/Starbucks_Clone_TexBrother/Pods/Target Support Files/Texture/Texture-dummy.m b/Starbucks_Clone_TexBrother/Pods/Target Support Files/Texture/Texture-dummy.m new file mode 100644 index 0000000..da713fe --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Target Support Files/Texture/Texture-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_Texture : NSObject +@end +@implementation PodsDummy_Texture +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Target Support Files/Texture/Texture-prefix.pch b/Starbucks_Clone_TexBrother/Pods/Target Support Files/Texture/Texture-prefix.pch new file mode 100644 index 0000000..beb2a24 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Target Support Files/Texture/Texture-prefix.pch @@ -0,0 +1,12 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + diff --git a/Starbucks_Clone_TexBrother/Pods/Target Support Files/Texture/Texture-umbrella.h b/Starbucks_Clone_TexBrother/Pods/Target Support Files/Texture/Texture-umbrella.h new file mode 100644 index 0000000..1cbbed0 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Target Support Files/Texture/Texture-umbrella.h @@ -0,0 +1,169 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + +#import "ASBlockTypes.h" +#import "ASButtonNode+Private.h" +#import "ASButtonNode+Yoga.h" +#import "ASButtonNode.h" +#import "ASCellNode.h" +#import "ASCollectionNode+Beta.h" +#import "ASCollectionNode.h" +#import "ASCollections.h" +#import "ASCollectionView.h" +#import "ASCollectionViewLayoutFacilitatorProtocol.h" +#import "ASCollectionViewProtocols.h" +#import "ASConfiguration.h" +#import "ASConfigurationDelegate.h" +#import "ASConfigurationInternal.h" +#import "ASContextTransitioning.h" +#import "ASControlNode+Subclasses.h" +#import "ASControlNode.h" +#import "ASDisplayNode+Beta.h" +#import "ASDisplayNode+Convenience.h" +#import "ASDisplayNode+InterfaceState.h" +#import "ASDisplayNode+LayoutSpec.h" +#import "ASDisplayNode+Subclasses.h" +#import "ASDisplayNode+Yoga.h" +#import "ASDisplayNode.h" +#import "ASDisplayNodeExtras.h" +#import "ASDKViewController.h" +#import "ASEditableTextNode.h" +#import "ASExperimentalFeatures.h" +#import "ASImageNode.h" +#import "ASLocking.h" +#import "ASMainThreadDeallocation.h" +#import "ASMapNode.h" +#import "ASMultiplexImageNode.h" +#import "ASNavigationController.h" +#import "ASNetworkImageLoadInfo.h" +#import "ASNetworkImageNode.h" +#import "ASNodeController+Beta.h" +#import "ASPagerFlowLayout.h" +#import "ASPagerNode+Beta.h" +#import "ASPagerNode.h" +#import "ASRangeManagingNode.h" +#import "ASRunLoopQueue.h" +#import "ASScrollNode.h" +#import "ASSectionController.h" +#import "ASSupplementaryNodeSource.h" +#import "ASTabBarController.h" +#import "ASTableNode+Beta.h" +#import "ASTableNode.h" +#import "ASTableView.h" +#import "ASTableViewInternal.h" +#import "ASTableViewProtocols.h" +#import "ASTextNode+Beta.h" +#import "ASTextNode.h" +#import "ASTextNode2.h" +#import "ASTextNodeCommon.h" +#import "ASVideoNode.h" +#import "ASVideoPlayerNode.h" +#import "ASVisibilityProtocols.h" +#import "AsyncDisplayKit+IGListKitMethods.h" +#import "AsyncDisplayKit.h" +#import "IGListAdapter+AsyncDisplayKit.h" +#import "UIImage+ASConvenience.h" +#import "UIResponder+AsyncDisplayKit.h" +#import "_ASTransitionContext.h" +#import "ASAbstractLayoutController.h" +#import "ASBasicImageDownloader.h" +#import "ASBatchContext.h" +#import "ASBatchFetchingDelegate.h" +#import "ASCollectionElement.h" +#import "ASCollectionFlowLayoutDelegate.h" +#import "ASCollectionGalleryLayoutDelegate.h" +#import "ASCollectionInternal.h" +#import "ASCollectionLayoutContext.h" +#import "ASCollectionLayoutDelegate.h" +#import "ASCollectionLayoutState.h" +#import "ASCollectionViewLayoutController.h" +#import "ASCollectionViewLayoutInspector.h" +#import "ASDataController.h" +#import "ASDelegateProxy.h" +#import "ASElementMap.h" +#import "ASGraphicsContext.h" +#import "ASHashing.h" +#import "ASHighlightOverlayLayer.h" +#import "ASImageContainerProtocolCategories.h" +#import "ASImageProtocols.h" +#import "ASIntegerMap.h" +#import "ASLayoutController.h" +#import "ASLayoutRangeType.h" +#import "ASMainSerialQueue.h" +#import "ASMutableAttributedStringBuilder.h" +#import "ASObjectDescriptionHelpers.h" +#import "ASPageTable.h" +#import "ASPhotosFrameworkImageRequest.h" +#import "ASPINRemoteImageDownloader.h" +#import "ASRangeController.h" +#import "ASRangeControllerUpdateRangeProtocol+Beta.h" +#import "ASRecursiveUnfairLock.h" +#import "ASScrollDirection.h" +#import "ASSectionContext.h" +#import "ASTableLayoutController.h" +#import "ASThread.h" +#import "ASTraitCollection.h" +#import "ASWeakProxy.h" +#import "ASWeakSet.h" +#import "CoreGraphics+ASConvenience.h" +#import "NSArray+Diffing.h" +#import "NSIndexSet+ASHelpers.h" +#import "NSMutableAttributedString+TextKitAdditions.h" +#import "_ASAsyncTransaction.h" +#import "_ASAsyncTransactionContainer+Private.h" +#import "_ASAsyncTransactionContainer.h" +#import "_ASAsyncTransactionGroup.h" +#import "UICollectionViewLayout+ASConvenience.h" +#import "UIView+ASConvenience.h" +#import "_ASCollectionReusableView.h" +#import "_ASCollectionViewCell.h" +#import "_ASDisplayLayer.h" +#import "_ASDisplayView.h" +#import "_ASDisplayViewAccessiblity.h" +#import "ASAbsoluteLayoutElement.h" +#import "ASAbsoluteLayoutSpec.h" +#import "ASAsciiArtBoxCreator.h" +#import "ASBackgroundLayoutSpec.h" +#import "ASCenterLayoutSpec.h" +#import "ASCornerLayoutSpec.h" +#import "ASDimension.h" +#import "ASDimensionInternal.h" +#import "ASInsetLayoutSpec.h" +#import "ASLayout+IGListDiffKit.h" +#import "ASLayout.h" +#import "ASLayoutElement.h" +#import "ASLayoutElementExtensibility.h" +#import "ASLayoutElementPrivate.h" +#import "ASLayoutSpec+Subclasses.h" +#import "ASLayoutSpec.h" +#import "ASOverlayLayoutSpec.h" +#import "ASRatioLayoutSpec.h" +#import "ASRelativeLayoutSpec.h" +#import "ASStackLayoutDefines.h" +#import "ASStackLayoutElement.h" +#import "ASStackLayoutSpec.h" +#import "ASYogaUtilities.h" +#import "ASAssert.h" +#import "ASAvailability.h" +#import "ASBaseDefines.h" +#import "ASDisplayNode+Ancestry.h" +#import "ASEqualityHelpers.h" +#import "ASLog.h" +#import "ASSignpost.h" +#import "AsyncDisplayKit+Debug.h" +#import "AsyncDisplayKit+Tips.h" +#import "ASTextNodeTypes.h" +#import "ASTextKitComponents.h" + +FOUNDATION_EXPORT double AsyncDisplayKitVersionNumber; +FOUNDATION_EXPORT const unsigned char AsyncDisplayKitVersionString[]; + diff --git a/Starbucks_Clone_TexBrother/Pods/Target Support Files/Texture/Texture.debug.xcconfig b/Starbucks_Clone_TexBrother/Pods/Target Support Files/Texture/Texture.debug.xcconfig new file mode 100644 index 0000000..c68536c --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Target Support Files/Texture/Texture.debug.xcconfig @@ -0,0 +1,15 @@ +CLANG_CXX_LANGUAGE_STANDARD = c++11 +CLANG_CXX_LIBRARY = libc++ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Texture +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/PINCache" "${PODS_CONFIGURATION_BUILD_DIR}/PINOperation" "${PODS_CONFIGURATION_BUILD_DIR}/PINRemoteImage" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 $(inherited) AS_USE_ASSETS_LIBRARY=1 $(inherited) AS_USE_MAPKIT=1 $(inherited) AS_USE_PHOTOS=1 $(inherited) AS_USE_VIDEO=1 +OTHER_LDFLAGS = $(inherited) -l"c++" -framework "AVFoundation" -framework "AssetsLibrary" -framework "CoreLocation" -framework "CoreMedia" -framework "MapKit" -framework "Photos" -weak_framework "UIKit" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/Texture +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Starbucks_Clone_TexBrother/Pods/Target Support Files/Texture/Texture.modulemap b/Starbucks_Clone_TexBrother/Pods/Target Support Files/Texture/Texture.modulemap new file mode 100644 index 0000000..b1552f6 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Target Support Files/Texture/Texture.modulemap @@ -0,0 +1,6 @@ +framework module AsyncDisplayKit { + umbrella header "Texture-umbrella.h" + + export * + module * { export * } +} diff --git a/Starbucks_Clone_TexBrother/Pods/Target Support Files/Texture/Texture.release.xcconfig b/Starbucks_Clone_TexBrother/Pods/Target Support Files/Texture/Texture.release.xcconfig new file mode 100644 index 0000000..c68536c --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Target Support Files/Texture/Texture.release.xcconfig @@ -0,0 +1,15 @@ +CLANG_CXX_LANGUAGE_STANDARD = c++11 +CLANG_CXX_LIBRARY = libc++ +CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Texture +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/PINCache" "${PODS_CONFIGURATION_BUILD_DIR}/PINOperation" "${PODS_CONFIGURATION_BUILD_DIR}/PINRemoteImage" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 $(inherited) AS_USE_ASSETS_LIBRARY=1 $(inherited) AS_USE_MAPKIT=1 $(inherited) AS_USE_PHOTOS=1 $(inherited) AS_USE_VIDEO=1 +OTHER_LDFLAGS = $(inherited) -l"c++" -framework "AVFoundation" -framework "AssetsLibrary" -framework "CoreLocation" -framework "CoreMedia" -framework "MapKit" -framework "Photos" -weak_framework "UIKit" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/Texture +PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES +USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/LICENSE b/Starbucks_Clone_TexBrother/Pods/Texture/LICENSE new file mode 100644 index 0000000..c63a7c0 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/LICENSE @@ -0,0 +1,181 @@ +The Texture project was created by Pinterest as a continuation, under a different +name and license, of the AsyncDisplayKit codebase originally developed by Facebook. + +All code in Texture is covered by the Apache License, Version 2.0. + +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/README.md b/Starbucks_Clone_TexBrother/Pods/Texture/README.md new file mode 100644 index 0000000..57e0c54 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/README.md @@ -0,0 +1,51 @@ +## Coming from AsyncDisplayKit? Learn more [here](https://medium.com/@Pinterest_Engineering/introducing-texture-a-new-home-for-asyncdisplaykit-e7c003308f50) + +![Texture](https://github.com/texturegroup/texture/raw/master/docs/static/images/logo.png) + +[![Apps Using](https://img.shields.io/cocoapods/at/Texture.svg?label=Apps%20Using%20Texture&colorB=28B9FE)](http://cocoapods.org/pods/Texture) +[![Downloads](https://img.shields.io/cocoapods/dt/Texture.svg?label=Total%20Downloads&colorB=28B9FE)](http://cocoapods.org/pods/Texture) + +[![Platform](https://img.shields.io/badge/platforms-iOS%20%7C%20tvOS-orange.svg)](http://texturegroup.org) +[![Languages](https://img.shields.io/badge/languages-ObjC%20%7C%20Swift-orange.svg)](http://texturegroup.org) + +[![Version](https://img.shields.io/cocoapods/v/Texture.svg)](http://cocoapods.org/pods/Texture) +[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-59C939.svg?style=flat)](https://github.com/Carthage/Carthage) +[![License](https://img.shields.io/cocoapods/l/Texture.svg)](https://github.com/texturegroup/texture/blob/master/LICENSE) + +## Installation + +Texture is available via CocoaPods or Carthage. See our [Installation](http://texturegroup.org/docs/installation.html) guide for instructions. + +## Performance Gains + +Texture's basic unit is the `node`. An ASDisplayNode is an abstraction over `UIView`, which in turn is an abstraction over `CALayer`. Unlike views, which can only be used on the main thread, nodes are thread-safe: you can instantiate and configure entire hierarchies of them in parallel on background threads. + +To keep its user interface smooth and responsive, your app should render at 60 frames per second — the gold standard on iOS. This means the main thread has one-sixtieth of a second to push each frame. That's 16 milliseconds to execute all layout and drawing code! And because of system overhead, your code usually has less than ten milliseconds to run before it causes a frame drop. + +Texture lets you move image decoding, text sizing and rendering, layout, and other expensive UI operations off the main thread, to keep the main thread available to respond to user interaction. + +## Advanced Developer Features + +As the framework has grown, many features have been added that can save developers tons of time by eliminating common boilerplate style structures common in modern iOS apps. If you've ever dealt with cell reuse bugs, tried to performantly preload data for a page or scroll style interface or even just tried to keep your app from dropping too many frames you can benefit from integrating Texture. + +## Learn More + +* Read the our [Getting Started](http://texturegroup.org/docs/getting-started.html) guide +* Get the [sample projects](https://github.com/texturegroup/texture/tree/master/examples) +* Browse the [API reference](http://texturegroup.org/appledocs.html) + +## Getting Help + +We use Slack for real-time debugging, community updates, and general talk about Texture. [Signup](http://asdk-slack-auto-invite.herokuapp.com) yourself or email textureframework@gmail.com to get an invite. + +## Release process + +For the release process see the [RELEASE](https://github.com/texturegroup/texture/blob/master/RELEASE.md) file. + +## Contributing + +We welcome any contributions. See the [CONTRIBUTING](https://github.com/texturegroup/texture/blob/master/CONTRIBUTING.md) file for how to get involved. + +## License + +The Texture project is available for free use, as described by the [LICENSE](https://github.com/texturegroup/texture/blob/master/LICENSE) (Apache 2.0). diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASBlockTypes.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASBlockTypes.h new file mode 100644 index 0000000..e1c7456 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASBlockTypes.h @@ -0,0 +1,20 @@ +// +// ASBlockTypes.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +@class ASCellNode; + +/** + * ASCellNode creation block. Used to lazily create the ASCellNode instance for a specified indexPath. + */ +typedef ASCellNode * _Nonnull(^ASCellNodeBlock)(void); + +// Type for the cancellation checker block passed into the async display blocks. YES means the operation has been cancelled, NO means continue. +typedef BOOL(^asdisplaynode_iscancelled_block_t)(void); diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASButtonNode+Private.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASButtonNode+Private.h new file mode 100644 index 0000000..22ca4eb --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASButtonNode+Private.h @@ -0,0 +1,45 @@ +// +// ASButtonNode+Private.h +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import +#import + +@interface ASButtonNode () { + NSAttributedString *_normalAttributedTitle; + NSAttributedString *_highlightedAttributedTitle; + NSAttributedString *_selectedAttributedTitle; + NSAttributedString *_selectedHighlightedAttributedTitle; + NSAttributedString *_disabledAttributedTitle; + + UIImage *_normalImage; + UIImage *_highlightedImage; + UIImage *_selectedImage; + UIImage *_selectedHighlightedImage; + UIImage *_disabledImage; + + UIImage *_normalBackgroundImage; + UIImage *_highlightedBackgroundImage; + UIImage *_selectedBackgroundImage; + UIImage *_selectedHighlightedBackgroundImage; + UIImage *_disabledBackgroundImage; + + CGFloat _contentSpacing; + UIEdgeInsets _contentEdgeInsets; + ASTextNode *_titleNode; + ASImageNode *_imageNode; + ASImageNode *_backgroundImageNode; + + BOOL _laysOutHorizontally; + ASVerticalAlignment _contentVerticalAlignment; + ASHorizontalAlignment _contentHorizontalAlignment; + ASButtonNodeImageAlignment _imageAlignment; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASButtonNode+Yoga.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASButtonNode+Yoga.h new file mode 100644 index 0000000..4fddc9d --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASButtonNode+Yoga.h @@ -0,0 +1,20 @@ +// +// ASButtonNode+Yoga.h +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface ASButtonNode (Yoga) + +- (void)updateYogaLayoutIfNeeded; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASButtonNode+Yoga.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASButtonNode+Yoga.mm new file mode 100644 index 0000000..f4493bc --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASButtonNode+Yoga.mm @@ -0,0 +1,105 @@ +// +// ASButtonNode+Yoga.mm +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import "ASButtonNode+Yoga.h" +#import +#import +#import + +#if YOGA +static void ASButtonNodeResolveHorizontalAlignmentForStyle(ASLayoutElementStyle *style, ASStackLayoutDirection _direction, ASHorizontalAlignment _horizontalAlignment, ASStackLayoutJustifyContent _justifyContent, ASStackLayoutAlignItems _alignItems) { + if (_direction == ASStackLayoutDirectionHorizontal) { + style.justifyContent = justifyContent(_horizontalAlignment, _justifyContent); + } else { + style.alignItems = alignment(_horizontalAlignment, _alignItems); + } +} + +static void ASButtonNodeResolveVerticalAlignmentForStyle(ASLayoutElementStyle *style, ASStackLayoutDirection _direction, ASVerticalAlignment _verticalAlignment, ASStackLayoutJustifyContent _justifyContent, ASStackLayoutAlignItems _alignItems) { + if (_direction == ASStackLayoutDirectionHorizontal) { + style.alignItems = alignment(_verticalAlignment, _alignItems); + } else { + style.justifyContent = justifyContent(_verticalAlignment, _justifyContent); + } +} + +@implementation ASButtonNode (Yoga) + +- (void)updateYogaLayoutIfNeeded +{ + NSMutableArray *children = [[NSMutableArray alloc] initWithCapacity:2]; + { + ASLockScopeSelf(); + + // Build up yoga children for button node again + unowned ASLayoutElementStyle *style = [self _locked_style]; + [style yogaNodeCreateIfNeeded]; + + // Setup stack layout values + style.flexDirection = _laysOutHorizontally ? ASStackLayoutDirectionHorizontal : ASStackLayoutDirectionVertical; + + // Resolve horizontal and vertical alignment + ASButtonNodeResolveHorizontalAlignmentForStyle(style, style.flexDirection, _contentHorizontalAlignment, style.justifyContent, style.alignItems); + ASButtonNodeResolveVerticalAlignmentForStyle(style, style.flexDirection, _contentVerticalAlignment, style.justifyContent, style.alignItems); + + // Setup new yoga children + if (_imageNode.image != nil) { + [_imageNode.style yogaNodeCreateIfNeeded]; + [children addObject:_imageNode]; + } + + if (_titleNode.attributedText.length > 0) { + [_titleNode.style yogaNodeCreateIfNeeded]; + if (_imageAlignment == ASButtonNodeImageAlignmentBeginning) { + [children addObject:_titleNode]; + } else { + [children insertObject:_titleNode atIndex:0]; + } + } + + // Add spacing between title and button + if (children.count == 2) { + unowned ASLayoutElementStyle *firstChildStyle = children.firstObject.style; + if (_laysOutHorizontally) { + firstChildStyle.margin = ASEdgeInsetsMake(UIEdgeInsetsMake(0, 0, 0, _contentSpacing)); + } else { + firstChildStyle.margin = ASEdgeInsetsMake(UIEdgeInsetsMake(0, 0, _contentSpacing, 0)); + } + } + + // Add padding to button + if (UIEdgeInsetsEqualToEdgeInsets(UIEdgeInsetsZero, _contentEdgeInsets) == NO) { + style.padding = ASEdgeInsetsMake(_contentEdgeInsets); + } + + // Add background node + if (_backgroundImageNode.image) { + [_backgroundImageNode.style yogaNodeCreateIfNeeded]; + [children insertObject:_backgroundImageNode atIndex:0]; + + _backgroundImageNode.style.positionType = YGPositionTypeAbsolute; + _backgroundImageNode.style.position = ASEdgeInsetsMake(UIEdgeInsetsZero); + } + } + + // Update new children + [self setYogaChildren:children]; +} + +@end + +#else + +@implementation ASButtonNode (Yoga) + +- (void)updateYogaLayoutIfNeeded {} + +@end + +#endif diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASButtonNode.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASButtonNode.h new file mode 100644 index 0000000..3963fb1 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASButtonNode.h @@ -0,0 +1,130 @@ +// +// ASButtonNode.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@class ASImageNode, ASTextNode; + +/** + Image alignment defines where the image will be placed relative to the text. + */ +typedef NS_ENUM(unsigned char, ASButtonNodeImageAlignment) { + /** Places the image before the text. */ + ASButtonNodeImageAlignmentBeginning, + /** Places the image after the text. */ + ASButtonNodeImageAlignmentEnd +}; + +@interface ASButtonNode : ASControlNode + +@property (readonly) ASTextNode * titleNode; +@property (readonly) ASImageNode * imageNode; +@property (readonly) ASImageNode * backgroundImageNode; + +/** + Spacing between image and title. Defaults to 8.0. + */ +@property CGFloat contentSpacing; + +/** + Whether button should be laid out vertically (image on top of text) or horizontally (image to the left of text). + ASButton node does not yet support RTL but it should be fairly easy to implement. + Defaults to YES. + */ +@property BOOL laysOutHorizontally; + +/** Horizontally align content (text or image). + Defaults to ASHorizontalAlignmentMiddle. + */ +@property ASHorizontalAlignment contentHorizontalAlignment; + +/** Vertically align content (text or image). + Defaults to ASVerticalAlignmentCenter. + */ +@property ASVerticalAlignment contentVerticalAlignment; + +/** + * @discussion The insets used around the title and image node + */ +@property UIEdgeInsets contentEdgeInsets; + +/** + * @discusstion Whether the image should be aligned at the beginning or at the end of node. Default is `ASButtonNodeImageAlignmentBeginning`. + */ +@property ASButtonNodeImageAlignment imageAlignment; + +/** + * Returns the styled title associated with the specified state. + * + * @param state The control state that uses the styled title. + * + * @return The title for the specified state. + */ +- (nullable NSAttributedString *)attributedTitleForState:(UIControlState)state AS_WARN_UNUSED_RESULT; + +/** + * Sets the styled title to use for the specified state. This will reset styled title previously set with -setTitle:withFont:withColor:forState. + * + * @param title The styled text string to use for the title. + * @param state The control state that uses the specified title. + */ +- (void)setAttributedTitle:(nullable NSAttributedString *)title forState:(UIControlState)state; + +#if TARGET_OS_IOS +/** + * Sets the title to use for the specified state. This will reset styled title previously set with -setAttributedTitle:forState. + * + * @param title The styled text string to use for the title. + * @param font The font to use for the title. + * @param color The color to use for the title. + * @param state The control state that uses the specified title. + */ +- (void)setTitle:(NSString *)title withFont:(nullable UIFont *)font withColor:(nullable UIColor *)color forState:(UIControlState)state; +#endif +/** + * Returns the image used for a button state. + * + * @param state The control state that uses the image. + * + * @return The image used for the specified state. + */ +- (nullable UIImage *)imageForState:(UIControlState)state AS_WARN_UNUSED_RESULT; + +/** + * Sets the image to use for the specified state. + * + * @param image The image to use for the specified state. + * @param state The control state that uses the specified title. + */ +- (void)setImage:(nullable UIImage *)image forState:(UIControlState)state; + +/** + * Sets the background image to use for the specified state. + * + * @param image The image to use for the specified state. + * @param state The control state that uses the specified title. + */ +- (void)setBackgroundImage:(nullable UIImage *)image forState:(UIControlState)state; + + +/** + * Returns the background image used for a button state. + * + * @param state The control state that uses the image. + * + * @return The background image used for the specified state. + */ +- (nullable UIImage *)backgroundImageForState:(UIControlState)state AS_WARN_UNUSED_RESULT; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASButtonNode.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASButtonNode.mm new file mode 100644 index 0000000..66b9cd3 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASButtonNode.mm @@ -0,0 +1,575 @@ +// +// ASButtonNode.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import +#import +#import +#import +#import + +@implementation ASButtonNode + +@synthesize contentSpacing = _contentSpacing; +@synthesize laysOutHorizontally = _laysOutHorizontally; +@synthesize contentVerticalAlignment = _contentVerticalAlignment; +@synthesize contentHorizontalAlignment = _contentHorizontalAlignment; +@synthesize contentEdgeInsets = _contentEdgeInsets; +@synthesize imageAlignment = _imageAlignment; +@synthesize titleNode = _titleNode; +@synthesize imageNode = _imageNode; +@synthesize backgroundImageNode = _backgroundImageNode; + +#pragma mark - Lifecycle + +- (instancetype)init +{ + if (self = [super init]) { + self.automaticallyManagesSubnodes = YES; + + _contentSpacing = 8.0; + _laysOutHorizontally = YES; + _contentHorizontalAlignment = ASHorizontalAlignmentMiddle; + _contentVerticalAlignment = ASVerticalAlignmentCenter; + _contentEdgeInsets = UIEdgeInsetsZero; + _imageAlignment = ASButtonNodeImageAlignmentBeginning; + self.accessibilityTraits = self.defaultAccessibilityTraits; + + [self updateYogaLayoutIfNeeded]; + } + return self; +} + +- (ASTextNode *)titleNode +{ + ASLockScopeSelf(); + if (!_titleNode) { + _titleNode = [[ASTextNode alloc] init]; + #if TARGET_OS_TV + // tvOS needs access to the underlying view + // of the button node to add a touch handler. + [_titleNode setLayerBacked:NO]; + #else + [_titleNode setLayerBacked:YES]; + #endif + _titleNode.style.flexShrink = 1.0; + _titleNode.textColorFollowsTintColor = YES; + } + return _titleNode; +} + +#pragma mark - Public Getter + +- (ASImageNode *)imageNode +{ + ASLockScopeSelf(); + if (!_imageNode) { + _imageNode = [[ASImageNode alloc] init]; + [_imageNode setLayerBacked:YES]; + } + return _imageNode; +} + +- (ASImageNode *)backgroundImageNode +{ + ASLockScopeSelf(); + if (!_backgroundImageNode) { + _backgroundImageNode = [[ASImageNode alloc] init]; + [_backgroundImageNode setLayerBacked:YES]; + [_backgroundImageNode setContentMode:UIViewContentModeScaleToFill]; + } + return _backgroundImageNode; +} + +- (void)setLayerBacked:(BOOL)layerBacked +{ + ASDisplayNodeAssert(!layerBacked, @"ASButtonNode must not be layer backed!"); + [super setLayerBacked:layerBacked]; +} + +- (void)setEnabled:(BOOL)enabled +{ + if (self.enabled != enabled) { + [super setEnabled:enabled]; + self.accessibilityTraits = self.defaultAccessibilityTraits; + [self updateButtonContent]; + } +} + +- (void)setHighlighted:(BOOL)highlighted +{ + if (self.highlighted != highlighted) { + [super setHighlighted:highlighted]; + [self updateButtonContent]; + } +} + +- (void)setSelected:(BOOL)selected +{ + if (self.selected != selected) { + [super setSelected:selected]; + [self updateButtonContent]; + } +} + +- (void)updateButtonContent +{ + [self updateBackgroundImage]; + [self updateImage]; + [self updateTitle]; +} + +- (void)setDisplaysAsynchronously:(BOOL)displaysAsynchronously +{ + [super setDisplaysAsynchronously:displaysAsynchronously]; + [self.backgroundImageNode setDisplaysAsynchronously:displaysAsynchronously]; + [self.imageNode setDisplaysAsynchronously:displaysAsynchronously]; + [self.titleNode setDisplaysAsynchronously:displaysAsynchronously]; +} + +-(void)tintColorDidChange +{ + [super tintColorDidChange]; + // UIButton documentation states that it tints the image and title of buttons when tintColor is set. + // | "The tint color to apply to the button title and image." + // | From: https://developer.apple.com/documentation/uikit/uibutton/1624025-tintcolor + [self lock]; + UIColor *tintColor = self.tintColor; + self.imageNode.tintColor = tintColor; + self.titleNode.tintColor = tintColor; + [self unlock]; + [self setNeedsDisplay]; +} + +- (void)updateImage +{ + [self lock]; + + UIImage *newImage; + if (self.enabled == NO && _disabledImage) { + newImage = _disabledImage; + } else if (self.highlighted && self.selected && _selectedHighlightedImage) { + newImage = _selectedHighlightedImage; + } else if (self.highlighted && _highlightedImage) { + newImage = _highlightedImage; + } else if (self.selected && _selectedImage) { + newImage = _selectedImage; + } else { + newImage = _normalImage; + } + + if ((_imageNode != nil || newImage != nil) && newImage != self.imageNode.image) { + _imageNode.image = newImage; + [self unlock]; + + [self updateYogaLayoutIfNeeded]; + [self setNeedsLayout]; + return; + } + + [self unlock]; +} + +- (void)updateTitle +{ + [self lock]; + + NSAttributedString *newTitle; + if (self.enabled == NO && _disabledAttributedTitle) { + newTitle = _disabledAttributedTitle; + } else if (self.highlighted && self.selected && _selectedHighlightedAttributedTitle) { + newTitle = _selectedHighlightedAttributedTitle; + } else if (self.highlighted && _highlightedAttributedTitle) { + newTitle = _highlightedAttributedTitle; + } else if (self.selected && _selectedAttributedTitle) { + newTitle = _selectedAttributedTitle; + } else { + newTitle = _normalAttributedTitle; + } + + NSAttributedString *attributedString = _titleNode.attributedText; + if ((attributedString.length > 0 || newTitle.length > 0) && [attributedString isEqualToAttributedString:newTitle] == NO) { + // Calling self.titleNode is essential here because _titleNode is lazily created by the getter. + self.titleNode.attributedText = newTitle; + [self unlock]; + + self.accessibilityLabel = self.defaultAccessibilityLabel; + [self updateYogaLayoutIfNeeded]; + [self setNeedsLayout]; + return; + } + + [self unlock]; +} + +- (void)updateBackgroundImage +{ + [self lock]; + + UIImage *newImage; + if (self.enabled == NO && _disabledBackgroundImage) { + newImage = _disabledBackgroundImage; + } else if (self.highlighted && self.selected && _selectedHighlightedBackgroundImage) { + newImage = _selectedHighlightedBackgroundImage; + } else if (self.highlighted && _highlightedBackgroundImage) { + newImage = _highlightedBackgroundImage; + } else if (self.selected && _selectedBackgroundImage) { + newImage = _selectedBackgroundImage; + } else { + newImage = _normalBackgroundImage; + } + + if ((_backgroundImageNode != nil || newImage != nil) && newImage != self.backgroundImageNode.image) { + _backgroundImageNode.image = newImage; + [self unlock]; + + [self updateYogaLayoutIfNeeded]; + [self setNeedsLayout]; + return; + } + + [self unlock]; +} + +- (CGFloat)contentSpacing +{ + ASLockScopeSelf(); + return _contentSpacing; +} + +- (void)setContentSpacing:(CGFloat)contentSpacing +{ + if (ASLockedSelfCompareAssign(_contentSpacing, contentSpacing)) { + [self updateYogaLayoutIfNeeded]; + [self setNeedsLayout]; + } +} + +- (BOOL)laysOutHorizontally +{ + ASLockScopeSelf(); + return _laysOutHorizontally; +} + +- (void)setLaysOutHorizontally:(BOOL)laysOutHorizontally +{ + if (ASLockedSelfCompareAssign(_laysOutHorizontally, laysOutHorizontally)) { + [self updateYogaLayoutIfNeeded]; + [self setNeedsLayout]; + } +} + +- (ASVerticalAlignment)contentVerticalAlignment +{ + ASLockScopeSelf(); + return _contentVerticalAlignment; +} + +- (void)setContentVerticalAlignment:(ASVerticalAlignment)contentVerticalAlignment +{ + ASLockScopeSelf(); + _contentVerticalAlignment = contentVerticalAlignment; +} + +- (ASHorizontalAlignment)contentHorizontalAlignment +{ + ASLockScopeSelf(); + return _contentHorizontalAlignment; +} + +- (void)setContentHorizontalAlignment:(ASHorizontalAlignment)contentHorizontalAlignment +{ + ASLockScopeSelf(); + _contentHorizontalAlignment = contentHorizontalAlignment; +} + +- (UIEdgeInsets)contentEdgeInsets +{ + ASLockScopeSelf(); + return _contentEdgeInsets; +} + +- (void)setContentEdgeInsets:(UIEdgeInsets)contentEdgeInsets +{ + ASLockScopeSelf(); + _contentEdgeInsets = contentEdgeInsets; +} + +- (ASButtonNodeImageAlignment)imageAlignment +{ + ASLockScopeSelf(); + return _imageAlignment; +} + +- (void)setImageAlignment:(ASButtonNodeImageAlignment)imageAlignment +{ + ASLockScopeSelf(); + _imageAlignment = imageAlignment; +} + + +#if TARGET_OS_IOS +- (void)setTitle:(NSString *)title withFont:(UIFont *)font withColor:(UIColor *)color forState:(UIControlState)state +{ + NSMutableDictionary *attributes = [NSMutableDictionary dictionary]; + attributes[NSFontAttributeName] = font ? : [UIFont systemFontOfSize:[UIFont buttonFontSize]]; + if (color != nil) { + // From apple's documentation: If color is not specified, NSForegroundColorAttributeName will fallback to black + // Only set if the color is nonnull + attributes[NSForegroundColorAttributeName] = color; + } + NSAttributedString *string = [[NSAttributedString alloc] initWithString:title attributes:[attributes copy]]; + [self setAttributedTitle:string forState:state]; +} +#endif + +- (NSAttributedString *)attributedTitleForState:(UIControlState)state +{ + ASLockScopeSelf(); + switch (state) { + case UIControlStateNormal: + return _normalAttributedTitle; + + case UIControlStateHighlighted: + return _highlightedAttributedTitle; + + case UIControlStateSelected: + return _selectedAttributedTitle; + + case UIControlStateSelected | UIControlStateHighlighted: + return _selectedHighlightedAttributedTitle; + + case UIControlStateDisabled: + return _disabledAttributedTitle; + + default: + return _normalAttributedTitle; + } +} + +- (void)setAttributedTitle:(NSAttributedString *)title forState:(UIControlState)state +{ + { + ASLockScopeSelf(); + switch (state) { + case UIControlStateNormal: + _normalAttributedTitle = [title copy]; + break; + + case UIControlStateHighlighted: + _highlightedAttributedTitle = [title copy]; + break; + + case UIControlStateSelected: + _selectedAttributedTitle = [title copy]; + break; + + case UIControlStateSelected | UIControlStateHighlighted: + _selectedHighlightedAttributedTitle = [title copy]; + break; + + case UIControlStateDisabled: + _disabledAttributedTitle = [title copy]; + break; + + default: + break; + } + } + + [self updateTitle]; +} + +- (UIImage *)imageForState:(UIControlState)state +{ + ASLockScopeSelf(); + switch (state) { + case UIControlStateNormal: + return _normalImage; + + case UIControlStateHighlighted: + return _highlightedImage; + + case UIControlStateSelected: + return _selectedImage; + + case UIControlStateSelected | UIControlStateHighlighted: + return _selectedHighlightedImage; + + case UIControlStateDisabled: + return _disabledImage; + + default: + return _normalImage; + } +} + +- (void)setImage:(UIImage *)image forState:(UIControlState)state +{ + { + ASLockScopeSelf(); + switch (state) { + case UIControlStateNormal: + _normalImage = image; + break; + + case UIControlStateHighlighted: + _highlightedImage = image; + break; + + case UIControlStateSelected: + _selectedImage = image; + break; + + case UIControlStateSelected | UIControlStateHighlighted: + _selectedHighlightedImage = image; + break; + + case UIControlStateDisabled: + _disabledImage = image; + break; + + default: + break; + } + } + + [self updateImage]; +} + +- (UIImage *)backgroundImageForState:(UIControlState)state +{ + ASLockScopeSelf(); + switch (state) { + case UIControlStateNormal: + return _normalBackgroundImage; + + case UIControlStateHighlighted: + return _highlightedBackgroundImage; + + case UIControlStateSelected: + return _selectedBackgroundImage; + + case UIControlStateSelected | UIControlStateHighlighted: + return _selectedHighlightedBackgroundImage; + + case UIControlStateDisabled: + return _disabledBackgroundImage; + + default: + return _normalBackgroundImage; + } +} + +- (void)setBackgroundImage:(UIImage *)image forState:(UIControlState)state +{ + { + ASLockScopeSelf(); + switch (state) { + case UIControlStateNormal: + _normalBackgroundImage = image; + break; + + case UIControlStateHighlighted: + _highlightedBackgroundImage = image; + break; + + case UIControlStateSelected: + _selectedBackgroundImage = image; + break; + + case UIControlStateSelected | UIControlStateHighlighted: + _selectedHighlightedBackgroundImage = image; + break; + + case UIControlStateDisabled: + _disabledBackgroundImage = image; + break; + + default: + break; + } + } + + [self updateBackgroundImage]; +} + + +- (NSString *)defaultAccessibilityLabel +{ + ASLockScopeSelf(); + return _titleNode.defaultAccessibilityLabel; +} + +- (UIAccessibilityTraits)defaultAccessibilityTraits +{ + return self.enabled ? UIAccessibilityTraitButton + : (UIAccessibilityTraitButton | UIAccessibilityTraitNotEnabled); +} + +#pragma mark - Layout + +#if !YOGA +- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize +{ + UIEdgeInsets contentEdgeInsets; + ASButtonNodeImageAlignment imageAlignment; + ASLayoutSpec *spec; + ASStackLayoutSpec *stack = [[ASStackLayoutSpec alloc] init]; + { + ASLockScopeSelf(); + stack.direction = _laysOutHorizontally ? ASStackLayoutDirectionHorizontal : ASStackLayoutDirectionVertical; + stack.spacing = _contentSpacing; + stack.horizontalAlignment = _contentHorizontalAlignment; + stack.verticalAlignment = _contentVerticalAlignment; + + contentEdgeInsets = _contentEdgeInsets; + imageAlignment = _imageAlignment; + } + + NSMutableArray *children = [[NSMutableArray alloc] initWithCapacity:2]; + if (_imageNode.image) { + [children addObject:_imageNode]; + } + + if (_titleNode.attributedText.length > 0) { + if (imageAlignment == ASButtonNodeImageAlignmentBeginning) { + [children addObject:_titleNode]; + } else { + [children insertObject:_titleNode atIndex:0]; + } + } + + stack.children = children; + + spec = stack; + + if (UIEdgeInsetsEqualToEdgeInsets(UIEdgeInsetsZero, contentEdgeInsets) == NO) { + spec = [ASInsetLayoutSpec insetLayoutSpecWithInsets:contentEdgeInsets child:spec]; + } + + if (_backgroundImageNode.image) { + spec = [ASBackgroundLayoutSpec backgroundLayoutSpecWithChild:spec background:_backgroundImageNode]; + } + + return spec; +} +#endif + +- (void)layout +{ + [super layout]; + + _backgroundImageNode.hidden = (_backgroundImageNode.image == nil); + _imageNode.hidden = (_imageNode.image == nil); + _titleNode.hidden = (_titleNode.attributedText.length == 0); +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASCellNode.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASCellNode.h new file mode 100644 index 0000000..c09becc --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASCellNode.h @@ -0,0 +1,259 @@ +// +// ASCellNode.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@class ASCellNode, ASTextNode; +@protocol ASRangeManagingNode; + +typedef NSUInteger ASCellNodeAnimation; + +typedef NS_ENUM(NSUInteger, ASCellNodeVisibilityEvent) { + /** + * Indicates a cell has just became visible + */ + ASCellNodeVisibilityEventVisible, + /** + * Its position (determined by scrollView.contentOffset) has changed while at least 1px remains visible. + * It is possible that 100% of the cell is visible both before and after and only its position has changed, + * or that the position change has resulted in more or less of the cell being visible. + * Use CGRectIntersect between cellFrame and scrollView.bounds to get this rectangle + */ + ASCellNodeVisibilityEventVisibleRectChanged, + /** + * Indicates a cell is no longer visible + */ + ASCellNodeVisibilityEventInvisible, + /** + * Indicates user has started dragging the visible cell + */ + ASCellNodeVisibilityEventWillBeginDragging, + /** + * Indicates user has ended dragging the visible cell + */ + ASCellNodeVisibilityEventDidEndDragging, +}; + +/** + * Generic cell node. Subclass this instead of `ASDisplayNode` to use with `ASTableView` and `ASCollectionView`. + + * @note When a cell node is contained inside a collection view (or table view), + * calling `-setNeedsLayout` will also notify the collection on the main thread + * so that the collection can update its item layout if the cell's size changed. + */ +@interface ASCellNode : ASDisplayNode + +/** + * @abstract When enabled, ensures that the cell is completely displayed before allowed onscreen. + * + * @default NO + * @discussion Normally, ASCellNodes are preloaded and have finished display before they are onscreen. + * However, if the Table or Collection's rangeTuningParameters are set to small values (or 0), + * or if the user is scrolling rapidly on a slow device, it is possible for a cell's display to + * be incomplete when it becomes visible. + * + * In this case, normally placeholder states are shown and scrolling continues uninterrupted. + * The finished, drawn content is then shown as soon as it is ready. + * + * With this property set to YES, the main thread will be blocked until display is complete for + * the cell. This is more similar to UIKit, and in fact makes AsyncDisplayKit scrolling visually + * indistinguishable from UIKit's, except being faster. + * + * Using this option does not eliminate all of the performance advantages of AsyncDisplayKit. + * Normally, a cell has been preloading and is almost done when it reaches the screen, so the + * blocking time is very short. If the rangeTuningParameters are set to 0, still this option + * outperforms UIKit: while the main thread is waiting, subnode display executes concurrently. + */ +@property BOOL neverShowPlaceholders; + +/* + * The kind of supplementary element this node represents, if any. + * + * @return The supplementary element kind, or @c nil if this node does not represent a supplementary element. + */ +@property (nullable, copy, readonly) NSString *supplementaryElementKind; + +/* + * The layout attributes currently assigned to this node, if any. + * + * @discussion This property is useful because it is set before @c collectionView:willDisplayNode:forItemAtIndexPath: + * is called, when the node is not yet in the hierarchy and its frame cannot be converted to/from other nodes. Instead + * you can use the layout attributes object to learn where and how the cell will be displayed. + */ +@property (nullable, copy, readonly) UICollectionViewLayoutAttributes *layoutAttributes; + +/** + * A Boolean value that is synchronized with the underlying collection or tableView cell property. + * Setting this value is equivalent to calling selectItem / deselectItem on the collection or table. + */ +@property (getter=isSelected) BOOL selected; + +/** + * A Boolean value that is synchronized with the underlying collection or tableView cell property. + * Setting this value is equivalent to calling highlightItem / unHighlightItem on the collection or table. + */ +@property (getter=isHighlighted) BOOL highlighted; + +/** + * The current index path of this cell node, or @c nil if this node is + * not a valid item inside a table node or collection node. + */ +@property (nullable, copy, readonly) NSIndexPath *indexPath; + +/** + * BETA: API is under development. We will attempt to provide an easy migration pathway for any changes. + * + * The view-model currently assigned to this node, if any. + * + * This property may be set off the main thread, but this method will never be invoked concurrently on the + */ +@property (nullable) id nodeModel; + +/** + * Asks the node whether it can be updated to the given node model. + * + * The default implementation returns YES if the class matches that of the current view-model. + */ +- (BOOL)canUpdateToNodeModel:(id)nodeModel; + +/** + * The backing view controller, or @c nil if the node wasn't initialized with backing view controller + * @note This property must be accessed on the main thread. + */ +@property (nullable, nonatomic, readonly) UIViewController *viewController; + + +/** + * The table- or collection-node that this cell is a member of, if any. + */ +@property (nullable, weak, readonly) id owningNode; + +/* + * ASCellNode must forward touch events in order for UITableView and UICollectionView tap handling to work. Overriding + * these methods (e.g. for highlighting) requires the super method be called. + */ +- (void)touchesBegan:(NSSet *)touches withEvent:(nullable UIEvent *)event ASDISPLAYNODE_REQUIRES_SUPER; +- (void)touchesMoved:(NSSet *)touches withEvent:(nullable UIEvent *)event ASDISPLAYNODE_REQUIRES_SUPER; +- (void)touchesEnded:(NSSet *)touches withEvent:(nullable UIEvent *)event ASDISPLAYNODE_REQUIRES_SUPER; +- (void)touchesCancelled:(nullable NSSet *)touches withEvent:(nullable UIEvent *)event ASDISPLAYNODE_REQUIRES_SUPER; + +/** + * Called by the system when ASCellNode is used with an ASCollectionNode. It will not be called by ASTableNode. + * When the UICollectionViewLayout object returns a new UICollectionViewLayoutAttributes object, the corresponding ASCellNode will be updated. + * See UICollectionViewCell's applyLayoutAttributes: for a full description. +*/ +- (void)applyLayoutAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes; + +/** + * @abstract Initializes a cell with a given view controller block. + * + * @param viewControllerBlock The block that will be used to create the backing view controller. + * @param didLoadBlock The block that will be called after the view controller's view is loaded. + * + * @return An ASCellNode created using the root view of the view controller provided by the viewControllerBlock. + * The view controller's root view is resized to match the calculated size produced during layout. + * + */ +- (instancetype)initWithViewControllerBlock:(ASDisplayNodeViewControllerBlock)viewControllerBlock didLoadBlock:(nullable ASDisplayNodeDidLoadBlock)didLoadBlock; + +/** + * @abstract Notifies the cell node of certain visibility events, such as changing visible rect. + * + * @warning In cases where an ASCellNode is used as a plain node – i.e. not returned from the + * nodeBlockForItemAtIndexPath/nodeForItemAtIndexPath data source methods – this method will + * deliver only the `Visible` and `Invisible` events, `scrollView` will be nil, and + * `cellFrame` will be the zero rect. + */ +- (void)cellNodeVisibilityEvent:(ASCellNodeVisibilityEvent)event inScrollView:(nullable UIScrollView *)scrollView withCellFrame:(CGRect)cellFrame; + +#pragma mark - UITableViewCell specific passthrough properties + +/* @abstract The selection style when a tap on a cell occurs + * @default UITableViewCellSelectionStyleDefault + * ASTableView uses these properties when configuring UITableViewCells that host ASCellNodes. + */ +@property UITableViewCellSelectionStyle selectionStyle; + +/* @abstract The focus style when a cell is focused + * @default UITableViewCellFocusStyleDefault + * ASTableView uses these properties when configuring UITableViewCells that host ASCellNodes. + */ +@property UITableViewCellFocusStyle focusStyle; + +/* @abstract The view used as the background of the cell when it is selected. + * ASTableView uses these properties when configuring UITableViewCells that host ASCellNodes. + * ASCollectionView uses these properties when configuring UICollectionViewCells that host ASCellNodes. + */ +@property (nullable) UIView *selectedBackgroundView; + +/* @abstract The view used as the background of the cell. + * ASTableView uses these properties when configuring UITableViewCells that host ASCellNodes. + * ASCollectionView uses these properties when configuring UICollectionViewCells that host ASCellNodes. + */ +@property (nullable) UIView *backgroundView; + +/* @abstract The accessory type view on the right side of the cell. Please take care of your ASLayoutSpec so that doesn't overlay the accessoryView + * @default UITableViewCellAccessoryNone + * ASTableView uses these properties when configuring UITableViewCells that host ASCellNodes. + */ +@property UITableViewCellAccessoryType accessoryType; + +/* @abstract The inset of the cell separator line + * ASTableView uses these properties when configuring UITableViewCells that host ASCellNodes. + */ +@property UIEdgeInsets separatorInset; + +@end + +@interface ASCellNode (Unavailable) + +- (instancetype)initWithLayerBlock:(ASDisplayNodeLayerBlock)viewBlock didLoadBlock:(nullable ASDisplayNodeDidLoadBlock)didLoadBlock NS_UNAVAILABLE; + +- (instancetype)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock didLoadBlock:(nullable ASDisplayNodeDidLoadBlock)didLoadBlock NS_UNAVAILABLE; + +- (void)setLayerBacked:(BOOL)layerBacked AS_UNAVAILABLE("ASCellNode does not support layer-backing, although subnodes may be layer-backed."); + +@end + + +/** + * Simple label-style cell node. Read its source for an example of custom s. + */ +@interface ASTextCellNode : ASCellNode + +/** + * Initializes a text cell with given text attributes and text insets + */ +- (instancetype)initWithAttributes:(NSDictionary *)textAttributes insets:(UIEdgeInsets)textInsets; + +/** + * Text to display. + */ +@property (nullable, copy) NSString *text; + +/** + * A dictionary containing key-value pairs for text attributes. You can specify the font, text color, text shadow color, and text shadow offset using the keys listed in NSString UIKit Additions Reference. + */ +@property (copy) NSDictionary *textAttributes; + +/** + * The text inset or outset for each edge. The default value is 15.0 horizontal and 11.0 vertical padding. + */ +@property UIEdgeInsets textInsets; + +/** + * The text node used by this cell node. + */ +@property (readonly) ASTextNode *textNode; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASCellNode.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASCellNode.mm new file mode 100644 index 0000000..208eb5f --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASCellNode.mm @@ -0,0 +1,489 @@ +// +// ASCellNode.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + +#import +#import +#import + +#pragma mark - +#pragma mark ASCellNode + +@interface ASCellNode () +{ + ASDisplayNodeViewControllerBlock _viewControllerBlock; + ASDisplayNodeDidLoadBlock _viewControllerDidLoadBlock; + ASDisplayNode *_viewControllerNode; + UIViewController *_viewController; + UICollectionViewLayoutAttributes *_layoutAttributes; + BOOL _suspendInteractionDelegate; + BOOL _selected; + BOOL _highlighted; + BOOL _neverShowPlaceholders; +} + +@end + +@implementation ASCellNode +@synthesize interactionDelegate = _interactionDelegate; + +- (instancetype)init +{ + if (!(self = [super init])) + return nil; + + // Use UITableViewCell defaults + _selectionStyle = UITableViewCellSelectionStyleDefault; + _focusStyle = UITableViewCellFocusStyleDefault; + self.clipsToBounds = YES; + + return self; +} + +- (instancetype)initWithViewControllerBlock:(ASDisplayNodeViewControllerBlock)viewControllerBlock didLoadBlock:(ASDisplayNodeDidLoadBlock)didLoadBlock +{ + if (!(self = [super init])) + return nil; + + ASDisplayNodeAssertNotNil(viewControllerBlock, @"should initialize with a valid block that returns a UIViewController"); + _viewControllerBlock = viewControllerBlock; + _viewControllerDidLoadBlock = didLoadBlock; + + return self; +} + +- (void)didLoad +{ + [super didLoad]; + + if (_viewControllerBlock != nil) { + + _viewController = _viewControllerBlock(); + _viewControllerBlock = nil; + + if ([_viewController isKindOfClass:[ASDKViewController class]]) { + ASDKViewController *asViewController = (ASDKViewController *)_viewController; + _viewControllerNode = asViewController.node; + [_viewController loadViewIfNeeded]; + } else { + // Careful to avoid retain cycle + UIViewController *viewController = _viewController; + _viewControllerNode = [[ASDisplayNode alloc] initWithViewBlock:^{ + return viewController.view; + }]; + } + [self addSubnode:_viewControllerNode]; + + // Since we just loaded our node, and added _viewControllerNode as a subnode, + // _viewControllerNode must have just loaded its view, so now is an appropriate + // time to execute our didLoadBlock, if we were given one. + if (_viewControllerDidLoadBlock != nil) { + _viewControllerDidLoadBlock(self); + _viewControllerDidLoadBlock = nil; + } + } +} + +- (void)layout +{ + [super layout]; + + _viewControllerNode.frame = self.bounds; +} + +- (void)_rootNodeDidInvalidateSize +{ + if (_interactionDelegate != nil) { + [_interactionDelegate nodeDidInvalidateSize:self]; + } else { + [super _rootNodeDidInvalidateSize]; + } +} + +- (void)_layoutTransitionMeasurementDidFinish +{ + if (_interactionDelegate != nil) { + [_interactionDelegate nodeDidInvalidateSize:self]; + } else { + [super _layoutTransitionMeasurementDidFinish]; + } +} + +- (BOOL)isSelected +{ + return ASLockedSelf(_selected); +} + +- (void)setSelected:(BOOL)selected +{ + if (ASLockedSelfCompareAssign(_selected, selected)) { + if (!_suspendInteractionDelegate) { + ASPerformBlockOnMainThread(^{ + [self->_interactionDelegate nodeSelectedStateDidChange:self]; + }); + } + } +} + +- (BOOL)isHighlighted +{ + return ASLockedSelf(_highlighted); +} + +- (void)setHighlighted:(BOOL)highlighted +{ + if (ASLockedSelfCompareAssign(_highlighted, highlighted)) { + if (!_suspendInteractionDelegate) { + ASPerformBlockOnMainThread(^{ + [self->_interactionDelegate nodeHighlightedStateDidChange:self]; + }); + } + } +} + +- (void)__setSelectedFromUIKit:(BOOL)selected; +{ + // Note: Race condition could mean redundant sets. Risk is low. + if (ASLockedSelf(_selected != selected)) { + _suspendInteractionDelegate = YES; + self.selected = selected; + _suspendInteractionDelegate = NO; + } +} + +- (void)__setHighlightedFromUIKit:(BOOL)highlighted; +{ + // Note: Race condition could mean redundant sets. Risk is low. + if (ASLockedSelf(_highlighted != highlighted)) { + _suspendInteractionDelegate = YES; + self.highlighted = highlighted; + _suspendInteractionDelegate = NO; + } +} + +- (BOOL)canUpdateToNodeModel:(id)nodeModel +{ + return [self.nodeModel class] == [nodeModel class]; +} + +- (NSIndexPath *)indexPath +{ + return [self.owningNode indexPathForNode:self]; +} + +- (UIViewController *)viewController +{ + ASDisplayNodeAssertMainThread(); + // Force the view to load so that we will create the + // view controller if we haven't already. + if (self.isNodeLoaded == NO) { + [self view]; + } + return _viewController; +} + +- (id)owningNode +{ + return self.collectionElement.owningNode; +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wobjc-missing-super-calls" + +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event +{ + ASDisplayNodeAssertMainThread(); + ASDisplayNodeAssert([self.view isKindOfClass:_ASDisplayView.class], @"ASCellNode views must be of type _ASDisplayView"); + [(_ASDisplayView *)self.view __forwardTouchesBegan:touches withEvent:event]; +} + +- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event +{ + ASDisplayNodeAssertMainThread(); + ASDisplayNodeAssert([self.view isKindOfClass:_ASDisplayView.class], @"ASCellNode views must be of type _ASDisplayView"); + [(_ASDisplayView *)self.view __forwardTouchesMoved:touches withEvent:event]; +} + +- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event +{ + ASDisplayNodeAssertMainThread(); + ASDisplayNodeAssert([self.view isKindOfClass:_ASDisplayView.class], @"ASCellNode views must be of type _ASDisplayView"); + [(_ASDisplayView *)self.view __forwardTouchesEnded:touches withEvent:event]; +} + +- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event +{ + ASDisplayNodeAssertMainThread(); + ASDisplayNodeAssert([self.view isKindOfClass:_ASDisplayView.class], @"ASCellNode views must be of type _ASDisplayView"); + [(_ASDisplayView *)self.view __forwardTouchesCancelled:touches withEvent:event]; +} + +#pragma clang diagnostic pop + +- (UICollectionViewLayoutAttributes *)layoutAttributes +{ + return ASLockedSelf(_layoutAttributes); +} + +- (void)setLayoutAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes +{ + ASDisplayNodeAssertMainThread(); + if (ASLockedSelfCompareAssignObjects(_layoutAttributes, layoutAttributes)) { + if (layoutAttributes != nil) { + [self applyLayoutAttributes:layoutAttributes]; + } + } +} + +- (void)applyLayoutAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes +{ + // To be overriden by subclasses +} + +- (void)cellNodeVisibilityEvent:(ASCellNodeVisibilityEvent)event inScrollView:(UIScrollView *)scrollView withCellFrame:(CGRect)cellFrame +{ + // To be overriden by subclasses +} + +- (void)didEnterVisibleState +{ + [super didEnterVisibleState]; + if (self.neverShowPlaceholders) { + [self recursivelyEnsureDisplaySynchronously:YES]; + } + [self handleVisibilityChange:YES]; +} + +- (void)didExitVisibleState +{ + [super didExitVisibleState]; + [self handleVisibilityChange:NO]; +} + ++ (BOOL)requestsVisibilityNotifications +{ + static NSCache *cache; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + cache = [[NSCache alloc] init]; + }); + NSNumber *result = [cache objectForKey:self]; + if (result == nil) { + BOOL overrides = ASSubclassOverridesSelector([ASCellNode class], self, @selector(cellNodeVisibilityEvent:inScrollView:withCellFrame:)); + result = overrides ? (NSNumber *)kCFBooleanTrue : (NSNumber *)kCFBooleanFalse; + [cache setObject:result forKey:self]; + } + return (result == (NSNumber *)kCFBooleanTrue); +} + +- (void)handleVisibilityChange:(BOOL)isVisible +{ + if ([self.class requestsVisibilityNotifications] == NO) { + return; // The work below is expensive, and only valuable for subclasses watching visibility events. + } + + // NOTE: This assertion is failing in some apps and will be enabled soon. + // ASDisplayNodeAssert(self.isNodeLoaded, @"Node should be loaded in order for it to become visible or invisible. If not in this situation, we shouldn't trigger creating the view."); + + UIView *view = self.view; + CGRect cellFrame = CGRectZero; + + // Ensure our _scrollView is still valid before converting. It's also possible that we have already been removed from the _scrollView, + // in which case it is not valid to perform a convertRect (this actually crashes on iOS 8). + UIScrollView *scrollView = (_scrollView != nil && view.superview != nil && [view isDescendantOfView:_scrollView]) ? _scrollView : nil; + if (scrollView) { + cellFrame = [view convertRect:view.bounds toView:scrollView]; + } + + // If we did not convert, we'll pass along CGRectZero and a nil scrollView. The EventInvisible call is thus equivalent to + // didExitVisibileState, but is more convenient for the developer than implementing multiple methods. + [self cellNodeVisibilityEvent:isVisible ? ASCellNodeVisibilityEventVisible + : ASCellNodeVisibilityEventInvisible + inScrollView:scrollView + withCellFrame:cellFrame]; +} + +- (NSMutableArray *)propertiesForDebugDescription +{ + NSMutableArray *result = [super propertiesForDebugDescription]; + + UIScrollView *scrollView = self.scrollView; + + id owningNode = self.owningNode; + if ([owningNode isKindOfClass:[ASCollectionNode class]]) { + NSIndexPath *ip = [(ASCollectionNode *)owningNode indexPathForNode:self]; + if (ip != nil) { + [result addObject:@{ @"indexPath" : ip }]; + } + [result addObject:@{ @"collectionNode" : owningNode }]; + } else if ([owningNode isKindOfClass:[ASTableNode class]]) { + NSIndexPath *ip = [(ASTableNode *)owningNode indexPathForNode:self]; + if (ip != nil) { + [result addObject:@{ @"indexPath" : ip }]; + } + [result addObject:@{ @"tableNode" : owningNode }]; + + } else if ([scrollView isKindOfClass:[ASCollectionView class]]) { + NSIndexPath *ip = [(ASCollectionView *)scrollView indexPathForNode:self]; + if (ip != nil) { + [result addObject:@{ @"indexPath" : ip }]; + } + [result addObject:@{ @"collectionView" : ASObjectDescriptionMakeTiny(scrollView) }]; + + } else if ([scrollView isKindOfClass:[ASTableView class]]) { + NSIndexPath *ip = [(ASTableView *)scrollView indexPathForNode:self]; + if (ip != nil) { + [result addObject:@{ @"indexPath" : ip }]; + } + [result addObject:@{ @"tableView" : ASObjectDescriptionMakeTiny(scrollView) }]; + } + + return result; +} + +- (NSString *)supplementaryElementKind +{ + return self.collectionElement.supplementaryElementKind; +} + +- (BOOL)supportsLayerBacking +{ + return NO; +} + +- (BOOL)shouldUseUIKitCell +{ + return NO; +} + +@end + + +#pragma mark - +#pragma mark ASWrapperCellNode + +// TODO: Consider if other calls, such as willDisplayCell, should be bridged to this class. +@implementation ASWrapperCellNode : ASCellNode + +- (BOOL)shouldUseUIKitCell +{ + return YES; +} + +@end + + +#pragma mark - +#pragma mark ASTextCellNode + +@implementation ASTextCellNode { + NSDictionary *_textAttributes; + UIEdgeInsets _textInsets; + NSString *_text; +} + +static const CGFloat kASTextCellNodeDefaultFontSize = 18.0f; +static const CGFloat kASTextCellNodeDefaultHorizontalPadding = 15.0f; +static const CGFloat kASTextCellNodeDefaultVerticalPadding = 11.0f; + +- (instancetype)init +{ + return [self initWithAttributes:[ASTextCellNode defaultTextAttributes] insets:[ASTextCellNode defaultTextInsets]]; +} + +- (instancetype)initWithAttributes:(NSDictionary *)textAttributes insets:(UIEdgeInsets)textInsets +{ + self = [super init]; + if (self) { + _textInsets = textInsets; + _textAttributes = [textAttributes copy]; + _textNode = [[ASTextNode alloc] init]; + self.automaticallyManagesSubnodes = YES; + } + return self; +} + +- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize +{ + return [ASInsetLayoutSpec insetLayoutSpecWithInsets:self.textInsets child:self.textNode]; +} + ++ (NSDictionary *)defaultTextAttributes +{ + return @{NSFontAttributeName : [UIFont systemFontOfSize:kASTextCellNodeDefaultFontSize]}; +} + ++ (UIEdgeInsets)defaultTextInsets +{ + return UIEdgeInsetsMake(kASTextCellNodeDefaultVerticalPadding, kASTextCellNodeDefaultHorizontalPadding, kASTextCellNodeDefaultVerticalPadding, kASTextCellNodeDefaultHorizontalPadding); +} + +- (NSDictionary *)textAttributes +{ + return ASLockedSelf(_textAttributes); +} + +- (void)setTextAttributes:(NSDictionary *)textAttributes +{ + ASDisplayNodeAssertNotNil(textAttributes, @"Invalid text attributes"); + ASLockScopeSelf(); + if (ASCompareAssignCopy(_textAttributes, textAttributes)) { + [self locked_updateAttributedText]; + } +} + +- (UIEdgeInsets)textInsets +{ + return ASLockedSelf(_textInsets); +} + +- (void)setTextInsets:(UIEdgeInsets)textInsets +{ + if (ASLockedSelfCompareAssignCustom(_textInsets, textInsets, UIEdgeInsetsEqualToEdgeInsets)) { + [self setNeedsLayout]; + } +} + +- (NSString *)text +{ + return ASLockedSelf(_text); +} + +- (void)setText:(NSString *)text +{ + ASLockScopeSelf(); + if (ASCompareAssignCopy(_text, text)) { + [self locked_updateAttributedText]; + } +} + +- (void)locked_updateAttributedText +{ + if (_text == nil) { + _textNode.attributedText = nil; + return; + } + + _textNode.attributedText = [[NSAttributedString alloc] initWithString:_text attributes:_textAttributes]; + [self setNeedsLayout]; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASCollectionNode+Beta.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASCollectionNode+Beta.h new file mode 100644 index 0000000..feb485b --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASCollectionNode+Beta.h @@ -0,0 +1,79 @@ +// +// ASCollectionNode+Beta.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +@protocol ASCollectionViewLayoutFacilitatorProtocol, ASCollectionLayoutDelegate, ASBatchFetchingDelegate; +@class ASElementMap; + +NS_ASSUME_NONNULL_BEGIN + +@interface ASCollectionNode (Beta) + +/** + * Allows providing a custom subclass of ASCollectionView to be managed by ASCollectionNode. + * + * @default [ASCollectionView class] is used whenever this property is unset or nil. + */ +@property (nullable, nonatomic) Class collectionViewClass; + +/** + * The elements that are currently displayed. The "UIKit index space". Must be accessed on main thread. + */ +@property (nonatomic, readonly) ASElementMap *visibleElements; + +@property (nullable, readonly) id layoutDelegate; + +@property (nullable, nonatomic, weak) id batchFetchingDelegate; + +/** + * When this mode is enabled, ASCollectionView matches the timing of UICollectionView as closely as + * possible, ensuring that all reload and edit operations are performed on the main thread as + * blocking calls. + * + * This mode is useful for applications that are debugging issues with their collection view + * implementation. In particular, some applications do not properly conform to the API requirement + * of UICollectionView, and these applications may experience difficulties with ASCollectionView. + * Providing this mode allows for developers to work towards resolving technical debt in their + * collection view data source, while ramping up asynchronous collection layout. + * + * NOTE: Because this mode results in expensive operations like cell layout being performed on the + * main thread, it should be used as a tool to resolve data source conformance issues with Apple + * collection view API. + * + * @default defaults to ASCellLayoutModeNone. + */ +@property (nonatomic) ASCellLayoutMode cellLayoutMode; + +/** + * Returns YES if the ASCollectionNode contents are completely synchronized with the underlying collection-view layout. + */ +@property (nonatomic, readonly, getter=isSynchronized) BOOL synchronized; + +/** + * Schedules a block to be performed (on the main thread) as soon as the completion block is called + * on performBatchUpdates:. + * + * When isSynchronized == YES, the block is run block immediately (before the method returns). + */ +- (void)onDidFinishSynchronizing:(void (^)(void))didFinishSynchronizing; + +- (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout layoutFacilitator:(nullable id)layoutFacilitator; + +- (instancetype)initWithLayoutDelegate:(id)layoutDelegate layoutFacilitator:(nullable id)layoutFacilitator; + +- (void)beginUpdates ASDISPLAYNODE_DEPRECATED_MSG("Use -performBatchUpdates:completion: instead."); + +- (void)endUpdatesAnimated:(BOOL)animated ASDISPLAYNODE_DEPRECATED_MSG("Use -performBatchUpdates:completion: instead."); + +- (void)endUpdatesAnimated:(BOOL)animated completion:(nullable void (^)(BOOL))completion ASDISPLAYNODE_DEPRECATED_MSG("Use -performBatchUpdates:completion: instead."); + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASCollectionNode.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASCollectionNode.h new file mode 100644 index 0000000..fde5a13 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASCollectionNode.h @@ -0,0 +1,956 @@ +// +// ASCollectionNode.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import +#import +#import +#import + +@protocol ASCollectionViewLayoutFacilitatorProtocol; +@protocol ASCollectionDelegate; +@protocol ASCollectionDataSource; +@class ASCollectionView; + +NS_ASSUME_NONNULL_BEGIN + +/** + * ASCollectionNode is a node based class that wraps an ASCollectionView. It can be used + * as a subnode of another node, and provide room for many (great) features and improvements later on. + */ +@interface ASCollectionNode : ASDisplayNode + +- (instancetype)init NS_UNAVAILABLE; + +/** + * Initializes an ASCollectionNode + * + * @discussion Initializes and returns a newly allocated collection node object with the specified layout. + * + * @param layout The layout object to use for organizing items. The collection view stores a strong reference to the specified object. Must not be nil. + */ +- (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout *)layout; + +/** + * Initializes an ASCollectionNode + * + * @discussion Initializes and returns a newly allocated collection node object with the specified frame and layout. + * + * @param frame The frame rectangle for the collection view, measured in points. The origin of the frame is relative to the superview in which you plan to add it. This frame is passed to the superclass during initialization. + * @param layout The layout object to use for organizing items. The collection view stores a strong reference to the specified object. Must not be nil. + */ +- (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout; + +/** + * Returns the corresponding ASCollectionView + * + * @return view The corresponding ASCollectionView. + */ +@property (readonly) ASCollectionView *view; + +/** + * The object that acts as the asynchronous delegate of the collection view + * + * @discussion The delegate must adopt the ASCollectionDelegate protocol. The collection view maintains a weak reference to the delegate object. + * + * The delegate object is responsible for providing size constraints for nodes and indicating whether batch fetching should begin. + * @note This is a convenience method which sets the asyncDelegate on the collection node's collection view. + */ +@property (nullable, weak) id delegate; + +/** + * The object that acts as the asynchronous data source of the collection view + * + * @discussion The datasource must adopt the ASCollectionDataSource protocol. The collection view maintains a weak reference to the datasource object. + * + * The datasource object is responsible for providing nodes or node creation blocks to the collection view. + * @note This is a convenience method which sets the asyncDatasource on the collection node's collection view. + */ +@property (nullable, weak) id dataSource; + +/** + * The number of screens left to scroll before the delegate -collectionNode:beginBatchFetchingWithContext: is called. + * + * Defaults to two screenfuls. + */ +@property (nonatomic) CGFloat leadingScreensForBatching; + +/* + * A Boolean value that determines whether the collection node will be flipped. + * If the value of this property is YES, the first cell node will be at the bottom of the collection node (as opposed to the top by default). This is useful for chat/messaging apps. The default value is NO. + */ +@property (nonatomic) BOOL inverted; + +/** + * A Boolean value that indicates whether users can select items in the collection node. + * If the value of this property is YES (the default), users can select items. If you want more fine-grained control over the selection of items, you must provide a delegate object and implement the appropriate methods of the UICollectionNodeDelegate protocol. + */ +@property (nonatomic) BOOL allowsSelection; + +/** + * A Boolean value that determines whether users can select more than one item in the collection node. + * This property controls whether multiple items can be selected simultaneously. The default value of this property is NO. + * When the value of this property is YES, tapping a cell adds it to the current selection (assuming the delegate permits the cell to be selected). Tapping the cell again removes it from the selection. + */ +@property (nonatomic) BOOL allowsMultipleSelection; + +/** + * A Boolean value that determines whether bouncing always occurs when vertical scrolling reaches the end of the content. + * The default value of this property is NO. + */ +@property (nonatomic) BOOL alwaysBounceVertical; + +/** + * A Boolean value that determines whether bouncing always occurs when horizontal scrolling reaches the end of the content view. + * The default value of this property is NO. + */ +@property (nonatomic) BOOL alwaysBounceHorizontal; + +/** + * A Boolean value that controls whether the vertical scroll indicator is visible. + * The default value of this property is YES. + */ +@property (nonatomic) BOOL showsVerticalScrollIndicator; + +/** + * A Boolean value that controls whether the horizontal scroll indicator is visible. + * The default value of this property is YES. + */ +@property (nonatomic) BOOL showsHorizontalScrollIndicator; + +/** + * A Boolean value that determines whether paging is enabled for the scroll view. + * The default value of this property is NO. + */ +@property (nonatomic, getter=isPagingEnabled) BOOL pagingEnabled __TVOS_PROHIBITED; + +/** + * The layout used to organize the node's items. + * + * @discussion Assigning a new layout object to this property causes the new layout to be applied (without animations) to the node’s items. + */ +@property (nonatomic) UICollectionViewLayout *collectionViewLayout; + +/** + * Optional introspection object for the collection node's layout. + * + * @discussion Since supplementary and decoration nodes are controlled by the layout, this object + * is used as a bridge to provide information to the internal data controller about the existence of these views and + * their associated index paths. For collections using `UICollectionViewFlowLayout`, a default inspector + * implementation `ASCollectionViewFlowLayoutInspector` is created and set on this property by default. Custom + * collection layout subclasses will need to provide their own implementation of an inspector object for their + * supplementary elements to be compatible with `ASCollectionNode`'s supplementary node support. + */ +@property (nonatomic, weak) id layoutInspector; + +/** + * The distance that the content view is inset from the collection node edges. Defaults to UIEdgeInsetsZero. + */ +@property (nonatomic) UIEdgeInsets contentInset; + +/** + * The offset of the content view's origin from the collection node's origin. Defaults to CGPointZero. + */ +@property (nonatomic) CGPoint contentOffset; + +/** + * Sets the offset from the content node’s origin to the collection node’s origin. + * + * @param contentOffset The offset + * + * @param animated YES to animate to this new offset at a constant velocity, NO to not aniamte and immediately make the transition. + */ +- (void)setContentOffset:(CGPoint)contentOffset animated:(BOOL)animated; + +/** + * Tuning parameters for a range type in full mode. + * + * @param rangeType The range type to get the tuning parameters for. + * + * @return A tuning parameter value for the given range type in full mode. + * + * @see ASLayoutRangeMode + * @see ASLayoutRangeType + */ +- (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType AS_WARN_UNUSED_RESULT; + +/** + * Set the tuning parameters for a range type in full mode. + * + * @param tuningParameters The tuning parameters to store for a range type. + * @param rangeType The range type to set the tuning parameters for. + * + * @see ASLayoutRangeMode + * @see ASLayoutRangeType + */ +- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType; + +/** + * Tuning parameters for a range type in the specified mode. + * + * @param rangeMode The range mode to get the running parameters for. + * @param rangeType The range type to get the tuning parameters for. + * + * @return A tuning parameter value for the given range type in the given mode. + * + * @see ASLayoutRangeMode + * @see ASLayoutRangeType + */ +- (ASRangeTuningParameters)tuningParametersForRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType AS_WARN_UNUSED_RESULT; + +/** + * Set the tuning parameters for a range type in the specified mode. + * + * @param tuningParameters The tuning parameters to store for a range type. + * @param rangeMode The range mode to set the running parameters for. + * @param rangeType The range type to set the tuning parameters for. + * + * @see ASLayoutRangeMode + * @see ASLayoutRangeType + */ +- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType; + +/** + * Scrolls the collection to the given item. + * + * @param indexPath The index path of the item. + * @param scrollPosition Where the item should end up after the scroll. + * @param animated Whether the scroll should be animated or not. + * + * This method must be called on the main thread. + */ +- (void)scrollToItemAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UICollectionViewScrollPosition)scrollPosition animated:(BOOL)animated; + +/** + * Determines collection node's current scroll direction. Supports 2-axis collection nodes. + * + * @return a bitmask of ASScrollDirection values. + */ +@property (nonatomic, readonly) ASScrollDirection scrollDirection; + +/** + * Determines collection node's scrollable directions. + * + * @return a bitmask of ASScrollDirection values. + */ +@property (nonatomic, readonly) ASScrollDirection scrollableDirections; + +#pragma mark - Editing + +/** + * Registers the given kind of supplementary node for use in creating node-backed supplementary elements. + * + * @param elementKind The kind of supplementary node that will be requested through the data source. + * + * @discussion Use this method to register support for the use of supplementary nodes in place of the default + * `registerClass:forSupplementaryViewOfKind:withReuseIdentifier:` and `registerNib:forSupplementaryViewOfKind:withReuseIdentifier:` + * methods. This method will register an internal backing view that will host the contents of the supplementary nodes + * returned from the data source. + */ +- (void)registerSupplementaryNodeOfKind:(NSString *)elementKind; + +/** + * Perform a batch of updates asynchronously, optionally disabling all animations in the batch. This method must be called from the main thread. + * The data source must be updated to reflect the changes before the update block completes. + * + * @param animated NO to disable animations for this batch + * @param updates The block that performs the relevant insert, delete, reload, or move operations. + * @param completion A completion handler block to execute when all of the operations are finished. This block takes a single + * Boolean parameter that contains the value YES if all of the related animations completed successfully or + * NO if they were interrupted. This parameter may be nil. If supplied, the block is run on the main thread. + */ +- (void)performBatchAnimated:(BOOL)animated updates:(nullable AS_NOESCAPE void (^)(void))updates completion:(nullable void (^)(BOOL finished))completion; + +/** + * Perform a batch of updates asynchronously, optionally disabling all animations in the batch. This method must be called from the main thread. + * The data source must be updated to reflect the changes before the update block completes. + * + * @param updates The block that performs the relevant insert, delete, reload, or move operations. + * @param completion A completion handler block to execute when all of the operations are finished. This block takes a single + * Boolean parameter that contains the value YES if all of the related animations completed successfully or + * NO if they were interrupted. This parameter may be nil. If supplied, the block is run on the main thread. + */ +- (void)performBatchUpdates:(nullable AS_NOESCAPE void (^)(void))updates completion:(nullable void (^)(BOOL finished))completion; + +/** + * Returns YES if the ASCollectionNode is still processing changes from performBatchUpdates:. + * This is typically the concurrent allocation (calling nodeBlocks) and layout of newly inserted + * ASCellNodes. If YES is returned, then calling -waitUntilAllUpdatesAreProcessed may take tens of + * milliseconds to return as it blocks on these concurrent operations. + * + * Returns NO if ASCollectionNode is fully synchronized with the underlying UICollectionView. This + * means that until the next performBatchUpdates: is called, it is safe to compare UIKit values + * (such as from UICollectionViewLayout) with your app's data source. + * + * This method will always return NO if called immediately after -waitUntilAllUpdatesAreProcessed. + */ +@property (nonatomic, readonly) BOOL isProcessingUpdates; + +/** + * Schedules a block to be performed (on the main thread) after processing of performBatchUpdates: + * is finished (completely synchronized to UIKit). The blocks will be run at the moment that + * -isProcessingUpdates changes from YES to NO; + * + * When isProcessingUpdates == NO, the block is run block immediately (before the method returns). + * + * Blocks scheduled by this mechanism are NOT guaranteed to run in the order they are scheduled. + * They may also be delayed if performBatchUpdates continues to be called; the blocks will wait until + * all running updates are finished. + * + * Calling -waitUntilAllUpdatesAreProcessed is one way to flush any pending update completion blocks. + */ +- (void)onDidFinishProcessingUpdates:(void (^)(void))didFinishProcessingUpdates; + +/** + * Blocks execution of the main thread until all section and item updates are committed to the view. This method must be called from the main thread. + */ +- (void)waitUntilAllUpdatesAreProcessed; + +/** + * Inserts one or more sections. + * + * @param sections An index set that specifies the sections to insert. + * + * @discussion This method must be called from the main thread. The data source must be updated to reflect the changes + * before this method is called. + */ +- (void)insertSections:(NSIndexSet *)sections; + +/** + * Deletes one or more sections. + * + * @param sections An index set that specifies the sections to delete. + * + * @discussion This method must be called from the main thread. The data source must be updated to reflect the changes + * before this method is called. + */ +- (void)deleteSections:(NSIndexSet *)sections; + +/** + * Reloads the specified sections. + * + * @param sections An index set that specifies the sections to reload. + * + * @discussion This method must be called from the main thread. The data source must be updated to reflect the changes + * before this method is called. + */ +- (void)reloadSections:(NSIndexSet *)sections; + +/** + * Moves a section to a new location. + * + * @param section The index of the section to move. + * + * @param newSection The index that is the destination of the move for the section. + * + * @discussion This method must be called from the main thread. The data source must be updated to reflect the changes + * before this method is called. + */ +- (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection; + +/** + * Inserts items at the locations identified by an array of index paths. + * + * @param indexPaths An array of NSIndexPath objects, each representing an item index and section index that together identify an item. + * + * @discussion This method must be called from the main thread. The data source must be updated to reflect the changes + * before this method is called. + */ +- (void)insertItemsAtIndexPaths:(NSArray *)indexPaths; + +/** + * Deletes the items specified by an array of index paths. + * + * @param indexPaths An array of NSIndexPath objects identifying the items to delete. + * + * @discussion This method must be called from the main thread. The data source must be updated to reflect the changes + * before this method is called. + */ +- (void)deleteItemsAtIndexPaths:(NSArray *)indexPaths; + +/** + * Reloads the specified items. + * + * @param indexPaths An array of NSIndexPath objects identifying the items to reload. + * + * @discussion This method must be called from the main thread. The data source must be updated to reflect the changes + * before this method is called. + */ +- (void)reloadItemsAtIndexPaths:(NSArray *)indexPaths; + +/** + * Moves the item at a specified location to a destination location. + * + * @param indexPath The index path identifying the item to move. + * + * @param newIndexPath The index path that is the destination of the move for the item. + * + * @discussion This method must be called from the main thread. The data source must be updated to reflect the changes + * before this method is called. + */ +- (void)moveItemAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath; + +/** + * Reload everything from scratch, destroying the working range and all cached nodes. + * + * @param completion block to run on completion of asynchronous loading or nil. If supplied, the block is run on + * the main thread. + * @warning This method is substantially more expensive than UICollectionView's version. + */ +- (void)reloadDataWithCompletion:(nullable void (^)(void))completion; + + +/** + * Reload everything from scratch, destroying the working range and all cached nodes. + * + * @warning This method is substantially more expensive than UICollectionView's version. + */ +- (void)reloadData; + +/** + * Triggers a relayout of all nodes. + * + * @discussion This method invalidates and lays out every cell node in the collection view. + */ +- (void)relayoutItems; + +#pragma mark - Selection + +/** + * The index paths of the selected items, or @c nil if no items are selected. + */ +@property (nullable, nonatomic, copy, readonly) NSArray *indexPathsForSelectedItems; + +/** + * Selects the item at the specified index path and optionally scrolls it into view. + * If the `allowsSelection` property is NO, calling this method has no effect. If there is an existing selection with a different index path and the `allowsMultipleSelection` property is NO, calling this method replaces the previous selection. + * This method does not cause any selection-related delegate methods to be called. + * + * @param indexPath The index path of the item to select. Specifying nil for this parameter clears the current selection. + * + * @param animated Specify YES to animate the change in the selection or NO to make the change without animating it. + * + * @param scrollPosition An option that specifies where the item should be positioned when scrolling finishes. For a list of possible values, see `UICollectionViewScrollPosition`. + * + * @discussion This method must be called from the main thread. + */ +- (void)selectItemAtIndexPath:(nullable NSIndexPath *)indexPath animated:(BOOL)animated scrollPosition:(UICollectionViewScrollPosition)scrollPosition; + +/** + * Deselects the item at the specified index. + * If the allowsSelection property is NO, calling this method has no effect. + * This method does not cause any selection-related delegate methods to be called. + * + * @param indexPath The index path of the item to select. Specifying nil for this parameter clears the current selection. + * + * @param animated Specify YES to animate the change in the selection or NO to make the change without animating it. + * + * @discussion This method must be called from the main thread. + */ +- (void)deselectItemAtIndexPath:(NSIndexPath *)indexPath animated:(BOOL)animated; + +#pragma mark - Querying Data + +/** + * Retrieves the number of items in the given section. + * + * @param section The section. + * + * @return The number of items. + */ +- (NSInteger)numberOfItemsInSection:(NSInteger)section AS_WARN_UNUSED_RESULT; + +/** + * The number of sections. + */ +@property (nonatomic, readonly) NSInteger numberOfSections; + +/** + * Similar to -visibleCells. + * + * @return an array containing the nodes being displayed on screen. This must be called on the main thread. + */ +@property (nonatomic, readonly) NSArray<__kindof ASCellNode *> *visibleNodes; + +/** + * Retrieves the node for the item at the given index path. + * + * @param indexPath The index path of the requested item. + * + * @return The node for the given item, or @c nil if no item exists at the specified path. + */ +- (nullable __kindof ASCellNode *)nodeForItemAtIndexPath:(NSIndexPath *)indexPath AS_WARN_UNUSED_RESULT; + +/** + * Retrieves the node-model for the item at the given index path, if any. + * + * @param indexPath The index path of the requested item. + * + * @return The node-model for the given item, or @c nil if no item exists at the specified path or no node-model was provided. + * + * @warning This API is beta and subject to change. We'll try to provide an easy migration path. + */ +- (nullable id)nodeModelForItemAtIndexPath:(NSIndexPath *)indexPath AS_WARN_UNUSED_RESULT; + +/** + * Retrieve the index path for the item with the given node. + * + * @param cellNode A node for an item in the collection node. + * + * @return The indexPath for this item. + */ +- (nullable NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode AS_WARN_UNUSED_RESULT; + +/** + * Retrieve the index paths of all visible items. + * + * @return an array containing the index paths of all visible items. This must be called on the main thread. + */ +@property (nonatomic, readonly) NSArray *indexPathsForVisibleItems; + +/** + * Retrieve the index path of the item at the given point. + * + * @param point The point of the requested item. + * + * @return The indexPath for the item at the given point. This must be called on the main thread. + */ +- (nullable NSIndexPath *)indexPathForItemAtPoint:(CGPoint)point AS_WARN_UNUSED_RESULT; + +/** + * Retrieve the cell at the given index path. + * + * @param indexPath The index path of the requested item. + * + * @return The cell for the given index path. This must be called on the main thread. + */ +- (nullable UICollectionViewCell *)cellForItemAtIndexPath:(NSIndexPath *)indexPath; + +/** + * Retrieves the context object for the given section, as provided by the data source in + * the @c collectionNode:contextForSection: method. + * + * @param section The section to get the context for. + * + * @return The context object, or @c nil if no context was provided. + * + * TODO: This method currently accepts @c section in the _view_ index space, but it should + * be in the node index space. To get the context in the view index space (e.g. for subclasses + * of @c UICollectionViewLayout, the user will call the same method on @c ASCollectionView. + */ +- (nullable id)contextForSection:(NSInteger)section AS_WARN_UNUSED_RESULT; + +@end + +@interface ASCollectionNode (Deprecated) + +- (void)waitUntilAllUpdatesAreCommitted ASDISPLAYNODE_DEPRECATED_MSG("This method has been renamed to -waitUntilAllUpdatesAreProcessed."); + +@end + +/** + * This is a node-based UICollectionViewDataSource. + */ +@protocol ASCollectionDataSource + +@optional + +/** + * Asks the data source for the number of items in the given section of the collection node. + * + * @see @c collectionView:numberOfItemsInSection: + */ +- (NSInteger)collectionNode:(ASCollectionNode *)collectionNode numberOfItemsInSection:(NSInteger)section; + +/** + * Asks the data source for the number of sections in the collection node. + * + * @see @c numberOfSectionsInCollectionView: + */ +- (NSInteger)numberOfSectionsInCollectionNode:(ASCollectionNode *)collectionNode; + +/** + * --BETA-- + * Asks the data source for a view-model for the item at the given index path. + * + * @param collectionNode The sender. + * @param indexPath The index path of the item. + * + * @return An object that contains all the data for this item. + */ +- (nullable id)collectionNode:(ASCollectionNode *)collectionNode nodeModelForItemAtIndexPath:(NSIndexPath *)indexPath; + +/** + * Similar to -collectionNode:nodeForItemAtIndexPath: + * This method takes precedence over collectionNode:nodeForItemAtIndexPath: if implemented. + * + * @param collectionNode The sender. + * @param indexPath The index path of the item. + * + * @return a block that creates the node for display for this item. + * Must be thread-safe (can be called on the main thread or a background + * queue) and should not implement reuse (it will be called once per row). + */ +- (ASCellNodeBlock)collectionNode:(ASCollectionNode *)collectionNode nodeBlockForItemAtIndexPath:(NSIndexPath *)indexPath; + +/** + * Similar to -collectionView:cellForItemAtIndexPath:. + * + * @param collectionNode The sender. + * @param indexPath The index path of the item. + * + * @return A node to display for the given item. This will be called on the main thread and should + * not implement reuse (it will be called once per item). Unlike UICollectionView's version, + * this method is not called when the item is about to display. + */ +- (ASCellNode *)collectionNode:(ASCollectionNode *)collectionNode nodeForItemAtIndexPath:(NSIndexPath *)indexPath; + +/** + * Asks the data source to provide a node-block to display for the given supplementary element in the collection view. + * + * @param collectionNode The sender. + * @param kind The kind of supplementary element. + * @param indexPath The index path of the supplementary element. + */ +- (ASCellNodeBlock)collectionNode:(ASCollectionNode *)collectionNode nodeBlockForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath; + +/** + * Asks the data source to provide a node to display for the given supplementary element in the collection view. + * + * @param collectionNode The sender. + * @param kind The kind of supplementary element. + * @param indexPath The index path of the supplementary element. + */ +- (ASCellNode *)collectionNode:(ASCollectionNode *)collectionNode nodeForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath; + +/** + * Asks the data source to provide a context object for the given section. This object + * can later be retrieved by calling @c contextForSection: and is useful when implementing + * custom @c UICollectionViewLayout subclasses. The context object is ret + * + * @param collectionNode The sender. + * @param section The index of the section to provide context for. + * + * @return A context object to assign to the given section, or @c nil. + */ +- (nullable id)collectionNode:(ASCollectionNode *)collectionNode contextForSection:(NSInteger)section; + +/** + * Asks the data source to provide an array of supplementary element kinds that exist in a given section. + * + * @param collectionNode The sender. + * @param section The index of the section to provide supplementary kinds for. + * + * @return The supplementary element kinds that exist in the given section, if any. + */ +- (NSArray *)collectionNode:(ASCollectionNode *)collectionNode supplementaryElementKindsInSection:(NSInteger)section; + +/** + * Asks the data source if it's possible to move the specified item interactively. + * + * See @p -[UICollectionViewDataSource collectionView:canMoveItemAtIndexPath:] @c. + * + * @param collectionNode The sender. + * @param node The display node for the item that may be moved. + * + * @return Whether the item represented by @p node may be moved. + */ +- (BOOL)collectionNode:(ASCollectionNode *)collectionNode canMoveItemWithNode:(ASCellNode *)node; + +/** + * Called when the user has interactively moved an item. The data source + * should update its internal data store to reflect the move. Note that you + * should not call [collectionNode moveItemAtIndexPath:toIndexPath:] – the + * collection node's internal state will be updated automatically. + * + * * See @p -[UICollectionViewDataSource collectionView:moveItemAtIndexPath:toIndexPath:] @c. + * + * @param collectionNode The sender. + * @param sourceIndexPath The original item index path. + * @param destinationIndexPath The new item index path. + */ +- (void)collectionNode:(ASCollectionNode *)collectionNode moveItemAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath; + +/** + * Generate a unique identifier for an element in a collection. This helps state restoration persist the scroll position + * of a collection view even when the data in that table changes. See the documentation for UIDataSourceModelAssociation for more information. + * + * @param indexPath The index path of the requested node. + * + * @param collectionNode The sender. + * + * @return a unique identifier for the element at the given path. Return nil if the index path does not exist in the collection. + */ +- (nullable NSString *)modelIdentifierForElementAtIndexPath:(NSIndexPath *)indexPath inNode:(ASCollectionNode *)collectionNode; + +/** + * Similar to -collectionView:cellForItemAtIndexPath:. See the documentation for UIDataSourceModelAssociation for more information. + * + * @param identifier The model identifier of the element, previously generated by a call to modelIdentifierForElementAtIndexPath + * + * @param collectionNode The sender. + * + * @return the index path to the current position of the matching element in the collection. Return nil if the element is not found. + */ +- (nullable NSIndexPath *)indexPathForElementWithModelIdentifier:(NSString *)identifier inNode:(ASCollectionNode *)collectionNode; + +/** + * Similar to -collectionView:cellForItemAtIndexPath:. + * + * @param collectionView The sender. + * + * @param indexPath The index path of the requested node. + * + * @return a node for display at this indexpath. This will be called on the main thread and should + * not implement reuse (it will be called once per row). Unlike UICollectionView's version, + * this method is not called when the row is about to display. + */ +- (ASCellNode *)collectionView:(ASCollectionView *)collectionView nodeForItemAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode's method instead."); + +/** + * Similar to -collectionView:nodeForItemAtIndexPath: + * This method takes precedence over collectionView:nodeForItemAtIndexPath: if implemented. + * + * @param collectionView The sender. + * + * @param indexPath The index path of the requested node. + * + * @return a block that creates the node for display at this indexpath. + * Must be thread-safe (can be called on the main thread or a background + * queue) and should not implement reuse (it will be called once per row). + */ +- (ASCellNodeBlock)collectionView:(ASCollectionView *)collectionView nodeBlockForItemAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode's method instead."); + +/** + * Asks the collection view to provide a supplementary node to display in the collection view. + * + * @param collectionView An object representing the collection view requesting this information. + * @param kind The kind of supplementary node to provide. + * @param indexPath The index path that specifies the location of the new supplementary node. + */ +- (ASCellNode *)collectionView:(ASCollectionView *)collectionView nodeForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode's method instead."); + +/** + * Indicator to lock the data source for data fetching in async mode. + * We should not update the data source until the data source has been unlocked. Otherwise, it will incur data inconsistency or exception + * due to the data access in async mode. + * + * @param collectionView The sender. + * @deprecated The data source is always accessed on the main thread, and this method will not be called. + */ +- (void)collectionViewLockDataSource:(ASCollectionView *)collectionView ASDISPLAYNODE_DEPRECATED_MSG("Data source accesses are on the main thread. Method will not be called."); + +/** + * Indicator to unlock the data source for data fetching in async mode. + * We should not update the data source until the data source has been unlocked. Otherwise, it will incur data inconsistency or exception + * due to the data access in async mode. + * + * @param collectionView The sender. + * @deprecated The data source is always accessed on the main thread, and this method will not be called. + */ +- (void)collectionViewUnlockDataSource:(ASCollectionView *)collectionView ASDISPLAYNODE_DEPRECATED_MSG("Data source accesses are on the main thread. Method will not be called."); + +@end + +/** + * This is a node-based UICollectionViewDelegate. + */ +@protocol ASCollectionDelegate + +@optional + +/** + * Provides the constrained size range for measuring the given item. + * + * @param collectionNode The sender. + * + * @param indexPath The index path of the item. + * + * @return A constrained size range for layout for the item at this index path. + */ +- (ASSizeRange)collectionNode:(ASCollectionNode *)collectionNode constrainedSizeForItemAtIndexPath:(NSIndexPath *)indexPath; + +- (void)collectionNode:(ASCollectionNode *)collectionNode willDisplayItemWithNode:(ASCellNode *)node; + +- (void)collectionNode:(ASCollectionNode *)collectionNode didEndDisplayingItemWithNode:(ASCellNode *)node; + +- (void)collectionNode:(ASCollectionNode *)collectionNode willDisplaySupplementaryElementWithNode:(ASCellNode *)node NS_AVAILABLE_IOS(8_0); +- (void)collectionNode:(ASCollectionNode *)collectionNode didEndDisplayingSupplementaryElementWithNode:(ASCellNode *)node; + +- (BOOL)collectionNode:(ASCollectionNode *)collectionNode shouldHighlightItemAtIndexPath:(NSIndexPath *)indexPath; +- (void)collectionNode:(ASCollectionNode *)collectionNode didHighlightItemAtIndexPath:(NSIndexPath *)indexPath; +- (void)collectionNode:(ASCollectionNode *)collectionNode didUnhighlightItemAtIndexPath:(NSIndexPath *)indexPath; +- (BOOL)collectionNode:(ASCollectionNode *)collectionNode shouldSelectItemAtIndexPath:(NSIndexPath *)indexPath; +- (BOOL)collectionNode:(ASCollectionNode *)collectionNode shouldDeselectItemAtIndexPath:(NSIndexPath *)indexPath; +- (void)collectionNode:(ASCollectionNode *)collectionNode didSelectItemAtIndexPath:(NSIndexPath *)indexPath; +- (void)collectionNode:(ASCollectionNode *)collectionNode didDeselectItemAtIndexPath:(NSIndexPath *)indexPath; + +- (BOOL)collectionNode:(ASCollectionNode *)collectionNode shouldShowMenuForItemAtIndexPath:(NSIndexPath *)indexPath; +- (BOOL)collectionNode:(ASCollectionNode *)collectionNode canPerformAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath sender:(nullable id)sender; +- (void)collectionNode:(ASCollectionNode *)collectionNode performAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath sender:(nullable id)sender; + +/** + * Receive a message that the collection node is near the end of its data set and more data should be fetched if + * necessary. + * + * @param collectionNode The sender. + * @param context A context object that must be notified when the batch fetch is completed. + * + * @discussion You must eventually call -completeBatchFetching: with an argument of YES in order to receive future + * notifications to do batch fetches. This method is called on a background queue. + * + * ASCollectionNode currently only supports batch events for tail loads. If you require a head load, consider + * implementing a UIRefreshControl. + */ +- (void)collectionNode:(ASCollectionNode *)collectionNode willBeginBatchFetchWithContext:(ASBatchContext *)context; + +/** + * Tell the collection node if batch fetching should begin. + * + * @param collectionNode The sender. + * + * @discussion Use this method to conditionally fetch batches. Example use cases are: limiting the total number of + * objects that can be fetched or no network connection. + * + * If not implemented, the collection node assumes that it should notify its asyncDelegate when batch fetching + * should occur. + */ +- (BOOL)shouldBatchFetchForCollectionNode:(ASCollectionNode *)collectionNode; + +/** + * Provides the constrained size range for measuring the node at the index path. + * + * @param collectionView The sender. + * + * @param indexPath The index path of the node. + * + * @return A constrained size range for layout the node at this index path. + */ +- (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode's constrainedSizeForItemAtIndexPath: instead. PLEASE NOTE the very subtle method name change."); + +/** + * Informs the delegate that the collection view will add the given node + * at the given index path to the view hierarchy. + * + * @param collectionView The sender. + * @param node The node that will be displayed. + * @param indexPath The index path of the item that will be displayed. + * + * @warning AsyncDisplayKit processes collection view edits asynchronously. The index path + * passed into this method may not correspond to the same item in your data source + * if your data source has been updated since the last edit was processed. + */ +- (void)collectionView:(ASCollectionView *)collectionView willDisplayNode:(ASCellNode *)node forItemAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode's method instead."); + +/** + * Informs the delegate that the collection view did remove the provided node from the view hierarchy. + * This may be caused by the node scrolling out of view, or by deleting the item + * or its containing section with @c deleteItemsAtIndexPaths: or @c deleteSections: . + * + * @param collectionView The sender. + * @param node The node which was removed from the view hierarchy. + * @param indexPath The index path at which the node was located before it was removed. + * + * @warning AsyncDisplayKit processes collection view edits asynchronously. The index path + * passed into this method may not correspond to the same item in your data source + * if your data source has been updated since the last edit was processed. + */ +- (void)collectionView:(ASCollectionView *)collectionView didEndDisplayingNode:(ASCellNode *)node forItemAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode's method instead."); + +- (void)collectionView:(ASCollectionView *)collectionView willBeginBatchFetchWithContext:(ASBatchContext *)context ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode's method instead."); + +/** + * Tell the collectionView if batch fetching should begin. + * + * @param collectionView The sender. + * + * @discussion Use this method to conditionally fetch batches. Example use cases are: limiting the total number of + * objects that can be fetched or no network connection. + * + * If not implemented, the collectionView assumes that it should notify its asyncDelegate when batch fetching + * should occur. + */ +- (BOOL)shouldBatchFetchForCollectionView:(ASCollectionView *)collectionView ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode's method instead."); + +/** + * Informs the delegate that the collection view will add the node + * at the given index path to the view hierarchy. + * + * @param collectionView The sender. + * @param indexPath The index path of the item that will be displayed. + * + * @warning AsyncDisplayKit processes collection view edits asynchronously. The index path + * passed into this method may not correspond to the same item in your data source + * if your data source has been updated since the last edit was processed. + * + * This method is deprecated. Use @c collectionView:willDisplayNode:forItemAtIndexPath: instead. + */ +- (void)collectionView:(ASCollectionView *)collectionView willDisplayNodeForItemAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode's method instead."); + +@end + +@protocol ASCollectionDataSourceInterop + +/** + * This method offers compatibility with synchronous, standard UICollectionViewCell objects. + * These cells will **not** have the performance benefits of ASCellNodes (like preloading, async layout, and + * async drawing) - even when mixed within the same ASCollectionNode. + * + * In order to use this method, you must: + * 1. Implement it on your ASCollectionDataSource object. + * 2. Call registerCellClass: on the collectionNode.view (in viewDidLoad, or register an onDidLoad: block). + * 3. Return nil from the nodeBlockForItem...: or nodeForItem...: method. NOTE: it is an error to return + * nil from within a nodeBlock, if you have returned a nodeBlock object. + * 4. Lastly, you must implement a method to provide the size for the cell. There are two ways this is done: + * 4a. UICollectionViewFlowLayout (incl. ASPagerNode). Implement + collectionNode:constrainedSizeForItemAtIndexPath:. + * 4b. Custom collection layouts. Set .layoutInspector and have it implement + collectionView:constrainedSizeForNodeAtIndexPath:. + * + * For an example of using this method with all steps above (including a custom layout, 4b.), + * see the app in examples/CustomCollectionView and enable kShowUICollectionViewCells = YES. + */ +- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath; + +@optional + +- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath; + +/** + * Implement this property and return YES if you want your interop data source to be + * used when dequeuing cells for node-backed items. + * + * If NO (the default), the interop data source will only be consulted in cases + * where no ASCellNode was provided to AsyncDisplayKit. + * + * If YES, the interop data source will always be consulted to dequeue cells, and + * will be expected to return _ASCollectionViewCells in cases where a node was provided. + * + * The default value is NO. + */ +@property (class, nonatomic, readonly) BOOL dequeuesCellsForNodeBackedItems; + +@end + +@protocol ASCollectionDelegateInterop + +@optional + +- (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath; + +- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath; + +- (void)collectionView:(UICollectionView *)collectionView willDisplaySupplementaryView:(UICollectionReusableView *)view forElementKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath; + +- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingSupplementaryView:(UICollectionReusableView *)view forElementOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASCollectionNode.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASCollectionNode.mm new file mode 100644 index 0000000..f1da3e5 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASCollectionNode.mm @@ -0,0 +1,1236 @@ +// +// ASCollectionNode.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + +#pragma mark - _ASCollectionPendingState + +@interface _ASCollectionPendingState : NSObject { +@public + std::vector> _tuningParameters; + + // Keep these enums by the bitfield struct to save memory. + ASLayoutRangeMode _rangeMode; + ASCellLayoutMode _cellLayoutMode; + struct { + unsigned int allowsSelection:1; // default is YES + unsigned int allowsMultipleSelection:1; // default is NO + unsigned int inverted:1; //default is NO + unsigned int alwaysBounceVertical:1; + unsigned int alwaysBounceHorizontal:1; + unsigned int animatesContentOffset:1; + unsigned int showsVerticalScrollIndicator:1; + unsigned int showsHorizontalScrollIndicator:1; + unsigned int pagingEnabled:1; + } _flags; +} +@property (nonatomic, weak) id delegate; +@property (nonatomic, weak) id dataSource; +@property (nonatomic) UICollectionViewLayout *collectionViewLayout; +@property (nonatomic) ASLayoutRangeMode rangeMode; +@property (nonatomic) BOOL allowsSelection; // default is YES +@property (nonatomic) BOOL allowsMultipleSelection; // default is NO +@property (nonatomic) BOOL inverted; //default is NO +@property (nonatomic) ASCellLayoutMode cellLayoutMode; +@property (nonatomic) CGFloat leadingScreensForBatching; +@property (nonatomic, weak) id layoutInspector; +@property (nonatomic) BOOL alwaysBounceVertical; +@property (nonatomic) BOOL alwaysBounceHorizontal; +@property (nonatomic) UIEdgeInsets contentInset; +@property (nonatomic) CGPoint contentOffset; +@property (nonatomic) BOOL animatesContentOffset; +@property (nonatomic) BOOL showsVerticalScrollIndicator; +@property (nonatomic) BOOL showsHorizontalScrollIndicator; +@property (nonatomic) BOOL pagingEnabled; +@end + +@implementation _ASCollectionPendingState + +#pragma mark Lifecycle + +- (instancetype)init +{ + self = [super init]; + if (self) { + _rangeMode = ASLayoutRangeModeUnspecified; + _tuningParameters = [ASAbstractLayoutController defaultTuningParameters]; + _flags.allowsSelection = YES; + _flags.allowsMultipleSelection = NO; + _flags.inverted = NO; + _contentInset = UIEdgeInsetsZero; + _contentOffset = CGPointZero; + _flags.animatesContentOffset = NO; + _flags.showsVerticalScrollIndicator = YES; + _flags.showsHorizontalScrollIndicator = YES; + _flags.pagingEnabled = NO; + } + return self; +} + +#pragma mark Properties + +- (ASLayoutRangeMode)rangeMode +{ + return _rangeMode; +} + +- (void)setRangeMode:(ASLayoutRangeMode)rangeMode +{ + _rangeMode = rangeMode; +} + +- (ASCellLayoutMode)cellLayoutMode +{ + return _cellLayoutMode; +} + +- (void)setCellLayoutMode:(ASCellLayoutMode)cellLayoutMode +{ + _cellLayoutMode = cellLayoutMode; +} + +- (BOOL)allowsSelection +{ + return _flags.allowsSelection; +} + +- (void)setAllowsSelection:(BOOL)allowsSelection +{ + _flags.allowsSelection = allowsSelection; +} + +- (BOOL)allowsMultipleSelection +{ + return _flags.allowsMultipleSelection; +} + +- (void)setAllowsMultipleSelection:(BOOL)allowsMultipleSelection +{ + _flags.allowsMultipleSelection = allowsMultipleSelection; +} + +- (BOOL)inverted +{ + return _flags.inverted; +} + +-(void)setInverted:(BOOL)inverted +{ + _flags.inverted = inverted; +} + +-(BOOL)alwaysBounceVertical +{ + return _flags.alwaysBounceVertical; +} + +-(void)setAlwaysBounceVertical:(BOOL)alwaysBounceVertical +{ + _flags.alwaysBounceVertical = alwaysBounceVertical; +} + +-(BOOL)alwaysBounceHorizontal +{ + return _flags.alwaysBounceHorizontal; +} + +-(void)setAlwaysBounceHorizontal:(BOOL)alwaysBounceHorizontal +{ + _flags.alwaysBounceHorizontal = alwaysBounceHorizontal; +} + +- (BOOL)animatesContentOffset +{ + return _flags.animatesContentOffset; +} + +-(void)setAnimatesContentOffset:(BOOL)animatesContentOffset +{ + _flags.animatesContentOffset = animatesContentOffset; +} + +- (BOOL)showsVerticalScrollIndicator +{ + return _flags.showsVerticalScrollIndicator; +} + +- (void)setShowsVerticalScrollIndicator:(BOOL)showsVerticalScrollIndicator +{ + _flags.showsVerticalScrollIndicator = showsVerticalScrollIndicator; +} + +-(BOOL)showsHorizontalScrollIndicator +{ + return _flags.showsHorizontalScrollIndicator; +} + +- (void)setShowsHorizontalScrollIndicator:(BOOL)showsHorizontalScrollIndicator +{ + _flags.showsHorizontalScrollIndicator = showsHorizontalScrollIndicator; +} + +-(BOOL)pagingEnabled +{ + return _flags.pagingEnabled; +} + +- (void)setPagingEnabled:(BOOL)pagingEnabled +{ + _flags.pagingEnabled = pagingEnabled; +} + +#pragma mark Tuning Parameters + +- (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType +{ + return [self tuningParametersForRangeMode:ASLayoutRangeModeFull rangeType:rangeType]; +} + +- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType +{ + return [self setTuningParameters:tuningParameters forRangeMode:ASLayoutRangeModeFull rangeType:rangeType]; +} + +- (ASRangeTuningParameters)tuningParametersForRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType +{ + ASDisplayNodeAssert(rangeMode < _tuningParameters.size() && rangeType < _tuningParameters[rangeMode].size(), @"Requesting a range that is OOB for the configured tuning parameters"); + return _tuningParameters[rangeMode][rangeType]; +} + +- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType +{ + ASDisplayNodeAssert(rangeMode < _tuningParameters.size() && rangeType < _tuningParameters[rangeMode].size(), @"Setting a range that is OOB for the configured tuning parameters"); + _tuningParameters[rangeMode][rangeType] = tuningParameters; +} + +@end + +#pragma mark - ASCollectionNode + +@interface ASCollectionNode () +{ + AS::RecursiveMutex _environmentStateLock; + Class _collectionViewClass; + id _batchFetchingDelegate; +} +@property (nonatomic) _ASCollectionPendingState *pendingState; +@property (nonatomic, weak) ASRangeController *rangeController; +@end + +@implementation ASCollectionNode + +#pragma mark Lifecycle + +- (Class)collectionViewClass +{ + return _collectionViewClass ? : [ASCollectionView class]; +} + +- (void)setCollectionViewClass:(Class)collectionViewClass +{ + if (_collectionViewClass != collectionViewClass) { + ASDisplayNodeAssert([collectionViewClass isSubclassOfClass:[ASCollectionView class]] || collectionViewClass == Nil, @"ASCollectionNode requires that .collectionViewClass is an ASCollectionView subclass"); + ASDisplayNodeAssert([self isNodeLoaded] == NO, @"ASCollectionNode's .collectionViewClass cannot be changed after the view is loaded"); + _collectionViewClass = collectionViewClass; + } +} + +- (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout *)layout +{ + return [self initWithFrame:CGRectZero collectionViewLayout:layout layoutFacilitator:nil]; +} + +- (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout +{ + return [self initWithFrame:frame collectionViewLayout:layout layoutFacilitator:nil]; +} + +- (instancetype)initWithLayoutDelegate:(id)layoutDelegate layoutFacilitator:(id)layoutFacilitator +{ + return [self initWithFrame:CGRectZero collectionViewLayout:[[ASCollectionLayout alloc] initWithLayoutDelegate:layoutDelegate] layoutFacilitator:layoutFacilitator]; +} + +- (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout layoutFacilitator:(id)layoutFacilitator +{ + if (self = [super init]) { + // Must call the setter here to make sure pendingState is created and the layout is configured. + [self setCollectionViewLayout:layout]; + + __weak __typeof__(self) weakSelf = self; + [self setViewBlock:^{ + __typeof__(self) strongSelf = weakSelf; + return [[[strongSelf collectionViewClass] alloc] _initWithFrame:frame collectionViewLayout:strongSelf->_pendingState.collectionViewLayout layoutFacilitator:layoutFacilitator owningNode:strongSelf]; + }]; + } + return self; +} + +#if ASDISPLAYNODE_ASSERTIONS_ENABLED +- (void)dealloc +{ + if (self.nodeLoaded) { + __weak UIView *view = self.view; + ASPerformBlockOnMainThread(^{ + ASDisplayNodeCAssertNil(view.superview, @"Node's view should be removed from hierarchy."); + }); + } +} +#endif + +#pragma mark ASDisplayNode + +- (void)didLoad +{ + [super didLoad]; + + ASCollectionView *view = self.view; + view.collectionNode = self; + + _rangeController = view.rangeController; + + if (_pendingState) { + _ASCollectionPendingState *pendingState = _pendingState; + self.pendingState = nil; + view.asyncDelegate = pendingState.delegate; + view.asyncDataSource = pendingState.dataSource; + view.inverted = pendingState.inverted; + view.allowsSelection = pendingState.allowsSelection; + view.allowsMultipleSelection = pendingState.allowsMultipleSelection; + view.cellLayoutMode = pendingState.cellLayoutMode; + view.layoutInspector = pendingState.layoutInspector; + view.showsVerticalScrollIndicator = pendingState.showsVerticalScrollIndicator; + view.showsHorizontalScrollIndicator = pendingState.showsHorizontalScrollIndicator; +#if !TARGET_OS_TV + view.pagingEnabled = pendingState.pagingEnabled; +#endif + + // Only apply these flags if they're enabled; the view might come with them turned on. + if (pendingState.alwaysBounceVertical) { + view.alwaysBounceVertical = YES; + } + if (pendingState.alwaysBounceHorizontal) { + view.alwaysBounceHorizontal = YES; + } + + UIEdgeInsets contentInset = pendingState.contentInset; + if (!UIEdgeInsetsEqualToEdgeInsets(contentInset, UIEdgeInsetsZero)) { + view.contentInset = contentInset; + } + + CGPoint contentOffset = pendingState.contentOffset; + if (!CGPointEqualToPoint(contentOffset, CGPointZero)) { + [view setContentOffset:contentOffset animated:pendingState.animatesContentOffset]; + } + + const auto tuningParametersVector = pendingState->_tuningParameters; + const auto tuningParametersVectorSize = tuningParametersVector.size(); + for (NSInteger rangeMode = 0; rangeMode < tuningParametersVectorSize; rangeMode++) { + const auto tuningparametersRangeModeVector = tuningParametersVector[rangeMode]; + const auto tuningParametersVectorRangeModeSize = tuningparametersRangeModeVector.size(); + for (NSInteger rangeType = 0; rangeType < tuningParametersVectorRangeModeSize; rangeType++) { + ASRangeTuningParameters tuningParameters = tuningparametersRangeModeVector[rangeType]; + [_rangeController setTuningParameters:tuningParameters + forRangeMode:(ASLayoutRangeMode)rangeMode + rangeType:(ASLayoutRangeType)rangeType]; + } + } + + if (pendingState.rangeMode != ASLayoutRangeModeUnspecified) { + [_rangeController updateCurrentRangeWithMode:pendingState.rangeMode]; + } + + // Don't need to set collectionViewLayout to the view as the layout was already used to init the view in view block. + } +} + +- (ASCollectionView *)view +{ + return (ASCollectionView *)[super view]; +} + +- (void)clearContents +{ + [super clearContents]; + [self.rangeController clearContents]; +} + +- (void)interfaceStateDidChange:(ASInterfaceState)newState fromState:(ASInterfaceState)oldState +{ + [super interfaceStateDidChange:newState fromState:oldState]; + [ASRangeController layoutDebugOverlayIfNeeded]; +} + +- (void)didEnterPreloadState +{ + [super didEnterPreloadState]; + // ASCollectionNode is often nested inside of other collections. In this case, ASHierarchyState's RangeManaged bit will be set. + // Intentionally allocate the view here and trigger a layout pass on it, which in turn will trigger the intial data load. + // We can get rid of this call later when ASDataController, ASRangeController and ASCollectionLayout can operate without the view. + // TODO (ASCL) If this node supports async layout, kick off the initial data load without allocating the view + if (ASHierarchyStateIncludesRangeManaged(self.hierarchyState) && CGRectEqualToRect(self.bounds, CGRectZero) == NO) { + [self.view layoutIfNeeded]; + } +} + +#if ASRangeControllerLoggingEnabled +- (void)didEnterVisibleState +{ + [super didEnterVisibleState]; + NSLog(@"%@ - visible: YES", self); +} + +- (void)didExitVisibleState +{ + [super didExitVisibleState]; + NSLog(@"%@ - visible: NO", self); +} +#endif + +- (void)didExitPreloadState +{ + [super didExitPreloadState]; + [self.rangeController clearPreloadedData]; +} + +#pragma mark Setter / Getter + +// TODO: Implement this without the view. Then revisit ASLayoutElementCollectionTableSetTraitCollection +- (ASDataController *)dataController +{ + return self.view.dataController; +} + +- (_ASCollectionPendingState *)pendingState +{ + if (!_pendingState && ![self isNodeLoaded]) { + self.pendingState = [[_ASCollectionPendingState alloc] init]; + } + ASDisplayNodeAssert(![self isNodeLoaded] || !_pendingState, @"ASCollectionNode should not have a pendingState once it is loaded"); + return _pendingState; +} + +- (void)setInverted:(BOOL)inverted +{ + self.transform = inverted ? CATransform3DMakeScale(1, -1, 1) : CATransform3DIdentity; + if ([self pendingState]) { + _pendingState.inverted = inverted; + } else { + ASDisplayNodeAssert([self isNodeLoaded], @"ASCollectionNode should be loaded if pendingState doesn't exist"); + self.view.inverted = inverted; + } +} + +- (BOOL)inverted +{ + if ([self pendingState]) { + return _pendingState.inverted; + } else { + return self.view.inverted; + } +} + +- (void)setLayoutInspector:(id)layoutInspector +{ + if ([self pendingState]) { + _pendingState.layoutInspector = layoutInspector; + } else { + ASDisplayNodeAssert([self isNodeLoaded], @"ASCollectionNode should be loaded if pendingState doesn't exist"); + self.view.layoutInspector = layoutInspector; + } +} + +- (id)layoutInspector +{ + if ([self pendingState]) { + return _pendingState.layoutInspector; + } else { + return self.view.layoutInspector; + } +} + +- (void)setLeadingScreensForBatching:(CGFloat)leadingScreensForBatching +{ + if ([self pendingState]) { + _pendingState.leadingScreensForBatching = leadingScreensForBatching; + } else { + ASDisplayNodeAssert([self isNodeLoaded], @"ASCollectionNode should be loaded if pendingState doesn't exist"); + self.view.leadingScreensForBatching = leadingScreensForBatching; + } +} + +- (CGFloat)leadingScreensForBatching +{ + if ([self pendingState]) { + return _pendingState.leadingScreensForBatching; + } else { + return self.view.leadingScreensForBatching; + } +} + +- (void)setDelegate:(id )delegate +{ + if ([self pendingState]) { + _pendingState.delegate = delegate; + } else { + ASDisplayNodeAssert([self isNodeLoaded], @"ASCollectionNode should be loaded if pendingState doesn't exist"); + + // Manually trampoline to the main thread. The view requires this be called on main + // and asserting here isn't an option – it is a common pattern for users to clear + // the delegate/dataSource in dealloc, which may be running on a background thread. + // It is important that we avoid retaining self in this block, so that this method is dealloc-safe. + ASCollectionView *view = self.view; + ASPerformBlockOnMainThread(^{ + view.asyncDelegate = delegate; + }); + } +} + +- (id )delegate +{ + if ([self pendingState]) { + return _pendingState.delegate; + } else { + return self.view.asyncDelegate; + } +} + +- (void)setDataSource:(id )dataSource +{ + if ([self pendingState]) { + _pendingState.dataSource = dataSource; + } else { + ASDisplayNodeAssert([self isNodeLoaded], @"ASCollectionNode should be loaded if pendingState doesn't exist"); + // Manually trampoline to the main thread. The view requires this be called on main + // and asserting here isn't an option – it is a common pattern for users to clear + // the delegate/dataSource in dealloc, which may be running on a background thread. + // It is important that we avoid retaining self in this block, so that this method is dealloc-safe. + ASCollectionView *view = self.view; + ASPerformBlockOnMainThread(^{ + view.asyncDataSource = dataSource; + }); + } +} + +- (id )dataSource +{ + if ([self pendingState]) { + return _pendingState.dataSource; + } else { + return self.view.asyncDataSource; + } +} + +- (void)setAllowsSelection:(BOOL)allowsSelection +{ + if ([self pendingState]) { + _pendingState.allowsSelection = allowsSelection; + } else { + ASDisplayNodeAssert([self isNodeLoaded], @"ASCollectionNode should be loaded if pendingState doesn't exist"); + self.view.allowsSelection = allowsSelection; + } +} + +- (BOOL)allowsSelection +{ + if ([self pendingState]) { + return _pendingState.allowsSelection; + } else { + return self.view.allowsSelection; + } +} + +- (void)setAllowsMultipleSelection:(BOOL)allowsMultipleSelection +{ + if ([self pendingState]) { + _pendingState.allowsMultipleSelection = allowsMultipleSelection; + } else { + ASDisplayNodeAssert([self isNodeLoaded], @"ASCollectionNode should be loaded if pendingState doesn't exist"); + self.view.allowsMultipleSelection = allowsMultipleSelection; + } +} + +- (BOOL)allowsMultipleSelection +{ + if ([self pendingState]) { + return _pendingState.allowsMultipleSelection; + } else { + return self.view.allowsMultipleSelection; + } +} + +- (void)setAlwaysBounceVertical:(BOOL)alwaysBounceVertical +{ + if ([self pendingState]) { + _pendingState.alwaysBounceVertical = alwaysBounceVertical; + } else { + ASDisplayNodeAssert([self isNodeLoaded], @"ASCollectionNode should be loaded if pendingState doesn't exist"); + self.view.alwaysBounceVertical = alwaysBounceVertical; + } +} + +- (BOOL)alwaysBounceVertical +{ + if ([self pendingState]) { + return _pendingState.alwaysBounceVertical; + } else { + return self.view.alwaysBounceVertical; + } +} + +- (void)setAlwaysBounceHorizontal:(BOOL)alwaysBounceHorizontal +{ + if ([self pendingState]) { + _pendingState.alwaysBounceHorizontal = alwaysBounceHorizontal; + } else { + ASDisplayNodeAssert([self isNodeLoaded], @"ASCollectionNode should be loaded if pendingState doesn't exist"); + self.view.alwaysBounceHorizontal = alwaysBounceHorizontal; + } +} + +- (BOOL)alwaysBounceHorizontal +{ + if ([self pendingState]) { + return _pendingState.alwaysBounceHorizontal; + } else { + return self.view.alwaysBounceHorizontal; + } +} + +- (void)setShowsVerticalScrollIndicator:(BOOL)showsVerticalScrollIndicator +{ + if ([self pendingState]) { + _pendingState.showsVerticalScrollIndicator = showsVerticalScrollIndicator; + } else { + ASDisplayNodeAssert([self isNodeLoaded], @"ASCollectionNode should be loaded if pendingState doesn't exist"); + self.view.showsVerticalScrollIndicator = showsVerticalScrollIndicator; + } +} + +- (BOOL)showsVerticalScrollIndicator +{ + if ([self pendingState]) { + return _pendingState.showsVerticalScrollIndicator; + } else { + return self.view.showsVerticalScrollIndicator; + } +} + +- (void)setShowsHorizontalScrollIndicator:(BOOL)showsHorizontalScrollIndicator +{ + if ([self pendingState]) { + _pendingState.showsHorizontalScrollIndicator = showsHorizontalScrollIndicator; + } else { + ASDisplayNodeAssert([self isNodeLoaded], @"ASCollectionNode should be loaded if pendingState doesn't exist"); + self.view.showsHorizontalScrollIndicator = showsHorizontalScrollIndicator; + } +} + +- (BOOL)showsHorizontalScrollIndicator +{ + if ([self pendingState]) { + return _pendingState.showsHorizontalScrollIndicator; + } else { + return self.view.showsHorizontalScrollIndicator; + } +} + +#if !TARGET_OS_TV +- (void)setPagingEnabled:(BOOL)pagingEnabled +{ + if ([self pendingState]) { + _pendingState.pagingEnabled = pagingEnabled; + } else { + ASDisplayNodeAssert([self isNodeLoaded], + @"ASCollectionNode should be loaded if pendingState doesn't exist"); + self.view.pagingEnabled = pagingEnabled; + } +} + +- (BOOL)isPagingEnabled +{ + if ([self pendingState]) { + return _pendingState.pagingEnabled; + } else { + return self.view.isPagingEnabled; + } +} +#endif + +- (void)setCollectionViewLayout:(UICollectionViewLayout *)layout +{ + if ([self pendingState]) { + [self _configureCollectionViewLayout:layout]; + _pendingState.collectionViewLayout = layout; + } else { + [self _configureCollectionViewLayout:layout]; + self.view.collectionViewLayout = layout; + } +} + +- (UICollectionViewLayout *)collectionViewLayout +{ + if ([self pendingState]) { + return _pendingState.collectionViewLayout; + } else { + return self.view.collectionViewLayout; + } +} + +- (void)setContentInset:(UIEdgeInsets)contentInset +{ + if ([self pendingState]) { + _pendingState.contentInset = contentInset; + } else { + ASDisplayNodeAssert([self isNodeLoaded], @"ASCollectionNode should be loaded if pendingState doesn't exist"); + self.view.contentInset = contentInset; + } +} + +- (UIEdgeInsets)contentInset +{ + if ([self pendingState]) { + return _pendingState.contentInset; + } else { + return self.view.contentInset; + } +} + +- (void)setContentOffset:(CGPoint)contentOffset +{ + [self setContentOffset:contentOffset animated:NO]; +} + +- (void)setContentOffset:(CGPoint)contentOffset animated:(BOOL)animated +{ + if ([self pendingState]) { + _pendingState.contentOffset = contentOffset; + _pendingState.animatesContentOffset = animated; + } else { + ASDisplayNodeAssert([self isNodeLoaded], @"ASCollectionNode should be loaded if pendingState doesn't exist"); + [self.view setContentOffset:contentOffset animated:animated]; + } +} + +- (CGPoint)contentOffset +{ + if ([self pendingState]) { + return _pendingState.contentOffset; + } else { + return self.view.contentOffset; + } +} + +- (ASScrollDirection)scrollDirection +{ + return [self isNodeLoaded] ? self.view.scrollDirection : ASScrollDirectionNone; +} + +- (ASScrollDirection)scrollableDirections +{ + return [self isNodeLoaded] ? self.view.scrollableDirections : ASScrollDirectionNone; +} + +- (ASElementMap *)visibleElements +{ + ASDisplayNodeAssertMainThread(); + // TODO Own the data controller when view is not yet loaded + return self.dataController.visibleMap; +} + +- (id)layoutDelegate +{ + UICollectionViewLayout *layout = self.collectionViewLayout; + if ([layout isKindOfClass:[ASCollectionLayout class]]) { + return ((ASCollectionLayout *)layout).layoutDelegate; + } + return nil; +} + +- (void)setBatchFetchingDelegate:(id)batchFetchingDelegate +{ + _batchFetchingDelegate = batchFetchingDelegate; +} + +- (id)batchFetchingDelegate +{ + return _batchFetchingDelegate; +} + +- (ASCellLayoutMode)cellLayoutMode +{ + if ([self pendingState]) { + return _pendingState.cellLayoutMode; + } else { + return self.view.cellLayoutMode; + } +} + +- (void)setCellLayoutMode:(ASCellLayoutMode)cellLayoutMode +{ + if ([self pendingState]) { + _pendingState.cellLayoutMode = cellLayoutMode; + } else { + self.view.cellLayoutMode = cellLayoutMode; + } +} + +#pragma mark - Range Tuning + +- (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType +{ + return [self tuningParametersForRangeMode:ASLayoutRangeModeFull rangeType:rangeType]; +} + +- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType +{ + [self setTuningParameters:tuningParameters forRangeMode:ASLayoutRangeModeFull rangeType:rangeType]; +} + +- (ASRangeTuningParameters)tuningParametersForRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType +{ + if ([self pendingState]) { + return [_pendingState tuningParametersForRangeMode:rangeMode rangeType:rangeType]; + } else { + return [self.rangeController tuningParametersForRangeMode:rangeMode rangeType:rangeType]; + } +} + +- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType +{ + if ([self pendingState]) { + [_pendingState setTuningParameters:tuningParameters forRangeMode:rangeMode rangeType:rangeType]; + } else { + return [self.rangeController setTuningParameters:tuningParameters forRangeMode:rangeMode rangeType:rangeType]; + } +} + +#pragma mark - Selection + +- (NSArray *)indexPathsForSelectedItems +{ + ASDisplayNodeAssertMainThread(); + ASCollectionView *view = self.view; + return [view convertIndexPathsToCollectionNode:view.indexPathsForSelectedItems]; +} + +- (void)selectItemAtIndexPath:(nullable NSIndexPath *)indexPath animated:(BOOL)animated scrollPosition:(UICollectionViewScrollPosition)scrollPosition +{ + ASDisplayNodeAssertMainThread(); + ASCollectionView *collectionView = self.view; + + indexPath = [collectionView convertIndexPathFromCollectionNode:indexPath waitingIfNeeded:YES]; + + if (indexPath != nil) { + [collectionView selectItemAtIndexPath:indexPath animated:animated scrollPosition:scrollPosition]; + } else { + NSLog(@"Failed to select item at index path %@ because the item never reached the view.", indexPath); + } +} + +- (void)deselectItemAtIndexPath:(NSIndexPath *)indexPath animated:(BOOL)animated +{ + ASDisplayNodeAssertMainThread(); + ASCollectionView *collectionView = self.view; + + indexPath = [collectionView convertIndexPathFromCollectionNode:indexPath waitingIfNeeded:YES]; + + if (indexPath != nil) { + [collectionView deselectItemAtIndexPath:indexPath animated:animated]; + } else { + NSLog(@"Failed to deselect item at index path %@ because the item never reached the view.", indexPath); + } +} + +- (void)scrollToItemAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UICollectionViewScrollPosition)scrollPosition animated:(BOOL)animated +{ + ASDisplayNodeAssertMainThread(); + ASCollectionView *collectionView = self.view; + + indexPath = [collectionView convertIndexPathFromCollectionNode:indexPath waitingIfNeeded:YES]; + + if (indexPath != nil) { + [collectionView scrollToItemAtIndexPath:indexPath atScrollPosition:scrollPosition animated:animated]; + } else { + NSLog(@"Failed to scroll to item at index path %@ because the item never reached the view.", indexPath); + } +} + +#pragma mark - Querying Data + +- (void)reloadDataInitiallyIfNeeded +{ + if (!self.dataController.initialReloadDataHasBeenCalled) { + [self reloadData]; + } +} + +- (NSInteger)numberOfItemsInSection:(NSInteger)section +{ + [self reloadDataInitiallyIfNeeded]; + return [self.dataController.pendingMap numberOfItemsInSection:section]; +} + +- (NSInteger)numberOfSections +{ + [self reloadDataInitiallyIfNeeded]; + return self.dataController.pendingMap.numberOfSections; +} + +- (NSArray<__kindof ASCellNode *> *)visibleNodes +{ + ASDisplayNodeAssertMainThread(); + return self.isNodeLoaded ? [self.view visibleNodes] : @[]; +} + +- (ASCellNode *)nodeForItemAtIndexPath:(NSIndexPath *)indexPath +{ + [self reloadDataInitiallyIfNeeded]; + return [self.dataController.pendingMap elementForItemAtIndexPath:indexPath].node; +} + +- (id)nodeModelForItemAtIndexPath:(NSIndexPath *)indexPath +{ + [self reloadDataInitiallyIfNeeded]; + return [self.dataController.pendingMap elementForItemAtIndexPath:indexPath].nodeModel; +} + +- (NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode +{ + return [self.dataController.pendingMap indexPathForElement:cellNode.collectionElement]; +} + +- (NSArray *)indexPathsForVisibleItems +{ + ASDisplayNodeAssertMainThread(); + NSMutableArray *indexPathsArray = [NSMutableArray new]; + for (ASCellNode *cell in [self visibleNodes]) { + NSIndexPath *indexPath = [self indexPathForNode:cell]; + if (indexPath) { + [indexPathsArray addObject:indexPath]; + } + } + return indexPathsArray; +} + +- (nullable NSIndexPath *)indexPathForItemAtPoint:(CGPoint)point +{ + ASDisplayNodeAssertMainThread(); + ASCollectionView *collectionView = self.view; + + NSIndexPath *indexPath = [collectionView indexPathForItemAtPoint:point]; + if (indexPath != nil) { + return [collectionView convertIndexPathToCollectionNode:indexPath]; + } + return indexPath; +} + +- (nullable UICollectionViewCell *)cellForItemAtIndexPath:(NSIndexPath *)indexPath +{ + ASDisplayNodeAssertMainThread(); + ASCollectionView *collectionView = self.view; + + indexPath = [collectionView convertIndexPathFromCollectionNode:indexPath waitingIfNeeded:YES]; + if (indexPath == nil) { + return nil; + } + return [collectionView cellForItemAtIndexPath:indexPath]; +} + +- (id)contextForSection:(NSInteger)section +{ + ASDisplayNodeAssertMainThread(); + return [self.dataController.pendingMap contextForSection:section]; +} + +#pragma mark - Editing + +- (void)registerSupplementaryNodeOfKind:(NSString *)elementKind +{ + [self.view registerSupplementaryNodeOfKind:elementKind]; +} + +- (void)performBatchAnimated:(BOOL)animated updates:(NS_NOESCAPE void (^)())updates completion:(void (^)(BOOL))completion +{ + ASDisplayNodeAssertMainThread(); + if (self.nodeLoaded) { + [self.view performBatchAnimated:animated updates:updates completion:completion]; + } else { + if (updates) { + updates(); + } + if (completion) { + completion(YES); + } + } +} + +- (void)performBatchUpdates:(NS_NOESCAPE void (^)())updates completion:(void (^)(BOOL))completion +{ + [self performBatchAnimated:UIView.areAnimationsEnabled updates:updates completion:completion]; +} + +- (BOOL)isProcessingUpdates +{ + return (self.nodeLoaded ? [self.view isProcessingUpdates] : NO); +} + +- (void)onDidFinishProcessingUpdates:(void (^)())completion +{ + if (!completion) { + return; + } + if (!self.nodeLoaded) { + completion(); + } else { + [self.view onDidFinishProcessingUpdates:completion]; + } +} + +- (BOOL)isSynchronized +{ + return (self.nodeLoaded ? [self.view isSynchronized] : YES); +} + +- (void)onDidFinishSynchronizing:(void (^)())completion +{ + if (!completion) { + return; + } + if (!self.nodeLoaded) { + completion(); + } else { + [self.view onDidFinishSynchronizing:completion]; + } +} + +- (void)waitUntilAllUpdatesAreProcessed +{ + ASDisplayNodeAssertMainThread(); + if (self.nodeLoaded) { + [self.view waitUntilAllUpdatesAreCommitted]; + } +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-implementations" +- (void)waitUntilAllUpdatesAreCommitted +{ + [self waitUntilAllUpdatesAreProcessed]; +} +#pragma clang diagnostic pop + +- (void)reloadDataWithCompletion:(void (^)())completion +{ + ASDisplayNodeAssertMainThread(); + if (!self.nodeLoaded) { + return; + } + + [self performBatchUpdates:^{ + [self.view.changeSet reloadData]; + } completion:^(BOOL finished){ + if (completion) { + completion(); + } + }]; +} + +- (void)reloadData +{ + [self reloadDataWithCompletion:nil]; +} + +- (void)relayoutItems +{ + ASDisplayNodeAssertMainThread(); + if (self.nodeLoaded) { + [self.view relayoutItems]; + } +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-implementations" +- (void)beginUpdates +{ + ASDisplayNodeAssertMainThread(); + if (self.nodeLoaded) { + [self.view beginUpdates]; + } +} + +- (void)endUpdatesAnimated:(BOOL)animated +{ + [self endUpdatesAnimated:animated completion:nil]; +} + +- (void)endUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL))completion +{ + ASDisplayNodeAssertMainThread(); + if (self.nodeLoaded) { + [self.view endUpdatesAnimated:animated completion:completion]; + } +} +#pragma clang diagnostic pop + +- (void)invalidateFlowLayoutDelegateMetrics { + ASDisplayNodeAssertMainThread(); + if (self.nodeLoaded) { + [self.view invalidateFlowLayoutDelegateMetrics]; + } +} + +- (void)insertSections:(NSIndexSet *)sections +{ + ASDisplayNodeAssertMainThread(); + if (self.nodeLoaded) { + [self.view insertSections:sections]; + } +} + +- (void)deleteSections:(NSIndexSet *)sections +{ + ASDisplayNodeAssertMainThread(); + if (self.nodeLoaded) { + [self.view deleteSections:sections]; + } +} + +- (void)reloadSections:(NSIndexSet *)sections +{ + ASDisplayNodeAssertMainThread(); + if (self.nodeLoaded) { + [self.view reloadSections:sections]; + } +} + +- (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection +{ + ASDisplayNodeAssertMainThread(); + if (self.nodeLoaded) { + [self.view moveSection:section toSection:newSection]; + } +} + +- (void)insertItemsAtIndexPaths:(NSArray *)indexPaths +{ + ASDisplayNodeAssertMainThread(); + if (self.nodeLoaded) { + [self.view insertItemsAtIndexPaths:indexPaths]; + } +} + +- (void)deleteItemsAtIndexPaths:(NSArray *)indexPaths +{ + ASDisplayNodeAssertMainThread(); + if (self.nodeLoaded) { + [self.view deleteItemsAtIndexPaths:indexPaths]; + } +} + +- (void)reloadItemsAtIndexPaths:(NSArray *)indexPaths +{ + ASDisplayNodeAssertMainThread(); + if (self.nodeLoaded) { + [self.view reloadItemsAtIndexPaths:indexPaths]; + } +} + +- (void)moveItemAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath +{ + ASDisplayNodeAssertMainThread(); + if (self.nodeLoaded) { + [self.view moveItemAtIndexPath:indexPath toIndexPath:newIndexPath]; + } +} + +#pragma mark - ASRangeControllerUpdateRangeProtocol + +- (void)updateCurrentRangeWithMode:(ASLayoutRangeMode)rangeMode; +{ + if ([self pendingState]) { + _pendingState.rangeMode = rangeMode; + } else { + [self.rangeController updateCurrentRangeWithMode:rangeMode]; + } +} + +#pragma mark - ASPrimitiveTraitCollection + +ASLayoutElementCollectionTableSetTraitCollection(_environmentStateLock) + +#pragma mark - Debugging (Private) + +- (NSMutableArray *)propertiesForDebugDescription +{ + NSMutableArray *result = [super propertiesForDebugDescription]; + [result addObject:@{ @"dataSource" : ASObjectDescriptionMakeTiny(self.dataSource) }]; + [result addObject:@{ @"delegate" : ASObjectDescriptionMakeTiny(self.delegate) }]; + return result; +} + +#pragma mark - UIGestureRecognizerDelegate Methods +// The value returned below are default implementation of UIKit's UIGestureRecognizerDelegate +- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer +{ + return YES; +} + +- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch +{ + return YES; +} + +- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceivePress:(UIPress *)press +{ + return YES; +} + +- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer +{ + return NO; +} + +- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRequireFailureOfGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer +{ + return NO; +} + +- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer +{ + return NO; +} + +#pragma mark - Private methods + +- (void)_configureCollectionViewLayout:(UICollectionViewLayout *)layout +{ + if ([layout isKindOfClass:[ASCollectionLayout class]]) { + ASCollectionLayout *collectionLayout = (ASCollectionLayout *)layout; + collectionLayout.collectionNode = self; + } +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASCollectionView.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASCollectionView.h new file mode 100644 index 0000000..1b250a2 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASCollectionView.h @@ -0,0 +1,492 @@ +// +// ASCollectionView.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import +#import +#import +#import +#import +#import + +@class ASCellNode; +@class ASCollectionNode; +@protocol ASCollectionDataSource; +@protocol ASCollectionDelegate; +@protocol ASCollectionViewLayoutInspecting; +@protocol ASSectionContext; + +NS_ASSUME_NONNULL_BEGIN + +/** + * Asynchronous UICollectionView with Intelligent Preloading capabilities. + * + * @note ASCollectionNode is strongly recommended over ASCollectionView. This class exists for adoption convenience. + */ +@interface ASCollectionView : UICollectionView + +/** + * Returns the corresponding ASCollectionNode + * + * @return collectionNode The corresponding ASCollectionNode, if one exists. + */ +@property (nonatomic, weak, readonly) ASCollectionNode *collectionNode; + +/** + * Retrieves the node for the item at the given index path. + * + * @param indexPath The index path of the requested node. + * @return The node at the given index path, or @c nil if no item exists at the specified path. + */ +- (nullable ASCellNode *)nodeForItemAtIndexPath:(NSIndexPath *)indexPath AS_WARN_UNUSED_RESULT; + +/** + * Similar to -indexPathForCell:. + * + * @param cellNode a cellNode in the collection view + * + * @return The index path for this cell node. + * + * @discussion This index path returned by this method is in the _view's_ index space + * and should only be used with @c ASCollectionView directly. To get an index path suitable + * for use with your data source and @c ASCollectionNode, call @c indexPathForNode: on the + * collection node instead. + */ +- (nullable NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode AS_WARN_UNUSED_RESULT; + +/** + * Similar to -supplementaryViewForElementKind:atIndexPath: + * + * @param elementKind The kind of supplementary node to locate. + * @param indexPath The index path of the requested supplementary node. + * + * @return The specified supplementary node or @c nil. + */ +- (nullable ASCellNode *)supplementaryNodeForElementKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath AS_WARN_UNUSED_RESULT; + +/** + * Retrieves the context object for the given section, as provided by the data source in + * the @c collectionNode:contextForSection: method. This method must be called on the main thread. + * + * @param section The section to get the context for. + * + * @return The context object, or @c nil if no context was provided. + */ +- (nullable id)contextForSection:(NSInteger)section AS_WARN_UNUSED_RESULT; + +@end + +@interface ASCollectionView (Deprecated) + +/* + * A Boolean value that determines whether the nodes that the data source renders will be flipped. + */ +@property (nonatomic) BOOL inverted ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode property instead."); + +/** + * The number of screens left to scroll before the delegate -collectionView:beginBatchFetchingWithContext: is called. + * + * Defaults to two screenfuls. + */ +@property (nonatomic) CGFloat leadingScreensForBatching ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode property instead."); + +/** + * Optional introspection object for the collection view's layout. + * + * @discussion Since supplementary and decoration views are controlled by the collection view's layout, this object + * is used as a bridge to provide information to the internal data controller about the existence of these views and + * their associated index paths. For collection views using `UICollectionViewFlowLayout`, a default inspector + * implementation `ASCollectionViewFlowLayoutInspector` is created and set on this property by default. Custom + * collection view layout subclasses will need to provide their own implementation of an inspector object for their + * supplementary views to be compatible with `ASCollectionView`'s supplementary node support. + */ +@property (nonatomic, weak) id layoutInspector ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode property instead."); + +/** + * Determines collection view's current scroll direction. Supports 2-axis collection views. + * + * @return a bitmask of ASScrollDirection values. + */ +@property (nonatomic, readonly) ASScrollDirection scrollDirection ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode property instead."); + +/** + * Determines collection view's scrollable directions. + * + * @return a bitmask of ASScrollDirection values. + */ +@property (nonatomic, readonly) ASScrollDirection scrollableDirections ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode property instead."); + +/** + * Forces the .contentInset to be UIEdgeInsetsZero. + * + * @discussion By default, UIKit sets the top inset to the navigation bar height, even for horizontally + * scrolling views. This can only be disabled by setting a property on the containing UIViewController, + * automaticallyAdjustsScrollViewInsets, which may not be accessible. ASPagerNode uses this to ensure + * its flow layout behaves predictably and does not log undefined layout warnings. + */ +@property (nonatomic) BOOL zeroContentInsets ASDISPLAYNODE_DEPRECATED_MSG("Set automaticallyAdjustsScrollViewInsets=NO on your view controller instead."); + +/** + * The distance that the content view is inset from the collection view edges. Defaults to UIEdgeInsetsZero. + */ +@property (nonatomic) UIEdgeInsets contentInset ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode property instead"); + +/** + * The point at which the origin of the content view is offset from the origin of the collection view. + */ +@property (nonatomic) CGPoint contentOffset ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode property instead."); + +/** + * The object that acts as the asynchronous delegate of the collection view + * + * @discussion The delegate must adopt the ASCollectionDelegate protocol. The collection view maintains a weak reference to the delegate object. + * + * The delegate object is responsible for providing size constraints for nodes and indicating whether batch fetching should begin. + */ +@property (nonatomic, weak) id asyncDelegate ASDISPLAYNODE_DEPRECATED_MSG("Please use ASCollectionNode's .delegate property instead."); + +/** + * The object that acts as the asynchronous data source of the collection view + * + * @discussion The datasource must adopt the ASCollectionDataSource protocol. The collection view maintains a weak reference to the datasource object. + * + * The datasource object is responsible for providing nodes or node creation blocks to the collection view. + */ +@property (nonatomic, weak) id asyncDataSource ASDISPLAYNODE_DEPRECATED_MSG("Please use ASCollectionNode's .dataSource property instead."); + +/** + * Initializes an ASCollectionView + * + * @discussion Initializes and returns a newly allocated collection view object with the specified layout. + * + * @param layout The layout object to use for organizing items. The collection view stores a strong reference to the specified object. Must not be nil. + */ +- (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout *)layout ASDISPLAYNODE_DEPRECATED_MSG("Please use ASCollectionNode instead of ASCollectionView."); + +/** + * Initializes an ASCollectionView + * + * @discussion Initializes and returns a newly allocated collection view object with the specified frame and layout. + * + * @param frame The frame rectangle for the collection view, measured in points. The origin of the frame is relative to the superview in which you plan to add it. This frame is passed to the superclass during initialization. + * @param layout The layout object to use for organizing items. The collection view stores a strong reference to the specified object. Must not be nil. + */ +- (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout ASDISPLAYNODE_DEPRECATED_MSG("Please use ASCollectionNode instead of ASCollectionView."); + +/** + * Tuning parameters for a range type in full mode. + * + * @param rangeType The range type to get the tuning parameters for. + * + * @return A tuning parameter value for the given range type in full mode. + * + * @see ASLayoutRangeMode + * @see ASLayoutRangeType + */ +- (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode method instead."); + +/** + * Set the tuning parameters for a range type in full mode. + * + * @param tuningParameters The tuning parameters to store for a range type. + * @param rangeType The range type to set the tuning parameters for. + * + * @see ASLayoutRangeMode + * @see ASLayoutRangeType + */ +- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode method instead."); + +/** + * Tuning parameters for a range type in the specified mode. + * + * @param rangeMode The range mode to get the running parameters for. + * @param rangeType The range type to get the tuning parameters for. + * + * @return A tuning parameter value for the given range type in the given mode. + * + * @see ASLayoutRangeMode + * @see ASLayoutRangeType + */ +- (ASRangeTuningParameters)tuningParametersForRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode method instead."); + +/** + * Set the tuning parameters for a range type in the specified mode. + * + * @param tuningParameters The tuning parameters to store for a range type. + * @param rangeMode The range mode to set the running parameters for. + * @param rangeType The range type to set the tuning parameters for. + * + * @see ASLayoutRangeMode + * @see ASLayoutRangeType + */ +- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode method instead."); + +- (nullable __kindof UICollectionViewCell *)cellForItemAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode method instead."); + +- (void)scrollToItemAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UICollectionViewScrollPosition)scrollPosition animated:(BOOL)animated ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode method instead."); + +- (void)selectItemAtIndexPath:(NSIndexPath *)indexPath animated:(BOOL)animated scrollPosition:(UICollectionViewScrollPosition)scrollPosition ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode method instead."); + +@property (nonatomic, copy, readonly) NSArray *indexPathsForVisibleItems ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode property instead."); + +@property (nullable, nonatomic, copy, readonly) NSArray *indexPathsForSelectedItems ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode property instead."); + +/** + * Perform a batch of updates asynchronously, optionally disabling all animations in the batch. This method must be called from the main thread. + * The asyncDataSource must be updated to reflect the changes before the update block completes. + * + * @param animated NO to disable animations for this batch + * @param updates The block that performs the relevant insert, delete, reload, or move operations. + * @param completion A completion handler block to execute when all of the operations are finished. This block takes a single + * Boolean parameter that contains the value YES if all of the related animations completed successfully or + * NO if they were interrupted. This parameter may be nil. If supplied, the block is run on the main thread. + */ +- (void)performBatchAnimated:(BOOL)animated updates:(nullable AS_NOESCAPE void (^)(void))updates completion:(nullable void (^)(BOOL finished))completion ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode method instead."); + +/** + * Perform a batch of updates asynchronously. This method must be called from the main thread. + * The asyncDataSource must be updated to reflect the changes before update block completes. + * + * @param updates The block that performs the relevant insert, delete, reload, or move operations. + * @param completion A completion handler block to execute when all of the operations are finished. This block takes a single + * Boolean parameter that contains the value YES if all of the related animations completed successfully or + * NO if they were interrupted. This parameter may be nil. If supplied, the block is run on the main thread. + */ +- (void)performBatchUpdates:(nullable AS_NOESCAPE void (^)(void))updates completion:(nullable void (^)(BOOL finished))completion ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode method instead."); + +/** + * Reload everything from scratch, destroying the working range and all cached nodes. + * + * @param completion block to run on completion of asynchronous loading or nil. If supplied, the block is run on + * the main thread. + * @warning This method is substantially more expensive than UICollectionView's version. + */ +- (void)reloadDataWithCompletion:(nullable void (^)(void))completion AS_UNAVAILABLE("Use ASCollectionNode method instead."); + +/** + * Reload everything from scratch, destroying the working range and all cached nodes. + * + * @warning This method is substantially more expensive than UICollectionView's version. + */ +- (void)reloadData AS_UNAVAILABLE("Use ASCollectionNode method instead."); + +/** + * Triggers a relayout of all nodes. + * + * @discussion This method invalidates and lays out every cell node in the collection. + */ +- (void)relayoutItems ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode method instead."); + +/** + * See ASCollectionNode.h for full documentation of these methods. + */ +@property (nonatomic, readonly) BOOL isProcessingUpdates; +- (void)onDidFinishProcessingUpdates:(void (^)(void))completion; +- (void)waitUntilAllUpdatesAreCommitted ASDISPLAYNODE_DEPRECATED_MSG("Use -[ASCollectionNode waitUntilAllUpdatesAreProcessed] instead."); + +/** + * See ASCollectionNode.h for full documentation of these methods. + */ +@property (nonatomic, readonly, getter=isSynchronized) BOOL synchronized; +- (void)onDidFinishSynchronizing:(void (^)(void))completion; + +/** + * Registers the given kind of supplementary node for use in creating node-backed supplementary views. + * + * @param elementKind The kind of supplementary node that will be requested through the data source. + * + * @discussion Use this method to register support for the use of supplementary nodes in place of the default + * `registerClass:forSupplementaryViewOfKind:withReuseIdentifier:` and `registerNib:forSupplementaryViewOfKind:withReuseIdentifier:` + * methods. This method will register an internal backing view that will host the contents of the supplementary nodes + * returned from the data source. + */ +- (void)registerSupplementaryNodeOfKind:(NSString *)elementKind ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode method instead."); + +/** + * Inserts one or more sections. + * + * @param sections An index set that specifies the sections to insert. + * + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes + * before this method is called. + */ +- (void)insertSections:(NSIndexSet *)sections ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode method instead."); + +/** + * Deletes one or more sections. + * + * @param sections An index set that specifies the sections to delete. + * + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes + * before this method is called. + */ +- (void)deleteSections:(NSIndexSet *)sections ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode method instead."); + +/** + * Reloads the specified sections. + * + * @param sections An index set that specifies the sections to reload. + * + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes + * before this method is called. + */ +- (void)reloadSections:(NSIndexSet *)sections ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode method instead."); + +/** + * Moves a section to a new location. + * + * @param section The index of the section to move. + * + * @param newSection The index that is the destination of the move for the section. + * + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes + * before this method is called. + */ +- (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode method instead."); + +/** + * Inserts items at the locations identified by an array of index paths. + * + * @param indexPaths An array of NSIndexPath objects, each representing an item index and section index that together identify an item. + * + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes + * before this method is called. + */ +- (void)insertItemsAtIndexPaths:(NSArray *)indexPaths ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode method instead."); + +/** + * Deletes the items specified by an array of index paths. + * + * @param indexPaths An array of NSIndexPath objects identifying the items to delete. + * + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes + * before this method is called. + */ +- (void)deleteItemsAtIndexPaths:(NSArray *)indexPaths ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode method instead."); + +/** + * Reloads the specified items. + * + * @param indexPaths An array of NSIndexPath objects identifying the items to reload. + * + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes + * before this method is called. + */ +- (void)reloadItemsAtIndexPaths:(NSArray *)indexPaths ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode method instead."); + +/** + * Moves the item at a specified location to a destination location. + * + * @param indexPath The index path identifying the item to move. + * + * @param newIndexPath The index path that is the destination of the move for the item. + * + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes + * before this method is called. + */ +- (void)moveItemAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode method instead."); + +/** + * Query the sized node at @c indexPath for its calculatedSize. + * + * @param indexPath The index path for the node of interest. + * + * This method is deprecated. Call @c calculatedSize on the node of interest instead. First deprecated in version 2.0. + */ +- (CGSize)calculatedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED_MSG("Call -calculatedSize on the node of interest instead."); + +/** + * Similar to -visibleCells. + * + * @return an array containing the nodes being displayed on screen. + */ +- (NSArray<__kindof ASCellNode *> *)visibleNodes AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode method instead."); + +- (void)setContentOffset:(CGPoint)contentOffset animated:(BOOL)animated ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode method instead."); + +@end + +ASDISPLAYNODE_DEPRECATED_MSG("Renamed to ASCollectionDataSource.") +@protocol ASCollectionViewDataSource +@end + +ASDISPLAYNODE_DEPRECATED_MSG("Renamed to ASCollectionDelegate.") +@protocol ASCollectionViewDelegate +@end + +/** + * Defines methods that let you coordinate a `UICollectionViewFlowLayout` in combination with an `ASCollectionNode`. + */ +@protocol ASCollectionDelegateFlowLayout + +@optional + +/** + * Asks the delegate for the inset that should be applied to the given section. + * + * @see the same method in UICollectionViewDelegate. + */ +- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section; + +/** + * Asks the delegate for the size range that should be used to measure the header in the given flow layout section. + * + * @param collectionNode The sender. + * @param section The section. + * + * @return The size range for the header, or @c ASSizeRangeZero if there is no header in this section. + * + * If you want the header to completely determine its own size, return @c ASSizeRangeUnconstrained. + * + * @note Only the scrollable dimension of the returned size range will be used. In a vertical flow, + * only the height will be used. In a horizontal flow, only the width will be used. The other dimension + * will be constrained to fill the collection node. + * + * @discussion If you do not implement this method, ASDK will fall back to calling @c collectionView:layout:referenceSizeForHeaderInSection: + * and using that as the exact constrained size. If you don't implement that method, ASDK will read the @c headerReferenceSize from the layout. + */ +- (ASSizeRange)collectionNode:(ASCollectionNode *)collectionNode sizeRangeForHeaderInSection:(NSInteger)section; + +/** + * Asks the delegate for the size range that should be used to measure the footer in the given flow layout section. + * + * @param collectionNode The sender. + * @param section The section. + * + * @return The size range for the footer, or @c ASSizeRangeZero if there is no footer in this section. + * + * If you want the footer to completely determine its own size, return @c ASSizeRangeUnconstrained. + * + * @note Only the scrollable dimension of the returned size range will be used. In a vertical flow, + * only the height will be used. In a horizontal flow, only the width will be used. The other dimension + * will be constrained to fill the collection node. + * + * @discussion If you do not implement this method, ASDK will fall back to calling @c collectionView:layout:referenceSizeForFooterInSection: + * and using that as the exact constrained size. If you don't implement that method, ASDK will read the @c footerReferenceSize from the layout. + */ +- (ASSizeRange)collectionNode:(ASCollectionNode *)collectionNode sizeRangeForFooterInSection:(NSInteger)section; + +/** + * Asks the delegate for the size of the header in the specified section. + */ +- (CGSize)collectionView:(ASCollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section ASDISPLAYNODE_DEPRECATED_MSG("Implement collectionNode:sizeRangeForHeaderInSection: instead."); + +/** + * Asks the delegate for the size of the footer in the specified section. + */ +- (CGSize)collectionView:(ASCollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForFooterInSection:(NSInteger)section ASDISPLAYNODE_DEPRECATED_MSG("Implement collectionNode:sizeRangeForFooterInSection: instead."); + +@end + +ASDISPLAYNODE_DEPRECATED_MSG("Renamed to ASCollectionDelegateFlowLayout.") +@protocol ASCollectionViewDelegateFlowLayout +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASCollectionView.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASCollectionView.mm new file mode 100644 index 0000000..afe9716 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASCollectionView.mm @@ -0,0 +1,2545 @@ +// +// ASCollectionView.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + +/** + * A macro to get self.collectionNode and assign it to a local variable, or return + * the given value if nil. + * + * Previously we would set ASCollectionNode's dataSource & delegate to nil + * during dealloc. However, our asyncDelegate & asyncDataSource must be set on the + * main thread, so if the node is deallocated off-main, we won't learn about the change + * until later on. Since our @c collectionNode parameter to delegate methods (e.g. + * collectionNode:didEndDisplayingItemWithNode:) is nonnull, it's important that we never + * unintentionally pass nil (this will crash in Swift, in production). So we can use + * this macro to ensure that our node is still alive before calling out to the user + * on its behalf. + */ +#define GET_COLLECTIONNODE_OR_RETURN(__var, __val) \ + ASCollectionNode *__var = self.collectionNode; \ + if (__var == nil) { \ + return __val; \ + } + +#define ASFlowLayoutDefault(layout, property, default) \ +({ \ + UICollectionViewFlowLayout *flowLayout = ASDynamicCast(layout, UICollectionViewFlowLayout); \ + flowLayout ? flowLayout.property : default; \ +}) + +// ASCellLayoutMode is an NSUInteger-based NS_OPTIONS field. Be careful with BOOL handling on the +// 32-bit Objective-C runtime, and pattern after ASInterfaceStateIncludesVisible() & friends. +#define ASCellLayoutModeIncludes(layoutMode) ((self->_cellLayoutMode & layoutMode) == layoutMode) + +/// What, if any, invalidation should we perform during the next -layoutSubviews. +typedef NS_ENUM(NSUInteger, ASCollectionViewInvalidationStyle) { + /// Perform no invalidation. + ASCollectionViewInvalidationStyleNone, + /// Perform invalidation with animation (use an empty batch update). + ASCollectionViewInvalidationStyleWithoutAnimation, + /// Perform invalidation without animation (use -invalidateLayout). + ASCollectionViewInvalidationStyleWithAnimation, +}; + +static const NSUInteger kASCollectionViewAnimationNone = UITableViewRowAnimationNone; + +/// Used for all cells and supplementaries. UICV keys by supp-kind+reuseID so this is plenty. +static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; + +#pragma mark - +#pragma mark ASCollectionView. + +@interface ASCollectionView () { + ASCollectionViewProxy *_proxyDataSource; + ASCollectionViewProxy *_proxyDelegate; + + ASDataController *_dataController; + ASRangeController *_rangeController; + ASCollectionViewLayoutController *_layoutController; + id _defaultLayoutInspector; + __weak id _layoutInspector; + NSHashTable<_ASCollectionViewCell *> *_cellsForVisibilityUpdates; + NSHashTable *_cellsForLayoutUpdates; + id _layoutFacilitator; + CGFloat _leadingScreensForBatching; + + // When we update our data controller in response to an interactive move, + // we don't want to tell the collection view about the change (it knows!) + BOOL _updatingInResponseToInteractiveMove; + BOOL _inverted; + + NSUInteger _superBatchUpdateCount; + BOOL _isDeallocating; + + ASBatchContext *_batchContext; + + CGSize _lastBoundsSizeUsedForMeasuringNodes; + + NSMutableSet *_registeredSupplementaryKinds; + + // CountedSet because UIKit may display the same element in multiple cells e.g. during animations. + NSCountedSet *_visibleElements; + + CGPoint _deceleratingVelocity; + + BOOL _zeroContentInsets; + + ASCollectionViewInvalidationStyle _nextLayoutInvalidationStyle; + + /** + * If YES, the `UICollectionView` will reload its data on next layout pass so we should not forward any updates to it. + + * Rationale: + * In `reloadData`, a collection view invalidates its data and marks itself as needing reload, and waits until `layoutSubviews` to requery its data source. + * This can lead to data inconsistency problems. + * Say you have an empty collection view. You call `reloadData`, then immediately insert an item into your data source and call `insertItemsAtIndexPaths:[0,0]`. + * You will get an assertion failure saying `Invalid number of items in section 0. + * The number of items after the update (1) must be equal to the number of items before the update (1) plus or minus the items added and removed (1 added, 0 removed).` + * The collection view never queried your data source before the update to see that it actually had 0 items. + */ + BOOL _superIsPendingDataLoad; + + /** + * It's important that we always check for batch fetching at least once, but also + * that we do not check for batch fetching for empty updates (as that may cause an infinite + * loop of batch fetching, where the batch completes and performBatchUpdates: is called without + * actually making any changes.) So to handle the case where a collection is completely empty + * (0 sections) we always check at least once after each update (initial reload is the first update.) + */ + BOOL _hasEverCheckedForBatchFetchingDueToUpdate; + + /** + * Set during beginInteractiveMovementForItemAtIndexPath and UIGestureRecognizerStateEnded + * (or UIGestureRecognizerStateFailed, UIGestureRecognizerStateCancelled. + */ + BOOL _reordering; + + /** + * Counter used to keep track of nested batch updates. + */ + NSInteger _batchUpdateCount; + + /** + * Keep a strong reference to node till view is ready to release. + */ + ASCollectionNode *_keepalive_node; + + struct { + unsigned int scrollViewDidScroll:1; + unsigned int scrollViewWillBeginDragging:1; + unsigned int scrollViewDidEndDragging:1; + unsigned int scrollViewWillEndDragging:1; + unsigned int scrollViewDidEndDecelerating:1; + unsigned int collectionViewWillDisplayNodeForItem:1; + unsigned int collectionViewWillDisplayNodeForItemDeprecated:1; + unsigned int collectionViewDidEndDisplayingNodeForItem:1; + unsigned int collectionViewShouldSelectItem:1; + unsigned int collectionViewDidSelectItem:1; + unsigned int collectionViewShouldDeselectItem:1; + unsigned int collectionViewDidDeselectItem:1; + unsigned int collectionViewShouldHighlightItem:1; + unsigned int collectionViewDidHighlightItem:1; + unsigned int collectionViewDidUnhighlightItem:1; + unsigned int collectionViewShouldShowMenuForItem:1; + unsigned int collectionViewCanPerformActionForItem:1; + unsigned int collectionViewPerformActionForItem:1; + unsigned int collectionViewWillBeginBatchFetch:1; + unsigned int shouldBatchFetchForCollectionView:1; + unsigned int collectionNodeWillDisplayItem:1; + unsigned int collectionNodeDidEndDisplayingItem:1; + unsigned int collectionNodeShouldSelectItem:1; + unsigned int collectionNodeDidSelectItem:1; + unsigned int collectionNodeShouldDeselectItem:1; + unsigned int collectionNodeDidDeselectItem:1; + unsigned int collectionNodeShouldHighlightItem:1; + unsigned int collectionNodeDidHighlightItem:1; + unsigned int collectionNodeDidUnhighlightItem:1; + unsigned int collectionNodeShouldShowMenuForItem:1; + unsigned int collectionNodeCanPerformActionForItem:1; + unsigned int collectionNodePerformActionForItem:1; + unsigned int collectionNodeWillBeginBatchFetch:1; + unsigned int collectionNodeWillDisplaySupplementaryElement:1; + unsigned int collectionNodeDidEndDisplayingSupplementaryElement:1; + unsigned int shouldBatchFetchForCollectionNode:1; + + // Interop flags + unsigned int interop:1; + unsigned int interopWillDisplayCell:1; + unsigned int interopDidEndDisplayingCell:1; + unsigned int interopWillDisplaySupplementaryView:1; + unsigned int interopdidEndDisplayingSupplementaryView:1; + } _asyncDelegateFlags; + + struct { + unsigned int collectionViewNodeForItem:1; + unsigned int collectionViewNodeBlockForItem:1; + unsigned int collectionViewNodeForSupplementaryElement:1; + unsigned int numberOfSectionsInCollectionView:1; + unsigned int collectionViewNumberOfItemsInSection:1; + unsigned int collectionNodeNodeForItem:1; + unsigned int collectionNodeNodeBlockForItem:1; + unsigned int nodeModelForItem:1; + unsigned int collectionNodeNodeForSupplementaryElement:1; + unsigned int collectionNodeNodeBlockForSupplementaryElement:1; + unsigned int collectionNodeSupplementaryElementKindsInSection:1; + unsigned int numberOfSectionsInCollectionNode:1; + unsigned int collectionNodeNumberOfItemsInSection:1; + unsigned int collectionNodeContextForSection:1; + unsigned int collectionNodeCanMoveItem:1; + unsigned int collectionNodeMoveItem:1; + + // Whether this data source conforms to ASCollectionDataSourceInterop + unsigned int interop:1; + // Whether this interop data source returns YES from +dequeuesCellsForNodeBackedItems + unsigned int interopAlwaysDequeue:1; + // Whether this interop data source implements viewForSupplementaryElementOfKind: + unsigned int interopViewForSupplementaryElement:1; + unsigned int modelIdentifierMethods:1; // if both modelIdentifierForElementAtIndexPath and indexPathForElementWithModelIdentifier are implemented + } _asyncDataSourceFlags; + + struct { + unsigned int constrainedSizeForSupplementaryNodeOfKindAtIndexPath:1; + unsigned int supplementaryNodesOfKindInSection:1; + unsigned int didChangeCollectionViewDataSource:1; + unsigned int didChangeCollectionViewDelegate:1; + } _layoutInspectorFlags; + + BOOL _hasDataControllerLayoutDelegate; +} + +@end + +@implementation ASCollectionView +{ + __weak id _asyncDelegate; + __weak id _asyncDataSource; +} + +// Using _ASDisplayLayer ensures things like -layout are properly forwarded to ASCollectionNode. ++ (Class)layerClass +{ + return [_ASDisplayLayer class]; +} + +#pragma mark - +#pragma mark Lifecycle. + +- (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout *)layout +{ + return [self initWithFrame:CGRectZero collectionViewLayout:layout]; +} + +- (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout +{ + return [self _initWithFrame:frame collectionViewLayout:layout layoutFacilitator:nil owningNode:nil]; +} + +- (instancetype)_initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout layoutFacilitator:(id)layoutFacilitator owningNode:(ASCollectionNode *)owningNode +{ + if (!(self = [super initWithFrame:frame collectionViewLayout:layout])) + return nil; + + // Disable UICollectionView prefetching. Use super, because self disables this method. + // Experiments done by Instagram show that this option being YES (default) + // when unused causes a significant hit to scroll performance. + // https://github.com/Instagram/IGListKit/issues/318 + if (AS_AVAILABLE_IOS_TVOS(10, 10)) { + super.prefetchingEnabled = NO; + } + + _layoutController = [[ASCollectionViewLayoutController alloc] initWithCollectionView:self]; + + _rangeController = [[ASRangeController alloc] init]; + _rangeController.dataSource = self; + _rangeController.delegate = self; + _rangeController.layoutController = _layoutController; + + _dataController = [[ASDataController alloc] initWithDataSource:self node:owningNode]; + _dataController.delegate = _rangeController; + + _batchContext = [[ASBatchContext alloc] init]; + + _leadingScreensForBatching = 2.0; + + _lastBoundsSizeUsedForMeasuringNodes = self.bounds.size; + + _layoutFacilitator = layoutFacilitator; + + _proxyDelegate = [[ASCollectionViewProxy alloc] initWithTarget:nil interceptor:self]; + super.delegate = (id)_proxyDelegate; + + _proxyDataSource = [[ASCollectionViewProxy alloc] initWithTarget:nil interceptor:self]; + super.dataSource = (id)_proxyDataSource; + + _registeredSupplementaryKinds = [[NSMutableSet alloc] init]; + _visibleElements = [[NSCountedSet alloc] init]; + + _cellsForVisibilityUpdates = [NSHashTable hashTableWithOptions:NSHashTableObjectPointerPersonality]; + _cellsForLayoutUpdates = [NSHashTable hashTableWithOptions:NSHashTableObjectPointerPersonality]; + self.backgroundColor = [UIColor whiteColor]; + + [self registerClass:[_ASCollectionViewCell class] forCellWithReuseIdentifier:kReuseIdentifier]; + + [self _configureCollectionViewLayout:layout]; + + self.panGestureRecognizer.delegate = self; + return self; +} + +- (void)dealloc +{ + ASDisplayNodeAssertMainThread(); + ASDisplayNodeCAssert(_batchUpdateCount == 0, @"ASCollectionView deallocated in the middle of a batch update."); + + // Sometimes the UIKit classes can call back to their delegate even during deallocation, due to animation completion blocks etc. + _isDeallocating = YES; + if (!ASActivateExperimentalFeature(ASExperimentalCollectionTeardown)) { + [self setAsyncDelegate:nil]; + [self setAsyncDataSource:nil]; + } +} + +#pragma mark - +#pragma mark Overrides. + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-implementations" +/** + * This method is not available to be called by the public i.e. + * it should only be called by UICollectionView itself. UICollectionView + * does this e.g. during the first layout pass, or if you call -numberOfSections + * before its content is loaded. + */ +- (void)reloadData +{ + [self _superReloadData:nil completion:nil]; + + // UICollectionView calls -reloadData during first layoutSubviews and when the data source changes. + // This fires off the first load of cell nodes. + if (_asyncDataSource != nil && !self.dataController.initialReloadDataHasBeenCalled) { + [self performBatchUpdates:^{ + [_changeSet reloadData]; + } completion:nil]; + } +} +#pragma clang diagnostic pop + +- (void)scrollToItemAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UICollectionViewScrollPosition)scrollPosition animated:(BOOL)animated +{ + if ([self validateIndexPath:indexPath]) { + [super scrollToItemAtIndexPath:indexPath atScrollPosition:scrollPosition animated:animated]; + } +} + +- (void)relayoutItems +{ + [_dataController relayoutAllNodesWithInvalidationBlock:^{ + [self.collectionViewLayout invalidateLayout]; + [self invalidateFlowLayoutDelegateMetrics]; + }]; +} + +- (BOOL)isProcessingUpdates +{ + return [_dataController isProcessingUpdates]; +} + +- (void)onDidFinishProcessingUpdates:(void (^)())completion +{ + [_dataController onDidFinishProcessingUpdates:completion]; +} + +- (void)waitUntilAllUpdatesAreCommitted +{ + ASDisplayNodeAssertMainThread(); + if (_batchUpdateCount > 0) { + // This assertion will be enabled soon. + // ASDisplayNodeFailAssert(@"Should not call %@ during batch update", NSStringFromSelector(_cmd)); + return; + } + + [_dataController waitUntilAllUpdatesAreProcessed]; +} + +- (BOOL)isSynchronized +{ + return [_dataController isSynchronized]; +} + +- (void)onDidFinishSynchronizing:(void (^)())completion +{ + [_dataController onDidFinishSynchronizing:completion]; +} + +- (void)setDataSource:(id)dataSource +{ + // UIKit can internally generate a call to this method upon changing the asyncDataSource; only assert for non-nil. We also allow this when we're doing interop. + ASDisplayNodeAssert(_asyncDelegateFlags.interop || dataSource == nil, @"ASCollectionView uses asyncDataSource, not UICollectionView's dataSource property."); +} + +- (void)setDelegate:(id)delegate +{ + // Our UIScrollView superclass sets its delegate to nil on dealloc. Only assert if we get a non-nil value here. We also allow this when we're doing interop. + ASDisplayNodeAssert(_asyncDelegateFlags.interop || delegate == nil, @"ASCollectionView uses asyncDelegate, not UICollectionView's delegate property."); +} + +- (void)proxyTargetHasDeallocated:(ASDelegateProxy *)proxy +{ + if (proxy == _proxyDelegate) { + [self setAsyncDelegate:nil]; + } else if (proxy == _proxyDataSource) { + [self setAsyncDataSource:nil]; + } +} + +- (id)asyncDataSource +{ + return _asyncDataSource; +} + +- (void)setAsyncDataSource:(id)asyncDataSource +{ + // Changing super.dataSource will trigger a setNeedsLayout, so this must happen on the main thread. + ASDisplayNodeAssertMainThread(); + + // Note: It's common to check if the value hasn't changed and short-circuit but we aren't doing that here to handle + // the (common) case of nilling the asyncDataSource in the ViewController's dealloc. In this case our _asyncDataSource + // will return as nil (ARC magic) even though the _proxyDataSource still exists. It's really important to hold a strong + // reference to the old dataSource in this case because calls to ASCollectionViewProxy will start failing and cause crashes. + NS_VALID_UNTIL_END_OF_SCOPE id oldDataSource = super.dataSource; + + if (asyncDataSource == nil) { + _asyncDataSource = nil; + _proxyDataSource = _isDeallocating ? nil : [[ASCollectionViewProxy alloc] initWithTarget:nil interceptor:self]; + _asyncDataSourceFlags = {}; + + } else { + _asyncDataSource = asyncDataSource; + _proxyDataSource = [[ASCollectionViewProxy alloc] initWithTarget:_asyncDataSource interceptor:self]; + + _asyncDataSourceFlags.collectionViewNodeForItem = [_asyncDataSource respondsToSelector:@selector(collectionView:nodeForItemAtIndexPath:)]; + _asyncDataSourceFlags.collectionViewNodeBlockForItem = [_asyncDataSource respondsToSelector:@selector(collectionView:nodeBlockForItemAtIndexPath:)]; + _asyncDataSourceFlags.numberOfSectionsInCollectionView = [_asyncDataSource respondsToSelector:@selector(numberOfSectionsInCollectionView:)]; + _asyncDataSourceFlags.collectionViewNumberOfItemsInSection = [_asyncDataSource respondsToSelector:@selector(collectionView:numberOfItemsInSection:)]; + _asyncDataSourceFlags.collectionViewNodeForSupplementaryElement = [_asyncDataSource respondsToSelector:@selector(collectionView:nodeForSupplementaryElementOfKind:atIndexPath:)]; + + _asyncDataSourceFlags.collectionNodeNodeForItem = [_asyncDataSource respondsToSelector:@selector(collectionNode:nodeForItemAtIndexPath:)]; + _asyncDataSourceFlags.collectionNodeNodeBlockForItem = [_asyncDataSource respondsToSelector:@selector(collectionNode:nodeBlockForItemAtIndexPath:)]; + _asyncDataSourceFlags.numberOfSectionsInCollectionNode = [_asyncDataSource respondsToSelector:@selector(numberOfSectionsInCollectionNode:)]; + _asyncDataSourceFlags.collectionNodeNumberOfItemsInSection = [_asyncDataSource respondsToSelector:@selector(collectionNode:numberOfItemsInSection:)]; + _asyncDataSourceFlags.collectionNodeContextForSection = [_asyncDataSource respondsToSelector:@selector(collectionNode:contextForSection:)]; + _asyncDataSourceFlags.collectionNodeNodeForSupplementaryElement = [_asyncDataSource respondsToSelector:@selector(collectionNode:nodeForSupplementaryElementOfKind:atIndexPath:)]; + _asyncDataSourceFlags.collectionNodeNodeBlockForSupplementaryElement = [_asyncDataSource respondsToSelector:@selector(collectionNode:nodeBlockForSupplementaryElementOfKind:atIndexPath:)]; + _asyncDataSourceFlags.collectionNodeSupplementaryElementKindsInSection = [_asyncDataSource respondsToSelector:@selector(collectionNode:supplementaryElementKindsInSection:)]; + _asyncDataSourceFlags.nodeModelForItem = [_asyncDataSource respondsToSelector:@selector(collectionNode:nodeModelForItemAtIndexPath:)]; + _asyncDataSourceFlags.collectionNodeCanMoveItem = [_asyncDataSource respondsToSelector:@selector(collectionNode:canMoveItemWithNode:)]; + _asyncDataSourceFlags.collectionNodeMoveItem = [_asyncDataSource respondsToSelector:@selector(collectionNode:moveItemAtIndexPath:toIndexPath:)]; + + _asyncDataSourceFlags.interop = [_asyncDataSource conformsToProtocol:@protocol(ASCollectionDataSourceInterop)]; + if (_asyncDataSourceFlags.interop) { + id interopDataSource = (id)_asyncDataSource; + _asyncDataSourceFlags.interopAlwaysDequeue = [[interopDataSource class] respondsToSelector:@selector(dequeuesCellsForNodeBackedItems)] && [[interopDataSource class] dequeuesCellsForNodeBackedItems]; + _asyncDataSourceFlags.interopViewForSupplementaryElement = [interopDataSource respondsToSelector:@selector(collectionView:viewForSupplementaryElementOfKind:atIndexPath:)]; + } + + _asyncDataSourceFlags.modelIdentifierMethods = [_asyncDataSource respondsToSelector:@selector(modelIdentifierForElementAtIndexPath:inNode:)] && [_asyncDataSource respondsToSelector:@selector(indexPathForElementWithModelIdentifier:inNode:)]; + + + ASDisplayNodeAssert(_asyncDataSourceFlags.collectionNodeNumberOfItemsInSection || _asyncDataSourceFlags.collectionViewNumberOfItemsInSection, @"Data source must implement collectionNode:numberOfItemsInSection:"); + ASDisplayNodeAssert(_asyncDataSourceFlags.collectionNodeNodeBlockForItem + || _asyncDataSourceFlags.collectionNodeNodeForItem + || _asyncDataSourceFlags.collectionViewNodeBlockForItem + || _asyncDataSourceFlags.collectionViewNodeForItem, @"Data source must implement collectionNode:nodeBlockForItemAtIndexPath: or collectionNode:nodeForItemAtIndexPath:"); + } + + _dataController.validationErrorSource = asyncDataSource; + super.dataSource = (id)_proxyDataSource; + + //Cache results of layoutInspector to ensure flags are up to date if getter lazily loads a new one. + id layoutInspector = self.layoutInspector; + if (_layoutInspectorFlags.didChangeCollectionViewDataSource) { + [layoutInspector didChangeCollectionViewDataSource:asyncDataSource]; + } + [self _asyncDelegateOrDataSourceDidChange]; +} + +- (id)asyncDelegate +{ + return _asyncDelegate; +} + +- (void)setAsyncDelegate:(id)asyncDelegate +{ + // Changing super.delegate will trigger a setNeedsLayout, so this must happen on the main thread. + ASDisplayNodeAssertMainThread(); + + // Note: It's common to check if the value hasn't changed and short-circuit but we aren't doing that here to handle + // the (common) case of nilling the asyncDelegate in the ViewController's dealloc. In this case our _asyncDelegate + // will return as nil (ARC magic) even though the _proxyDataSource still exists. It's really important to hold a strong + // reference to the old delegate in this case because calls to ASCollectionViewProxy will start failing and cause crashes. + NS_VALID_UNTIL_END_OF_SCOPE id oldDelegate = super.delegate; + + if (asyncDelegate == nil) { + _asyncDelegate = nil; + _proxyDelegate = _isDeallocating ? nil : [[ASCollectionViewProxy alloc] initWithTarget:nil interceptor:self]; + _asyncDelegateFlags = {}; + } else { + _asyncDelegate = asyncDelegate; + _proxyDelegate = [[ASCollectionViewProxy alloc] initWithTarget:_asyncDelegate interceptor:self]; + + _asyncDelegateFlags.scrollViewDidScroll = [_asyncDelegate respondsToSelector:@selector(scrollViewDidScroll:)]; + _asyncDelegateFlags.scrollViewWillEndDragging = [_asyncDelegate respondsToSelector:@selector(scrollViewWillEndDragging:withVelocity:targetContentOffset:)]; + _asyncDelegateFlags.scrollViewDidEndDecelerating = [_asyncDelegate respondsToSelector:@selector(scrollViewDidEndDecelerating:)]; + _asyncDelegateFlags.scrollViewWillBeginDragging = [_asyncDelegate respondsToSelector:@selector(scrollViewWillBeginDragging:)]; + _asyncDelegateFlags.scrollViewDidEndDragging = [_asyncDelegate respondsToSelector:@selector(scrollViewDidEndDragging:willDecelerate:)]; + _asyncDelegateFlags.collectionViewWillDisplayNodeForItem = [_asyncDelegate respondsToSelector:@selector(collectionView:willDisplayNode:forItemAtIndexPath:)]; + if (_asyncDelegateFlags.collectionViewWillDisplayNodeForItem == NO) { + _asyncDelegateFlags.collectionViewWillDisplayNodeForItemDeprecated = [_asyncDelegate respondsToSelector:@selector(collectionView:willDisplayNodeForItemAtIndexPath:)]; + } + _asyncDelegateFlags.collectionViewDidEndDisplayingNodeForItem = [_asyncDelegate respondsToSelector:@selector(collectionView:didEndDisplayingNode:forItemAtIndexPath:)]; + _asyncDelegateFlags.collectionViewWillBeginBatchFetch = [_asyncDelegate respondsToSelector:@selector(collectionView:willBeginBatchFetchWithContext:)]; + _asyncDelegateFlags.shouldBatchFetchForCollectionView = [_asyncDelegate respondsToSelector:@selector(shouldBatchFetchForCollectionView:)]; + _asyncDelegateFlags.collectionViewShouldSelectItem = [_asyncDelegate respondsToSelector:@selector(collectionView:shouldSelectItemAtIndexPath:)]; + _asyncDelegateFlags.collectionViewDidSelectItem = [_asyncDelegate respondsToSelector:@selector(collectionView:didSelectItemAtIndexPath:)]; + _asyncDelegateFlags.collectionViewShouldDeselectItem = [_asyncDelegate respondsToSelector:@selector(collectionView:shouldDeselectItemAtIndexPath:)]; + _asyncDelegateFlags.collectionViewDidDeselectItem = [_asyncDelegate respondsToSelector:@selector(collectionView:didDeselectItemAtIndexPath:)]; + _asyncDelegateFlags.collectionViewShouldHighlightItem = [_asyncDelegate respondsToSelector:@selector(collectionView:shouldHighlightItemAtIndexPath:)]; + _asyncDelegateFlags.collectionViewDidHighlightItem = [_asyncDelegate respondsToSelector:@selector(collectionView:didHighlightItemAtIndexPath:)]; + _asyncDelegateFlags.collectionViewDidUnhighlightItem = [_asyncDelegate respondsToSelector:@selector(collectionView:didUnhighlightItemAtIndexPath:)]; + _asyncDelegateFlags.collectionViewShouldShowMenuForItem = [_asyncDelegate respondsToSelector:@selector(collectionView:shouldShowMenuForItemAtIndexPath:)]; + _asyncDelegateFlags.collectionViewCanPerformActionForItem = [_asyncDelegate respondsToSelector:@selector(collectionView:canPerformAction:forItemAtIndexPath:withSender:)]; + _asyncDelegateFlags.collectionViewPerformActionForItem = [_asyncDelegate respondsToSelector:@selector(collectionView:performAction:forItemAtIndexPath:withSender:)]; + _asyncDelegateFlags.collectionNodeWillDisplayItem = [_asyncDelegate respondsToSelector:@selector(collectionNode:willDisplayItemWithNode:)]; + _asyncDelegateFlags.collectionNodeDidEndDisplayingItem = [_asyncDelegate respondsToSelector:@selector(collectionNode:didEndDisplayingItemWithNode:)]; + _asyncDelegateFlags.collectionNodeWillBeginBatchFetch = [_asyncDelegate respondsToSelector:@selector(collectionNode:willBeginBatchFetchWithContext:)]; + _asyncDelegateFlags.shouldBatchFetchForCollectionNode = [_asyncDelegate respondsToSelector:@selector(shouldBatchFetchForCollectionNode:)]; + _asyncDelegateFlags.collectionNodeShouldSelectItem = [_asyncDelegate respondsToSelector:@selector(collectionNode:shouldSelectItemAtIndexPath:)]; + _asyncDelegateFlags.collectionNodeDidSelectItem = [_asyncDelegate respondsToSelector:@selector(collectionNode:didSelectItemAtIndexPath:)]; + _asyncDelegateFlags.collectionNodeShouldDeselectItem = [_asyncDelegate respondsToSelector:@selector(collectionNode:shouldDeselectItemAtIndexPath:)]; + _asyncDelegateFlags.collectionNodeDidDeselectItem = [_asyncDelegate respondsToSelector:@selector(collectionNode:didDeselectItemAtIndexPath:)]; + _asyncDelegateFlags.collectionNodeShouldHighlightItem = [_asyncDelegate respondsToSelector:@selector(collectionNode:shouldHighlightItemAtIndexPath:)]; + _asyncDelegateFlags.collectionNodeDidHighlightItem = [_asyncDelegate respondsToSelector:@selector(collectionNode:didHighlightItemAtIndexPath:)]; + _asyncDelegateFlags.collectionNodeDidUnhighlightItem = [_asyncDelegate respondsToSelector:@selector(collectionNode:didUnhighlightItemAtIndexPath:)]; + _asyncDelegateFlags.collectionNodeShouldShowMenuForItem = [_asyncDelegate respondsToSelector:@selector(collectionNode:shouldShowMenuForItemAtIndexPath:)]; + _asyncDelegateFlags.collectionNodeCanPerformActionForItem = [_asyncDelegate respondsToSelector:@selector(collectionNode:canPerformAction:forItemAtIndexPath:sender:)]; + _asyncDelegateFlags.collectionNodePerformActionForItem = [_asyncDelegate respondsToSelector:@selector(collectionNode:performAction:forItemAtIndexPath:sender:)]; + _asyncDelegateFlags.collectionNodeWillDisplaySupplementaryElement = [_asyncDelegate respondsToSelector:@selector(collectionNode:willDisplaySupplementaryElementWithNode:)]; + _asyncDelegateFlags.collectionNodeDidEndDisplayingSupplementaryElement = [_asyncDelegate respondsToSelector:@selector(collectionNode:didEndDisplayingSupplementaryElementWithNode:)]; + _asyncDelegateFlags.interop = [_asyncDelegate conformsToProtocol:@protocol(ASCollectionDelegateInterop)]; + if (_asyncDelegateFlags.interop) { + id interopDelegate = (id)_asyncDelegate; + _asyncDelegateFlags.interopWillDisplayCell = [interopDelegate respondsToSelector:@selector(collectionView:willDisplayCell:forItemAtIndexPath:)]; + _asyncDelegateFlags.interopDidEndDisplayingCell = [interopDelegate respondsToSelector:@selector(collectionView:didEndDisplayingCell:forItemAtIndexPath:)]; + _asyncDelegateFlags.interopWillDisplaySupplementaryView = [interopDelegate respondsToSelector:@selector(collectionView:willDisplaySupplementaryView:forElementKind:atIndexPath:)]; + _asyncDelegateFlags.interopdidEndDisplayingSupplementaryView = [interopDelegate respondsToSelector:@selector(collectionView:didEndDisplayingSupplementaryView:forElementOfKind:atIndexPath:)]; + } + } + + super.delegate = (id)_proxyDelegate; + + //Cache results of layoutInspector to ensure flags are up to date if getter lazily loads a new one. + id layoutInspector = self.layoutInspector; + if (_layoutInspectorFlags.didChangeCollectionViewDelegate) { + [layoutInspector didChangeCollectionViewDelegate:asyncDelegate]; + } + [self _asyncDelegateOrDataSourceDidChange]; +} + +- (void)_asyncDelegateOrDataSourceDidChange +{ + ASDisplayNodeAssertMainThread(); + + if (_asyncDataSource == nil && _asyncDelegate == nil && !ASActivateExperimentalFeature(ASExperimentalSkipClearData)) { + [_dataController clearData]; + } +} + +- (void)setCollectionViewLayout:(nonnull UICollectionViewLayout *)collectionViewLayout +{ + ASDisplayNodeAssertMainThread(); + [super setCollectionViewLayout:collectionViewLayout]; + + [self _configureCollectionViewLayout:collectionViewLayout]; + + // Trigger recreation of layout inspector with new collection view layout + if (_layoutInspector != nil) { + _layoutInspector = nil; + [self layoutInspector]; + } +} + +- (id)layoutInspector +{ + if (_layoutInspector == nil) { + UICollectionViewLayout *layout = self.collectionViewLayout; + if (layout == nil) { + // Layout hasn't been set yet, we're still init'ing + return nil; + } + + _defaultLayoutInspector = [layout asdk_layoutInspector]; + ASDisplayNodeAssertNotNil(_defaultLayoutInspector, @"You must not return nil from -asdk_layoutInspector. Return [super asdk_layoutInspector] if you have to! Layout: %@", layout); + + // Explicitly call the setter to wire up the _layoutInspectorFlags + self.layoutInspector = _defaultLayoutInspector; + } + + return _layoutInspector; +} + +- (void)setLayoutInspector:(id)layoutInspector +{ + _layoutInspector = layoutInspector; + + _layoutInspectorFlags.constrainedSizeForSupplementaryNodeOfKindAtIndexPath = [_layoutInspector respondsToSelector:@selector(collectionView:constrainedSizeForSupplementaryNodeOfKind:atIndexPath:)]; + _layoutInspectorFlags.supplementaryNodesOfKindInSection = [_layoutInspector respondsToSelector:@selector(collectionView:supplementaryNodesOfKind:inSection:)]; + _layoutInspectorFlags.didChangeCollectionViewDataSource = [_layoutInspector respondsToSelector:@selector(didChangeCollectionViewDataSource:)]; + _layoutInspectorFlags.didChangeCollectionViewDelegate = [_layoutInspector respondsToSelector:@selector(didChangeCollectionViewDelegate:)]; + + if (_layoutInspectorFlags.didChangeCollectionViewDataSource) { + [_layoutInspector didChangeCollectionViewDataSource:self.asyncDataSource]; + } + if (_layoutInspectorFlags.didChangeCollectionViewDelegate) { + [_layoutInspector didChangeCollectionViewDelegate:self.asyncDelegate]; + } +} + +- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType +{ + [_rangeController setTuningParameters:tuningParameters forRangeMode:ASLayoutRangeModeFull rangeType:rangeType]; +} + +- (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType +{ + return [_rangeController tuningParametersForRangeMode:ASLayoutRangeModeFull rangeType:rangeType]; +} + +- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType +{ + [_rangeController setTuningParameters:tuningParameters forRangeMode:rangeMode rangeType:rangeType]; +} + +- (ASRangeTuningParameters)tuningParametersForRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType +{ + return [_rangeController tuningParametersForRangeMode:rangeMode rangeType:rangeType]; +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-implementations" +- (void)setZeroContentInsets:(BOOL)zeroContentInsets +{ + _zeroContentInsets = zeroContentInsets; +} + +- (BOOL)zeroContentInsets +{ + return _zeroContentInsets; +} +#pragma clang diagnostic pop + +/// Uses latest size range from data source and -layoutThatFits:. +- (CGSize)sizeForElement:(ASCollectionElement *)element +{ + ASDisplayNodeAssertMainThread(); + if (element == nil) { + return CGSizeZero; + } + + ASCellNode *node = element.node; + ASDisplayNodeAssertNotNil(node, @"Node must not be nil!"); + + BOOL useUIKitCell = node.shouldUseUIKitCell; + if (useUIKitCell) { + ASWrapperCellNode *wrapperNode = (ASWrapperCellNode *)node; + if (wrapperNode.sizeForItemBlock) { + return wrapperNode.sizeForItemBlock(wrapperNode, element.constrainedSize.max); + } else { + // In this case, it is possible the model indexPath for this element will be nil. Attempt to convert it, + // and call out to the delegate directly. If it has been deleted from the model, the size returned will be the layout's default. + NSIndexPath *indexPath = [_dataController.visibleMap indexPathForElement:element]; + return [self _sizeForUIKitCellWithKind:element.supplementaryElementKind atIndexPath:indexPath]; + } + } else { + return [node layoutThatFits:element.constrainedSize].size; + } +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-implementations" +- (CGSize)calculatedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath +{ + ASDisplayNodeAssertMainThread(); + + ASCollectionElement *e = [_dataController.visibleMap elementForItemAtIndexPath:indexPath]; + return [self sizeForElement:e]; +} +#pragma clang diagnostic pop + +- (ASCellNode *)nodeForItemAtIndexPath:(NSIndexPath *)indexPath +{ + return [_dataController.visibleMap elementForItemAtIndexPath:indexPath].node; +} + +- (NSIndexPath *)convertIndexPathFromCollectionNode:(NSIndexPath *)indexPath waitingIfNeeded:(BOOL)wait +{ + if (indexPath == nil) { + return nil; + } + + NSIndexPath *viewIndexPath = [_dataController.visibleMap convertIndexPath:indexPath fromMap:_dataController.pendingMap]; + if (viewIndexPath == nil && wait) { + [self waitUntilAllUpdatesAreCommitted]; + return [self convertIndexPathFromCollectionNode:indexPath waitingIfNeeded:NO]; + } + return viewIndexPath; +} + +/** + * Asserts that the index path is a valid view-index-path, and returns it if so, nil otherwise. + */ +- (nullable NSIndexPath *)validateIndexPath:(nullable NSIndexPath *)indexPath +{ + if (indexPath == nil) { + return nil; + } + + NSInteger section = indexPath.section; + if (section >= self.numberOfSections) { + ASDisplayNodeFailAssert(@"Collection view index path has invalid section %lu, section count = %lu", (unsigned long)section, (unsigned long)self.numberOfSections); + return nil; + } + + NSInteger item = indexPath.item; + // item == NSNotFound means e.g. "scroll to this section" and is acceptable + if (item != NSNotFound && item >= [self numberOfItemsInSection:section]) { + ASDisplayNodeFailAssert(@"Collection view index path has invalid item %lu in section %lu, item count = %lu", (unsigned long)indexPath.item, (unsigned long)section, (unsigned long)[self numberOfItemsInSection:section]); + return nil; + } + + return indexPath; +} + +- (NSIndexPath *)convertIndexPathToCollectionNode:(NSIndexPath *)indexPath +{ + if ([self validateIndexPath:indexPath] == nil) { + return nil; + } + + return [_dataController.pendingMap convertIndexPath:indexPath fromMap:_dataController.visibleMap]; +} + +- (NSArray *)convertIndexPathsToCollectionNode:(NSArray *)indexPaths +{ + return ASArrayByFlatMapping(indexPaths, NSIndexPath *viewIndexPath, [self convertIndexPathToCollectionNode:viewIndexPath]); +} + +- (ASCellNode *)supplementaryNodeForElementKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath +{ + return [_dataController.visibleMap supplementaryElementOfKind:elementKind atIndexPath:indexPath].node; +} + +- (NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode +{ + return [_dataController.visibleMap indexPathForElement:cellNode.collectionElement]; +} + +- (NSArray *)visibleNodes +{ + NSArray *indexPaths = [self indexPathsForVisibleItems]; + NSMutableArray *visibleNodes = [[NSMutableArray alloc] init]; + + for (NSIndexPath *indexPath in indexPaths) { + ASCellNode *node = [self nodeForItemAtIndexPath:indexPath]; + if (node) { + // It is possible for UICollectionView to return indexPaths before the node is completed. + [visibleNodes addObject:node]; + } + } + + return visibleNodes; +} + +- (void)invalidateFlowLayoutDelegateMetrics +{ + // Subclass hook +} + +- (nullable NSString *)modelIdentifierForElementAtIndexPath:(NSIndexPath *)indexPath inView:(UIView *)view { + if (_asyncDataSourceFlags.modelIdentifierMethods) { + GET_COLLECTIONNODE_OR_RETURN(collectionNode, nil); + NSIndexPath *convertedPath = [self convertIndexPathToCollectionNode:indexPath]; + if (convertedPath == nil) { + return nil; + } else { + return [_asyncDataSource modelIdentifierForElementAtIndexPath:convertedPath inNode:collectionNode]; + } + } else { + return nil; + } +} + +- (nullable NSIndexPath *)indexPathForElementWithModelIdentifier:(NSString *)identifier inView:(UIView *)view { + if (_asyncDataSourceFlags.modelIdentifierMethods) { + GET_COLLECTIONNODE_OR_RETURN(collectionNode, nil); + return [_asyncDataSource indexPathForElementWithModelIdentifier:identifier inNode:collectionNode]; + } else { + return nil; + } +} + +#pragma mark Internal + +- (void)_configureCollectionViewLayout:(nonnull UICollectionViewLayout *)layout +{ + _hasDataControllerLayoutDelegate = [layout conformsToProtocol:@protocol(ASDataControllerLayoutDelegate)]; + if (_hasDataControllerLayoutDelegate) { + _dataController.layoutDelegate = (id)layout; + } +} + +/** + This method is called only for UIKit Passthrough cells - either regular Items or Supplementary elements. + It checks if the delegate implements the UICollectionViewFlowLayout methods that provide sizes, and if not, + uses the default values set on the flow layout. If a flow layout is not in use, UICollectionView Passthrough + cells must be sized by logic in the Layout object, and Texture does not participate in these paths. +*/ +- (CGSize)_sizeForUIKitCellWithKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath +{ + CGSize size = CGSizeZero; + UICollectionViewLayout *l = self.collectionViewLayout; + + if (kind == nil) { + ASDisplayNodeAssert(_asyncDataSourceFlags.interop, @"This code should not be called except for UIKit passthrough compatibility"); + SEL sizeForItem = @selector(collectionView:layout:sizeForItemAtIndexPath:); + if (indexPath && [_asyncDelegate respondsToSelector:sizeForItem]) { + size = [(id)_asyncDelegate collectionView:self layout:l sizeForItemAtIndexPath:indexPath]; + } else { + size = ASFlowLayoutDefault(l, itemSize, CGSizeZero); + } + } else if ([kind isEqualToString:UICollectionElementKindSectionHeader]) { + ASDisplayNodeAssert(_asyncDataSourceFlags.interopViewForSupplementaryElement, @"This code should not be called except for UIKit passthrough compatibility"); + SEL sizeForHeader = @selector(collectionView:layout:referenceSizeForHeaderInSection:); + if (indexPath && [_asyncDelegate respondsToSelector:sizeForHeader]) { + size = [(id)_asyncDelegate collectionView:self layout:l referenceSizeForHeaderInSection:indexPath.section]; + } else { + size = ASFlowLayoutDefault(l, headerReferenceSize, CGSizeZero); + } + } else if ([kind isEqualToString:UICollectionElementKindSectionFooter]) { + ASDisplayNodeAssert(_asyncDataSourceFlags.interopViewForSupplementaryElement, @"This code should not be called except for UIKit passthrough compatibility"); + SEL sizeForFooter = @selector(collectionView:layout:referenceSizeForFooterInSection:); + if (indexPath && [_asyncDelegate respondsToSelector:sizeForFooter]) { + size = [(id)_asyncDelegate collectionView:self layout:l referenceSizeForFooterInSection:indexPath.section]; + } else { + size = ASFlowLayoutDefault(l, footerReferenceSize, CGSizeZero); + } + } + + return size; +} + +- (void)_superReloadData:(void(^)())updates completion:(void(^)(BOOL finished))completion +{ + if (updates) { + updates(); + } + [super reloadData]; + if (completion) { + completion(YES); + } +} + +/** + * Performing nested batch updates with super (e.g. resizing a cell node & updating collection view + * during same frame) can cause super to throw data integrity exceptions because it checks the data + * source counts before the update is complete. + * + * Always call [self _superPerform:] rather than [super performBatch:] so that we can keep our + * `superPerformingBatchUpdates` flag updated. +*/ +- (void)_superPerformBatchUpdates:(void(^)())updates completion:(void(^)(BOOL finished))completion +{ + ASDisplayNodeAssertMainThread(); + + _superBatchUpdateCount++; + [super performBatchUpdates:updates completion:completion]; + _superBatchUpdateCount--; +} + +#pragma mark Assertions. + +- (ASDataController *)dataController +{ + return _dataController; +} + +- (void)beginUpdates +{ + ASDisplayNodeAssertMainThread(); + // _changeSet must be available during batch update + ASDisplayNodeAssertTrue((_batchUpdateCount > 0) == (_changeSet != nil)); + + if (_batchUpdateCount == 0) { + _changeSet = [[_ASHierarchyChangeSet alloc] initWithOldData:[_dataController itemCountsFromDataSource]]; + _changeSet.rootActivity = as_activity_create("Perform async collection update", AS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT); + _changeSet.submitActivity = as_activity_create("Submit changes for collection update", _changeSet.rootActivity, OS_ACTIVITY_FLAG_DEFAULT); + } + _batchUpdateCount++; +} + +- (void)endUpdatesAnimated:(BOOL)animated completion:(nullable void (^)(BOOL))completion +{ + ASDisplayNodeAssertMainThread(); + ASDisplayNodeAssertNotNil(_changeSet, @"_changeSet must be available when batch update ends"); + + _batchUpdateCount--; + // Prevent calling endUpdatesAnimated:completion: in an unbalanced way + NSAssert(_batchUpdateCount >= 0, @"endUpdatesAnimated:completion: called without having a balanced beginUpdates call"); + + [_changeSet addCompletionHandler:completion]; + + if (_batchUpdateCount == 0) { + _ASHierarchyChangeSet *changeSet = _changeSet; + + // Nil out _changeSet before forwarding to _dataController to allow the change set to cause subsequent batch updates on the same run loop + _changeSet = nil; + changeSet.animated = animated; + [_dataController updateWithChangeSet:changeSet]; + } +} + +- (void)performBatchAnimated:(BOOL)animated updates:(NS_NOESCAPE void (^)())updates completion:(void (^)(BOOL))completion +{ + ASDisplayNodeAssertMainThread(); + [self beginUpdates]; + as_activity_scope(_changeSet.rootActivity); + { + // Only include client code in the submit activity, the rest just lives in the root activity. + as_activity_scope(_changeSet.submitActivity); + if (updates) { + updates(); + } + } + [self endUpdatesAnimated:animated completion:completion]; +} + +- (void)performBatchUpdates:(NS_NOESCAPE void (^)())updates completion:(void (^)(BOOL))completion +{ + // We capture the current state of whether animations are enabled if they don't provide us with one. + [self performBatchAnimated:[UIView areAnimationsEnabled] updates:updates completion:completion]; +} + +- (void)registerSupplementaryNodeOfKind:(NSString *)elementKind +{ + ASDisplayNodeAssert(elementKind != nil, @"A kind is needed for supplementary node registration"); + [_registeredSupplementaryKinds addObject:elementKind]; + [self registerClass:[_ASCollectionReusableView class] forSupplementaryViewOfKind:elementKind withReuseIdentifier:kReuseIdentifier]; +} + +- (void)insertSections:(NSIndexSet *)sections +{ + ASDisplayNodeAssertMainThread(); + if (sections.count == 0) { return; } + [self performBatchUpdates:^{ + [_changeSet insertSections:sections animationOptions:kASCollectionViewAnimationNone]; + } completion:nil]; +} + +- (void)deleteSections:(NSIndexSet *)sections +{ + ASDisplayNodeAssertMainThread(); + if (sections.count == 0) { return; } + [self performBatchUpdates:^{ + [_changeSet deleteSections:sections animationOptions:kASCollectionViewAnimationNone]; + } completion:nil]; +} + +- (void)reloadSections:(NSIndexSet *)sections +{ + ASDisplayNodeAssertMainThread(); + if (sections.count == 0) { return; } + [self performBatchUpdates:^{ + [_changeSet reloadSections:sections animationOptions:kASCollectionViewAnimationNone]; + } completion:nil]; +} + +- (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection +{ + ASDisplayNodeAssertMainThread(); + [self performBatchUpdates:^{ + [_changeSet moveSection:section toSection:newSection animationOptions:kASCollectionViewAnimationNone]; + } completion:nil]; +} + +- (id)contextForSection:(NSInteger)section +{ + ASDisplayNodeAssertMainThread(); + return [_dataController.visibleMap contextForSection:section]; +} + +- (void)insertItemsAtIndexPaths:(NSArray *)indexPaths +{ + ASDisplayNodeAssertMainThread(); + if (indexPaths.count == 0) { return; } + [self performBatchUpdates:^{ + [_changeSet insertItems:indexPaths animationOptions:kASCollectionViewAnimationNone]; + } completion:nil]; +} + +- (void)deleteItemsAtIndexPaths:(NSArray *)indexPaths +{ + ASDisplayNodeAssertMainThread(); + if (indexPaths.count == 0) { return; } + [self performBatchUpdates:^{ + [_changeSet deleteItems:indexPaths animationOptions:kASCollectionViewAnimationNone]; + } completion:nil]; +} + +- (void)reloadItemsAtIndexPaths:(NSArray *)indexPaths +{ + ASDisplayNodeAssertMainThread(); + if (indexPaths.count == 0) { return; } + [self performBatchUpdates:^{ + [_changeSet reloadItems:indexPaths animationOptions:kASCollectionViewAnimationNone]; + } completion:nil]; +} + +- (void)moveItemAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath +{ + ASDisplayNodeAssertMainThread(); + if (!_reordering) { + [self performBatchUpdates:^{ + [_changeSet moveItemAtIndexPath:indexPath toIndexPath:newIndexPath animationOptions:kASCollectionViewAnimationNone]; + } completion:nil]; + } else { + [super moveItemAtIndexPath:indexPath toIndexPath:newIndexPath]; + } +} + +- (BOOL)beginInteractiveMovementForItemAtIndexPath:(NSIndexPath *)indexPath { + BOOL success = [super beginInteractiveMovementForItemAtIndexPath:indexPath]; + _reordering = success; + return success; +} + +- (void)endInteractiveMovement { + _reordering = NO; + [super endInteractiveMovement]; +} + +- (void)cancelInteractiveMovement { + _reordering = NO; + [super cancelInteractiveMovement]; +} + +#pragma mark - +#pragma mark Intercepted selectors. + +- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView +{ + if (_superIsPendingDataLoad) { + [_rangeController setNeedsUpdate]; + [self _scheduleCheckForBatchFetchingForNumberOfChanges:1]; + _superIsPendingDataLoad = NO; + } + return _dataController.visibleMap.numberOfSections; +} + +- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section +{ + return [_dataController.visibleMap numberOfItemsInSection:section]; +} + +- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)layout + sizeForItemAtIndexPath:(NSIndexPath *)indexPath +{ + ASDisplayNodeAssertMainThread(); + ASCollectionElement *e = [_dataController.visibleMap elementForItemAtIndexPath:indexPath]; + return e ? [self sizeForElement:e] : ASFlowLayoutDefault(layout, itemSize, CGSizeZero); +} + +- (CGSize)collectionView:(UICollectionView *)cv layout:(UICollectionViewLayout *)l + referenceSizeForHeaderInSection:(NSInteger)section +{ + ASDisplayNodeAssertMainThread(); + ASElementMap *map = _dataController.visibleMap; + ASCollectionElement *e = [map supplementaryElementOfKind:UICollectionElementKindSectionHeader + atIndexPath:[NSIndexPath indexPathForItem:0 inSection:section]]; + return e ? [self sizeForElement:e] : ASFlowLayoutDefault(l, headerReferenceSize, CGSizeZero); +} + +- (CGSize)collectionView:(UICollectionView *)cv layout:(UICollectionViewLayout *)l + referenceSizeForFooterInSection:(NSInteger)section +{ + ASDisplayNodeAssertMainThread(); + ASElementMap *map = _dataController.visibleMap; + ASCollectionElement *e = [map supplementaryElementOfKind:UICollectionElementKindSectionFooter + atIndexPath:[NSIndexPath indexPathForItem:0 inSection:section]]; + return e ? [self sizeForElement:e] : ASFlowLayoutDefault(l, footerReferenceSize, CGSizeZero); +} + +// For the methods that call delegateIndexPathForSection:withSelector:, translate the section from +// visibleMap to pendingMap. If the section no longer exists, or the delegate doesn't implement +// the selector, we will return NSNotFound (and then use the ASFlowLayoutDefault). +- (NSInteger)delegateIndexForSection:(NSInteger)section withSelector:(SEL)selector +{ + if ([_asyncDelegate respondsToSelector:selector]) { + return [_dataController.pendingMap convertSection:section fromMap:_dataController.visibleMap]; + } else { + return NSNotFound; + } +} + +- (UIEdgeInsets)collectionView:(UICollectionView *)cv layout:(UICollectionViewLayout *)l + insetForSectionAtIndex:(NSInteger)section +{ + section = [self delegateIndexForSection:section withSelector:_cmd]; + if (section != NSNotFound) { + return [(id)_asyncDelegate collectionView:cv layout:l insetForSectionAtIndex:section]; + } + return ASFlowLayoutDefault(l, sectionInset, UIEdgeInsetsZero); +} + +- (CGFloat)collectionView:(UICollectionView *)cv layout:(UICollectionViewLayout *)l + minimumInteritemSpacingForSectionAtIndex:(NSInteger)section +{ + section = [self delegateIndexForSection:section withSelector:_cmd]; + if (section != NSNotFound) { + return [(id)_asyncDelegate collectionView:cv layout:l + minimumInteritemSpacingForSectionAtIndex:section]; + } + return ASFlowLayoutDefault(l, minimumInteritemSpacing, 10.0); // Default is documented as 10.0 +} + +- (CGFloat)collectionView:(UICollectionView *)cv layout:(UICollectionViewLayout *)l + minimumLineSpacingForSectionAtIndex:(NSInteger)section +{ + section = [self delegateIndexForSection:section withSelector:_cmd]; + if (section != NSNotFound) { + return [(id)_asyncDelegate collectionView:cv layout:l + minimumLineSpacingForSectionAtIndex:section]; + } + return ASFlowLayoutDefault(l, minimumLineSpacing, 10.0); // Default is documented as 10.0 +} + +- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath +{ + if ([_registeredSupplementaryKinds containsObject:kind] == NO) { + [self registerSupplementaryNodeOfKind:kind]; + } + + UICollectionReusableView *view = nil; + ASCollectionElement *element = [_dataController.visibleMap supplementaryElementOfKind:kind atIndexPath:indexPath]; + ASCellNode *node = element.node; + ASWrapperCellNode *wrapperNode = (node.shouldUseUIKitCell ? (ASWrapperCellNode *)node : nil); + BOOL shouldDequeueExternally = _asyncDataSourceFlags.interopAlwaysDequeue || (_asyncDataSourceFlags.interopViewForSupplementaryElement && wrapperNode); + + if (wrapperNode.viewForSupplementaryBlock) { + view = wrapperNode.viewForSupplementaryBlock(wrapperNode); + } else if (shouldDequeueExternally) { + // This codepath is used for both IGListKit mode, and app-level UICollectionView interop. + view = [(id)_asyncDataSource collectionView:collectionView viewForSupplementaryElementOfKind:kind atIndexPath:indexPath]; + } else { + ASDisplayNodeAssert(node != nil, @"Supplementary node should exist. Kind = %@, indexPath = %@, collectionDataSource = %@", kind, indexPath, self); + view = [self dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:kReuseIdentifier forIndexPath:indexPath]; + } + + if (_ASCollectionReusableView *reusableView = ASDynamicCastStrict(view, _ASCollectionReusableView)) { + reusableView.element = element; + } + + if (node) { + [_rangeController configureContentView:view forCellNode:node]; + } + + return view; +} + +- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath +{ + UICollectionViewCell *cell = nil; + ASCollectionElement *element = [_dataController.visibleMap elementForItemAtIndexPath:indexPath]; + ASCellNode *node = element.node; + ASWrapperCellNode *wrapperNode = (node.shouldUseUIKitCell ? (ASWrapperCellNode *)node : nil); + BOOL shouldDequeueExternally = _asyncDataSourceFlags.interopAlwaysDequeue || (_asyncDataSourceFlags.interop && wrapperNode); + + if (wrapperNode.cellForItemBlock) { + cell = wrapperNode.cellForItemBlock(wrapperNode); + } else if (shouldDequeueExternally) { + cell = [(id)_asyncDataSource collectionView:collectionView cellForItemAtIndexPath:indexPath]; + } else { + cell = [self dequeueReusableCellWithReuseIdentifier:kReuseIdentifier forIndexPath:indexPath]; + } + + ASDisplayNodeAssert(element != nil, @"Element should exist. indexPath = %@, collectionDataSource = %@", indexPath, self); + ASDisplayNodeAssert(cell != nil, @"UICollectionViewCell must not be nil. indexPath = %@, collectionDataSource = %@", indexPath, self); + + if (_ASCollectionViewCell *asCell = ASDynamicCastStrict(cell, _ASCollectionViewCell)) { + asCell.element = element; + [_rangeController configureContentView:cell.contentView forCellNode:node]; + } + + return cell; +} + +- (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)rawCell forItemAtIndexPath:(NSIndexPath *)indexPath +{ + if (_asyncDelegateFlags.interopWillDisplayCell) { + ASCellNode *node = [self nodeForItemAtIndexPath:indexPath]; + if (node.shouldUseUIKitCell) { + [(id )_asyncDelegate collectionView:collectionView willDisplayCell:rawCell forItemAtIndexPath:indexPath]; + } + } + + _ASCollectionViewCell *cell = ASDynamicCastStrict(rawCell, _ASCollectionViewCell); + if (cell == nil) { + [_rangeController setNeedsUpdate]; + return; + } + + ASCollectionElement *element = cell.element; + if (element) { + ASDisplayNodeAssertTrue([_dataController.visibleMap elementForItemAtIndexPath:indexPath] == element); + [_visibleElements addObject:element]; + } else { + ASDisplayNodeAssert(NO, @"Unexpected nil element for willDisplayCell: %@, %@, %@", rawCell, self, indexPath); + return; + } + + ASCellNode *cellNode = element.node; + cellNode.scrollView = collectionView; + + // Update the selected background view in collectionView:willDisplayCell:forItemAtIndexPath: otherwise it could be too + // early e.g. if the selectedBackgroundView was set in didLoad() + cell.selectedBackgroundView = cellNode.selectedBackgroundView; + cell.backgroundView = cellNode.backgroundView; + + // Under iOS 10+, cells may be removed/re-added to the collection view without + // receiving prepareForReuse/applyLayoutAttributes, as an optimization for e.g. + // if the user is scrolling back and forth across a small set of items. + // In this case, we have to fetch the layout attributes manually. + // This may be possible under iOS < 10 but it has not been observed yet. + if (cell.layoutAttributes == nil) { + cell.layoutAttributes = [collectionView layoutAttributesForItemAtIndexPath:indexPath]; + } + + ASDisplayNodeAssertNotNil(cellNode, @"Expected node associated with cell that will be displayed not to be nil. indexPath: %@", indexPath); + + if (_asyncDelegateFlags.collectionNodeWillDisplayItem && self.collectionNode != nil) { + [_asyncDelegate collectionNode:self.collectionNode willDisplayItemWithNode:cellNode]; + } else if (_asyncDelegateFlags.collectionViewWillDisplayNodeForItem) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + [_asyncDelegate collectionView:self willDisplayNode:cellNode forItemAtIndexPath:indexPath]; + } else if (_asyncDelegateFlags.collectionViewWillDisplayNodeForItemDeprecated) { + [_asyncDelegate collectionView:self willDisplayNodeForItemAtIndexPath:indexPath]; + } +#pragma clang diagnostic pop + + [_rangeController setNeedsUpdate]; + + if ([cell consumesCellNodeVisibilityEvents]) { + [_cellsForVisibilityUpdates addObject:cell]; + } +} + +- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(UICollectionViewCell *)rawCell forItemAtIndexPath:(NSIndexPath *)indexPath +{ + if (_asyncDelegateFlags.interopDidEndDisplayingCell) { + ASCellNode *node = [self nodeForItemAtIndexPath:indexPath]; + if (node.shouldUseUIKitCell) { + [(id )_asyncDelegate collectionView:collectionView didEndDisplayingCell:rawCell forItemAtIndexPath:indexPath]; + } + } + + _ASCollectionViewCell *cell = ASDynamicCastStrict(rawCell, _ASCollectionViewCell); + if (cell == nil) { + [_rangeController setNeedsUpdate]; + return; + } + + // Retrieve the element from cell instead of visible map because at this point visible map could have been updated and no longer holds the element. + ASCollectionElement *element = cell.element; + if (element) { + [_visibleElements removeObject:element]; + } else { + ASDisplayNodeAssert(NO, @"Unexpected nil element for didEndDisplayingCell: %@, %@, %@", rawCell, self, indexPath); + return; + } + + ASCellNode *cellNode = element.node; + + if (_asyncDelegateFlags.collectionNodeDidEndDisplayingItem) { + if (ASCollectionNode *collectionNode = self.collectionNode) { + [_asyncDelegate collectionNode:collectionNode didEndDisplayingItemWithNode:cellNode]; + } + } else if (_asyncDelegateFlags.collectionViewDidEndDisplayingNodeForItem) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + [_asyncDelegate collectionView:self didEndDisplayingNode:cellNode forItemAtIndexPath:indexPath]; +#pragma clang diagnostic pop + } + + [_rangeController setNeedsUpdate]; + + [_cellsForVisibilityUpdates removeObject:cell]; + + cellNode.scrollView = nil; + cell.layoutAttributes = nil; +} + +- (void)collectionView:(UICollectionView *)collectionView willDisplaySupplementaryView:(UICollectionReusableView *)rawView forElementKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath +{ + if (_asyncDelegateFlags.interopWillDisplaySupplementaryView) { + ASCellNode *node = [self supplementaryNodeForElementKind:elementKind atIndexPath:indexPath]; + if (node.shouldUseUIKitCell) { + [(id )_asyncDelegate collectionView:collectionView willDisplaySupplementaryView:rawView forElementKind:elementKind atIndexPath:indexPath]; + } + } + + _ASCollectionReusableView *view = ASDynamicCastStrict(rawView, _ASCollectionReusableView); + if (view == nil) { + return; + } + + ASCollectionElement *element = view.element; + if (element) { + ASDisplayNodeAssertTrue([_dataController.visibleMap supplementaryElementOfKind:elementKind atIndexPath:indexPath] == view.element); + [_visibleElements addObject:element]; + } else { + ASDisplayNodeAssert(NO, @"Unexpected nil element for willDisplaySupplementaryView: %@, %@, %@", rawView, self, indexPath); + return; + } + + // Under iOS 10+, cells may be removed/re-added to the collection view without + // receiving prepareForReuse/applyLayoutAttributes, as an optimization for e.g. + // if the user is scrolling back and forth across a small set of items. + // In this case, we have to fetch the layout attributes manually. + // This may be possible under iOS < 10 but it has not been observed yet. + if (view.layoutAttributes == nil) { + view.layoutAttributes = [collectionView layoutAttributesForSupplementaryElementOfKind:elementKind atIndexPath:indexPath]; + } + + if (_asyncDelegateFlags.collectionNodeWillDisplaySupplementaryElement) { + GET_COLLECTIONNODE_OR_RETURN(collectionNode, (void)0); + ASCellNode *node = element.node; + ASDisplayNodeAssert([node.supplementaryElementKind isEqualToString:elementKind], @"Expected node for supplementary element to have kind '%@', got '%@'.", elementKind, node.supplementaryElementKind); + [_asyncDelegate collectionNode:collectionNode willDisplaySupplementaryElementWithNode:node]; + } +} + +- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingSupplementaryView:(UICollectionReusableView *)rawView forElementOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath +{ + if (_asyncDelegateFlags.interopdidEndDisplayingSupplementaryView) { + ASCellNode *node = [self supplementaryNodeForElementKind:elementKind atIndexPath:indexPath]; + if (node.shouldUseUIKitCell) { + [(id )_asyncDelegate collectionView:collectionView didEndDisplayingSupplementaryView:rawView forElementOfKind:elementKind atIndexPath:indexPath]; + } + } + + _ASCollectionReusableView *view = ASDynamicCastStrict(rawView, _ASCollectionReusableView); + if (view == nil) { + return; + } + + // Retrieve the element from cell instead of visible map because at this point visible map could have been updated and no longer holds the element. + ASCollectionElement *element = view.element; + if (element) { + [_visibleElements removeObject:element]; + } else { + ASDisplayNodeAssert(NO, @"Unexpected nil element for didEndDisplayingSupplementaryView: %@, %@, %@", rawView, self, indexPath); + return; + } + + if (_asyncDelegateFlags.collectionNodeDidEndDisplayingSupplementaryElement) { + GET_COLLECTIONNODE_OR_RETURN(collectionNode, (void)0); + ASCellNode *node = element.node; + ASDisplayNodeAssert([node.supplementaryElementKind isEqualToString:elementKind], @"Expected node for supplementary element to have kind '%@', got '%@'.", elementKind, node.supplementaryElementKind); + [_asyncDelegate collectionNode:collectionNode didEndDisplayingSupplementaryElementWithNode:node]; + } +} + +- (BOOL)collectionView:(UICollectionView *)collectionView shouldSelectItemAtIndexPath:(NSIndexPath *)indexPath +{ + if (_asyncDelegateFlags.collectionNodeShouldSelectItem) { + GET_COLLECTIONNODE_OR_RETURN(collectionNode, NO); + indexPath = [self convertIndexPathToCollectionNode:indexPath]; + if (indexPath != nil) { + return [_asyncDelegate collectionNode:collectionNode shouldSelectItemAtIndexPath:indexPath]; + } + } else if (_asyncDelegateFlags.collectionViewShouldSelectItem) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + return [_asyncDelegate collectionView:self shouldSelectItemAtIndexPath:indexPath]; +#pragma clang diagnostic pop + } + return YES; +} + +- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(nonnull NSIndexPath *)indexPath +{ + if (_asyncDelegateFlags.collectionNodeDidSelectItem) { + GET_COLLECTIONNODE_OR_RETURN(collectionNode, (void)0); + indexPath = [self convertIndexPathToCollectionNode:indexPath]; + if (indexPath != nil) { + [_asyncDelegate collectionNode:collectionNode didSelectItemAtIndexPath:indexPath]; + } + } else if (_asyncDelegateFlags.collectionViewDidSelectItem) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + [_asyncDelegate collectionView:self didSelectItemAtIndexPath:indexPath]; +#pragma clang diagnostic pop + } +} + +- (BOOL)collectionView:(UICollectionView *)collectionView shouldDeselectItemAtIndexPath:(NSIndexPath *)indexPath +{ + if (_asyncDelegateFlags.collectionNodeShouldDeselectItem) { + GET_COLLECTIONNODE_OR_RETURN(collectionNode, NO); + indexPath = [self convertIndexPathToCollectionNode:indexPath]; + if (indexPath != nil) { + return [_asyncDelegate collectionNode:collectionNode shouldDeselectItemAtIndexPath:indexPath]; + } + } else if (_asyncDelegateFlags.collectionViewShouldDeselectItem) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + return [_asyncDelegate collectionView:self shouldDeselectItemAtIndexPath:indexPath]; +#pragma clang diagnostic pop + } + return YES; +} + +- (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(nonnull NSIndexPath *)indexPath +{ + if (_asyncDelegateFlags.collectionNodeDidDeselectItem) { + GET_COLLECTIONNODE_OR_RETURN(collectionNode, (void)0); + indexPath = [self convertIndexPathToCollectionNode:indexPath]; + if (indexPath != nil) { + [_asyncDelegate collectionNode:collectionNode didDeselectItemAtIndexPath:indexPath]; + } + } else if (_asyncDelegateFlags.collectionViewDidDeselectItem) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + [_asyncDelegate collectionView:self didDeselectItemAtIndexPath:indexPath]; +#pragma clang diagnostic pop + } +} + +- (BOOL)collectionView:(UICollectionView *)collectionView shouldHighlightItemAtIndexPath:(NSIndexPath *)indexPath +{ + if (_asyncDelegateFlags.collectionNodeShouldHighlightItem) { + GET_COLLECTIONNODE_OR_RETURN(collectionNode, NO); + indexPath = [self convertIndexPathToCollectionNode:indexPath]; + if (indexPath != nil) { + return [_asyncDelegate collectionNode:collectionNode shouldHighlightItemAtIndexPath:indexPath]; + } else { + return YES; + } + } else if (_asyncDelegateFlags.collectionViewShouldHighlightItem) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + return [_asyncDelegate collectionView:self shouldHighlightItemAtIndexPath:indexPath]; +#pragma clang diagnostic pop + } + return YES; +} + +- (void)collectionView:(UICollectionView *)collectionView didHighlightItemAtIndexPath:(nonnull NSIndexPath *)indexPath +{ + if (_asyncDelegateFlags.collectionNodeDidHighlightItem) { + GET_COLLECTIONNODE_OR_RETURN(collectionNode, (void)0); + indexPath = [self convertIndexPathToCollectionNode:indexPath]; + if (indexPath != nil) { + [_asyncDelegate collectionNode:collectionNode didHighlightItemAtIndexPath:indexPath]; + } + } else if (_asyncDelegateFlags.collectionViewDidHighlightItem) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + [_asyncDelegate collectionView:self didHighlightItemAtIndexPath:indexPath]; +#pragma clang diagnostic pop + } +} + +- (void)collectionView:(UICollectionView *)collectionView didUnhighlightItemAtIndexPath:(nonnull NSIndexPath *)indexPath +{ + if (_asyncDelegateFlags.collectionNodeDidUnhighlightItem) { + GET_COLLECTIONNODE_OR_RETURN(collectionNode, (void)0); + indexPath = [self convertIndexPathToCollectionNode:indexPath]; + if (indexPath != nil) { + [_asyncDelegate collectionNode:collectionNode didUnhighlightItemAtIndexPath:indexPath]; + } + } else if (_asyncDelegateFlags.collectionViewDidUnhighlightItem) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + [_asyncDelegate collectionView:self didUnhighlightItemAtIndexPath:indexPath]; +#pragma clang diagnostic pop + } +} + +- (BOOL)collectionView:(UICollectionView *)collectionView shouldShowMenuForItemAtIndexPath:(nonnull NSIndexPath *)indexPath +{ + if (_asyncDelegateFlags.collectionNodeShouldShowMenuForItem) { + GET_COLLECTIONNODE_OR_RETURN(collectionNode, NO); + indexPath = [self convertIndexPathToCollectionNode:indexPath]; + if (indexPath != nil) { + return [_asyncDelegate collectionNode:collectionNode shouldShowMenuForItemAtIndexPath:indexPath]; + } + } else if (_asyncDelegateFlags.collectionViewShouldShowMenuForItem) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + return [_asyncDelegate collectionView:self shouldShowMenuForItemAtIndexPath:indexPath]; +#pragma clang diagnostic pop + } + return NO; +} + +- (BOOL)collectionView:(UICollectionView *)collectionView canPerformAction:(nonnull SEL)action forItemAtIndexPath:(nonnull NSIndexPath *)indexPath withSender:(nullable id)sender +{ + if (_asyncDelegateFlags.collectionNodeCanPerformActionForItem) { + GET_COLLECTIONNODE_OR_RETURN(collectionNode, NO); + indexPath = [self convertIndexPathToCollectionNode:indexPath]; + if (indexPath != nil) { + return [_asyncDelegate collectionNode:collectionNode canPerformAction:action forItemAtIndexPath:indexPath sender:sender]; + } + } else if (_asyncDelegateFlags.collectionViewCanPerformActionForItem) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + return [_asyncDelegate collectionView:self canPerformAction:action forItemAtIndexPath:indexPath withSender:sender]; +#pragma clang diagnostic pop + } + return NO; +} + +- (void)collectionView:(UICollectionView *)collectionView performAction:(nonnull SEL)action forItemAtIndexPath:(nonnull NSIndexPath *)indexPath withSender:(nullable id)sender +{ + if (_asyncDelegateFlags.collectionNodePerformActionForItem) { + GET_COLLECTIONNODE_OR_RETURN(collectionNode, (void)0); + indexPath = [self convertIndexPathToCollectionNode:indexPath]; + if (indexPath != nil) { + [_asyncDelegate collectionNode:collectionNode performAction:action forItemAtIndexPath:indexPath sender:sender]; + } + } else if (_asyncDelegateFlags.collectionViewPerformActionForItem) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + [_asyncDelegate collectionView:self performAction:action forItemAtIndexPath:indexPath withSender:sender]; +#pragma clang diagnostic pop + } +} + +- (BOOL)collectionView:(UICollectionView *)collectionView canMoveItemAtIndexPath:(NSIndexPath *)indexPath +{ + // Mimic UIKit's gating logic. + // If the data source doesn't support moving, then all bets are off. + if (!_asyncDataSourceFlags.collectionNodeMoveItem) { + return NO; + } + + // Currently we do not support interactive moves when using async layout. The reason is, we do not have a mechanism + // to propagate the "presentation data" element map (containing the speculative in-progress moves) to the layout delegate, + // and this can cause exceptions to be thrown from UICV. For example, if you drag an item out of a section, + // the element map will still contain N items in that section, even though there's only N-1 shown, and UICV will + // throw an exception that you specified an element that doesn't exist. + // + // In iOS >= 11, this is made much easier by the UIDataSourceTranslating API. In previous versions of iOS our best bet + // would be to capture the invalidation contexts that are sent during interactive moves and make our own data source translator. + if ([self.collectionViewLayout isKindOfClass:[ASCollectionLayout class]]) { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + os_log_debug(ASCollectionLog(), "Collection node item interactive movement is not supported when using a layout delegate. This message will only be logged once. Node: %@", ASObjectDescriptionMakeTiny(self)); + }); + return NO; + } + + // If the data source implements canMoveItem, let them decide. + if (_asyncDataSourceFlags.collectionNodeCanMoveItem) { + if (ASCellNode *cellNode = [self nodeForItemAtIndexPath:indexPath]) { + GET_COLLECTIONNODE_OR_RETURN(collectionNode, NO); + return [_asyncDataSource collectionNode:collectionNode canMoveItemWithNode:cellNode]; + } + } + + // Otherwise allow the move for all items. + return YES; +} + +- (void)collectionView:(UICollectionView *)collectionView moveItemAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath +{ + ASDisplayNodeAssert(_asyncDataSourceFlags.collectionNodeMoveItem, @"Should not allow interactive collection item movement if data source does not support it."); + + // Inform the data source first, in case they call nodeForItemAtIndexPath:. + // We want to make sure we return them the node for the item they have in mind. + if (ASCollectionNode *collectionNode = self.collectionNode) { + [_asyncDataSource collectionNode:collectionNode moveItemAtIndexPath:sourceIndexPath toIndexPath:destinationIndexPath]; + } + + // Now we update our data controller's store. + // Get up to date + [self waitUntilAllUpdatesAreCommitted]; + // Set our flag to suppress informing super about the change. + ASDisplayNodeAssertFalse(_updatingInResponseToInteractiveMove); + _updatingInResponseToInteractiveMove = YES; + // Submit the move + [self moveItemAtIndexPath:sourceIndexPath toIndexPath:destinationIndexPath]; + // Wait for it to finish – should be fast! + [self waitUntilAllUpdatesAreCommitted]; + // Clear the flag + _updatingInResponseToInteractiveMove = NO; +} + +- (void)scrollViewDidScroll:(UIScrollView *)scrollView +{ + ASInterfaceState interfaceState = [self interfaceStateForRangeController:_rangeController]; + if (ASInterfaceStateIncludesVisible(interfaceState)) { + [self _checkForBatchFetching]; + } + for (_ASCollectionViewCell *cell in _cellsForVisibilityUpdates) { + // _cellsForVisibilityUpdates only includes cells for ASCellNode subclasses with overrides of the visibility method. + [cell cellNodeVisibilityEvent:ASCellNodeVisibilityEventVisibleRectChanged inScrollView:scrollView]; + } + if (_asyncDelegateFlags.scrollViewDidScroll) { + [_asyncDelegate scrollViewDidScroll:scrollView]; + } +} + +- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset +{ + CGPoint contentOffset = scrollView.contentOffset; + _deceleratingVelocity = CGPointMake( + contentOffset.x - ((targetContentOffset != NULL) ? targetContentOffset->x : 0), + contentOffset.y - ((targetContentOffset != NULL) ? targetContentOffset->y : 0) + ); + + if (targetContentOffset != NULL) { + ASDisplayNodeAssert(_batchContext != nil, @"Batch context should exist"); + [self _beginBatchFetchingIfNeededWithContentOffset:*targetContentOffset velocity:velocity]; + } + + if (_asyncDelegateFlags.scrollViewWillEndDragging) { + [_asyncDelegate scrollViewWillEndDragging:scrollView withVelocity:velocity targetContentOffset:(targetContentOffset ? : &contentOffset)]; + } +} + +- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView +{ + _deceleratingVelocity = CGPointZero; + + if (_asyncDelegateFlags.scrollViewDidEndDecelerating) { + [_asyncDelegate scrollViewDidEndDecelerating:scrollView]; + } +} + +- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView +{ + // If a scroll happens the current range mode needs to go to full + _rangeController.contentHasBeenScrolled = YES; + [_rangeController updateCurrentRangeWithMode:ASLayoutRangeModeFull]; + + for (_ASCollectionViewCell *cell in _cellsForVisibilityUpdates) { + [cell cellNodeVisibilityEvent:ASCellNodeVisibilityEventWillBeginDragging inScrollView:scrollView]; + } + if (_asyncDelegateFlags.scrollViewWillBeginDragging) { + [_asyncDelegate scrollViewWillBeginDragging:scrollView]; + } +} + +- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate +{ + for (_ASCollectionViewCell *cell in _cellsForVisibilityUpdates) { + [cell cellNodeVisibilityEvent:ASCellNodeVisibilityEventDidEndDragging inScrollView:scrollView]; + } + if (_asyncDelegateFlags.scrollViewDidEndDragging) { + [_asyncDelegate scrollViewDidEndDragging:scrollView willDecelerate:decelerate]; + } +} + +#pragma mark - Scroll Direction. + +- (BOOL)inverted +{ + return _inverted; +} + +- (void)setInverted:(BOOL)inverted +{ + _inverted = inverted; +} + +- (void)setLeadingScreensForBatching:(CGFloat)leadingScreensForBatching +{ + if (_leadingScreensForBatching != leadingScreensForBatching) { + _leadingScreensForBatching = leadingScreensForBatching; + // Push this to the next runloop to be sure the scroll view has the right content size + dispatch_async(dispatch_get_main_queue(), ^{ + [self _checkForBatchFetching]; + }); + } +} + +- (CGFloat)leadingScreensForBatching +{ + return _leadingScreensForBatching; +} + +- (ASScrollDirection)scrollDirection +{ + CGPoint scrollVelocity; + if (self.isTracking) { + scrollVelocity = [self.panGestureRecognizer velocityInView:self.superview]; + } else { + scrollVelocity = _deceleratingVelocity; + } + + ASScrollDirection scrollDirection = [self _scrollDirectionForVelocity:scrollVelocity]; + return ASScrollDirectionApplyTransform(scrollDirection, self.transform); +} + +- (ASScrollDirection)_scrollDirectionForVelocity:(CGPoint)scrollVelocity +{ + ASScrollDirection direction = ASScrollDirectionNone; + ASScrollDirection scrollableDirections = [self scrollableDirections]; + + if (ASScrollDirectionContainsHorizontalDirection(scrollableDirections)) { // Can scroll horizontally. + if (scrollVelocity.x < 0.0) { + direction |= ASScrollDirectionRight; + } else if (scrollVelocity.x > 0.0) { + direction |= ASScrollDirectionLeft; + } + } + if (ASScrollDirectionContainsVerticalDirection(scrollableDirections)) { // Can scroll vertically. + if (scrollVelocity.y < 0.0) { + direction |= ASScrollDirectionDown; + } else if (scrollVelocity.y > 0.0) { + direction |= ASScrollDirectionUp; + } + } + + return direction; +} + +- (ASScrollDirection)scrollableDirections +{ + ASDisplayNodeAssertNotNil(self.layoutInspector, @"Layout inspector should be assigned."); + return [self.layoutInspector scrollableDirections]; +} + +- (void)layoutSubviews +{ + if (_cellsForLayoutUpdates.count > 0) { + NSArray *nodes = [_cellsForLayoutUpdates allObjects]; + [_cellsForLayoutUpdates removeAllObjects]; + + NSMutableArray *nodesSizeChanged = [[NSMutableArray alloc] init]; + + [_dataController relayoutNodes:nodes nodesSizeChanged:nodesSizeChanged]; + [self nodesDidRelayout:nodesSizeChanged]; + } + + // Flush any pending invalidation action if needed. + ASCollectionViewInvalidationStyle invalidationStyle = _nextLayoutInvalidationStyle; + _nextLayoutInvalidationStyle = ASCollectionViewInvalidationStyleNone; + switch (invalidationStyle) { + case ASCollectionViewInvalidationStyleWithAnimation: + if (0 == _superBatchUpdateCount) { + if (ASCellLayoutModeIncludes(ASCellLayoutModeAlwaysReloadData)) { + [self _superReloadData:nil completion:nil]; + } else { + [self _superPerformBatchUpdates:nil completion:nil]; + } + } + break; + case ASCollectionViewInvalidationStyleWithoutAnimation: + [self.collectionViewLayout invalidateLayout]; + break; + default: + break; + } + + // To ensure _maxSizeForNodesConstrainedSize is up-to-date for every usage, this call to super must be done last + [super layoutSubviews]; + + if (_zeroContentInsets) { + self.contentInset = UIEdgeInsetsZero; + } + + // Update range controller immediately if possible & needed. + // Calling -updateIfNeeded in here with self.window == nil (early in the collection view's life) + // may cause UICollectionView data related crashes. We'll update in -didMoveToWindow anyway. + if (self.window != nil) { + [_rangeController updateIfNeeded]; + } +} + + +#pragma mark - Batch Fetching + +- (ASBatchContext *)batchContext +{ + return _batchContext; +} + +- (BOOL)canBatchFetch +{ + // if the delegate does not respond to this method, there is no point in starting to fetch + BOOL canFetch = _asyncDelegateFlags.collectionNodeWillBeginBatchFetch || _asyncDelegateFlags.collectionViewWillBeginBatchFetch; + if (canFetch && _asyncDelegateFlags.shouldBatchFetchForCollectionNode) { + GET_COLLECTIONNODE_OR_RETURN(collectionNode, NO); + return [_asyncDelegate shouldBatchFetchForCollectionNode:collectionNode]; + } else if (canFetch && _asyncDelegateFlags.shouldBatchFetchForCollectionView) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + return [_asyncDelegate shouldBatchFetchForCollectionView:self]; +#pragma clang diagnostic pop + } else { + return canFetch; + } +} + +- (id)batchFetchingDelegate{ + return self.collectionNode.batchFetchingDelegate; +} + +- (void)_scheduleCheckForBatchFetchingForNumberOfChanges:(NSUInteger)changes +{ + // Prevent fetching will continually trigger in a loop after reaching end of content and no new content was provided + if (changes == 0 && _hasEverCheckedForBatchFetchingDueToUpdate) { + return; + } + _hasEverCheckedForBatchFetchingDueToUpdate = YES; + + // Push this to the next runloop to be sure the scroll view has the right content size + dispatch_async(dispatch_get_main_queue(), ^{ + [self _checkForBatchFetching]; + }); +} + +- (void)_checkForBatchFetching +{ + // Dragging will be handled in scrollViewWillEndDragging:withVelocity:targetContentOffset: + if (self.isDragging || self.isTracking) { + return; + } + + [self _beginBatchFetchingIfNeededWithContentOffset:self.contentOffset velocity:CGPointZero]; +} + +- (void)_beginBatchFetchingIfNeededWithContentOffset:(CGPoint)contentOffset velocity:(CGPoint)velocity +{ + if (ASDisplayShouldFetchBatchForScrollView(self, self.scrollDirection, self.scrollableDirections, contentOffset, velocity)) { + [self _beginBatchFetching]; + } +} + +- (void)_beginBatchFetching +{ + as_activity_create_for_scope("Batch fetch for collection node"); + [_batchContext beginBatchFetching]; + if (_asyncDelegateFlags.collectionNodeWillBeginBatchFetch) { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + GET_COLLECTIONNODE_OR_RETURN(collectionNode, (void)0); + os_log_debug(ASCollectionLog(), "Beginning batch fetch for %@ with context %@", collectionNode, self->_batchContext); + [self->_asyncDelegate collectionNode:collectionNode willBeginBatchFetchWithContext:self->_batchContext]; + }); + } else if (_asyncDelegateFlags.collectionViewWillBeginBatchFetch) { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + [self->_asyncDelegate collectionView:self willBeginBatchFetchWithContext:self->_batchContext]; +#pragma clang diagnostic pop + }); + } +} + +#pragma mark - ASDataControllerSource + +- (BOOL)dataController:(ASDataController *)dataController shouldEagerlyLayoutNode:(ASCellNode *)node +{ + NSAssert(!ASCellLayoutModeIncludes(ASCellLayoutModeAlwaysLazy), + @"ASCellLayoutModeAlwaysLazy flag is no longer supported"); + return !node.shouldUseUIKitCell; +} + +- (BOOL)dataController:(ASDataController *)dataController shouldSynchronouslyProcessChangeSet:(_ASHierarchyChangeSet *)changeSet +{ + // If we have AlwaysSync set, block and donate main priority. + if (ASCellLayoutModeIncludes(ASCellLayoutModeAlwaysSync)) { + return YES; + } + // Prioritize AlwaysAsync over the remaining heuristics for the Default mode. + if (ASCellLayoutModeIncludes(ASCellLayoutModeAlwaysAsync)) { + return NO; + } + // Reload data is expensive, don't block main while doing so. + if (changeSet.includesReloadData) { + return NO; + } + // If we have very few ASCellNodes (besides UIKit passthrough ones), match UIKit by blocking. + if (changeSet.countForAsyncLayout < 2) { + return YES; + } + CGSize contentSize = self.contentSize; + CGSize boundsSize = self.bounds.size; + if (contentSize.height <= boundsSize.height && contentSize.width <= boundsSize.width) { + return YES; + } + return NO; // ASCellLayoutModeNone +} + +- (BOOL)dataControllerShouldSerializeNodeCreation:(ASDataController *)dataController +{ + return ASCellLayoutModeIncludes(ASCellLayoutModeSerializeNodeCreation); +} + +- (id)dataController:(ASDataController *)dataController nodeModelForItemAtIndexPath:(NSIndexPath *)indexPath +{ + if (!_asyncDataSourceFlags.nodeModelForItem) { + return nil; + } + + GET_COLLECTIONNODE_OR_RETURN(collectionNode, nil); + return [_asyncDataSource collectionNode:collectionNode nodeModelForItemAtIndexPath:indexPath]; +} + +- (ASCellNodeBlock)dataController:(ASDataController *)dataController nodeBlockAtIndexPath:(NSIndexPath *)indexPath shouldAsyncLayout:(BOOL *)shouldAsyncLayout +{ + ASDisplayNodeAssertMainThread(); + ASCellNodeBlock block = nil; + ASCellNode *cell = nil; + + if (_asyncDataSourceFlags.collectionNodeNodeBlockForItem) { + GET_COLLECTIONNODE_OR_RETURN(collectionNode, ^{ return [[ASCellNode alloc] init]; }); + block = [_asyncDataSource collectionNode:collectionNode nodeBlockForItemAtIndexPath:indexPath]; + } + if (!block && !cell && _asyncDataSourceFlags.collectionNodeNodeForItem) { + GET_COLLECTIONNODE_OR_RETURN(collectionNode, ^{ return [[ASCellNode alloc] init]; }); + cell = [_asyncDataSource collectionNode:collectionNode nodeForItemAtIndexPath:indexPath]; + } +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + if (!block && !cell && _asyncDataSourceFlags.collectionViewNodeBlockForItem) { + block = [_asyncDataSource collectionView:self nodeBlockForItemAtIndexPath:indexPath]; + } + if (!block && !cell && _asyncDataSourceFlags.collectionViewNodeForItem) { + cell = [_asyncDataSource collectionView:self nodeForItemAtIndexPath:indexPath]; + } +#pragma clang diagnostic pop + + if (block == nil) { + if (cell == nil || ASDynamicCast(cell, ASCellNode) == nil) { + // In this case, either the client is expecting a UIKit passthrough cell to be created automatically, + // or it is an error. + if (_asyncDataSourceFlags.interop) { + cell = [[ASWrapperCellNode alloc] init]; + } else { + ASDisplayNodeFailAssert(@"ASCollection could not get a node block for item at index path %@: %@, %@. If you are trying to display a UICollectionViewCell, make sure your dataSource conforms to the protocol!", indexPath, cell, block); + cell = [[ASCellNode alloc] init]; + } + } + + // This condition is intended to run for either cells received from the datasource, or created just above. + if (cell.shouldUseUIKitCell) { + *shouldAsyncLayout = NO; + } + } + + // Wrap the node block + BOOL disableRangeController = ASCellLayoutModeIncludes(ASCellLayoutModeDisableRangeController); + __weak __typeof__(self) weakSelf = self; + return ^{ + __typeof__(self) strongSelf = weakSelf; + ASCellNode *node = (block ? block() : cell); + ASDisplayNodeAssert([node isKindOfClass:[ASCellNode class]], @"ASCollectionNode provided a non-ASCellNode! %@, %@", node, strongSelf); + + if (!disableRangeController) { + [node enterHierarchyState:ASHierarchyStateRangeManaged]; + } + if (node.interactionDelegate == nil) { + node.interactionDelegate = strongSelf; + } + if (strongSelf.inverted) { + node.transform = CATransform3DMakeScale(1, -1, 1); + } + return node; + }; +} + +- (NSUInteger)dataController:(ASDataController *)dataController rowsInSection:(NSUInteger)section +{ + if (_asyncDataSourceFlags.collectionNodeNumberOfItemsInSection) { + GET_COLLECTIONNODE_OR_RETURN(collectionNode, 0); + return [_asyncDataSource collectionNode:collectionNode numberOfItemsInSection:section]; + } else if (_asyncDataSourceFlags.collectionViewNumberOfItemsInSection) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + return [_asyncDataSource collectionView:self numberOfItemsInSection:section]; +#pragma clang diagnostic pop + } else { + return 0; + } +} + +- (NSUInteger)numberOfSectionsInDataController:(ASDataController *)dataController { + if (_asyncDataSourceFlags.numberOfSectionsInCollectionNode) { + GET_COLLECTIONNODE_OR_RETURN(collectionNode, 0); + return [_asyncDataSource numberOfSectionsInCollectionNode:collectionNode]; + } else if (_asyncDataSourceFlags.numberOfSectionsInCollectionView) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + return [_asyncDataSource numberOfSectionsInCollectionView:self]; +#pragma clang diagnostic pop + } else { + return 1; + } +} + +- (BOOL)dataController:(ASDataController *)dataController presentedSizeForElement:(ASCollectionElement *)element matchesSize:(CGSize)size +{ + NSIndexPath *indexPath = [self indexPathForNode:element.node]; + if (indexPath == nil) { + ASDisplayNodeFailAssert(@"Data controller should not ask for presented size for element that is not presented."); + return YES; + } + + UICollectionViewLayoutAttributes *attributes; + if (element.supplementaryElementKind == nil) { + attributes = [self layoutAttributesForItemAtIndexPath:indexPath]; + } else { + attributes = [self layoutAttributesForSupplementaryElementOfKind:element.supplementaryElementKind atIndexPath:indexPath]; + } + return CGSizeEqualToSizeWithIn(attributes.size, size, FLT_EPSILON); +} + +#pragma mark - ASDataControllerSource optional methods + +- (ASCellNodeBlock)dataController:(ASDataController *)dataController supplementaryNodeBlockOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath shouldAsyncLayout:(BOOL *)shouldAsyncLayout +{ + ASDisplayNodeAssertMainThread(); + ASCellNodeBlock block = nil; + ASCellNode *cell = nil; + if (_asyncDataSourceFlags.collectionNodeNodeBlockForSupplementaryElement) { + GET_COLLECTIONNODE_OR_RETURN(collectionNode, ^{ return [[ASCellNode alloc] init]; }); + block = [_asyncDataSource collectionNode:collectionNode nodeBlockForSupplementaryElementOfKind:kind atIndexPath:indexPath]; + } + if (!block && !cell && _asyncDataSourceFlags.collectionNodeNodeForSupplementaryElement) { + GET_COLLECTIONNODE_OR_RETURN(collectionNode, ^{ return [[ASCellNode alloc] init]; }); + cell = [_asyncDataSource collectionNode:collectionNode nodeForSupplementaryElementOfKind:kind atIndexPath:indexPath]; + } + if (!block && !cell && _asyncDataSourceFlags.collectionViewNodeForSupplementaryElement) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + cell = [_asyncDataSource collectionView:self nodeForSupplementaryElementOfKind:kind atIndexPath:indexPath]; +#pragma clang diagnostic pop + } + + if (block == nil) { + if (cell == nil || ASDynamicCast(cell, ASCellNode) == nil) { + // In this case, the app code returned nil for the node and the nodeBlock. + // If the UIKit method is implemented, then we should use a passthrough cell. + // Otherwise the CGSizeZero default will cause UIKit to not show it (so this isn't an error like the cellForItem case). + + BOOL useUIKitCell = _asyncDataSourceFlags.interopViewForSupplementaryElement; + if (useUIKitCell) { + cell = [[ASWrapperCellNode alloc] init]; + } else { + cell = [[ASCellNode alloc] init]; + } + } + + // This condition is intended to run for either cells received from the datasource, or created just above. + if (cell.shouldUseUIKitCell) { + *shouldAsyncLayout = NO; + } + + block = ^{ return cell; }; + } + + // Wrap the node block + // BOOL disableRangeController = ASCellLayoutModeIncludes(ASCellLayoutModeDisableRangeController); + __weak __typeof__(self) weakSelf = self; + return ^{ + __typeof__(self) strongSelf = weakSelf; + ASCellNode *node = block(); + ASDisplayNodeAssert([node isKindOfClass:[ASCellNode class]], + @"ASCollectionNode provided a non-ASCellNode! %@, %@", node, strongSelf); + + // TODO: ASRangeController doesn't currently support managing interfaceState for supplementary nodes. + // For now, we allow the standard ASInterfaceStateInHierarchy behavior by ensuring we do not inform + // the node that it should expect external management of interfaceState. + /* + if (!disableRangeController) { + [node enterHierarchyState:ASHierarchyStateRangeManaged]; + } + */ + + if (node.interactionDelegate == nil) { + node.interactionDelegate = strongSelf; + } + if (strongSelf.inverted) { + node.transform = CATransform3DMakeScale(1, -1, 1); + } + return node; + }; +} + +- (NSArray *)dataController:(ASDataController *)dataController supplementaryNodeKindsInSections:(NSIndexSet *)sections +{ + if (_asyncDataSourceFlags.collectionNodeSupplementaryElementKindsInSection) { + const auto kinds = [[NSMutableSet alloc] init]; + GET_COLLECTIONNODE_OR_RETURN(collectionNode, @[]); + [sections enumerateIndexesUsingBlock:^(NSUInteger section, BOOL * _Nonnull stop) { + NSArray *kindsForSection = [_asyncDataSource collectionNode:collectionNode supplementaryElementKindsInSection:section]; + [kinds addObjectsFromArray:kindsForSection]; + }]; + return [kinds allObjects]; + } else { + // TODO: Lock this + return [_registeredSupplementaryKinds allObjects]; + } +} + +- (ASSizeRange)dataController:(ASDataController *)dataController constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath +{ + return [self.layoutInspector collectionView:self constrainedSizeForNodeAtIndexPath:indexPath]; +} + +- (ASSizeRange)dataController:(ASDataController *)dataController constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath +{ + if (_layoutInspectorFlags.constrainedSizeForSupplementaryNodeOfKindAtIndexPath) { + return [self.layoutInspector collectionView:self constrainedSizeForSupplementaryNodeOfKind:kind atIndexPath:indexPath]; + } + + ASDisplayNodeAssert(NO, @"To support supplementary nodes in ASCollectionView, it must have a layoutInspector for layout inspection. (See ASCollectionViewFlowLayoutInspector for an example.)"); + return ASSizeRangeMake(CGSizeZero, CGSizeZero); +} + +- (NSUInteger)dataController:(ASDataController *)dataController supplementaryNodesOfKind:(NSString *)kind inSection:(NSUInteger)section +{ + if (_asyncDataSource == nil) { + return 0; + } + + if (_layoutInspectorFlags.supplementaryNodesOfKindInSection) { + return [self.layoutInspector collectionView:self supplementaryNodesOfKind:kind inSection:section]; + } + + ASDisplayNodeAssert(NO, @"To support supplementary nodes in ASCollectionView, it must have a layoutInspector for layout inspection. (See ASCollectionViewFlowLayoutInspector for an example.)"); + return 0; +} + +- (id)dataController:(ASDataController *)dataController contextForSection:(NSInteger)section +{ + ASDisplayNodeAssertMainThread(); + id context = nil; + + if (_asyncDataSourceFlags.collectionNodeContextForSection) { + GET_COLLECTIONNODE_OR_RETURN(collectionNode, nil); + context = [_asyncDataSource collectionNode:collectionNode contextForSection:section]; + } + + if (context != nil) { + context.collectionView = self; + } + return context; +} + +#pragma mark - ASRangeControllerDataSource + +- (ASRangeController *)rangeController +{ + return _rangeController; +} + +- (NSHashTable *)visibleElementsForRangeController:(ASRangeController *)rangeController +{ + return ASPointerTableByFlatMapping(_visibleElements, id element, element); +} + +- (ASElementMap *)elementMapForRangeController:(ASRangeController *)rangeController +{ + return _dataController.visibleMap; +} + +- (ASScrollDirection)scrollDirectionForRangeController:(ASRangeController *)rangeController +{ + return self.scrollDirection; +} + +- (ASInterfaceState)interfaceStateForRangeController:(ASRangeController *)rangeController +{ + return ASInterfaceStateForDisplayNode(self.collectionNode, self.window); +} + +- (NSString *)nameForRangeControllerDataSource +{ + return self.asyncDataSource ? NSStringFromClass([self.asyncDataSource class]) : NSStringFromClass([self class]); +} + +#pragma mark - ASRangeControllerDelegate + +- (BOOL)rangeControllerShouldUpdateRanges:(ASRangeController *)rangeController +{ + return !ASCellLayoutModeIncludes(ASCellLayoutModeDisableRangeController); +} + +- (void)rangeController:(ASRangeController *)rangeController updateWithChangeSet:(_ASHierarchyChangeSet *)changeSet updates:(dispatch_block_t)updates +{ + ASDisplayNodeAssertMainThread(); + if (!self.asyncDataSource || _superIsPendingDataLoad || _updatingInResponseToInteractiveMove) { + updates(); + [changeSet executeCompletionHandlerWithFinished:NO]; + return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes + } + + //TODO Do we need to notify _layoutFacilitator before reloadData? + for (_ASHierarchyItemChange *change in [changeSet itemChangesOfType:_ASHierarchyChangeTypeDelete]) { + [_layoutFacilitator collectionViewWillEditCellsAtIndexPaths:change.indexPaths batched:YES]; + } + + for (_ASHierarchySectionChange *change in [changeSet sectionChangesOfType:_ASHierarchyChangeTypeDelete]) { + [_layoutFacilitator collectionViewWillEditSectionsAtIndexSet:change.indexSet batched:YES]; + } + + for (_ASHierarchySectionChange *change in [changeSet sectionChangesOfType:_ASHierarchyChangeTypeInsert]) { + [_layoutFacilitator collectionViewWillEditSectionsAtIndexSet:change.indexSet batched:YES]; + } + + for (_ASHierarchyItemChange *change in [changeSet itemChangesOfType:_ASHierarchyChangeTypeInsert]) { + [_layoutFacilitator collectionViewWillEditCellsAtIndexPaths:change.indexPaths batched:YES]; + } + + ASPerformBlockWithoutAnimation(!changeSet.animated, ^{ + as_activity_scope(as_activity_create("Commit collection update", changeSet.rootActivity, OS_ACTIVITY_FLAG_DEFAULT)); + if (changeSet.includesReloadData) { + self->_superIsPendingDataLoad = YES; + updates(); + [self _superReloadData:nil completion:nil]; + os_log_debug(ASCollectionLog(), "Did reloadData %@", self.collectionNode); + [changeSet executeCompletionHandlerWithFinished:YES]; + } else { + [self->_layoutFacilitator collectionViewWillPerformBatchUpdates]; + + __block NSUInteger numberOfUpdates = 0; + const auto completion = ^(BOOL finished) { + as_activity_scope(as_activity_create("Handle collection update completion", changeSet.rootActivity, OS_ACTIVITY_FLAG_DEFAULT)); + as_log_verbose(ASCollectionLog(), "Update animation finished %{public}@", self.collectionNode); + // Flush any range changes that happened as part of the update animations ending. + [self->_rangeController updateIfNeeded]; + [self _scheduleCheckForBatchFetchingForNumberOfChanges:numberOfUpdates]; + [changeSet executeCompletionHandlerWithFinished:finished]; + }; + + BOOL shouldReloadData = ASCellLayoutModeIncludes(ASCellLayoutModeAlwaysReloadData); + // TODO: Consider adding !changeSet.isEmpty as a check to also disable shouldReloadData. + if (ASCellLayoutModeIncludes(ASCellLayoutModeAlwaysBatchUpdateSectionReload) && + [changeSet sectionChangesOfType:_ASHierarchyChangeTypeReload].count > 0) { + shouldReloadData = NO; + } + + if (shouldReloadData) { + // When doing a reloadData, the insert / delete calls are not necessary. + // Calling updates() is enough, as it commits .pendingMap to .visibleMap. + [self _superReloadData:updates completion:completion]; + } else { + [self _superPerformBatchUpdates:^{ + updates(); + + for (_ASHierarchyItemChange *change in [changeSet itemChangesOfType:_ASHierarchyChangeTypeReload]) { + [super reloadItemsAtIndexPaths:change.indexPaths]; + numberOfUpdates++; + } + + for (_ASHierarchySectionChange *change in [changeSet sectionChangesOfType:_ASHierarchyChangeTypeReload]) { + [super reloadSections:change.indexSet]; + numberOfUpdates++; + } + + for (_ASHierarchyItemChange *change in [changeSet itemChangesOfType:_ASHierarchyChangeTypeOriginalDelete]) { + [super deleteItemsAtIndexPaths:change.indexPaths]; + numberOfUpdates++; + } + + for (_ASHierarchySectionChange *change in [changeSet sectionChangesOfType:_ASHierarchyChangeTypeOriginalDelete]) { + [super deleteSections:change.indexSet]; + numberOfUpdates++; + } + + for (_ASHierarchySectionChange *change in [changeSet sectionChangesOfType:_ASHierarchyChangeTypeOriginalInsert]) { + [super insertSections:change.indexSet]; + numberOfUpdates++; + } + + for (_ASHierarchyItemChange *change in [changeSet itemChangesOfType:_ASHierarchyChangeTypeOriginalInsert]) { + [super insertItemsAtIndexPaths:change.indexPaths]; + numberOfUpdates++; + } + } completion:completion]; + } + + os_log_debug(ASCollectionLog(), "Completed batch update %{public}@", self.collectionNode); + + // Flush any range changes that happened as part of submitting the update. + as_activity_scope(changeSet.rootActivity); + [self->_rangeController updateIfNeeded]; + } + }); +} + +#pragma mark - ASCellNodeDelegate + +- (void)nodeSelectedStateDidChange:(ASCellNode *)node +{ + NSIndexPath *indexPath = [self indexPathForNode:node]; + if (indexPath) { + if (node.isSelected) { + [super selectItemAtIndexPath:indexPath animated:NO scrollPosition:UICollectionViewScrollPositionNone]; + } else { + [super deselectItemAtIndexPath:indexPath animated:NO]; + } + } +} + +- (void)nodeHighlightedStateDidChange:(ASCellNode *)node +{ + NSIndexPath *indexPath = [self indexPathForNode:node]; + if (indexPath) { + [self cellForItemAtIndexPath:indexPath].highlighted = node.isHighlighted; + } +} + +- (void)nodeDidInvalidateSize:(ASCellNode *)node +{ + [_cellsForLayoutUpdates addObject:node]; + [self setNeedsLayout]; +} + +- (void)nodesDidRelayout:(NSArray *)nodes +{ + ASDisplayNodeAssertMainThread(); + + if (nodes.count == 0) { + return; + } + + const auto uikitIndexPaths = ASArrayByFlatMapping(nodes, ASCellNode *node, [self indexPathForNode:node]); + + [_layoutFacilitator collectionViewWillEditCellsAtIndexPaths:uikitIndexPaths batched:NO]; + + ASCollectionViewInvalidationStyle invalidationStyle = _nextLayoutInvalidationStyle; + for (ASCellNode *node in nodes) { + if (invalidationStyle == ASCollectionViewInvalidationStyleNone) { + // We nodesDidRelayout also while we are in layoutSubviews. This should be no problem as CA will ignore this + // call while be in a layout pass + [self setNeedsLayout]; + invalidationStyle = ASCollectionViewInvalidationStyleWithAnimation; + } + + // If we think we're going to animate, check if this node will prevent it. + if (invalidationStyle == ASCollectionViewInvalidationStyleWithAnimation) { + // TODO: Incorporate `shouldAnimateSizeChanges` into ASEnvironmentState for performance benefit. + static dispatch_once_t onceToken; + static BOOL (^shouldNotAnimateBlock)(ASDisplayNode *); + dispatch_once(&onceToken, ^{ + shouldNotAnimateBlock = ^BOOL(ASDisplayNode * _Nonnull node) { + return (node.shouldAnimateSizeChanges == NO); + }; + }); + if (ASDisplayNodeFindFirstNode(node, shouldNotAnimateBlock) != nil) { + // One single non-animated node causes the whole layout update to be non-animated + invalidationStyle = ASCollectionViewInvalidationStyleWithoutAnimation; + break; + } + } + } + _nextLayoutInvalidationStyle = invalidationStyle; +} + +#pragma mark - _ASDisplayView behavior substitutions +// Need these to drive interfaceState so we know when we are visible, if not nested in another range-managing element. +// Because our superclass is a true UIKit class, we cannot also subclass _ASDisplayView. +- (void)willMoveToWindow:(UIWindow *)newWindow +{ + BOOL visible = (newWindow != nil); + ASDisplayNode *node = self.collectionNode; + if (visible && !node.inHierarchy) { + [node __enterHierarchy]; + } +} + +- (void)didMoveToWindow +{ + BOOL visible = (self.window != nil); + ASDisplayNode *node = self.collectionNode; + BOOL rangeControllerNeedsUpdate = ![node supportsRangeManagedInterfaceState];; + + if (!visible && node.inHierarchy) { + if (rangeControllerNeedsUpdate) { + rangeControllerNeedsUpdate = NO; + // Exit CellNodes first before Collection to match UIKit behaviors (tear down bottom up). + // Although we have not yet cleared the interfaceState's Visible bit (this happens in __exitHierarchy), + // the ASRangeController will get the correct value from -interfaceStateForRangeController:. + [_rangeController updateRanges]; + } + [node __exitHierarchy]; + } + + // Updating the visible node index paths only for not range managed nodes. Range managed nodes will get their + // their update in the layout pass + if (rangeControllerNeedsUpdate) { + [_rangeController updateRanges]; + } + + // When we aren't visible, we will only fetch up to the visible area. Now that we are visible, + // we will fetch visible area + leading screens, so we need to check. + if (visible) { + [self _checkForBatchFetching]; + } +} + +- (void)willMoveToSuperview:(UIView *)newSuperview +{ + if (self.superview == nil && newSuperview != nil) { + _keepalive_node = self.collectionNode; + } +} + +- (void)didMoveToSuperview +{ + if (self.superview == nil) { + _keepalive_node = nil; + } +} + +#pragma mark ASCALayerExtendedDelegate + +/** + * TODO: This code was added when we used @c calculatedSize as the size for + * items (e.g. collectionView:layout:sizeForItemAtIndexPath:) and so it + * was critical that we remeasured all nodes at this time. + * + * The assumption was that cv-bounds-size-change -> constrained-size-change, so + * this was the time when we get new constrained sizes for all items and remeasure + * them. However, the constrained sizes for items can be invalidated for many other + * reasons, hence why we never reuse the old constrained size anymore. + * + * UICollectionView inadvertently triggers a -prepareLayout call to its layout object + * between [super setFrame:] and [self layoutSubviews] during size changes. So we need + * to get in there and re-measure our nodes before that -prepareLayout call. + * We can't wait until -layoutSubviews or the end of -setFrame:. + * + * @see @p testThatNodeCalculatedSizesAreUpdatedBeforeFirstPrepareLayoutAfterRotation + */ +- (void)layer:(CALayer *)layer didChangeBoundsWithOldValue:(CGRect)oldBounds newValue:(CGRect)newBounds +{ + CGSize newSize = newBounds.size; + CGSize lastUsedSize = _lastBoundsSizeUsedForMeasuringNodes; + if (CGSizeEqualToSize(lastUsedSize, newSize)) { + return; + } + if (_hasDataControllerLayoutDelegate || self.collectionViewLayout == nil) { + // Let the layout delegate handle bounds changes if it's available. If no layout, it will init in the new state. + return; + } + + _lastBoundsSizeUsedForMeasuringNodes = newSize; + + // Laying out all nodes is expensive. + // We only need to do this if the bounds changed in the non-scrollable direction. + // If, for example, a vertical flow layout has its height changed due to a status bar + // appearance update, we do not need to relayout all nodes. + // For a more permanent fix to the unsafety mentioned above, see https://github.com/facebook/AsyncDisplayKit/pull/2182 + ASScrollDirection scrollDirection = self.scrollableDirections; + BOOL fixedVertically = (ASScrollDirectionContainsVerticalDirection (scrollDirection) == NO); + BOOL fixedHorizontally = (ASScrollDirectionContainsHorizontalDirection(scrollDirection) == NO); + + BOOL changedInNonScrollingDirection = (fixedHorizontally && newSize.width != lastUsedSize.width) || + (fixedVertically && newSize.height != lastUsedSize.height); + + if (changedInNonScrollingDirection) { + [self relayoutItems]; + } +} + +#pragma mark - UICollectionView dead-end intercepts + +- (void)setPrefetchDataSource:(id)prefetchDataSource +{ + return; +} + +- (void)setPrefetchingEnabled:(BOOL)prefetchingEnabled +{ + return; +} + +#pragma mark - Accessibility overrides + +- (NSArray *)accessibilityElements +{ + [self waitUntilAllUpdatesAreCommitted]; + return [super accessibilityElements]; +} + +#pragma mark - UIGestureRecognizerDelegate Method +- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer +{ + return [self.collectionNode gestureRecognizerShouldBegin:gestureRecognizer]; +} + +- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch +{ + return [self.collectionNode gestureRecognizer:gestureRecognizer shouldReceiveTouch:touch]; +} + +- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceivePress:(UIPress *)press +{ + return [self.collectionNode gestureRecognizer:gestureRecognizer shouldReceivePress:press]; +} + +- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer +{ + return [self.collectionNode gestureRecognizer:gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:otherGestureRecognizer]; +} + +- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRequireFailureOfGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer +{ + return [self.collectionNode gestureRecognizer:gestureRecognizer shouldRequireFailureOfGestureRecognizer:otherGestureRecognizer]; +} + +- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer +{ + return [self.collectionNode gestureRecognizer:gestureRecognizer shouldBeRequiredToFailByGestureRecognizer:otherGestureRecognizer]; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASCollectionViewLayoutFacilitatorProtocol.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASCollectionViewLayoutFacilitatorProtocol.h new file mode 100644 index 0000000..3db2dc9 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASCollectionViewLayoutFacilitatorProtocol.h @@ -0,0 +1,44 @@ +// +// ASCollectionViewLayoutFacilitatorProtocol.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#pragma once +#import + +/** + * This facilitator protocol is intended to help Layout to better + * gel with the CollectionView + */ +@protocol ASCollectionViewLayoutFacilitatorProtocol + +/** + * Inform that the collectionView is editing the cells at a list of indexPaths + * + * @param indexPaths an array of NSIndexPath objects of cells being/will be edited. + * @param isBatched indicates whether the editing operation will be batched by the collectionView + * + * NOTE: when isBatched, used in combination with -collectionViewWillPerformBatchUpdates + */ +- (void)collectionViewWillEditCellsAtIndexPaths:(NSArray *)indexPaths batched:(BOOL)isBatched; + +/** + * Inform that the collectionView is editing the sections at a set of indexes + * + * @param indexes an NSIndexSet of section indexes being/will be edited. + * @param batched indicates whether the editing operation will be batched by the collectionView + * + * NOTE: when batched, used in combination with -collectionViewWillPerformBatchUpdates + */ +- (void)collectionViewWillEditSectionsAtIndexSet:(NSIndexSet *)indexes batched:(BOOL)batched; + +/** + * Informs the delegate that the collectionView is about to call performBatchUpdates + */ +- (void)collectionViewWillPerformBatchUpdates; + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASCollectionViewProtocols.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASCollectionViewProtocols.h new file mode 100644 index 0000000..ec1b8d5 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASCollectionViewProtocols.h @@ -0,0 +1,107 @@ +// +// ASCollectionViewProtocols.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +typedef NS_OPTIONS(unsigned short, ASCellLayoutMode) { + /** + * No options set. If cell layout mode is set to ASCellLayoutModeNone, the default values for + * each flag listed below is used. + */ + ASCellLayoutModeNone = 0, + /** + * If ASCellLayoutModeAlwaysSync is enabled it will cause the ASDataController to wait on the + * background queue, and this ensures that any new / changed cells are in the hierarchy by the + * very next CATransaction / frame draw. + * + * Note: Sync & Async flags force the behavior to be always one or the other, regardless of the + * items. Default: If neither ASCellLayoutModeAlwaysSync or ASCellLayoutModeAlwaysAsync is set, + * default behavior is synchronous when there are 0 or 1 ASCellNodes in the data source, and + * asynchronous when there are 2 or more. + */ + ASCellLayoutModeAlwaysSync = 1 << 1, // Default OFF + ASCellLayoutModeAlwaysAsync = 1 << 2, // Default OFF + ASCellLayoutModeForceIfNeeded = 1 << 3, // Deprecated, default OFF. + ASCellLayoutModeAlwaysPassthroughDelegate = 1 << 4, // Deprecated, default ON. + /** Instead of using performBatchUpdates: prefer using reloadData for changes for collection view */ + ASCellLayoutModeAlwaysReloadData = 1 << 5, // Default OFF + /** If flag is enabled nodes are *not* gonna be range managed. */ + ASCellLayoutModeDisableRangeController = 1 << 6, // Default OFF + ASCellLayoutModeAlwaysLazy = 1 << 7, // Deprecated, default OFF. + /** + * Defines if the node creation should happen serialized and not in parallel within the + * data controller + */ + ASCellLayoutModeSerializeNodeCreation = 1 << 8, // Default OFF + /** + * When set, the performBatchUpdates: API (including animation) is used when handling Section + * Reload operations. This is useful only when ASCellLayoutModeAlwaysReloadData is enabled and + * cell height animations are desired. + */ + ASCellLayoutModeAlwaysBatchUpdateSectionReload = 1 << 9, // Default OFF +}; + +NS_ASSUME_NONNULL_BEGIN + +/** + * This is a subset of UICollectionViewDataSource. + * + * @see ASCollectionDataSource + */ +@protocol ASCommonCollectionDataSource + +@optional + +- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section ASDISPLAYNODE_DEPRECATED_MSG("Implement -collectionNode:numberOfItemsInSection: instead."); + +- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView ASDISPLAYNODE_DEPRECATED_MSG("Implement -numberOfSectionsInCollectionNode: instead."); + +- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED_MSG("Implement - collectionNode:nodeForSupplementaryElementOfKind:atIndexPath: instead."); + +@end + + +/** + * This is a subset of UICollectionViewDelegate. + * + * @see ASCollectionDelegate + */ +@protocol ASCommonCollectionDelegate + +@optional + +- (UICollectionViewTransitionLayout *)collectionView:(UICollectionView *)collectionView transitionLayoutForOldLayout:(UICollectionViewLayout *)fromLayout newLayout:(UICollectionViewLayout *)toLayout; + +- (void)collectionView:(UICollectionView *)collectionView willDisplaySupplementaryView:(UICollectionReusableView *)view forElementKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED_MSG("Implement -collectionNode:willDisplaySupplementaryView:forElementKind:atIndexPath: instead."); +- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingSupplementaryView:(UICollectionReusableView *)view forElementOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED_MSG("Implement -collectionNode:didEndDisplayingSupplementaryView:forElementKind:atIndexPath: instead."); + +- (BOOL)collectionView:(UICollectionView *)collectionView shouldHighlightItemAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED_MSG("Implement collectionNode:shouldHighlightItemAtIndexPath: instead."); +- (void)collectionView:(UICollectionView *)collectionView didHighlightItemAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED_MSG("Implement collectionNode:didHighlightItemAtIndexPath: instead."); +- (void)collectionView:(UICollectionView *)collectionView didUnhighlightItemAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED_MSG("Implement collectionNode:didUnhighlightItemAtIndexPath: instead."); + +- (BOOL)collectionView:(UICollectionView *)collectionView shouldSelectItemAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED_MSG("Implement collectionNode:shouldSelectItemAtIndexPath: instead."); +- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED_MSG("Implement collectionNode:didSelectItemAtIndexPath: instead."); +- (BOOL)collectionView:(UICollectionView *)collectionView shouldDeselectItemAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED_MSG("Implement collectionNode:shouldDeselectItemAtIndexPath: instead."); +- (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED_MSG("Implement collectionNode:didDeselectItemAtIndexPath: instead."); + +- (BOOL)collectionView:(UICollectionView *)collectionView shouldShowMenuForItemAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED_MSG("Implement collectionNode:shouldShowMenuForItemAtIndexPath: instead."); +- (BOOL)collectionView:(UICollectionView *)collectionView canPerformAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(nullable id)sender ASDISPLAYNODE_DEPRECATED_MSG("Implement collectionNode:canPerformAction:forItemAtIndexPath:withSender: instead."); +- (void)collectionView:(UICollectionView *)collectionView performAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(nullable id)sender ASDISPLAYNODE_DEPRECATED_MSG("Implement collectionNode:performAction:forItemAtIndexPath:withSender: instead."); + +- (nullable UIContextMenuConfiguration *)collectionView:(UICollectionView *)collectionView contextMenuConfigurationForItemAtIndexPath:(NSIndexPath *)indexPath point:(CGPoint)point API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(watchos, tvos); + +- (nullable UITargetedPreview *)collectionView:(UICollectionView *)collectionView previewForHighlightingContextMenuWithConfiguration:(UIContextMenuConfiguration *)configuration API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(watchos, tvos); + +- (nullable UITargetedPreview *)collectionView:(UICollectionView *)collectionView previewForDismissingContextMenuWithConfiguration:(UIContextMenuConfiguration *)configuration API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(watchos, tvos); + +- (void)collectionView:(UICollectionView *)collectionView willPerformPreviewActionForMenuWithConfiguration:(UIContextMenuConfiguration *)configuration animator:(id)animator API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(watchos, tvos); +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASCollections.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASCollections.h new file mode 100644 index 0000000..ca3b31c --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASCollections.h @@ -0,0 +1,38 @@ +// +// ASCollections.h +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface NSArray<__covariant ObjectType> (ASCollections) + +/** + * Create an immutable NSArray from a C-array of strong pointers. + * + * Note: The memory for the array you pass in will be zero'd (to prevent ARC from releasing + * the references when the array goes out of scope.) + * + * Can be combined with vector like: + * vector vec; + * vec.push_back(@"foo"); + * vec.push_back(@"bar"); + * NSArray *arr = [NSArray arrayTransferring:vec.data() count:vec.size()] + * ** vec is now { nil, nil } ** + * + * Unfortunately making a convenience method to do this is currently impossible because + * vector can't be converted to vector by the compiler (silly). + * + * See the private __CFArrayCreateTransfer function. + */ ++ (NSArray *)arrayByTransferring:(ObjectType _Nonnull __strong * _Nonnull)pointers + count:(NSUInteger)count NS_RETURNS_RETAINED; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASCollections.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASCollections.mm new file mode 100644 index 0000000..592dee2 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASCollections.mm @@ -0,0 +1,61 @@ +// +// ASCollections.mm +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +/** + * A private allocator that signals to our retain callback to skip the retain. + * It behaves the same as the default allocator, but acts as a signal that we + * are creating a transfer array so we should skip the retain. + */ +static CFAllocatorRef gTransferAllocator; + +static const void *ASTransferRetain(CFAllocatorRef allocator, const void *val) { + if (allocator == gTransferAllocator) { + // Transfer allocator. Ignore retain and pass through. + return val; + } else { + // Other allocator. Retain like normal. + // This happens when they make a mutable copy. + return (&kCFTypeArrayCallBacks)->retain(allocator, val); + } +} + +@implementation NSArray (ASCollections) + ++ (NSArray *)arrayByTransferring:(__strong id *)pointers count:(NSUInteger)count NS_RETURNS_RETAINED +{ + // Custom callbacks that point to our ASTransferRetain callback. + static CFArrayCallBacks callbacks; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + callbacks = kCFTypeArrayCallBacks; + callbacks.retain = ASTransferRetain; + CFAllocatorContext ctx; + CFAllocatorGetContext(NULL, &ctx); + gTransferAllocator = CFAllocatorCreate(NULL, &ctx); + }); + + // NSZeroArray fast path. + if (count == 0) { + return @[]; // Does not actually call +array when optimized. + } + + // NSSingleObjectArray fast path. Retain/release here is worth it. + if (count == 1) { + NSArray *result = [[NSArray alloc] initWithObjects:pointers count:1]; + pointers[0] = nil; + return result; + } + + NSArray *result = (__bridge_transfer NSArray *)CFArrayCreate(gTransferAllocator, (const void **)(void *)pointers, count, &callbacks); + memset(pointers, 0, count * sizeof(id)); + return result; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASConfiguration.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASConfiguration.h new file mode 100644 index 0000000..c529dad --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASConfiguration.h @@ -0,0 +1,58 @@ +// +// ASConfiguration.h +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import + +@protocol ASConfigurationDelegate; + +NS_ASSUME_NONNULL_BEGIN + +static NSInteger const ASConfigurationSchemaCurrentVersion = 1; + +AS_SUBCLASSING_RESTRICTED +@interface ASConfiguration : NSObject + +/** + * Initialize this configuration with the provided dictionary, + * or nil to create an empty configuration. + * + * The schema is located in `schemas/configuration.json`. + */ +- (instancetype)initWithDictionary:(nullable NSDictionary *)dictionary; + +/** + * The delegate for configuration-related events. + * Delegate methods are called from a serial queue. + */ +@property (nonatomic, nullable) id delegate; + +/** + * The experimental features to enable in Texture. + * See ASExperimentalFeatures for functions to convert to/from a string array. + */ +@property (nonatomic) ASExperimentalFeatures experimentalFeatures; + +@end + +/** + * Implement this method in a category to make your + * configuration available to Texture. It will be read + * only once and copied. + * + * NOTE: To specify your configuration at compile-time, you can + * define AS_FIXED_CONFIG_JSON as a C-string of JSON. This method + * will then be implemented to parse that string and generate + * a configuration. + */ +@interface ASConfiguration (UserProvided) ++ (ASConfiguration *)textureConfiguration NS_RETURNS_RETAINED; +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASConfiguration.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASConfiguration.mm new file mode 100644 index 0000000..a201dc2 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASConfiguration.mm @@ -0,0 +1,63 @@ +// +// ASConfiguration.mm +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +/// Not too performance-sensitive here. + +@implementation ASConfiguration + +- (instancetype)initWithDictionary:(NSDictionary *)dictionary +{ + if (self = [super init]) { + if (dictionary != nil) { + const auto featureStrings = ASDynamicCast(dictionary[@"experimental_features"], NSArray); + const auto version = ASDynamicCast(dictionary[@"version"], NSNumber).integerValue; + if (version != ASConfigurationSchemaCurrentVersion) { + NSLog(@"Texture warning: configuration schema is old version (%ld vs %ld)", (long)version, (long)ASConfigurationSchemaCurrentVersion); + } + self.experimentalFeatures = ASExperimentalFeaturesFromArray(featureStrings); + } else { + self.experimentalFeatures = kNilOptions; + } + } + return self; +} + +- (id)copyWithZone:(NSZone *)zone +{ + ASConfiguration *config = [[ASConfiguration alloc] initWithDictionary:nil]; + config.experimentalFeatures = self.experimentalFeatures; + config.delegate = self.delegate; + return config; +} + +@end + +//#define AS_FIXED_CONFIG_JSON "{ \"version\" : 1, \"experimental_features\": [ \"exp_text_node\" ] }" + +#ifdef AS_FIXED_CONFIG_JSON + +@implementation ASConfiguration (UserProvided) + ++ (ASConfiguration *)textureConfiguration NS_RETURNS_RETAINED +{ + NSData *data = [@AS_FIXED_CONFIG_JSON dataUsingEncoding:NSUTF8StringEncoding]; + NSError *error; + NSDictionary *d = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error]; + if (!d) { + NSAssert(NO, @"Error parsing fixed config string '%s': %@", AS_FIXED_CONFIG_JSON, error); + return nil; + } else { + return [[ASConfiguration alloc] initWithDictionary:d]; + } +} + +@end + +#endif // AS_FIXED_CONFIG_JSON diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASConfigurationDelegate.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASConfigurationDelegate.h new file mode 100644 index 0000000..fde3950 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASConfigurationDelegate.h @@ -0,0 +1,37 @@ +// +// ASConfigurationDelegate.h +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * Used to communicate configuration-related events to the client. + */ +@protocol ASConfigurationDelegate + +/** + * Texture performed its first behavior related to the feature(s). + * This can be useful for tracking the impact of the behavior (A/B testing). + */ +- (void)textureDidActivateExperimentalFeatures:(ASExperimentalFeatures)features; + +@optional + +/** + * Texture framework initialized. This method is called synchronously + * on the main thread from ASInitializeFrameworkMainThread if you defined + * AS_INITIALIZE_FRAMEWORK_MANUALLY or otherwise from the default initialization point + * (currently a static constructor, called before main()). + */ +- (void)textureDidInitialize; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASConfigurationInternal.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASConfigurationInternal.h new file mode 100644 index 0000000..fa69496 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASConfigurationInternal.h @@ -0,0 +1,57 @@ +// +// ASConfigurationInternal.h +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +/// Note this has to be public because it's imported by public header ASThread.h =/ +/// It will be private again after exp_unfair_lock ends. + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * Quickly check if an experiment is enabled and notify the delegate + * that it's been activated. + * + * The delegate will be notified asynchronously. + */ +#if DEBUG +#define ASActivateExperimentalFeature(opt) _ASActivateExperimentalFeature(opt) +#else +#define ASActivateExperimentalFeature(opt) ({\ + static BOOL result;\ + static dispatch_once_t onceToken;\ + dispatch_once(&onceToken, ^{ result = _ASActivateExperimentalFeature(opt); });\ + result;\ +}) +#endif + +/** + * Internal function. Use the macro without the underbar. + */ +ASDK_EXTERN BOOL _ASActivateExperimentalFeature(ASExperimentalFeatures option); + +/** + * Notify the configuration delegate that the framework initialized, if needed. + */ +ASDK_EXTERN void ASNotifyInitialized(void); + +AS_SUBCLASSING_RESTRICTED +@interface ASConfigurationManager : NSObject + +/** + * No API for now. + * Just use ASActivateExperimentalFeature to access this efficiently. + */ + +/* Exposed for testing purposes only */ ++ (void)test_resetWithConfiguration:(nullable ASConfiguration *)configuration; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASConfigurationInternal.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASConfigurationInternal.mm new file mode 100644 index 0000000..4bcc1ff --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASConfigurationInternal.mm @@ -0,0 +1,110 @@ +// +// ASConfigurationInternal.mm +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import "ASConfigurationInternal.h" +#import +#import +#import + +static ASConfigurationManager *ASSharedConfigurationManager; +static dispatch_once_t ASSharedConfigurationManagerOnceToken; + +NS_INLINE ASConfigurationManager *ASConfigurationManagerGet() { + dispatch_once(&ASSharedConfigurationManagerOnceToken, ^{ + ASSharedConfigurationManager = [[ASConfigurationManager alloc] init]; + }); + return ASSharedConfigurationManager; +} + +@implementation ASConfigurationManager { + ASConfiguration *_config; + dispatch_queue_t _delegateQueue; + BOOL _frameworkInitialized; + _Atomic(ASExperimentalFeatures) _activatedExperiments; +} + ++ (ASConfiguration *)defaultConfiguration NS_RETURNS_RETAINED +{ + ASConfiguration *config = [[ASConfiguration alloc] init]; + // TODO(wsdwsd0829): Fix #788 before enabling it. + // config.experimentalFeatures = ASExperimentalInterfaceStateCoalescing; + return config; +} + +- (instancetype)init +{ + if (self = [super init]) { + _delegateQueue = dispatch_queue_create("org.TextureGroup.Texture.ConfigNotifyQueue", DISPATCH_QUEUE_SERIAL); + if ([ASConfiguration respondsToSelector:@selector(textureConfiguration)]) { + _config = [[ASConfiguration textureConfiguration] copy]; + } else { + _config = [ASConfigurationManager defaultConfiguration]; + } + } + return self; +} + +- (void)frameworkDidInitialize +{ + ASDisplayNodeAssertMainThread(); + if (_frameworkInitialized) { + ASDisplayNodeFailAssert(@"Framework initialized twice."); + return; + } + _frameworkInitialized = YES; + + const auto delegate = _config.delegate; + if ([delegate respondsToSelector:@selector(textureDidInitialize)]) { + [delegate textureDidInitialize]; + } +} + +- (BOOL)activateExperimentalFeature:(ASExperimentalFeatures)requested +{ + if (_config == nil) { + return NO; + } + + NSAssert(__builtin_popcountl(requested) == 1, @"Cannot activate multiple features at once with this method."); + + // We need to call out, whether it's enabled or not. + // A/B testing requires even "control" users to be activated. + ASExperimentalFeatures enabled = requested & _config.experimentalFeatures; + ASExperimentalFeatures prevTriggered = atomic_fetch_or(&_activatedExperiments, requested); + ASExperimentalFeatures newlyTriggered = requested & ~prevTriggered; + + // Notify delegate if needed. + if (newlyTriggered != 0) { + unowned id del = _config.delegate; + dispatch_async(_delegateQueue, ^{ + [del textureDidActivateExperimentalFeatures:newlyTriggered]; + }); + } + + return (enabled != 0); +} + +// Define this even when !DEBUG, since we may run our tests in release mode. ++ (void)test_resetWithConfiguration:(ASConfiguration *)configuration +{ + ASConfigurationManager *inst = ASConfigurationManagerGet(); + inst->_config = configuration ?: [self defaultConfiguration]; + atomic_store(&inst->_activatedExperiments, 0); +} + +@end + +BOOL _ASActivateExperimentalFeature(ASExperimentalFeatures feature) +{ + return [ASConfigurationManagerGet() activateExperimentalFeature:feature]; +} + +void ASNotifyInitialized() +{ + [ASConfigurationManagerGet() frameworkDidInitialize]; +} diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASContextTransitioning.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASContextTransitioning.h new file mode 100644 index 0000000..aea3501 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASContextTransitioning.h @@ -0,0 +1,72 @@ +// +// ASContextTransitioning.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +@class ASDisplayNode; +@class ASLayout; + +NS_ASSUME_NONNULL_BEGIN + +ASDK_EXTERN NSString * const ASTransitionContextFromLayoutKey; +ASDK_EXTERN NSString * const ASTransitionContextToLayoutKey; + +@protocol ASContextTransitioning + +/** + @abstract Defines if the given transition is animated + */ +- (BOOL)isAnimated; + +/** + * @abstract Retrieve either the "from" or "to" layout + */ +- (nullable ASLayout *)layoutForKey:(NSString *)key; + +/** + * @abstract Retrieve either the "from" or "to" constrainedSize + */ +- (ASSizeRange)constrainedSizeForKey:(NSString *)key; + +/** + * @abstract Retrieve the subnodes from either the "from" or "to" layout + */ +- (NSArray *)subnodesForKey:(NSString *)key; + +/** + * @abstract Subnodes that have been inserted in the layout transition + */ +- (NSArray *)insertedSubnodes; + +/** + * @abstract Subnodes that will be removed in the layout transition + */ +- (NSArray *)removedSubnodes; + +/** + @abstract The frame for the given node before the transition began. + @discussion Returns CGRectNull if the node was not in the hierarchy before the transition. + */ +- (CGRect)initialFrameForNode:(ASDisplayNode *)node; + +/** + @abstract The frame for the given node when the transition completes. + @discussion Returns CGRectNull if the node is no longer in the hierarchy after the transition. + */ +- (CGRect)finalFrameForNode:(ASDisplayNode *)node; + +/** + @abstract Invoke this method when the transition is completed in `animateLayoutTransition:` + @discussion Passing NO to `didComplete` will set the original layout as the new layout. + */ +- (void)completeTransition:(BOOL)didComplete; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASControlNode+Subclasses.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASControlNode+Subclasses.h new file mode 100644 index 0000000..2e9cdb2 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASControlNode+Subclasses.h @@ -0,0 +1,68 @@ +// +// ASControlNode+Subclasses.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * The subclass header _ASControlNode+Subclasses_ defines methods to be + * overridden by custom nodes that subclass ASControlNode. + * + * These methods should never be called directly by other classes. + */ + +@interface ASControlNode (Subclassing) + +/** + @abstract Sends action messages for the given control events. + @param controlEvents A bitmask whose set flags specify the control events for which action messages are sent. See "Control Events" in ASControlNode.h for bitmask constants. + @param touchEvent An event object encapsulating the information specific to the user event. + @discussion ASControlNode implements this method to send all action messages associated with controlEvents. The list of targets is constructed from prior invocations of addTarget:action:forControlEvents:. + */ +- (void)sendActionsForControlEvents:(ASControlNodeEvent)controlEvents withEvent:(nullable UIEvent *)touchEvent; + +/** + @abstract Sent to the control when tracking begins. + @param touch The touch on the receiving control. + @param touchEvent An event object encapsulating the information specific to the user event. + @result YES if the receiver should respond continuously (respond when touch is dragged); NO otherwise. + */ +- (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(nullable UIEvent *)touchEvent; + +/** + @abstract Sent continuously to the control as it tracks a touch within the control's bounds. + @param touch The touch on the receiving control. + @param touchEvent An event object encapsulating the information specific to the user event. + @result YES if touch tracking should continue; NO otherwise. + */ +- (BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(nullable UIEvent *)touchEvent; + +/** + @abstract Sent to the control when tracking should be cancelled. + @param touchEvent An event object encapsulating the information specific to the user event. This parameter may be nil, indicating that the cancelation was caused by something other than an event, such as the display node being removed from its supernode. + */ +- (void)cancelTrackingWithEvent:(nullable UIEvent *)touchEvent; + +/** + @abstract Sent to the control when the last touch completely ends, telling it to stop tracking. + @param touch The touch that ended. + @param touchEvent An event object encapsulating the information specific to the user event. + */ +- (void)endTrackingWithTouch:(nullable UITouch *)touch withEvent:(nullable UIEvent *)touchEvent; + +/** + @abstract Settable version of highlighted property. + */ +@property (getter=isHighlighted) BOOL highlighted; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASControlNode.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASControlNode.h new file mode 100644 index 0000000..0918fdb --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASControlNode.h @@ -0,0 +1,149 @@ +// +// ASControlNode.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#pragma once + +NS_ASSUME_NONNULL_BEGIN + +/** + @abstract Kinds of events possible for control nodes. + @discussion These events are identical to their UIControl counterparts. + */ +typedef NS_OPTIONS(NSUInteger, ASControlNodeEvent) +{ + /** A touch-down event in the control node. */ + ASControlNodeEventTouchDown = 1 << 0, + /** A repeated touch-down event in the control node; for this event the value of the UITouch tapCount method is greater than one. */ + ASControlNodeEventTouchDownRepeat = 1 << 1, + /** An event where a finger is dragged inside the bounds of the control node. */ + ASControlNodeEventTouchDragInside = 1 << 2, + /** An event where a finger is dragged just outside the bounds of the control. */ + ASControlNodeEventTouchDragOutside = 1 << 3, + /** A touch-up event in the control node where the finger is inside the bounds of the node. */ + ASControlNodeEventTouchUpInside = 1 << 4, + /** A touch-up event in the control node where the finger is outside the bounds of the node. */ + ASControlNodeEventTouchUpOutside = 1 << 5, + /** A system event canceling the current touches for the control node. */ + ASControlNodeEventTouchCancel = 1 << 6, + /** A system event triggered when controls like switches, slides, etc change state. */ + ASControlNodeEventValueChanged = 1 << 12, + /** A system event when the Play/Pause button on the Apple TV remote is pressed. */ + ASControlNodeEventPrimaryActionTriggered = 1 << 13, + + /** All events, including system events. */ + ASControlNodeEventAllEvents = 0xFFFFFFFF +}; + +/** + * Compatibility aliases for @c ASControlState enum. + * We previously provided our own enum, but when it was imported + * into Swift, the @c normal (0) option disappeared. + * + * Apple's UIControlState enum gets special treatment here, and + * UIControlStateNormal is available in Swift. + */ +typedef UIControlState ASControlState ASDISPLAYNODE_DEPRECATED_MSG("Use UIControlState."); +static UIControlState const ASControlStateNormal ASDISPLAYNODE_DEPRECATED_MSG("Use UIControlStateNormal.") = UIControlStateNormal; +static UIControlState const ASControlStateDisabled ASDISPLAYNODE_DEPRECATED_MSG("Use UIControlStateDisabled.") = UIControlStateDisabled; +static UIControlState const ASControlStateHighlighted ASDISPLAYNODE_DEPRECATED_MSG("Use UIControlStateHighlighted.") = UIControlStateHighlighted; +static UIControlState const ASControlStateSelected ASDISPLAYNODE_DEPRECATED_MSG("Use UIControlStateSelected.") = UIControlStateSelected; + +/** + @abstract ASControlNode is the base class for control nodes (such as buttons), or nodes that track touches to invoke targets with action messages. + @discussion ASControlNode cannot be used directly. It instead defines the common interface and behavior structure for all its subclasses. Subclasses should import "ASControlNode+Subclasses.h" for information on methods intended to be overriden. + */ +@interface ASControlNode : ASDisplayNode + +#pragma mark - Control State + +/** + @abstract Indicates whether or not the receiver is enabled. + @discussion Specify YES to make the control enabled; otherwise, specify NO to make it disabled. The default value is YES. If the enabled state is NO, the control ignores touch events and subclasses may draw differently. + */ +@property (getter=isEnabled) BOOL enabled; + +/** + @abstract Indicates whether or not the receiver is highlighted. + @discussion This is set automatically when the there is a touch inside the control and removed on exit or touch up. This is different from touchInside in that it includes an area around the control, rather than just for touches inside the control. + */ +@property (getter=isHighlighted) BOOL highlighted; + +/** + @abstract Indicates whether or not the receiver is highlighted. + @discussion This is set automatically when the receiver is tapped. + */ +@property (getter=isSelected) BOOL selected; + +#pragma mark - Tracking Touches +/** + @abstract Indicates whether or not the receiver is currently tracking touches related to an event. + @discussion YES if the receiver is tracking touches; NO otherwise. + */ +@property (readonly, getter=isTracking) BOOL tracking; + +/** + @abstract Indicates whether or not a touch is inside the bounds of the receiver. + @discussion YES if a touch is inside the receiver's bounds; NO otherwise. + */ +@property (readonly, getter=isTouchInside) BOOL touchInside; + +#pragma mark - Action Messages +/** + @abstract Adds a target-action pair for a particular event (or events). + @param target The object to which the action message is sent. If this is nil, the responder chain is searched for an object willing to respond to the action message. target is not retained. + @param action A selector identifying an action message. May optionally include the sender and the event as parameters, in that order. May not be NULL. + @param controlEvents A bitmask specifying the control events for which the action message is sent. May not be 0. See "Control Events" for bitmask constants. + @discussion You may call this method multiple times, and you may specify multiple target-action pairs for a particular event. Targets are held weakly. + */ +- (void)addTarget:(nullable id)target action:(SEL)action forControlEvents:(ASControlNodeEvent)controlEvents; + +/** + @abstract Returns the actions that are associated with a target and a particular control event. + @param target The target object. May not be nil. + @param controlEvent A single constant of type ASControlNodeEvent that specifies a particular user action on the control; for a list of these constants, see "Control Events". May not be 0 or ASControlNodeEventAllEvents. + @result An array of selector names as NSString objects, or nil if there are no action selectors associated with controlEvent. + */ +- (nullable NSArray *)actionsForTarget:(id)target forControlEvent:(ASControlNodeEvent)controlEvent AS_WARN_UNUSED_RESULT; + +/** + @abstract Returns all target objects associated with the receiver. + @result A set of all targets for the receiver. The set may include NSNull to indicate at least one nil target (meaning, the responder chain is searched for a target.) + */ +- (NSSet *)allTargets AS_WARN_UNUSED_RESULT; + +/** + @abstract Removes a target-action pair for a particular event. + @param target The target object. Pass nil to remove all targets paired with action and the specified control events. + @param action A selector identifying an action message. Pass NULL to remove all action messages paired with target. + @param controlEvents A bitmask specifying the control events associated with target and action. See "Control Events" for bitmask constants. May not be 0. + */ +- (void)removeTarget:(nullable id)target action:(nullable SEL)action forControlEvents:(ASControlNodeEvent)controlEvents; + +/** + @abstract Sends the actions for the control events for a particular event. + @param controlEvents A bitmask specifying the control events for which to send actions. See "Control Events" for bitmask constants. May not be 0. + @param event The event which triggered these control actions. May be nil. + */ +- (void)sendActionsForControlEvents:(ASControlNodeEvent)controlEvents withEvent:(nullable UIEvent *)event; +@end + +#if TARGET_OS_TV +@interface ASControlNode (tvOS) + +/** + @abstract How the node looks when it isn't focused. Exposed here so that subclasses can override. + */ +- (void)setDefaultFocusAppearance; + +@end +#endif + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASControlNode.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASControlNode.mm new file mode 100644 index 0000000..4bc0ae6 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASControlNode.mm @@ -0,0 +1,516 @@ +// +// ASControlNode.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import +#import +#import +#import +#import +#import +#if TARGET_OS_TV +#import +#endif + +// UIControl allows dragging some distance outside of the control itself during +// tracking. This value depends on the device idiom (25 or 70 points), so +// so replicate that effect with the same values here for our own controls. +#define kASControlNodeExpandedInset (([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) ? -25.0f : -70.0f) + +// Initial capacities for dispatch tables. +#define kASControlNodeEventDispatchTableInitialCapacity 4 + +@interface ASControlNode () +{ +@private + // Control Attributes + BOOL _enabled; + BOOL _highlighted; + + // Tracking + BOOL _tracking; + BOOL _touchInside; + + // Target action pairs stored in an array for each event type + // ASControlEvent -> [ASTargetAction0, ASTargetAction1] + NSMutableDictionary, NSMutableArray *> *_controlEventDispatchTable; +} + +// Read-write overrides. +@property (getter=isTracking) BOOL tracking; +@property (getter=isTouchInside) BOOL touchInside; + +/** + @abstract Returns a key to be used in _controlEventDispatchTable that identifies the control event. + @param controlEvent A control event. + @result A key for use in _controlEventDispatchTable. + */ +id _ASControlNodeEventKeyForControlEvent(ASControlNodeEvent controlEvent); + +/** + @abstract Enumerates the ASControlNode events included mask, invoking the block for each event. + @param mask An ASControlNodeEvent mask. + @param block The block to be invoked for each ASControlNodeEvent included in mask. + */ +void _ASEnumerateControlEventsIncludedInMaskWithBlock(ASControlNodeEvent mask, void (^block)(ASControlNodeEvent anEvent)); + +/** + @abstract Returns the expanded bounds used to determine if a touch is considered 'inside' during tracking. + @param controlNode A control node. + @result The expanded bounds of the node. + */ +CGRect _ASControlNodeGetExpandedBounds(ASControlNode *controlNode); + + +@end + +@implementation ASControlNode +{ + ASImageNode *_debugHighlightOverlay; +} + +#pragma mark - Lifecycle + +- (instancetype)init +{ + if (!(self = [super init])) + return nil; + + _enabled = YES; + + // As we have no targets yet, we start off with user interaction off. When a target is added, it'll get turned back on. + self.userInteractionEnabled = NO; + + return self; +} + +#if TARGET_OS_TV +- (void)didLoad +{ + [super didLoad]; + + // On tvOS all controls, such as buttons, interact with the focus system even if they don't have a target set on them. + // Here we add our own internal tap gesture to handle this behaviour. + self.userInteractionEnabled = YES; + UITapGestureRecognizer *tapGestureRec = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(_pressDown)]; + tapGestureRec.allowedPressTypes = @[@(UIPressTypeSelect)]; + [self.view addGestureRecognizer:tapGestureRec]; +} +#endif + +- (void)setUserInteractionEnabled:(BOOL)userInteractionEnabled +{ + [super setUserInteractionEnabled:userInteractionEnabled]; + self.isAccessibilityElement = userInteractionEnabled; +} + +- (void)__exitHierarchy +{ + [super __exitHierarchy]; + + // If a control node is exit the hierarchy and is tracking we have to cancel it + if (self.tracking) { + [self _cancelTrackingWithEvent:nil]; + } +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wobjc-missing-super-calls" + +#pragma mark - ASDisplayNode Overrides + +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event +{ + // If we're not interested in touches, we have nothing to do. + if (!self.enabled) { + return; + } + + // Check if the tracking should start + UITouch *theTouch = [touches anyObject]; + if (![self beginTrackingWithTouch:theTouch withEvent:event]) { + return; + } + + // If we get more than one touch down on us, cancel. + // Additionally, if we're already tracking a touch, a second touch beginning is cause for cancellation. + if (touches.count > 1 || self.tracking) { + [self _cancelTrackingWithEvent:event]; + } else { + // Otherwise, begin tracking. + self.tracking = YES; + + // No need to check bounds on touchesBegan as we wouldn't get the call if it wasn't in our bounds. + self.touchInside = YES; + self.highlighted = YES; + + // Send the appropriate touch-down control event depending on how many times we've been tapped. + ASControlNodeEvent controlEventMask = (theTouch.tapCount == 1) ? ASControlNodeEventTouchDown : ASControlNodeEventTouchDownRepeat; + [self sendActionsForControlEvents:controlEventMask withEvent:event]; + } +} + +- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event +{ + // If we're not interested in touches, we have nothing to do. + if (!self.enabled) { + return; + } + + NSParameterAssert(touches.count == 1); + UITouch *theTouch = [touches anyObject]; + + // Check if tracking should continue + if (!self.tracking || ![self continueTrackingWithTouch:theTouch withEvent:event]) { + self.tracking = NO; + return; + } + + CGPoint touchLocation = [theTouch locationInView:self.view]; + + // Update our touchInside state. + BOOL dragIsInsideBounds = [self pointInside:touchLocation withEvent:nil]; + + // Update our highlighted state. + CGRect expandedBounds = _ASControlNodeGetExpandedBounds(self); + BOOL dragIsInsideExpandedBounds = CGRectContainsPoint(expandedBounds, touchLocation); + self.touchInside = dragIsInsideExpandedBounds; + self.highlighted = dragIsInsideExpandedBounds; + + [self sendActionsForControlEvents:(dragIsInsideBounds ? ASControlNodeEventTouchDragInside : ASControlNodeEventTouchDragOutside) + withEvent:event]; +} + +- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event +{ + // If we're not interested in touches, we have nothing to do. + if (!self.enabled) { + return; + } + + // Note that we've cancelled tracking. + [self _cancelTrackingWithEvent:event]; +} + +- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event +{ + // If we're not interested in touches, we have nothing to do. + if (!self.enabled) { + return; + } + + // On iPhone 6s, iOS 9.2 (and maybe other versions) sometimes calls -touchesEnded:withEvent: + // twice on the view for one call to -touchesBegan:withEvent:. On ASControlNode, it used to + // trigger an action twice unintentionally. Now, we ignore that event if we're not in a tracking + // state in order to have a correct behavior. + // It might be related to that issue: http://www.openradar.me/22910171 + if (!self.tracking) { + return; + } + + NSParameterAssert([touches count] == 1); + UITouch *theTouch = [touches anyObject]; + CGPoint touchLocation = [theTouch locationInView:self.view]; + + // Update state. + self.tracking = NO; + self.touchInside = NO; + self.highlighted = NO; + + // Note that we've ended tracking. + [self endTrackingWithTouch:theTouch withEvent:event]; + + // Send the appropriate touch-up control event. + CGRect expandedBounds = _ASControlNodeGetExpandedBounds(self); + BOOL touchUpIsInsideExpandedBounds = CGRectContainsPoint(expandedBounds, touchLocation); + + [self sendActionsForControlEvents:(touchUpIsInsideExpandedBounds ? ASControlNodeEventTouchUpInside : ASControlNodeEventTouchUpOutside) + withEvent:event]; +} + +- (void)_cancelTrackingWithEvent:(UIEvent *)event +{ + // We're no longer tracking and there is no touch to be inside. + self.tracking = NO; + self.touchInside = NO; + self.highlighted = NO; + + // Send the cancel event. + [self sendActionsForControlEvents:ASControlNodeEventTouchCancel withEvent:event]; +} + +#pragma clang diagnostic pop + +- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event +{ + ASDisplayNodeAssertMainThread(); + + // If not enabled we should not care about receving touches + if (! self.enabled) { + return nil; + } + + return [super hitTest:point withEvent:event]; +} + +- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer +{ + // If we're interested in touches, this is a tap (the only gesture we care about) and passed -hitTest for us, then no, you may not begin. Sir. + if (self.enabled && [gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]] && gestureRecognizer.view != self.view) { + UITapGestureRecognizer *tapRecognizer = (UITapGestureRecognizer *)gestureRecognizer; + // Allow double-tap gestures + return tapRecognizer.numberOfTapsRequired != 1; + } + + // Otherwise, go ahead. :] + return YES; +} + +- (BOOL)supportsLayerBacking +{ + return super.supportsLayerBacking && !self.userInteractionEnabled; +} + +#pragma mark - Action Messages + +- (void)addTarget:(id)target action:(SEL)action forControlEvents:(ASControlNodeEvent)controlEventMask +{ + NSParameterAssert(action); + NSParameterAssert(controlEventMask != 0); + + // ASControlNode cannot be layer backed if adding a target + ASDisplayNodeAssert(!self.isLayerBacked, @"ASControlNode is layer backed, will never be able to call target in target:action: pair."); + + ASLockScopeSelf(); + + if (!_controlEventDispatchTable) { + _controlEventDispatchTable = [[NSMutableDictionary alloc] initWithCapacity:kASControlNodeEventDispatchTableInitialCapacity]; // enough to handle common types without re-hashing the dictionary when adding entries. + + // only show tap-able areas for views with 1 or more addTarget:action: pairs + if ([ASControlNode enableHitTestDebug] && _debugHighlightOverlay == nil) { + // do not use ASPerformBlockOnMainThread here, if it performs the block synchronously it will continue + // holding the lock while calling addSubnode. + dispatch_async(dispatch_get_main_queue(), ^{ + // add a highlight overlay node with area of ASControlNode + UIEdgeInsets + self.clipsToBounds = NO; + self->_debugHighlightOverlay = [[ASImageNode alloc] init]; + self->_debugHighlightOverlay.zPosition = 1000; // ensure we're over the top of any siblings + self->_debugHighlightOverlay.layerBacked = YES; + [self addSubnode:self->_debugHighlightOverlay]; + }); + } + } + + // Create new target action pair + ASControlTargetAction *targetAction = [[ASControlTargetAction alloc] init]; + targetAction.action = action; + targetAction.target = target; + + // Enumerate the events in the mask, adding the target-action pair for each control event included in controlEventMask + _ASEnumerateControlEventsIncludedInMaskWithBlock(controlEventMask, ^ + (ASControlNodeEvent controlEvent) + { + // Do we already have an event table for this control event? + id eventKey = _ASControlNodeEventKeyForControlEvent(controlEvent); + NSMutableArray *eventTargetActionArray = self->_controlEventDispatchTable[eventKey]; + + if (!eventTargetActionArray) { + eventTargetActionArray = [[NSMutableArray alloc] init]; + } + + // Remove any prior target-action pair for this event, as UIKit does. + [eventTargetActionArray removeObject:targetAction]; + + // Register the new target-action as the last one to be sent. + [eventTargetActionArray addObject:targetAction]; + + if (eventKey) { + [self->_controlEventDispatchTable setObject:eventTargetActionArray forKey:eventKey]; + } + }); + + self.userInteractionEnabled = YES; +} + +- (NSArray *)actionsForTarget:(id)target forControlEvent:(ASControlNodeEvent)controlEvent +{ + NSParameterAssert(target); + NSParameterAssert(controlEvent != 0 && controlEvent != ASControlNodeEventAllEvents); + + ASLockScopeSelf(); + + // Grab the event target action array for this event. + NSMutableArray *eventTargetActionArray = _controlEventDispatchTable[_ASControlNodeEventKeyForControlEvent(controlEvent)]; + if (!eventTargetActionArray) { + return nil; + } + + NSMutableArray *actions = [[NSMutableArray alloc] init]; + + // Collect all actions for this target. + for (ASControlTargetAction *targetAction in eventTargetActionArray) { + if ((target == nil && targetAction.createdWithNoTarget) || (target != nil && target == targetAction.target)) { + [actions addObject:NSStringFromSelector(targetAction.action)]; + } + } + + return actions; +} + +- (NSSet *)allTargets +{ + ASLockScopeSelf(); + + NSMutableSet *targets = [[NSMutableSet alloc] init]; + + // Look at each event... + for (NSMutableArray *eventTargetActionArray in [_controlEventDispatchTable objectEnumerator]) { + // and each event's targets... + for (ASControlTargetAction *targetAction in eventTargetActionArray) { + [targets addObject:targetAction.target]; + } + } + + return targets; +} + +- (void)removeTarget:(id)target action:(SEL)action forControlEvents:(ASControlNodeEvent)controlEventMask +{ + NSParameterAssert(controlEventMask != 0); + + ASLockScopeSelf(); + + // Enumerate the events in the mask, removing the target-action pair for each control event included in controlEventMask. + _ASEnumerateControlEventsIncludedInMaskWithBlock(controlEventMask, ^ + (ASControlNodeEvent controlEvent) + { + // Grab the dispatch table for this event (if we have it). + id eventKey = _ASControlNodeEventKeyForControlEvent(controlEvent); + NSMutableArray *eventTargetActionArray = self->_controlEventDispatchTable[eventKey]; + if (!eventTargetActionArray) { + return; + } + + NSPredicate *filterPredicate = [NSPredicate predicateWithBlock:^BOOL(ASControlTargetAction *_Nullable evaluatedObject, NSDictionary * _Nullable bindings) { + if (!target || evaluatedObject.target == target) { + if (!action) { + return NO; + } else if (evaluatedObject.action == action) { + return NO; + } + } + + return YES; + }]; + [eventTargetActionArray filterUsingPredicate:filterPredicate]; + + if (eventTargetActionArray.count == 0) { + // If there are no targets for this event anymore, remove it. + [self->_controlEventDispatchTable removeObjectForKey:eventKey]; + } + }); +} + +#pragma mark - + +- (void)sendActionsForControlEvents:(ASControlNodeEvent)controlEvents withEvent:(UIEvent *)event +{ + ASDisplayNodeAssertMainThread(); //We access self.view below, it's not safe to call this off of main. + NSParameterAssert(controlEvents != 0); + + NSMutableArray *resolvedEventTargetActionArray = [[NSMutableArray alloc] init]; + + { + ASLockScopeSelf(); + + // Enumerate the events in the mask, invoking the target-action pairs for each. + _ASEnumerateControlEventsIncludedInMaskWithBlock(controlEvents, ^ + (ASControlNodeEvent controlEvent) + { + // Iterate on each target action pair + for (ASControlTargetAction *targetAction in self->_controlEventDispatchTable[_ASControlNodeEventKeyForControlEvent(controlEvent)]) { + ASControlTargetAction *resolvedTargetAction = [[ASControlTargetAction alloc] init]; + resolvedTargetAction.action = targetAction.action; + resolvedTargetAction.target = targetAction.target; + + // NSNull means that a nil target was set, so start at self and travel the responder chain + if (!resolvedTargetAction.target && targetAction.createdWithNoTarget) { + // if the target cannot perform the action, travel the responder chain to try to find something that does + resolvedTargetAction.target = [self.view targetForAction:resolvedTargetAction.action withSender:self]; + } + + if (resolvedTargetAction.target) { + [resolvedEventTargetActionArray addObject:resolvedTargetAction]; + } + } + }); + } + + //We don't want to hold the lock while calling out, we could potentially walk up the ownership tree causing a deadlock. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" + for (ASControlTargetAction *targetAction in resolvedEventTargetActionArray) { + [targetAction.target performSelector:targetAction.action withObject:self withObject:event]; + } +#pragma clang diagnostic pop +} + +#pragma mark - Convenience + +id _ASControlNodeEventKeyForControlEvent(ASControlNodeEvent controlEvent) +{ + return @(controlEvent); +} + +void _ASEnumerateControlEventsIncludedInMaskWithBlock(ASControlNodeEvent mask, void (^block)(ASControlNodeEvent anEvent)) +{ + if (block == nil) { + return; + } + // Start with our first event (touch down) and work our way up to the last event (PrimaryActionTriggered) + for (ASControlNodeEvent thisEvent = ASControlNodeEventTouchDown; thisEvent <= ASControlNodeEventPrimaryActionTriggered; thisEvent <<= 1) { + // If it's included in the mask, invoke the block. + if ((mask & thisEvent) == thisEvent) + block(thisEvent); + } +} + +CGRect _ASControlNodeGetExpandedBounds(ASControlNode *controlNode) { + return CGRectInset(UIEdgeInsetsInsetRect(controlNode.view.bounds, controlNode.hitTestSlop), kASControlNodeExpandedInset, kASControlNodeExpandedInset); +} + +#pragma mark - For Subclasses + +- (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)touchEvent +{ + return YES; +} + +- (BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)touchEvent +{ + return YES; +} + +- (void)cancelTrackingWithEvent:(UIEvent *)touchEvent +{ + // Subclass hook +} + +- (void)endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)touchEvent +{ + // Subclass hook +} + +#pragma mark - Debug +- (ASImageNode *)debugHighlightOverlay +{ + return _debugHighlightOverlay; +} +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDKViewController.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDKViewController.h new file mode 100644 index 0000000..035b09e --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDKViewController.h @@ -0,0 +1,101 @@ +// +// ASDKViewController.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import + +@class ASTraitCollection; + +NS_ASSUME_NONNULL_BEGIN + +typedef ASTraitCollection * _Nonnull (^ASDisplayTraitsForTraitCollectionBlock)(UITraitCollection *traitCollection); +typedef ASTraitCollection * _Nonnull (^ASDisplayTraitsForTraitWindowSizeBlock)(CGSize windowSize); + +/** + * ASDKViewController allows you to have a completely node backed hierarchy. It automatically + * handles @c ASVisibilityDepth, automatic range mode and propogating @c ASDisplayTraits to contained nodes. + * + * You can opt-out of node backed hierarchy and use it like a normal UIViewController. + * More importantly, you can use it as a base class for all of your view controllers among which some use a node hierarchy and some don't. + * See examples/ASDKgram project for actual implementation. + */ +@interface ASDKViewController<__covariant DisplayNodeType : ASDisplayNode *> : UIViewController + +/** + * ASDKViewController initializer. + * + * @param node An ASDisplayNode which will provide the root view (self.view) + * @return An ASDKViewController instance whose root view will be backed by the provided ASDisplayNode. + * + * @see ASVisibilityDepth + */ +- (instancetype)initWithNode:(DisplayNodeType)node NS_DESIGNATED_INITIALIZER; + +/** +* ASDKViewController initializer. Useful for interoperability with normal UIViewControllers. +* +* @return An ASDKViewController instance with a nil node whose root view will be backed by a standard UIView as with a normal UIViewController. +* +* @see ASVisibilityDepth +*/ +- (instancetype)init NS_DESIGNATED_INITIALIZER; + +NS_ASSUME_NONNULL_END + +/** + * @return node Returns the ASDisplayNode which provides the backing view to the view controller. + */ +@property (nonatomic, readonly, null_unspecified) DisplayNodeType node; + +NS_ASSUME_NONNULL_BEGIN + +/** + * Set this block to customize the ASDisplayTraits returned when the VC transitions to the given traitCollection. + */ +@property (nonatomic, copy) ASDisplayTraitsForTraitCollectionBlock overrideDisplayTraitsWithTraitCollection; + +/** + * Set this block to customize the ASDisplayTraits returned when the VC transitions to the given window size. + */ +@property (nonatomic, copy) ASDisplayTraitsForTraitWindowSizeBlock overrideDisplayTraitsWithWindowSize ASDISPLAYNODE_DEPRECATED_MSG("This property is actually never accessed inside the framework"); + +/** + * @abstract Passthrough property to the the .interfaceState of the node. + * @return The current ASInterfaceState of the node, indicating whether it is visible and other situational properties. + * @see ASInterfaceState + */ +@property (nonatomic, readonly) ASInterfaceState interfaceState; + + +// AsyncDisplayKit 2.0 BETA: This property is still being tested, but it allows +// blocking as a view controller becomes visible to ensure no placeholders flash onscreen. +// Refer to examples/SynchronousConcurrency, AsyncViewController.m +@property (nonatomic) BOOL neverShowPlaceholders; + +/* Custom container UIViewController subclasses can use this property to add to the overlay + that UIViewController calculates for the safeAreaInsets for contained view controllers. + */ +@property(nonatomic) UIEdgeInsets additionalSafeAreaInsets; + +@end + +@interface ASDKViewController (ASRangeControllerUpdateRangeProtocol) + +/** + * Automatically adjust range mode based on view events. If you set this to YES, the view controller or its node + * must conform to the ASRangeControllerUpdateRangeProtocol. + * + * Default value is YES *if* node or view controller conform to ASRangeControllerUpdateRangeProtocol otherwise it is NO. + */ +@property (nonatomic) BOOL automaticallyAdjustRangeModeBasedOnViewEvents; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDKViewController.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDKViewController.mm new file mode 100644 index 0000000..da4a19e --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDKViewController.mm @@ -0,0 +1,358 @@ +// +// ASDKViewController.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import +#import +#import +#import + +@implementation ASDKViewController +{ + BOOL _ensureDisplayed; + BOOL _automaticallyAdjustRangeModeBasedOnViewEvents; + BOOL _parentManagesVisibilityDepth; + NSInteger _visibilityDepth; + BOOL _selfConformsToRangeModeProtocol; + BOOL _nodeConformsToRangeModeProtocol; + UIEdgeInsets _fallbackAdditionalSafeAreaInsets; +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wobjc-designated-initializers" + +- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil +{ + if (!(self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) { + return nil; + } + + [self _initializeInstance]; + + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + if (!(self = [super initWithCoder:aDecoder])) { + return nil; + } + + [self _initializeInstance]; + + return self; +} + +#pragma clang diagnostic pop + +- (instancetype)initWithNode:(ASDisplayNode *)node +{ + if (!(self = [super initWithNibName:nil bundle:nil])) { + return nil; + } + + _node = node; + [self _initializeInstance]; + + return self; +} + +- (instancetype)init +{ + if (!(self = [super initWithNibName:nil bundle:nil])) { + return nil; + } + + [self _initializeInstance]; + + return self; +} + +- (void)_initializeInstance +{ + if (_node == nil) { + return; + } + + _node.viewControllerRoot = YES; + + _selfConformsToRangeModeProtocol = [self conformsToProtocol:@protocol(ASRangeControllerUpdateRangeProtocol)]; + _nodeConformsToRangeModeProtocol = [_node conformsToProtocol:@protocol(ASRangeControllerUpdateRangeProtocol)]; + _automaticallyAdjustRangeModeBasedOnViewEvents = _selfConformsToRangeModeProtocol || _nodeConformsToRangeModeProtocol; + + _fallbackAdditionalSafeAreaInsets = UIEdgeInsetsZero; + + // In case the node will get loaded + if (_node.nodeLoaded) { + // Node already loaded the view + [self view]; + } else { + // If the node didn't load yet add ourselves as on did load observer to load the view in case the node gets loaded + // before the view controller + __weak __typeof__(self) weakSelf = self; + [_node onDidLoad:^(__kindof ASDisplayNode * _Nonnull node) { + if ([weakSelf isViewLoaded] == NO) { + [weakSelf view]; + } + }]; + } +} + +- (void)loadView +{ + // Apple applies a frame and autoresizing masks we need. Allocating a view is not + // nearly as expensive as adding and removing it from a hierarchy, and fortunately + // we can avoid that here. Enabling layerBacking on a single node in the hierarchy + // will have a greater performance benefit than the impact of this transient view. + [super loadView]; + + if (_node == nil) { + return; + } + + ASDisplayNodeAssertTrue(!_node.layerBacked); + + UIView *view = self.view; + CGRect frame = view.frame; + UIViewAutoresizing autoresizingMask = view.autoresizingMask; + + // We have what we need, so now create and assign the view we actually want. + view = _node.view; + _node.frame = frame; + _node.autoresizingMask = autoresizingMask; + self.view = view; + + // ensure that self.node has a valid trait collection before a subclass's implementation of viewDidLoad. + // Any subnodes added in viewDidLoad will then inherit the proper environment. + ASPrimitiveTraitCollection traitCollection = [self primitiveTraitCollectionForUITraitCollection:self.traitCollection]; + [self propagateNewTraitCollection:traitCollection]; +} + +- (void)viewWillLayoutSubviews +{ + [super viewWillLayoutSubviews]; + + // Before layout, make sure that our trait collection containerSize actually matches the size of our bounds. + // If not, we need to update the traits and propagate them. + + CGSize boundsSize = self.view.bounds.size; + if (CGSizeEqualToSize(self.node.primitiveTraitCollection.containerSize, boundsSize) == NO) { + [UIView performWithoutAnimation:^{ + ASPrimitiveTraitCollection traitCollection = [self primitiveTraitCollectionForUITraitCollection:self.traitCollection]; + traitCollection.containerSize = boundsSize; + + // this method will call measure + [self propagateNewTraitCollection:traitCollection]; + }]; + } else { + // Call layoutThatFits: to let the node prepare for a layout that will happen shortly in the layout pass of the view. + // If the node's constrained size didn't change between the last layout pass it's a no-op + [_node layoutThatFits:[self nodeConstrainedSize]]; + } +} + +- (void)viewDidLayoutSubviews +{ + if (_ensureDisplayed && self.neverShowPlaceholders) { + _ensureDisplayed = NO; + [_node recursivelyEnsureDisplaySynchronously:YES]; + } + [super viewDidLayoutSubviews]; + + if (!AS_AT_LEAST_IOS11) { + [self _updateNodeFallbackSafeArea]; + } +} + +- (void)_updateNodeFallbackSafeArea +{ + UIEdgeInsets safeArea = UIEdgeInsetsMake(self.topLayoutGuide.length, 0, self.bottomLayoutGuide.length, 0); + UIEdgeInsets additionalInsets = self.additionalSafeAreaInsets; + + safeArea = ASConcatInsets(safeArea, additionalInsets); + + _node.fallbackSafeAreaInsets = safeArea; +} + +ASVisibilityDidMoveToParentViewController; + +- (void)viewWillAppear:(BOOL)animated +{ + as_activity_create_for_scope("ASDKViewController will appear"); + os_log_debug(ASNodeLog(), "View controller %@ will appear", self); + + [super viewWillAppear:animated]; + + _ensureDisplayed = YES; + + // A layout pass is forced this early to get nodes like ASCollectionNode, ASTableNode etc. + // into the hierarchy before UIKit applies the scroll view inset adjustments, if automatic subnode management + // is enabled. Otherwise the insets would not be applied. + [_node.view layoutIfNeeded]; + + if (_parentManagesVisibilityDepth == NO) { + [self setVisibilityDepth:0]; + } +} + +ASVisibilitySetVisibilityDepth; + +ASVisibilityViewDidDisappearImplementation; + +ASVisibilityDepthImplementation; + +- (void)visibilityDepthDidChange +{ + ASLayoutRangeMode rangeMode = ASLayoutRangeModeForVisibilityDepth(self.visibilityDepth); +#if ASEnableVerboseLogging + NSString *rangeModeString; + switch (rangeMode) { + case ASLayoutRangeModeMinimum: + rangeModeString = @"Minimum"; + break; + + case ASLayoutRangeModeFull: + rangeModeString = @"Full"; + break; + + case ASLayoutRangeModeVisibleOnly: + rangeModeString = @"Visible Only"; + break; + + case ASLayoutRangeModeLowMemory: + rangeModeString = @"Low Memory"; + break; + + default: + break; + } + as_log_verbose(ASNodeLog(), "Updating visibility of %@ to: %@ (visibility depth: %zd)", self, rangeModeString, self.visibilityDepth); +#endif + [self updateCurrentRangeModeWithModeIfPossible:rangeMode]; +} + +#pragma mark - Automatic range mode + +- (BOOL)automaticallyAdjustRangeModeBasedOnViewEvents +{ + return _automaticallyAdjustRangeModeBasedOnViewEvents; +} + +- (void)setAutomaticallyAdjustRangeModeBasedOnViewEvents:(BOOL)automaticallyAdjustRangeModeBasedOnViewEvents +{ + if (automaticallyAdjustRangeModeBasedOnViewEvents != _automaticallyAdjustRangeModeBasedOnViewEvents) { + if (automaticallyAdjustRangeModeBasedOnViewEvents && _selfConformsToRangeModeProtocol == NO && _nodeConformsToRangeModeProtocol == NO) { + NSLog(@"Warning: automaticallyAdjustRangeModeBasedOnViewEvents set to YES in %@, but range mode updating is not possible because neither view controller nor node %@ conform to ASRangeControllerUpdateRangeProtocol.", self, _node); + } + _automaticallyAdjustRangeModeBasedOnViewEvents = automaticallyAdjustRangeModeBasedOnViewEvents; + } +} + +- (void)updateCurrentRangeModeWithModeIfPossible:(ASLayoutRangeMode)rangeMode +{ + if (!_automaticallyAdjustRangeModeBasedOnViewEvents) { + return; + } + + if (_selfConformsToRangeModeProtocol) { + id rangeUpdater = (id)self; + [rangeUpdater updateCurrentRangeWithMode:rangeMode]; + } + + if (_nodeConformsToRangeModeProtocol) { + id rangeUpdater = (id)_node; + [rangeUpdater updateCurrentRangeWithMode:rangeMode]; + } +} + +#pragma mark - Layout Helpers + +- (ASSizeRange)nodeConstrainedSize +{ + return ASSizeRangeMake(self.view.bounds.size); +} + +- (ASInterfaceState)interfaceState +{ + return _node.interfaceState; +} + +- (UIEdgeInsets)additionalSafeAreaInsets +{ + if (AS_AVAILABLE_IOS_TVOS(11.0, 11.0)) { + return super.additionalSafeAreaInsets; + } + + return _fallbackAdditionalSafeAreaInsets; +} + +- (void)setAdditionalSafeAreaInsets:(UIEdgeInsets)additionalSafeAreaInsets +{ + if (AS_AVAILABLE_IOS_TVOS(11.0, 11.0)) { + [super setAdditionalSafeAreaInsets:additionalSafeAreaInsets]; + } else { + _fallbackAdditionalSafeAreaInsets = additionalSafeAreaInsets; + [self _updateNodeFallbackSafeArea]; + } +} + +#pragma mark - ASTraitEnvironment + +- (ASPrimitiveTraitCollection)primitiveTraitCollectionForUITraitCollection:(UITraitCollection *)traitCollection +{ + if (self.overrideDisplayTraitsWithTraitCollection) { + ASTraitCollection *asyncTraitCollection = self.overrideDisplayTraitsWithTraitCollection(traitCollection); + return [asyncTraitCollection primitiveTraitCollection]; + } + + ASDisplayNodeAssertMainThread(); + ASPrimitiveTraitCollection asyncTraitCollection = ASPrimitiveTraitCollectionFromUITraitCollection(traitCollection); + asyncTraitCollection.containerSize = self.view.frame.size; + return asyncTraitCollection; +} + +- (void)propagateNewTraitCollection:(ASPrimitiveTraitCollection)traitCollection +{ + ASPrimitiveTraitCollection oldTraitCollection = self.node.primitiveTraitCollection; + + if (ASPrimitiveTraitCollectionIsEqualToASPrimitiveTraitCollection(traitCollection, oldTraitCollection) == NO) { + as_activity_scope_verbose(as_activity_create("Propagate ASDKViewController trait collection", AS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT)); + os_log_debug(ASNodeLog(), "Propagating new traits for %@: %@", self, NSStringFromASPrimitiveTraitCollection(traitCollection)); + ASTraitCollectionPropagateDown(self.node, traitCollection); + + // Once we've propagated all the traits, layout this node. + // Remeasure the node with the latest constrained size – old constrained size may be incorrect. + as_activity_scope_verbose(as_activity_create("Layout ASDKViewController node with new traits", AS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT)); + [_node layoutThatFits:[self nodeConstrainedSize]]; + } +} + +- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection +{ + [super traitCollectionDidChange:previousTraitCollection]; + + ASPrimitiveTraitCollection traitCollection = [self primitiveTraitCollectionForUITraitCollection:self.traitCollection]; + traitCollection.containerSize = self.view.bounds.size; + [self propagateNewTraitCollection:traitCollection]; +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-implementations" +- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation +{ + [super didRotateFromInterfaceOrientation:fromInterfaceOrientation]; + + ASPrimitiveTraitCollection traitCollection = _node.primitiveTraitCollection; + traitCollection.containerSize = self.view.bounds.size; + [self propagateNewTraitCollection:traitCollection]; +} +#pragma clang diagnostic pop + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDisplayNode+Beta.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDisplayNode+Beta.h new file mode 100644 index 0000000..882042a --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDisplayNode+Beta.h @@ -0,0 +1,171 @@ +// +// ASDisplayNode+Beta.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import + +#if YOGA + #import YOGA_HEADER_PATH + #import + #import +#endif + +NS_ASSUME_NONNULL_BEGIN + +ASDK_EXTERN void ASPerformBlockOnMainThread(void (^block)(void)); +ASDK_EXTERN void ASPerformBlockOnBackgroundThread(void (^block)(void)); // DISPATCH_QUEUE_PRIORITY_DEFAULT + +/** + * Bitmask to indicate what performance measurements the cell should record. + */ +typedef NS_OPTIONS(unsigned char, ASDisplayNodePerformanceMeasurementOptions) { + ASDisplayNodePerformanceMeasurementOptionLayoutSpec = 1 << 0, + ASDisplayNodePerformanceMeasurementOptionLayoutComputation = 1 << 1 +}; + +typedef struct { + CFTimeInterval layoutSpecTotalTime; + NSInteger layoutSpecNumberOfPasses; + CFTimeInterval layoutComputationTotalTime; + NSInteger layoutComputationNumberOfPasses; +} ASDisplayNodePerformanceMeasurements; + +@interface ASDisplayNode (Beta) + +/** + * ASTableView and ASCollectionView now throw exceptions on invalid updates + * like their UIKit counterparts. If YES, these classes will log messages + * on invalid updates rather than throwing exceptions. + * + * Note that even if AsyncDisplayKit's exception is suppressed, the app may still crash + * as it proceeds with an invalid update. + * + * This property defaults to NO. It will be removed in a future release. + */ ++ (BOOL)suppressesInvalidCollectionUpdateExceptions AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED_MSG("Collection update exceptions are thrown if assertions are enabled."); ++ (void)setSuppressesInvalidCollectionUpdateExceptions:(BOOL)suppresses; + +/** + * @abstract Recursively ensures node and all subnodes are displayed. + * @see Full documentation in ASDisplayNode+FrameworkPrivate.h + */ +- (void)recursivelyEnsureDisplaySynchronously:(BOOL)synchronously; + +/** + * @abstract allow modification of a context before the node's content is drawn + * + * @discussion Set the block to be called after the context has been created and before the node's content is drawn. + * You can override this to modify the context before the content is drawn. You are responsible for saving and + * restoring context if necessary. Restoring can be done in contextDidDisplayNodeContent + * This block can be called from *any* thread and it is unsafe to access any UIKit main thread properties from it. + */ +@property (nullable) ASDisplayNodeContextModifier willDisplayNodeContentWithRenderingContext; + +/** + * @abstract allow modification of a context after the node's content is drawn + */ +@property (nullable) ASDisplayNodeContextModifier didDisplayNodeContentWithRenderingContext; + +/** + * @abstract A bitmask representing which actions (layout spec, layout generation) should be measured. + */ +@property ASDisplayNodePerformanceMeasurementOptions measurementOptions; + +/** + * @abstract A simple struct representing performance measurements collected. + */ +@property (readonly) ASDisplayNodePerformanceMeasurements performanceMeasurements; + +/** + * @abstract Whether this node acts as an accessibility container. If set to YES, then this node's accessibility label will represent + * an aggregation of all child nodes' accessibility labels. Nodes in this node's subtree that are also accessibility containers will + * not be included in this aggregation, and will be exposed as separate accessibility elements to UIKit. + */ +@property BOOL isAccessibilityContainer; + +/** + * @abstract Returns the default accessibility property values set by Texture on this node. For + * example, the default accessibility label for a text node may be its text content, while most + * other nodes would have nil default labels. + */ +@property (nullable, readonly, copy) NSString *defaultAccessibilityLabel; +@property (nullable, readonly, copy) NSString *defaultAccessibilityHint; +@property (nullable, readonly, copy) NSString *defaultAccessibilityValue; +@property (nullable, readonly, copy) NSString *defaultAccessibilityIdentifier; +@property (readonly) UIAccessibilityTraits defaultAccessibilityTraits; + +/** + * @abstract Invoked when a user performs a custom action on an accessible node. Nodes that are children of accessibility containers, have + * an accessibity label and have an interactive UIAccessibilityTrait will automatically receive custom-action handling. + * + * @return Return a boolean value that determine whether to propagate through the responder chain. + * To halt propagation, return YES; otherwise, return NO. + */ +- (BOOL)performAccessibilityCustomAction:(UIAccessibilityCustomAction *)action; + +/** + * @abstract Currently used by ASNetworkImageNode and ASMultiplexImageNode to allow their placeholders to stay if they are loading an image from the network. + * Otherwise, a display pass is scheduled and completes, but does not actually draw anything - and ASDisplayNode considers the element finished. + */ +- (BOOL)placeholderShouldPersist AS_WARN_UNUSED_RESULT; + +/** + * @abstract Indicates that the receiver and all subnodes have finished displaying. May be called more than once, for example if the receiver has + * a network image node. This is called after the first display pass even if network image nodes have not downloaded anything (text would be done, + * and other nodes that are ready to do their final display). Each render of every progressive jpeg network node would cause this to be called, so + * this hook could be called up to 1 + (pJPEGcount * pJPEGrenderCount) times. The render count depends on how many times the downloader calls the + * progressImage block. + */ +AS_CATEGORY_IMPLEMENTABLE +- (void)hierarchyDisplayDidFinish NS_REQUIRES_SUPER; + +/** + * Only called on the root during yoga layout. + */ +AS_CATEGORY_IMPLEMENTABLE +- (void)willCalculateLayout:(ASSizeRange)constrainedSize NS_REQUIRES_SUPER; + +/** + * Only ASLayoutRangeModeVisibleOnly or ASLayoutRangeModeLowMemory are recommended. Default is ASLayoutRangeModeVisibleOnly, + * because this is the only way to ensure an application will not have blank / flashing views as the user navigates back after + * a memory warning. Apps that wish to use the more effective / aggressive ASLayoutRangeModeLowMemory may need to take steps + * to mitigate this behavior, including: restoring a larger range mode to the next controller before the user navigates there, + * enabling .neverShowPlaceholders on ASCellNodes so that the navigation operation is blocked on redisplay completing, etc. + */ ++ (void)setRangeModeForMemoryWarnings:(ASLayoutRangeMode)rangeMode; + +/** + * @abstract Whether to draw all descendent nodes' contents into this node's layer's backing store. + * + * @discussion + * When called, causes all descendent nodes' contents to be drawn directly into this node's layer's backing + * store. + * + * If a node's descendants are static (never animated or never change attributes after creation) then that node is a + * good candidate for rasterization. Rasterizing descendants has two main benefits: + * 1) Backing stores for descendant layers are not created. Instead the layers are drawn directly into the rasterized + * container. This can save a great deal of memory. + * 2) Since the entire subtree is drawn into one backing store, compositing and blending are eliminated in that subtree + * which can help improve animation/scrolling/etc performance. + * + * Rasterization does not currently support descendants with transform, sublayerTransform, or alpha. Those properties + * will be ignored when rasterizing descendants. + * + * Note: this has nothing to do with -[CALayer shouldRasterize], which doesn't work with ASDisplayNode's asynchronous + * rendering model. + * + * Note: You cannot add subnodes whose layers/views are already loaded to a rasterized node. + * Note: You cannot call this method after the receiver's layer/view is loaded. + */ +- (void)enableSubtreeRasterization; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDisplayNode+Convenience.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDisplayNode+Convenience.h new file mode 100644 index 0000000..3c00f67 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDisplayNode+Convenience.h @@ -0,0 +1,28 @@ +// +// ASDisplayNode+Convenience.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@class UIViewController; + +@interface ASDisplayNode (Convenience) + +/** + * @abstract Returns the view controller nearest to this node in the view hierarchy. + * + * @warning This property may only be accessed on the main thread. This property may + * be @c nil until the node's view is actually hosted in the view hierarchy. + */ +@property (nonatomic, nullable, readonly) __kindof UIViewController *closestViewController; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDisplayNode+Convenience.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDisplayNode+Convenience.mm new file mode 100644 index 0000000..c441290 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDisplayNode+Convenience.mm @@ -0,0 +1,38 @@ +// +// ASDisplayNode+Convenience.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import "ASDisplayNode+Convenience.h" + +#import +#import + +@implementation ASDisplayNode (Convenience) + +- (__kindof UIViewController *)closestViewController +{ + ASDisplayNodeAssertMainThread(); + + // Careful not to trigger node loading here. + if (!self.nodeLoaded) { + return nil; + } + + // Get the closest view. + UIView *view = ASFindClosestViewOfLayer(self.layer); + // Travel up the responder chain to find a view controller. + for (UIResponder *responder in [view asdk_responderChainEnumerator]) { + UIViewController *vc = ASDynamicCast(responder, UIViewController); + if (vc != nil) { + return vc; + } + } + return nil; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDisplayNode+InterfaceState.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDisplayNode+InterfaceState.h new file mode 100644 index 0000000..96ff703 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDisplayNode+InterfaceState.h @@ -0,0 +1,148 @@ +// +// ASDisplayNode+InterfaceState.h +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +/** + * Interface state is available on ASDisplayNode and ASDKViewController, and + * allows checking whether a node is in an interface situation where it is prudent to trigger certain + * actions: measurement, data loading, display, and visibility (the latter for animations or other onscreen-only effects). + * + * The defualt state, ASInterfaceStateNone, means that the element is not predicted to be onscreen soon and + * preloading should not be performed. Swift: use [] for the default behavior. + */ +typedef NS_OPTIONS(unsigned char, ASInterfaceState) +{ + /** The element is not predicted to be onscreen soon and preloading should not be performed */ + ASInterfaceStateNone = 0, + /** The element may be added to a view soon that could become visible. Measure the layout, including size calculation. */ + ASInterfaceStateMeasureLayout = 1 << 0, + /** The element is likely enough to come onscreen that disk and/or network data required for display should be fetched. */ + ASInterfaceStatePreload = 1 << 1, + /** The element is very likely to become visible, and concurrent rendering should be executed for any -setNeedsDisplay. */ + ASInterfaceStateDisplay = 1 << 2, + /** The element is physically onscreen by at least 1 pixel. + In practice, all other bit fields should also be set when this flag is set. */ + ASInterfaceStateVisible = 1 << 3, + + /** + * The node is not contained in a cell but it is in a window. + * + * Currently we only set `interfaceState` to other values for + * nodes contained in table views or collection views. + */ + ASInterfaceStateInHierarchy = ASInterfaceStateMeasureLayout | ASInterfaceStatePreload | ASInterfaceStateDisplay | ASInterfaceStateVisible, +}; + +@protocol ASInterfaceStateDelegate + +/** + * @abstract Called whenever any bit in the ASInterfaceState bitfield is changed. + * @discussion Subclasses may use this to monitor when they become visible, should free cached data, and much more. + * @see ASInterfaceState + */ +- (void)interfaceStateDidChange:(ASInterfaceState)newState fromState:(ASInterfaceState)oldState; + +/** + * @abstract Called whenever the node becomes visible. + * @discussion Subclasses may use this to monitor when they become visible. + * @note This method is guaranteed to be called on main. + */ +- (void)didEnterVisibleState; + +/** + * @abstract Called whenever the node is no longer visible. + * @discussion Subclasses may use this to monitor when they are no longer visible. + * @note This method is guaranteed to be called on main. + */ +- (void)didExitVisibleState; + +/** + * @abstract Called whenever the the node has entered the display state. + * @discussion Subclasses may use this to monitor when a node should be rendering its content. + * @note This method is guaranteed to be called on main. + */ +- (void)didEnterDisplayState; + +/** + * @abstract Called whenever the the node has exited the display state. + * @discussion Subclasses may use this to monitor when a node should no longer be rendering its content. + * @note This method is guaranteed to be called on main. + */ +- (void)didExitDisplayState; + +/** + * @abstract Called whenever the the node has entered the preload state. + * @discussion Subclasses may use this to monitor data for a node should be preloaded, either from a local or remote source. + * @note This method is guaranteed to be called on main. + */ +- (void)didEnterPreloadState; + +/** + * @abstract Called whenever the the node has exited the preload state. + * @discussion Subclasses may use this to monitor whether preloading data for a node should be canceled. + * @note This method is guaranteed to be called on main. + */ +- (void)didExitPreloadState; + +/** + * @abstract Called when the node has completed applying the layout. + * @discussion Can be used for operations that are performed after layout has completed. + * @note This method is guaranteed to be called on main. + */ +- (void)nodeDidLayout; + +/** + * @abstract Called when the node loads. + * @discussion Can be used for operations that are performed after the node's view is available. + * @note This method is guaranteed to be called on main. + */ +- (void)nodeDidLoad; + +/** + * @abstract Indicates that the receiver and all subnodes have finished displaying. + * @discussion May be called more than once, for example if the receiver has a network image node. + * This is called after the first display pass even if network image nodes have not downloaded anything + * (text would be done, and other nodes that are ready to do their final display). Each render of + * every progressive jpeg network node would cause this to be called, so this hook could be called up to + * 1 + (pJPEGcount * pJPEGrenderCount) times. The render count depends on how many times the downloader calls + * the progressImage block. + * @note This method is guaranteed to be called on main. + */ +- (void)hierarchyDisplayDidFinish; + +@optional +/** + * @abstract Called when the node is about to calculate layout. This is only called before + * Yoga-driven layouts. + * @discussion Can be used for operations that are performed after the node's view is available. + * @note This method is guaranteed to be called on main, but implementations should be careful not + * to attempt to ascend the node tree when handling this, as the root node is locked when this is + * called. + */ +- (void)nodeWillCalculateLayout:(ASSizeRange)constrainedSize; + +/** + * @abstract Called when the node's layer is about to enter the hierarchy. + * @discussion May be called more than once if the layer is participating in a higher-level + * animation, such as a UIViewController transition. These animations can cause the layer to get + * re-parented multiple times, and each time will trigger this call. + * @note This method is guaranteed to be called on main. + */ +- (void)didEnterHierarchy; + +/** + * @abstract Called when the node's layer has exited the hierarchy. + * @discussion May be called more than once if the layer is participating in a higher-level + * animation, such as a UIViewController transition. These animations can cause the layer to get + * re-parented multiple times, and each time will trigger this call. + * @note This method is guaranteed to be called on main. + */ +- (void)didExitHierarchy; + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDisplayNode+Layout.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDisplayNode+Layout.mm new file mode 100644 index 0000000..ab99d3a --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDisplayNode+Layout.mm @@ -0,0 +1,1098 @@ +// +// ASDisplayNode+Layout.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + +using AS::MutexLocker; + +@interface ASDisplayNode (ASLayoutElementStyleDelegate) +@end + +@implementation ASDisplayNode (ASLayoutElementStyleDelegate) + +#pragma mark + +- (void)style:(ASLayoutElementStyle *)style propertyDidChange:(NSString *)propertyName { + [self setNeedsLayout]; +} + +@end + +#pragma mark - ASDisplayNode (ASLayoutElement) + +@implementation ASDisplayNode (ASLayoutElement) + +#pragma mark + +- (BOOL)implementsLayoutMethod +{ + MutexLocker l(__instanceLock__); + return (_methodOverrides & (ASDisplayNodeMethodOverrideLayoutSpecThatFits | + ASDisplayNodeMethodOverrideCalcLayoutThatFits | + ASDisplayNodeMethodOverrideCalcSizeThatFits)) != 0 || _layoutSpecBlock != nil; +} + + +- (ASLayoutElementStyle *)style +{ + MutexLocker l(__instanceLock__); + return [self _locked_style]; +} + +- (ASLayoutElementStyle *)_locked_style +{ + DISABLED_ASAssertLocked(__instanceLock__); + if (_style == nil) { +#if YOGA + // In Yoga mode we use the delegate to inform the tree if properties changes + _style = [[ASLayoutElementStyle alloc] initWithDelegate:self]; +#else + _style = [[ASLayoutElementStyle alloc] init]; +#endif + } + return _style; +} + +- (ASLayoutElementType)layoutElementType +{ + return ASLayoutElementTypeDisplayNode; +} + +- (NSArray> *)sublayoutElements +{ + return self.subnodes; +} + +#pragma mark Measurement Pass + +- (ASLayout *)layoutThatFits:(ASSizeRange)constrainedSize +{ + return [self layoutThatFits:constrainedSize parentSize:constrainedSize.max]; +} + +- (ASLayout *)layoutThatFits:(ASSizeRange)constrainedSize parentSize:(CGSize)parentSize +{ + ASScopedLockSelfOrToRoot(); + + // If one or multiple layout transitions are in flight it still can happen that layout information is requested + // on other threads. As the pending and calculated layout to be updated in the layout transition in here just a + // layout calculation wil be performed without side effect + if ([self _isLayoutTransitionInvalid]) { + return [self calculateLayoutThatFits:constrainedSize restrictedToSize:self.style.size relativeToParentSize:parentSize]; + } + + ASLayout *layout = nil; + NSUInteger version = _layoutVersion; + if (_calculatedDisplayNodeLayout.isValid(constrainedSize, parentSize, version)) { + ASDisplayNodeAssertNotNil(_calculatedDisplayNodeLayout.layout, @"-[ASDisplayNode layoutThatFits:parentSize:] _calculatedDisplayNodeLayout.layout should not be nil! %@", self); + layout = _calculatedDisplayNodeLayout.layout; + } else if (_pendingDisplayNodeLayout.isValid(constrainedSize, parentSize, version)) { + ASDisplayNodeAssertNotNil(_pendingDisplayNodeLayout.layout, @"-[ASDisplayNode layoutThatFits:parentSize:] _pendingDisplayNodeLayout.layout should not be nil! %@", self); + layout = _pendingDisplayNodeLayout.layout; + } else { + // Create a pending display node layout for the layout pass + layout = [self calculateLayoutThatFits:constrainedSize + restrictedToSize:self.style.size + relativeToParentSize:parentSize]; + as_log_verbose(ASLayoutLog(), "Established pending layout for %@ in %s", self, sel_getName(_cmd)); + _pendingDisplayNodeLayout = ASDisplayNodeLayout(layout, constrainedSize, parentSize,version); + ASDisplayNodeAssertNotNil(layout, @"-[ASDisplayNode layoutThatFits:parentSize:] newly calculated layout should not be nil! %@", self); + } + + return layout ?: [ASLayout layoutWithLayoutElement:self size:{0, 0}]; +} + +#pragma mark ASLayoutElementStyleExtensibility + +ASLayoutElementStyleExtensibilityForwarding + +#pragma mark ASPrimitiveTraitCollection + +- (ASPrimitiveTraitCollection)primitiveTraitCollection +{ + AS::MutexLocker l(__instanceLock__); + return _primitiveTraitCollection; +} + +- (void)setPrimitiveTraitCollection:(ASPrimitiveTraitCollection)traitCollection +{ + AS::UniqueLock l(__instanceLock__); + if (ASPrimitiveTraitCollectionIsEqualToASPrimitiveTraitCollection(traitCollection, _primitiveTraitCollection) == NO) { + ASPrimitiveTraitCollection previousTraitCollection = _primitiveTraitCollection; + _primitiveTraitCollection = traitCollection; + + l.unlock(); + [self asyncTraitCollectionDidChangeWithPreviousTraitCollection:previousTraitCollection]; + } +} + +- (ASTraitCollection *)asyncTraitCollection +{ + return [ASTraitCollection traitCollectionWithASPrimitiveTraitCollection:self.primitiveTraitCollection]; +} + +#pragma mark - ASLayoutElementAsciiArtProtocol + +- (NSString *)asciiArtString +{ + return [ASLayoutSpec asciiArtStringForChildren:@[] parentName:[self asciiArtName]]; +} + +- (NSString *)asciiArtName +{ + NSMutableString *result = [NSMutableString stringWithCString:object_getClassName(self) encoding:NSASCIIStringEncoding]; + if (_debugName) { + [result appendFormat:@" (%@)", _debugName]; + } + return result; +} + +@end + +#pragma mark - +#pragma mark - ASDisplayNode (ASLayout) + +@implementation ASDisplayNode (ASLayout) + +- (ASLayoutEngineType)layoutEngineType +{ +#if YOGA + MutexLocker l(__instanceLock__); + YGNodeRef yogaNode = _style.yogaNode; + BOOL hasYogaParent = (_yogaParent != nil); + BOOL hasYogaChildren = (_yogaChildren.count > 0); + if (yogaNode != NULL && (hasYogaParent || hasYogaChildren)) { + return ASLayoutEngineTypeYoga; + } +#endif + + return ASLayoutEngineTypeLayoutSpec; +} + +- (ASLayout *)calculatedLayout +{ + MutexLocker l(__instanceLock__); + return _calculatedDisplayNodeLayout.layout; +} + +- (CGSize)calculatedSize +{ + MutexLocker l(__instanceLock__); + if (_pendingDisplayNodeLayout.isValid(_layoutVersion)) { + return _pendingDisplayNodeLayout.layout.size; + } + return _calculatedDisplayNodeLayout.layout.size; +} + +- (ASSizeRange)constrainedSizeForCalculatedLayout +{ + MutexLocker l(__instanceLock__); + return [self _locked_constrainedSizeForCalculatedLayout]; +} + +- (ASSizeRange)_locked_constrainedSizeForCalculatedLayout +{ + DISABLED_ASAssertLocked(__instanceLock__); + if (_pendingDisplayNodeLayout.isValid(_layoutVersion)) { + return _pendingDisplayNodeLayout.constrainedSize; + } + return _calculatedDisplayNodeLayout.constrainedSize; +} + +@end + +#pragma mark - +#pragma mark - ASDisplayNode (ASLayoutElementStylability) + +@implementation ASDisplayNode (ASLayoutElementStylability) + +- (instancetype)styledWithBlock:(AS_NOESCAPE void (^)(__kindof ASLayoutElementStyle *style))styleBlock +{ + styleBlock(self.style); + return self; +} + +@end + +#pragma mark - +#pragma mark - ASDisplayNode (ASLayoutInternal) + +@implementation ASDisplayNode (ASLayoutInternal) + +/** + * @abstract Informs the root node that the intrinsic size of the receiver is no longer valid. + * + * @discussion The size of a root node is determined by each subnode. Calling invalidateSize will let the root node know + * that the intrinsic size of the receiver node is no longer valid and a resizing of the root node needs to happen. + */ +- (void)_u_setNeedsLayoutFromAbove +{ + ASDisplayNodeAssertThreadAffinity(self); + DISABLED_ASAssertUnlocked(__instanceLock__); + + as_activity_create_for_scope("Set needs layout from above"); + + // Mark the node for layout in the next layout pass + [self setNeedsLayout]; + + __instanceLock__.lock(); + // Escalate to the root; entire tree must allow adjustments so the layout fits the new child. + // Much of the layout will be re-used as cached (e.g. other items in an unconstrained stack) + ASDisplayNode *supernode = _supernode; + __instanceLock__.unlock(); + + if (supernode) { + // Threading model requires that we unlock before calling a method on our parent. + [supernode _u_setNeedsLayoutFromAbove]; + } else { + // Let the root node method know that the size was invalidated + [self _rootNodeDidInvalidateSize]; + } +} + +// TODO It would be easier to work with if we could `ASAssertUnlocked` here, but we +// cannot due to locking to root in `_u_measureNodeWithBoundsIfNecessary`. +- (void)_rootNodeDidInvalidateSize +{ + ASDisplayNodeAssertThreadAffinity(self); + __instanceLock__.lock(); + + // We are the root node and need to re-flow the layout; at least one child needs a new size. + CGSize boundsSizeForLayout = ASCeilSizeValues(self.bounds.size); + + // Figure out constrainedSize to use + ASSizeRange constrainedSize = ASSizeRangeMake(boundsSizeForLayout); + if (_pendingDisplayNodeLayout.layout != nil) { + constrainedSize = _pendingDisplayNodeLayout.constrainedSize; + } else if (_calculatedDisplayNodeLayout.layout != nil) { + constrainedSize = _calculatedDisplayNodeLayout.constrainedSize; + } + + __instanceLock__.unlock(); + + // Perform a measurement pass to get the full tree layout, adapting to the child's new size. + ASLayout *layout = [self layoutThatFits:constrainedSize]; + + // Check if the returned layout has a different size than our current bounds. + if (CGSizeEqualToSize(boundsSizeForLayout, layout.size) == NO) { + // If so, inform our container we need an update (e.g Table, Collection, ViewController, etc). + [self displayNodeDidInvalidateSizeNewSize:layout.size]; + } +} + +// TODO +// We should remove this logic, which is relatively new, and instead +// rely on the parent / host of the root node to do this size change. That's always been the +// expectation with other node containers like ASTableView, ASCollectionView, ASDKViewController, etc. +// E.g. in ASCellNode the _interactionDelegate is a Table or Collection that will resize in this +// case. By resizing without participating with the parent, we could get cases where our parent size +// does not match, especially if there is a size constraint that is applied at that level. +// +// In general a node should never need to set its own size, instead allowing its parent to do so - +// even in the root case. Anyhow this is a separate / pre-existing issue, but I think it could be +// causing real issues in cases of resizing nodes. +- (void)displayNodeDidInvalidateSizeNewSize:(CGSize)size +{ + ASDisplayNodeAssertThreadAffinity(self); + + // The default implementation of display node changes the size of itself to the new size + CGRect oldBounds = self.bounds; + CGSize oldSize = oldBounds.size; + CGSize newSize = size; + + if (! CGSizeEqualToSize(oldSize, newSize)) { + self.bounds = (CGRect){ oldBounds.origin, newSize }; + + // Frame's origin must be preserved. Since it is computed from bounds size, anchorPoint + // and position (see frame setter in ASDisplayNode+UIViewBridge), position needs to be adjusted. + CGPoint anchorPoint = self.anchorPoint; + CGPoint oldPosition = self.position; + CGFloat xDelta = (newSize.width - oldSize.width) * anchorPoint.x; + CGFloat yDelta = (newSize.height - oldSize.height) * anchorPoint.y; + self.position = CGPointMake(oldPosition.x + xDelta, oldPosition.y + yDelta); + } +} + +- (void)_u_measureNodeWithBoundsIfNecessary:(CGRect)bounds +{ + DISABLED_ASAssertUnlocked(__instanceLock__); + ASScopedLockSelfOrToRoot(); + + // Check if we are a subnode in a layout transition. + // In this case no measurement is needed as it's part of the layout transition + if ([self _locked_isLayoutTransitionInvalid]) { + return; + } + + CGSize boundsSizeForLayout = ASCeilSizeValues(bounds.size); + + // Prefer a newer and not yet applied _pendingDisplayNodeLayout over _calculatedDisplayNodeLayout + // If there is no such _pending, check if _calculated is valid to reuse (avoiding recalculation below). + BOOL pendingLayoutIsPreferred = NO; + if (_pendingDisplayNodeLayout.isValid(_layoutVersion)) { + NSUInteger calculatedVersion = _calculatedDisplayNodeLayout.version; + NSUInteger pendingVersion = _pendingDisplayNodeLayout.version; + if (pendingVersion > calculatedVersion) { + pendingLayoutIsPreferred = YES; // Newer _pending + } else if (pendingVersion == calculatedVersion + && !ASSizeRangeEqualToSizeRange(_pendingDisplayNodeLayout.constrainedSize, + _calculatedDisplayNodeLayout.constrainedSize)) { + pendingLayoutIsPreferred = YES; // _pending with a different constrained size + } + } + BOOL calculatedLayoutIsReusable = (_calculatedDisplayNodeLayout.isValid(_layoutVersion) + && (_calculatedDisplayNodeLayout.requestedLayoutFromAbove + || CGSizeEqualToSize(_calculatedDisplayNodeLayout.layout.size, boundsSizeForLayout))); + if (!pendingLayoutIsPreferred && calculatedLayoutIsReusable) { + return; + } + + as_activity_create_for_scope("Update node layout for current bounds"); + as_log_verbose(ASLayoutLog(), "Node %@, bounds size %@, calculatedSize %@, calculatedIsDirty %d", + self, + NSStringFromCGSize(boundsSizeForLayout), + NSStringFromCGSize(_calculatedDisplayNodeLayout.layout.size), + _calculatedDisplayNodeLayout.version < _layoutVersion); + // _calculatedDisplayNodeLayout is not reusable we need to transition to a new one + [self cancelLayoutTransition]; + + BOOL didCreateNewContext = NO; + ASLayoutElementContext *context = ASLayoutElementGetCurrentContext(); + if (context == nil) { + context = [[ASLayoutElementContext alloc] init]; + ASLayoutElementPushContext(context); + didCreateNewContext = YES; + } + + // Figure out previous and pending layouts for layout transition + ASDisplayNodeLayout nextLayout = _pendingDisplayNodeLayout; + BOOL isLayoutSizeDifferentFromBounds = !CGSizeEqualToSize(nextLayout.layout.size, boundsSizeForLayout); + + // nextLayout was likely created by a call to layoutThatFits:, check if it is valid and can be applied. + // If our bounds size is different than it, or invalid, recalculate. Use #define to avoid nullptr-> + BOOL pendingLayoutApplicable = NO; + if (nextLayout.layout == nil) { + as_log_verbose(ASLayoutLog(), "No pending layout."); + } else if (!nextLayout.isValid(_layoutVersion)) { + as_log_verbose(ASLayoutLog(), "Pending layout is stale."); + } else if (isLayoutSizeDifferentFromBounds) { + as_log_verbose(ASLayoutLog(), "Pending layout size %@ doesn't match bounds size.", NSStringFromCGSize(nextLayout.layout.size)); + } else { + as_log_verbose(ASLayoutLog(), "Using pending layout %@.", nextLayout.layout); + pendingLayoutApplicable = YES; + } + + if (!pendingLayoutApplicable) { + as_log_verbose(ASLayoutLog(), "Measuring with previous constrained size."); + // Use the last known constrainedSize passed from a parent during layout (if never, use bounds). + NSUInteger version = _layoutVersion; + ASSizeRange constrainedSize = [self _locked_constrainedSizeForLayoutPass]; +#if YOGA + // This flag indicates to the Texture+Yoga code that this next layout is intended to be + // displayed (vs. just for measurement). This will cause it to call setNeedsLayout on any nodes + // whose layout changes as a result of the Yoga recalculation. This is necessary because a + // change in one Yoga node can change the layout for any other node in the tree. + self.willApplyNextYogaCalculatedLayout = YES; +#endif + ASLayout *layout = [self calculateLayoutThatFits:constrainedSize + restrictedToSize:self.style.size + relativeToParentSize:boundsSizeForLayout]; +#if YOGA + self.willApplyNextYogaCalculatedLayout = NO; +#endif + nextLayout = ASDisplayNodeLayout(layout, constrainedSize, boundsSizeForLayout, version); + // Now that the constrained size of pending layout might have been reused, the layout is useless + // Release it and any orphaned subnodes it retains + _pendingDisplayNodeLayout.layout = nil; + } + + if (didCreateNewContext) { + ASLayoutElementPopContext(); + } + + // If our new layout's desired size for self doesn't match current size, ask our parent to update it. + // This can occur for either pre-calculated or newly-calculated layouts. + if (nextLayout.requestedLayoutFromAbove == NO + && CGSizeEqualToSize(boundsSizeForLayout, nextLayout.layout.size) == NO) { + as_log_verbose(ASLayoutLog(), "Layout size doesn't match bounds size. Requesting layout from above."); + // The layout that we have specifies that this node (self) would like to be a different size + // than it currently is. Because that size has been computed within the constrainedSize, we + // expect that calling setNeedsLayoutFromAbove will result in our parent resizing us to this. + // However, in some cases apps may manually interfere with this (setting a different bounds). + // In this case, we need to detect that we've already asked to be resized to match this + // particular ASLayout object, and shouldn't loop asking again unless we have a different ASLayout. + nextLayout.requestedLayoutFromAbove = YES; + + { + __instanceLock__.unlock(); + [self _u_setNeedsLayoutFromAbove]; + __instanceLock__.lock(); + } + + // If we request that our root layout we may generate a new _pendingDisplayNodeLayout.layout which has + // requestedLayoutFromAbove set to NO. If the pending layout has a different constrained size than nextLayout's + // and the layout sizes don't change we could end up back here asking the root to layout again causing an + // infinite layout loop. Instead, we nil out the _pendingDisplayNodeLayout.layout here because it can be + // considered an undesired artifact of the layout request. nextLayout will become _calculatedDisplayNodeLayout + // when the pending layout transition which will be created later in this method is applied. + // We will use _calculatedLayout the next time around, so requestedLayoutFromAbove will be set to YES and we + // will break out of this layout loop. + _pendingDisplayNodeLayout.layout = nil; + + // Update the layout's version here because _u_setNeedsLayoutFromAbove calls __setNeedsLayout which in turn increases _layoutVersion + // Failing to do this will cause the layout to be invalid immediately + nextLayout.version = _layoutVersion; + } + + // Prepare to transition to nextLayout + ASDisplayNodeAssertNotNil(nextLayout.layout, @"nextLayout.layout should not be nil! %@", self); + _pendingLayoutTransition = [[ASLayoutTransition alloc] initWithNode:self + pendingLayout:nextLayout + previousLayout:_calculatedDisplayNodeLayout]; + + // If a parent is currently executing a layout transition, perform our layout application after it. + if (ASHierarchyStateIncludesLayoutPending(_hierarchyState) == NO) { + // If no transition, apply our new layout immediately (common case). + [self _completePendingLayoutTransition]; + } +} + +- (ASSizeRange)_constrainedSizeForLayoutPass +{ + MutexLocker l(__instanceLock__); + return [self _locked_constrainedSizeForLayoutPass]; +} + +- (ASSizeRange)_locked_constrainedSizeForLayoutPass +{ + // TODO: The logic in -_u_setNeedsLayoutFromAbove seems correct and doesn't use this method. + // logic seems correct. For what case does -this method need to do the CGSizeEqual checks? + // IF WE CAN REMOVE BOUNDS CHECKS HERE, THEN WE CAN ALSO REMOVE "REQUESTED FROM ABOVE" CHECK + + DISABLED_ASAssertLocked(__instanceLock__); + + CGSize boundsSizeForLayout = ASCeilSizeValues(self.threadSafeBounds.size); + + // Checkout if constrained size of pending or calculated display node layout can be used + if (_pendingDisplayNodeLayout.requestedLayoutFromAbove + || CGSizeEqualToSize(_pendingDisplayNodeLayout.layout.size, boundsSizeForLayout)) { + // We assume the size from the last returned layoutThatFits: layout was applied so use the pending display node + // layout constrained size + return _pendingDisplayNodeLayout.constrainedSize; + } else if (_calculatedDisplayNodeLayout.layout != nil + && (_calculatedDisplayNodeLayout.requestedLayoutFromAbove + || CGSizeEqualToSize(_calculatedDisplayNodeLayout.layout.size, boundsSizeForLayout))) { + // We assume the _calculatedDisplayNodeLayout is still valid and the frame is not different + return _calculatedDisplayNodeLayout.constrainedSize; + } else { + // In this case neither the _pendingDisplayNodeLayout or the _calculatedDisplayNodeLayout constrained size can + // be reused, so the current bounds is used. This is usual the case if a frame was set manually that differs to + // the one returned from layoutThatFits: or layoutThatFits: was never called + return ASSizeRangeMake(boundsSizeForLayout); + } +} + +- (void)_layoutSublayouts +{ + ASDisplayNodeAssertThreadAffinity(self); + DISABLED_ASAssertUnlocked(__instanceLock__); + + ASLayout *layout; + { + MutexLocker l(__instanceLock__); + if (_calculatedDisplayNodeLayout.version < _layoutVersion) { + return; + } + layout = _calculatedDisplayNodeLayout.layout; + } + + for (ASDisplayNode *node in self.subnodes) { + CGRect frame = [layout frameForElement:node]; + if (CGRectIsNull(frame)) { + // There is no frame for this node in our layout. + // This currently can happen if we get a CA layout pass + // while waiting for the client to run animateLayoutTransition: + } else { + node.frame = frame; + } + } +} + +@end + +#pragma mark - +#pragma mark - ASDisplayNode (ASAutomatic Subnode Management) + +@implementation ASDisplayNode (ASAutomaticSubnodeManagement) + +#pragma mark Automatically Manages Subnodes + +- (BOOL)automaticallyManagesSubnodes +{ + MutexLocker l(__instanceLock__); + return _flags.automaticallyManagesSubnodes; +} + +- (void)setAutomaticallyManagesSubnodes:(BOOL)automaticallyManagesSubnodes +{ + MutexLocker l(__instanceLock__); + _flags.automaticallyManagesSubnodes = automaticallyManagesSubnodes; +} + +@end + +#pragma mark - +#pragma mark - ASDisplayNode (ASLayoutTransition) + +@implementation ASDisplayNode (ASLayoutTransition) + +- (BOOL)_isLayoutTransitionInvalid +{ + MutexLocker l(__instanceLock__); + return [self _locked_isLayoutTransitionInvalid]; +} + +- (BOOL)_locked_isLayoutTransitionInvalid +{ + DISABLED_ASAssertLocked(__instanceLock__); + if (ASHierarchyStateIncludesLayoutPending(_hierarchyState)) { + ASLayoutElementContext *context = ASLayoutElementGetCurrentContext(); + if (context == nil || _pendingTransitionID != context.transitionID) { + return YES; + } + } + return NO; +} + +/// Starts a new transition and returns the transition id +- (int32_t)_startNewTransition +{ + static std::atomic gNextTransitionID; + int32_t newTransitionID = gNextTransitionID.fetch_add(1) + 1; + _transitionID = newTransitionID; + return newTransitionID; +} + +/// Returns NO if there was no transition to cancel/finish. +- (BOOL)_finishOrCancelTransition +{ + int32_t oldValue = _transitionID.exchange(ASLayoutElementContextInvalidTransitionID); + return oldValue != ASLayoutElementContextInvalidTransitionID; +} + +#pragma mark Layout Transition + +- (void)transitionLayoutWithAnimation:(BOOL)animated + shouldMeasureAsync:(BOOL)shouldMeasureAsync + measurementCompletion:(void(^)())completion +{ + ASDisplayNodeAssertMainThread(); + [self transitionLayoutWithSizeRange:[self _constrainedSizeForLayoutPass] + animated:animated + shouldMeasureAsync:shouldMeasureAsync + measurementCompletion:completion]; +} + +- (void)transitionLayoutWithSizeRange:(ASSizeRange)constrainedSize + animated:(BOOL)animated + shouldMeasureAsync:(BOOL)shouldMeasureAsync + measurementCompletion:(void(^)())completion +{ + ASDisplayNodeAssertMainThread(); + as_activity_create_for_scope("Transition node layout"); + os_log_debug(ASLayoutLog(), "Transition layout for %@ sizeRange %@ anim %d asyncMeasure %d", self, NSStringFromASSizeRange(constrainedSize), animated, shouldMeasureAsync); + + if (constrainedSize.max.width <= 0.0 || constrainedSize.max.height <= 0.0) { + // Using CGSizeZero for the sizeRange can cause negative values in client layout code. + // Most likely called transitionLayout: without providing a size, before first layout pass. + as_log_verbose(ASLayoutLog(), "Ignoring transition due to bad size range."); + return; + } + + { + MutexLocker l(__instanceLock__); + + // Check if we are a subnode in a layout transition. + // In this case no measurement is needed as we're part of the layout transition. + if ([self _locked_isLayoutTransitionInvalid]) { + return; + } + + if (ASHierarchyStateIncludesLayoutPending(_hierarchyState)) { + ASDisplayNodeAssert(NO, @"Can't start a transition when one of the supernodes is performing one."); + return; + } + } + + // Invalidate calculated layout because this method acts as an animated "setNeedsLayout" for nodes. + // If the user has reconfigured the node and calls this, we should never return a stale layout + // for subsequent calls to layoutThatFits: regardless of size range. We choose this method rather than + // -setNeedsLayout because that method also triggers a CA layout invalidation, which isn't necessary at this time. + // See https://github.com/TextureGroup/Texture/issues/463 + [self invalidateCalculatedLayout]; + + // Every new layout transition has a transition id associated to check in subsequent transitions for cancelling + int32_t transitionID = [self _startNewTransition]; + as_log_verbose(ASLayoutLog(), "Transition ID is %d", transitionID); + // NOTE: This block captures self. It's cheaper than hitting the weak table. + asdisplaynode_iscancelled_block_t isCancelled = ^{ + BOOL result = (self->_transitionID != transitionID); + if (result) { + as_log_verbose(ASLayoutLog(), "Transition %d canceled, superseded by %d", transitionID, _transitionID.load()); + } + return result; + }; + + // Move all subnodes in layout pending state for this transition + ASDisplayNodePerformBlockOnEverySubnode(self, NO, ^(ASDisplayNode * _Nonnull node) { + ASDisplayNodeAssert(node->_transitionID == ASLayoutElementContextInvalidTransitionID, @"Can't start a transition when one of the subnodes is performing one."); + node.hierarchyState |= ASHierarchyStateLayoutPending; + node->_pendingTransitionID = transitionID; + }); + + // Transition block that executes the layout transition + void (^transitionBlock)(void) = ^{ + if (isCancelled()) { + return; + } + + // Perform a full layout creation pass with passed in constrained size to create the new layout for the transition + NSUInteger newLayoutVersion = self->_layoutVersion; + ASLayout *newLayout; + { + ASScopedLockSelfOrToRoot(); + + ASLayoutElementContext *ctx = [[ASLayoutElementContext alloc] init]; + ctx.transitionID = transitionID; + ASLayoutElementPushContext(ctx); + + BOOL automaticallyManagesSubnodesDisabled = (self.automaticallyManagesSubnodes == NO); + self.automaticallyManagesSubnodes = YES; // Temporary flag for 1.9.x + newLayout = [self calculateLayoutThatFits:constrainedSize + restrictedToSize:self.style.size + relativeToParentSize:constrainedSize.max]; + if (automaticallyManagesSubnodesDisabled) { + self.automaticallyManagesSubnodes = NO; // Temporary flag for 1.9.x + } + + ASLayoutElementPopContext(); + } + + if (isCancelled()) { + return; + } + + ASPerformBlockOnMainThread(^{ + if (isCancelled()) { + return; + } + as_activity_create_for_scope("Commit layout transition"); + ASLayoutTransition *pendingLayoutTransition; + _ASTransitionContext *pendingLayoutTransitionContext; + { + // Grab __instanceLock__ here to make sure this transition isn't invalidated + // right after it passed the validation test and before it proceeds + MutexLocker l(self->__instanceLock__); + + // Update calculated layout + const auto previousLayout = self->_calculatedDisplayNodeLayout; + const auto pendingLayout = ASDisplayNodeLayout(newLayout, + constrainedSize, + constrainedSize.max, + newLayoutVersion); + [self _locked_setCalculatedDisplayNodeLayout:pendingLayout]; + + // Setup pending layout transition for animation + self->_pendingLayoutTransition = pendingLayoutTransition = [[ASLayoutTransition alloc] initWithNode:self + pendingLayout:pendingLayout + previousLayout:previousLayout]; + // Setup context for pending layout transition. we need to hold a strong reference to the context + self->_pendingLayoutTransitionContext = pendingLayoutTransitionContext = [[_ASTransitionContext alloc] initWithAnimation:animated + layoutDelegate:self->_pendingLayoutTransition + completionDelegate:self]; + } + + // Apply complete layout transitions for all subnodes + { + as_activity_create_for_scope("Complete pending layout transitions for subtree"); + ASDisplayNodePerformBlockOnEverySubnode(self, NO, ^(ASDisplayNode * _Nonnull node) { + [node _completePendingLayoutTransition]; + node.hierarchyState &= (~ASHierarchyStateLayoutPending); + }); + } + + // Measurement pass completion + // Give the subclass a change to hook into before calling the completion block + [self _layoutTransitionMeasurementDidFinish]; + if (completion) { + completion(); + } + + // Apply the subnode insertion immediately to be able to animate the nodes + [pendingLayoutTransition applySubnodeInsertionsAndMoves]; + + // Kick off animating the layout transition + { + as_activity_create_for_scope("Animate layout transition"); + [self animateLayoutTransition:pendingLayoutTransitionContext]; + } + + // Mark transaction as finished + [self _finishOrCancelTransition]; + }); + }; + + // Start transition based on flag on current or background thread + if (shouldMeasureAsync) { + ASPerformBlockOnBackgroundThread(transitionBlock); + } else { + transitionBlock(); + } +} + +- (void)cancelLayoutTransition +{ + if ([self _finishOrCancelTransition]) { + // Tell subnodes to exit layout pending state and clear related properties + ASDisplayNodePerformBlockOnEverySubnode(self, NO, ^(ASDisplayNode * _Nonnull node) { + node.hierarchyState &= (~ASHierarchyStateLayoutPending); + }); + } +} + +- (void)setDefaultLayoutTransitionDuration:(NSTimeInterval)defaultLayoutTransitionDuration +{ + MutexLocker l(__instanceLock__); + _defaultLayoutTransitionDuration = defaultLayoutTransitionDuration; +} + +- (NSTimeInterval)defaultLayoutTransitionDuration +{ + MutexLocker l(__instanceLock__); + return _defaultLayoutTransitionDuration; +} + +- (void)setDefaultLayoutTransitionDelay:(NSTimeInterval)defaultLayoutTransitionDelay +{ + MutexLocker l(__instanceLock__); + _defaultLayoutTransitionDelay = defaultLayoutTransitionDelay; +} + +- (NSTimeInterval)defaultLayoutTransitionDelay +{ + MutexLocker l(__instanceLock__); + return _defaultLayoutTransitionDelay; +} + +- (void)setDefaultLayoutTransitionOptions:(UIViewAnimationOptions)defaultLayoutTransitionOptions +{ + MutexLocker l(__instanceLock__); + _defaultLayoutTransitionOptions = defaultLayoutTransitionOptions; +} + +- (UIViewAnimationOptions)defaultLayoutTransitionOptions +{ + MutexLocker l(__instanceLock__); + return _defaultLayoutTransitionOptions; +} + +#pragma mark + +/* + * Hook for subclasses to perform an animation based on the given ASContextTransitioning. By default a fade in and out + * animation is provided. + */ +- (void)animateLayoutTransition:(id)context +{ + if ([context isAnimated] == NO) { + [self _layoutSublayouts]; + [context completeTransition:YES]; + return; + } + + ASDisplayNode *node = self; + + NSAssert(node.isNodeLoaded == YES, @"Invalid node state"); + + NSArray *removedSubnodes = [context removedSubnodes]; + NSMutableArray *insertedSubnodes = [[context insertedSubnodes] mutableCopy]; + const auto movedSubnodes = [[NSMutableArray alloc] init]; + + const auto insertedSubnodeContexts = [[NSMutableArray<_ASAnimatedTransitionContext *> alloc] init]; + const auto removedSubnodeContexts = [[NSMutableArray<_ASAnimatedTransitionContext *> alloc] init]; + + for (ASDisplayNode *subnode in [context subnodesForKey:ASTransitionContextToLayoutKey]) { + if ([insertedSubnodes containsObject:subnode] == NO) { + // This is an existing subnode, check if it is resized, moved or both + CGRect fromFrame = [context initialFrameForNode:subnode]; + CGRect toFrame = [context finalFrameForNode:subnode]; + if (CGSizeEqualToSize(fromFrame.size, toFrame.size) == NO) { + [insertedSubnodes addObject:subnode]; + } + if (CGPointEqualToPoint(fromFrame.origin, toFrame.origin) == NO) { + [movedSubnodes addObject:subnode]; + } + } + } + + // Create contexts for inserted and removed subnodes + for (ASDisplayNode *insertedSubnode in insertedSubnodes) { + [insertedSubnodeContexts addObject:[_ASAnimatedTransitionContext contextForNode:insertedSubnode alpha:insertedSubnode.alpha]]; + } + for (ASDisplayNode *removedSubnode in removedSubnodes) { + [removedSubnodeContexts addObject:[_ASAnimatedTransitionContext contextForNode:removedSubnode alpha:removedSubnode.alpha]]; + } + + // Fade out inserted subnodes + for (ASDisplayNode *insertedSubnode in insertedSubnodes) { + insertedSubnode.frame = [context finalFrameForNode:insertedSubnode]; + insertedSubnode.alpha = 0; + } + + // Adjust groupOpacity for animation + BOOL originAllowsGroupOpacity = node.allowsGroupOpacity; + node.allowsGroupOpacity = YES; + + [UIView animateWithDuration:self.defaultLayoutTransitionDuration delay:self.defaultLayoutTransitionDelay options:self.defaultLayoutTransitionOptions animations:^{ + // Fade removed subnodes and views out + for (ASDisplayNode *removedSubnode in removedSubnodes) { + removedSubnode.alpha = 0; + } + + // Fade inserted subnodes in + for (_ASAnimatedTransitionContext *insertedSubnodeContext in insertedSubnodeContexts) { + insertedSubnodeContext.node.alpha = insertedSubnodeContext.alpha; + } + + // Update frame of self and moved subnodes + CGSize fromSize = [context layoutForKey:ASTransitionContextFromLayoutKey].size; + CGSize toSize = [context layoutForKey:ASTransitionContextToLayoutKey].size; + BOOL isResized = (CGSizeEqualToSize(fromSize, toSize) == NO); + if (isResized == YES) { + CGPoint position = node.frame.origin; + node.frame = CGRectMake(position.x, position.y, toSize.width, toSize.height); + } + for (ASDisplayNode *movedSubnode in movedSubnodes) { + movedSubnode.frame = [context finalFrameForNode:movedSubnode]; + } + } completion:^(BOOL finished) { + // Restore all removed subnode alpha values + for (_ASAnimatedTransitionContext *removedSubnodeContext in removedSubnodeContexts) { + removedSubnodeContext.node.alpha = removedSubnodeContext.alpha; + } + + // Restore group opacity + node.allowsGroupOpacity = originAllowsGroupOpacity; + + // Subnode removals are automatically performed + [context completeTransition:finished]; + }]; +} + +/** + * Hook for subclasses to clean up nodes after the transition happened. Furthermore this can be used from subclasses + * to manually perform deletions. + */ +- (void)didCompleteLayoutTransition:(id)context +{ + ASDisplayNodeAssertMainThread(); + + __instanceLock__.lock(); + ASLayoutTransition *pendingLayoutTransition = _pendingLayoutTransition; + __instanceLock__.unlock(); + + [pendingLayoutTransition applySubnodeRemovals]; +} + +/** + * Completes the pending layout transition immediately without going through the the Layout Transition Animation API + */ +- (void)_completePendingLayoutTransition +{ + __instanceLock__.lock(); + ASLayoutTransition *pendingLayoutTransition = _pendingLayoutTransition; + __instanceLock__.unlock(); + + if (pendingLayoutTransition != nil) { + [self _setCalculatedDisplayNodeLayout:pendingLayoutTransition.pendingLayout]; + [self _completeLayoutTransition:pendingLayoutTransition]; + [self _pendingLayoutTransitionDidComplete]; + } +} + +/** + * Can be directly called to commit the given layout transition immediately to complete without calling through to the + * Layout Transition Animation API + */ +- (void)_completeLayoutTransition:(ASLayoutTransition *)layoutTransition +{ + // Layout transition is not supported for nodes that do not have automatic subnode management enabled + if (layoutTransition == nil || self.automaticallyManagesSubnodes == NO) { + return; + } + + // Trampoline to the main thread if necessary + if (ASDisplayNodeThreadIsMain() || layoutTransition.isSynchronous == NO) { + // Committing the layout transition will result in subnode insertions and removals, both of which must be called without the lock held + // TODO: Disabled due to PR: https://github.com/TextureGroup/Texture/pull/1204 + DISABLED_ASAssertUnlocked(__instanceLock__); + [layoutTransition commitTransition]; + } else { + // Subnode insertions and removals need to happen always on the main thread if at least one subnode is already loaded + ASPerformBlockOnMainThread(^{ + [layoutTransition commitTransition]; + }); + } +} + +- (void)_assertSubnodeState +{ + // Verify that any orphaned nodes are removed. + // This can occur in rare cases if main thread layout is flushed while a background layout is calculating. + + if (self.automaticallyManagesSubnodes == NO) { + return; + } + + MutexLocker l(__instanceLock__); + NSArray *sublayouts = _calculatedDisplayNodeLayout.layout.sublayouts; + unowned ASLayout *cSublayouts[sublayouts.count]; + [sublayouts getObjects:cSublayouts range:NSMakeRange(0, AS_ARRAY_SIZE(cSublayouts))]; + + // Fast-path if we are in the correct state (likely). + if (_subnodes.count == AS_ARRAY_SIZE(cSublayouts)) { + NSUInteger i = 0; + BOOL matches = YES; + for (ASDisplayNode *subnode in _subnodes) { + if (subnode != cSublayouts[i].layoutElement) { + matches = NO; + } + i++; + } + if (matches) { + return; + } + } + + NSArray *layoutNodes = ASArrayByFlatMapping(sublayouts, ASLayout *layout, (ASDisplayNode *)layout.layoutElement); + NSIndexSet *insertions, *deletions; + [_subnodes asdk_diffWithArray:layoutNodes insertions:&insertions deletions:&deletions]; + if (insertions.count > 0) { + NSLog(@"Warning: node's layout includes subnode that has not been added: node = %@, subnodes = %@, subnodes in layout = %@", self, _subnodes, layoutNodes); + } + + // Remove any nodes that are in the tree but should not be. + // Go in reverse order so we don't shift our indexes. + if (deletions) { + for (NSUInteger i = deletions.lastIndex; i != NSNotFound; i = [deletions indexLessThanIndex:i]) { + NSLog(@"Automatically removing orphaned subnode %@, from parent %@", _subnodes[i], self); + [_subnodes[i] removeFromSupernode]; + } + } +} + +- (void)_pendingLayoutTransitionDidComplete +{ + // This assertion introduces a breaking behavior for nodes that has ASM enabled but also manually manage some subnodes. + // Let's gate it behind YOGA flag. +#if YOGA + [self _assertSubnodeState]; +#endif + + // Subclass hook + // TODO: Disabled due to PR: https://github.com/TextureGroup/Texture/pull/1204 + DISABLED_ASAssertUnlocked(__instanceLock__); + [self calculatedLayoutDidChange]; + + // Grab lock after calling out to subclass + MutexLocker l(__instanceLock__); + + // We generate placeholders at -layoutThatFits: time so that a node is guaranteed to have a placeholder ready to go. + // This is also because measurement is usually asynchronous, but placeholders need to be set up synchronously. + // First measurement is guaranteed to be before the node is onscreen, so we can create the image async. but still have it appear sync. + if (_flags.placeholderEnabled && !_placeholderImage && [self _locked_displaysAsynchronously]) { + + // Zero-sized nodes do not require a placeholder. + CGSize layoutSize = _calculatedDisplayNodeLayout.layout.size; + if (layoutSize.width * layoutSize.height <= 0.0) { + return; + } + + // If we've displayed our contents, we don't need a placeholder. + // Contents is a thread-affined property and can't be read off main after loading. + if (self.isNodeLoaded) { + ASPerformBlockOnMainThread(^{ + if (self.contents == nil) { + self->_placeholderImage = [self placeholderImage]; + } + }); + } else { + if (self.contents == nil) { + _placeholderImage = [self placeholderImage]; + } + } + } + + // Cleanup pending layout transition + _pendingLayoutTransition = nil; +} + +- (void)_setCalculatedDisplayNodeLayout:(const ASDisplayNodeLayout &)displayNodeLayout +{ + MutexLocker l(__instanceLock__); + [self _locked_setCalculatedDisplayNodeLayout:displayNodeLayout]; +} + +- (void)_locked_setCalculatedDisplayNodeLayout:(const ASDisplayNodeLayout &)displayNodeLayout +{ + DISABLED_ASAssertLocked(__instanceLock__); + ASDisplayNodeAssertTrue(displayNodeLayout.layout.layoutElement == self); + ASDisplayNodeAssertTrue(displayNodeLayout.layout.size.width >= 0.0); + ASDisplayNodeAssertTrue(displayNodeLayout.layout.size.height >= 0.0); + + _calculatedDisplayNodeLayout = displayNodeLayout; +} + +@end + +#pragma mark - +#pragma mark - ASDisplayNode (YogaLayout) + +@implementation ASDisplayNode (YogaLayout) + +- (BOOL)locked_shouldLayoutFromYogaRoot { +#if YOGA + YGNodeRef yogaNode = _style.yogaNode; + BOOL hasYogaParent = (_yogaParent != nil); + BOOL hasYogaChildren = (_yogaChildren.count > 0); + BOOL usesYoga = (yogaNode != NULL && (hasYogaParent || hasYogaChildren)); + if (usesYoga) { + if ([self shouldHaveYogaMeasureFunc] == NO) { + return YES; + } else { + return NO; + } + } else { + return NO; + } +#else + return NO; +#endif +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDisplayNode+LayoutSpec.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDisplayNode+LayoutSpec.h new file mode 100644 index 0000000..d7cd862 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDisplayNode+LayoutSpec.h @@ -0,0 +1,66 @@ +// +// ASDisplayNode+LayoutSpec.h +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +@class ASLayout; + +NS_ASSUME_NONNULL_BEGIN + +@interface ASDisplayNode (ASLayoutSpec) + +/** + * @abstract Provides a way to declare a block to provide an ASLayoutSpec without having to subclass ASDisplayNode and + * implement layoutSpecThatFits: + * + * @return A block that takes a constrainedSize ASSizeRange argument, and must return an ASLayoutSpec that includes all + * of the subnodes to position in the layout. This input-output relationship is identical to the subclass override + * method -layoutSpecThatFits: + * + * @warning Subclasses that implement -layoutSpecThatFits: must not also use .layoutSpecBlock. Doing so will trigger + * an exception. A future version of the framework may support using both, calling them serially, with the + * .layoutSpecBlock superseding any values set by the method override. + * + * @code ^ASLayoutSpec *(__kindof ASDisplayNode * _Nonnull node, ASSizeRange constrainedSize) {}; + */ +@property (nullable) ASLayoutSpecBlock layoutSpecBlock; + +@end + +// These methods are intended to be used internally to Texture, and should not be called directly. +@interface ASDisplayNode (ASLayoutSpecPrivate) + +/// For internal usage only +- (ASLayout *)calculateLayoutLayoutSpec:(ASSizeRange)constrainedSize; + +@end + +@interface ASDisplayNode (ASLayoutSpecSubclasses) + +/** + * @abstract Return a layout spec that describes the layout of the receiver and its children. + * + * @param constrainedSize The minimum and maximum sizes the receiver should fit in. + * + * @discussion Subclasses that override should expect this method to be called on a non-main thread. The returned layout spec + * is used to calculate an ASLayout and cached by ASDisplayNode for quick access during -layout. Other expensive work that needs to + * be done before display can be performed here, and using ivars to cache any valuable intermediate results is + * encouraged. + * + * @note This method should not be called directly outside of ASDisplayNode; use -layoutThatFits: instead. + * + * @warning Subclasses that implement -layoutSpecThatFits: must not use .layoutSpecBlock. Doing so will trigger an + * exception. A future version of the framework may support using both, calling them serially, with the .layoutSpecBlock + * superseding any values set by the method override. + */ +- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDisplayNode+LayoutSpec.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDisplayNode+LayoutSpec.mm new file mode 100644 index 0000000..93ce0e6 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDisplayNode+LayoutSpec.mm @@ -0,0 +1,141 @@ +// +// ASDisplayNode+LayoutSpec.mm +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import +#import +#import +#import +#import +#import + + +@implementation ASDisplayNode (ASLayoutSpec) + +- (void)setLayoutSpecBlock:(ASLayoutSpecBlock)layoutSpecBlock +{ + // For now there should never be an override of layoutSpecThatFits: and a layoutSpecBlock together. + ASDisplayNodeAssert(!(_methodOverrides & ASDisplayNodeMethodOverrideLayoutSpecThatFits), + @"Nodes with a .layoutSpecBlock must not also implement -layoutSpecThatFits:"); + AS::MutexLocker l(__instanceLock__); + _layoutSpecBlock = layoutSpecBlock; +} + +- (ASLayoutSpecBlock)layoutSpecBlock +{ + AS::MutexLocker l(__instanceLock__); + return _layoutSpecBlock; +} + +- (ASLayout *)calculateLayoutLayoutSpec:(ASSizeRange)constrainedSize +{ + AS::UniqueLock l(__instanceLock__); + + // Manual size calculation via calculateSizeThatFits: + if (_layoutSpecBlock == NULL && (_methodOverrides & ASDisplayNodeMethodOverrideLayoutSpecThatFits) == 0) { + CGSize size = [self calculateSizeThatFits:constrainedSize.max]; + return [ASLayout layoutWithLayoutElement:self size:ASSizeRangeClamp(constrainedSize, size) sublayouts:nil]; + } + + // Size calcualtion with layout elements + BOOL measureLayoutSpec = _measurementOptions & ASDisplayNodePerformanceMeasurementOptionLayoutSpec; + if (measureLayoutSpec) { + _layoutSpecNumberOfPasses++; + } + + // Get layout element from the node + id layoutElement = [self _locked_layoutElementThatFits:constrainedSize]; +#if ASEnableVerboseLogging + for (NSString *asciiLine in [[layoutElement asciiArtString] componentsSeparatedByString:@"\n"]) { + as_log_verbose(ASLayoutLog(), "%@", asciiLine); + } +#endif + + + // Certain properties are necessary to set on an element of type ASLayoutSpec + if (layoutElement.layoutElementType == ASLayoutElementTypeLayoutSpec) { + ASLayoutSpec *layoutSpec = (ASLayoutSpec *)layoutElement; + +#if AS_DEDUPE_LAYOUT_SPEC_TREE + NSHashTable *duplicateElements = [layoutSpec findDuplicatedElementsInSubtree]; + if (duplicateElements.count > 0) { + ASDisplayNodeFailAssert(@"Node %@ returned a layout spec that contains the same elements in multiple positions. Elements: %@", self, duplicateElements); + // Use an empty layout spec to avoid crashes + layoutSpec = [[ASLayoutSpec alloc] init]; + } +#endif + + ASDisplayNodeAssert(layoutSpec.isMutable, @"Node %@ returned layout spec %@ that has already been used. Layout specs should always be regenerated.", self, layoutSpec); + + layoutSpec.isMutable = NO; + } + + // Manually propagate the trait collection here so that any layoutSpec children of layoutSpec will get a traitCollection + { + AS::SumScopeTimer t(_layoutSpecTotalTime, measureLayoutSpec); + ASTraitCollectionPropagateDown(layoutElement, self.primitiveTraitCollection); + } + + BOOL measureLayoutComputation = _measurementOptions & ASDisplayNodePerformanceMeasurementOptionLayoutComputation; + if (measureLayoutComputation) { + _layoutComputationNumberOfPasses++; + } + + // Layout element layout creation + ASLayout *layout = ({ + AS::SumScopeTimer t(_layoutComputationTotalTime, measureLayoutComputation); + [layoutElement layoutThatFits:constrainedSize]; + }); + ASDisplayNodeAssertNotNil(layout, @"[ASLayoutElement layoutThatFits:] should never return nil! %@, %@", self, layout); + + // Make sure layoutElementObject of the root layout is `self`, so that the flattened layout will be structurally correct. + BOOL isFinalLayoutElement = (layout.layoutElement != self); + if (isFinalLayoutElement) { + layout.position = CGPointZero; + layout = [ASLayout layoutWithLayoutElement:self size:layout.size sublayouts:@[layout]]; + } + + // PR #1157: Reduces accuracy of _unflattenedLayout for debugging/Weaver + if ([ASDisplayNode shouldStoreUnflattenedLayouts]) { + _unflattenedLayout = layout; + } + layout = [layout filteredNodeLayoutTree]; + + return layout; +} + +- (id)_locked_layoutElementThatFits:(ASSizeRange)constrainedSize +{ + DISABLED_ASAssertLocked(__instanceLock__); + + BOOL measureLayoutSpec = _measurementOptions & ASDisplayNodePerformanceMeasurementOptionLayoutSpec; + + if (_layoutSpecBlock != NULL) { + return ({ + AS::MutexLocker l(__instanceLock__); + AS::SumScopeTimer t(_layoutSpecTotalTime, measureLayoutSpec); + _layoutSpecBlock(self, constrainedSize); + }); + } else { + return ({ + AS::SumScopeTimer t(_layoutSpecTotalTime, measureLayoutSpec); + [self layoutSpecThatFits:constrainedSize]; + }); + } +} + +- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize +{ + __ASDisplayNodeCheckForLayoutMethodOverrides; + + ASDisplayNodeAssert(NO, @"-[ASDisplayNode layoutSpecThatFits:] should never return an empty value. One way this is caused is by calling -[super layoutSpecThatFits:] which is not currently supported."); + return [[ASLayoutSpec alloc] init]; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDisplayNode+Subclasses.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDisplayNode+Subclasses.h new file mode 100644 index 0000000..9adb933 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDisplayNode+Subclasses.h @@ -0,0 +1,502 @@ +// +// ASDisplayNode+Subclasses.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import +#import + +@class ASLayoutSpec, _ASDisplayLayer; + +NS_ASSUME_NONNULL_BEGIN + +/** + * The subclass header _ASDisplayNode+Subclasses_ defines the following methods that either must or can be overriden by + * subclasses of ASDisplayNode. + * + * These methods should never be called directly by other classes. + * + * ## Drawing + * + * Implement one of +displayWithParameters:isCancelled: or +drawRect:withParameters:isCancelled: to provide + * drawing for your node. + * + * Use -drawParametersForAsyncLayer: to copy any properties that are involved in drawing into an immutable object for + * use on the display queue. The display and drawRect implementations *MUST* be thread-safe, as they can be called on + * the displayQueue (asynchronously) or the main thread (synchronously/displayImmediately). + * + * Class methods that require passing in copies of the values are used to minimize the need for locking around instance + * variable access, and the possibility of the asynchronous display pass grabbing an inconsistent state across multiple + * variables. + */ + +@interface ASDisplayNode (Subclassing) + +#pragma mark - Properties +/** @name Properties */ + +/** + * @abstract Return the calculated layout. + * + * @discussion For node subclasses that implement manual layout (e.g., they have a custom -layout method), + * calculatedLayout may be accessed on subnodes to retrieved cached information about their size. + * This allows -layout to be very fast, saving time on the main thread. + * Note: .calculatedLayout will only be set for nodes that have had -layoutThatFits: called on them. + * For manual layout, make sure you call -layoutThatFits: in your implementation of -calculateSizeThatFits:. + * + * For node subclasses that use automatic layout (e.g., they implement -layoutSpecThatFits:), + * it is typically not necessary to use .calculatedLayout at any point. For these nodes, + * the ASLayoutSpec implementation will automatically call -layoutThatFits: on all of the subnodes, + * and the ASDisplayNode base class implementation of -layout will automatically make use of .calculatedLayout on the subnodes. + * + * @return Layout that wraps calculated size returned by -calculateSizeThatFits: (in manual layout mode), + * or layout already calculated from layout spec returned by -layoutSpecThatFits: (in automatic layout mode). + * + * @warning Subclasses must not override this; it returns the last cached layout and is never expensive. + */ +@property (nullable, readonly) ASLayout *calculatedLayout; + +#pragma mark - View Lifecycle +/** @name View Lifecycle */ + +/** + * @abstract Called on the main thread immediately after self.view is created. + * + * @discussion This is the best time to add gesture recognizers to the view. + */ +AS_CATEGORY_IMPLEMENTABLE +- (void)didLoad ASDISPLAYNODE_REQUIRES_SUPER; + +/** + * An empty method that you can implement in a category to add global + * node initialization behavior. This method will be called by [ASDisplayNode init]. + */ +AS_CATEGORY_IMPLEMENTABLE +- (void)baseDidInit; + +/** + * An empty method that you can implement in a category to add global + * node deallocation behavior. This method will be called by [ASDisplayNode dealloc]. + */ +AS_CATEGORY_IMPLEMENTABLE +- (void)baseWillDealloc; + +#pragma mark - Layout +/** @name Layout */ + +/** + * @abstract Called on the main thread by the view's -layoutSubviews. + * + * @discussion Subclasses override this method to layout all subnodes or subviews. + */ +- (void)layout ASDISPLAYNODE_REQUIRES_SUPER; + +/** + * @abstract Called on the main thread by the view's -layoutSubviews, after -layout. + * + * @discussion Gives a chance for subclasses to perform actions after the subclass and superclass have finished laying + * out. + */ +AS_CATEGORY_IMPLEMENTABLE +- (void)layoutDidFinish ASDISPLAYNODE_REQUIRES_SUPER; + +/** + * @abstract Called on a background thread if !isNodeLoaded - called on the main thread if isNodeLoaded. + * + * @discussion When the .calculatedLayout property is set to a new ASLayout (directly from -calculateLayoutThatFits: or + * calculated via use of -layoutSpecThatFits:), subclasses may inspect it here. + */ +AS_CATEGORY_IMPLEMENTABLE +- (void)calculatedLayoutDidChange ASDISPLAYNODE_REQUIRES_SUPER; + + +#pragma mark - Layout calculation +/** @name Layout calculation */ + +/** + * @abstract Calculate a layout based on given size range. + * + * @param constrainedSize The minimum and maximum sizes the receiver should fit in. + * + * @return An ASLayout instance defining the layout of the receiver (and its children, if the box layout model is used). + * + * @discussion This method is called on a non-main thread. The default implementation calls either -layoutSpecThatFits: + * or -calculateSizeThatFits:, whichever method is overriden. Subclasses rarely need to override this method, + * override -layoutSpecThatFits: or -calculateSizeThatFits: instead. + * + * @note This method should not be called directly outside of ASDisplayNode; use -layoutThatFits: or -calculatedLayout instead. + */ +- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize; + +/** + * ASDisplayNode's implementation of -layoutThatFits:parentSize: calls this method to resolve the node's size + * against parentSize, intersect it with constrainedSize, and call -calculateLayoutThatFits: with the result. + * + * In certain advanced cases, you may want to customize this logic. Overriding this method allows you to receive all + * three parameters and do the computation yourself. + * + * @warning Overriding this method should be done VERY rarely. + */ +- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize + restrictedToSize:(ASLayoutElementSize)size + relativeToParentSize:(CGSize)parentSize; + +/** + * @abstract Return the calculated size. + * + * @param constrainedSize The maximum size the receiver should fit in. + * + * @discussion Subclasses that override should expect this method to be called on a non-main thread. The returned size + * is wrapped in an ASLayout and cached for quick access during -layout. Other expensive work that needs to + * be done before display can be performed here, and using ivars to cache any valuable intermediate results is + * encouraged. + * + * @note Subclasses that override are committed to manual layout. Therefore, -layout: must be overriden to layout all subnodes or subviews. + * + * @note This method should not be called directly outside of ASDisplayNode; use -layoutThatFits: or layoutThatFits:parentSize: instead. + */ +- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize; + +/** + * @abstract Invalidate previously measured and cached layout. + * + * @discussion Subclasses should call this method to invalidate the previously measured and cached layout for the display + * node, when the contents of the node change in such a way as to require measuring it again. + */ +- (void)invalidateCalculatedLayout; + +#pragma mark - Observing Node State Changes +/** @name Observing node state changes */ + +/** + * Declare methods as requiring super calls (this can't be required in the protocol). + * For descriptions, see definition. + */ + +AS_CATEGORY_IMPLEMENTABLE +- (void)didEnterVisibleState ASDISPLAYNODE_REQUIRES_SUPER; + +AS_CATEGORY_IMPLEMENTABLE +- (void)didExitVisibleState ASDISPLAYNODE_REQUIRES_SUPER; + +AS_CATEGORY_IMPLEMENTABLE +- (void)didEnterDisplayState ASDISPLAYNODE_REQUIRES_SUPER; + +AS_CATEGORY_IMPLEMENTABLE +- (void)didExitDisplayState ASDISPLAYNODE_REQUIRES_SUPER; + +AS_CATEGORY_IMPLEMENTABLE +- (void)didEnterPreloadState ASDISPLAYNODE_REQUIRES_SUPER; + +AS_CATEGORY_IMPLEMENTABLE +- (void)didExitPreloadState ASDISPLAYNODE_REQUIRES_SUPER; + +AS_CATEGORY_IMPLEMENTABLE +- (void)interfaceStateDidChange:(ASInterfaceState)newState + fromState:(ASInterfaceState)oldState ASDISPLAYNODE_REQUIRES_SUPER; + +/** + * @abstract Called when the node's ASTraitCollection changes + * + * @discussion Subclasses can override this method to react to a trait collection change. + * + * @param previousTraitCollection The ASPrimitiveTraitCollection object before the interface environment changed. + */ +AS_CATEGORY_IMPLEMENTABLE +- (void)asyncTraitCollectionDidChangeWithPreviousTraitCollection:(ASPrimitiveTraitCollection)previousTraitCollection ASDISPLAYNODE_REQUIRES_SUPER; + +#pragma mark - Drawing +/** @name Drawing */ + +/** + * @summary Delegate method to draw layer contents into a CGBitmapContext. The current UIGraphics context will be set + * to an appropriate context. + * + * @param bounds Region to draw in. + * @param parameters An object describing all of the properties you need to draw. Return this from + * -drawParametersForAsyncLayer: + * @param isCancelledBlock Execute this block to check whether the current drawing operation has been cancelled to avoid + * unnecessary work. A return value of YES means cancel drawing and return. + * @param isRasterizing YES if the layer is being rasterized into another layer, in which case drawRect: probably wants + * to avoid doing things like filling its bounds with a zero-alpha color to clear the backing store. + * + * @note Called on the display queue and/or main queue (MUST BE THREAD SAFE) + */ ++ (void)drawRect:(CGRect)bounds withParameters:(nullable id)parameters + isCancelled:(AS_NOESCAPE asdisplaynode_iscancelled_block_t)isCancelledBlock + isRasterizing:(BOOL)isRasterizing; + +/** + * @summary Delegate override to provide new layer contents as a UIImage. + * + * @param parameters An object describing all of the properties you need to draw. Return this from + * -drawParametersForAsyncLayer: + * @param isCancelledBlock Execute this block to check whether the current drawing operation has been cancelled to avoid + * unnecessary work. A return value of YES means cancel drawing and return. + * + * @return A UIImage with contents that are ready to display on the main thread. Make sure that the image is already + * decoded before returning it here. + * + * @note Called on the display queue and/or main queue (MUST BE THREAD SAFE) + */ ++ (nullable UIImage *)displayWithParameters:(nullable id)parameters + isCancelled:(AS_NOESCAPE asdisplaynode_iscancelled_block_t)isCancelledBlock; + +/** + * @abstract Delegate override for drawParameters + * + * @param layer The layer that will be drawn into. + * + * @note Called on the main thread only + */ +- (nullable id)drawParametersForAsyncLayer:(_ASDisplayLayer *)layer; + +/** + * @abstract Indicates that the receiver is about to display. + * + * @discussion Deprecated in 2.5. + * + * @discussion Subclasses may override this method to be notified when display (asynchronous or synchronous) is + * about to begin. + * + * @note Called on the main thread only + */ +- (void)displayWillStart ASDISPLAYNODE_REQUIRES_SUPER ASDISPLAYNODE_DEPRECATED_MSG("Use displayWillStartAsynchronously: instead."); + +/** + * @abstract Indicates that the receiver is about to display. + * + * @discussion Subclasses may override this method to be notified when display (asynchronous or synchronous) is + * about to begin. + * + * @note Called on the main thread only + */ +- (void)displayWillStartAsynchronously:(BOOL)asynchronously ASDISPLAYNODE_REQUIRES_SUPER; + +/** + * @abstract Indicates that the receiver has finished displaying. + * + * @discussion Subclasses may override this method to be notified when display (asynchronous or synchronous) has + * completed. + * + * @note Called on the main thread only + */ +- (void)displayDidFinish ASDISPLAYNODE_REQUIRES_SUPER; + +/** + * Called just before the view is added to a window. + */ +- (void)willEnterHierarchy ASDISPLAYNODE_REQUIRES_SUPER; + +/** + * Called after the view is removed from the window. + */ +- (void)didExitHierarchy ASDISPLAYNODE_REQUIRES_SUPER; + +/** + * Called just after the view is added to a window. + * Note: this may be called multiple times during view controller transitions. To overcome this: use didEnterVisibleState or its equavalents. + */ +- (void)didEnterHierarchy ASDISPLAYNODE_REQUIRES_SUPER; + +/** + * @abstract Whether the view or layer of this display node is currently in a window + */ +@property (readonly, getter=isInHierarchy) BOOL inHierarchy; + +/** + * Provides an opportunity to clear backing store and other memory-intensive intermediates, such as text layout managers + * on the current node. + * + * @discussion Called by -recursivelyClearContents. Always called on main thread. Base class implements self.contents = nil, clearing any backing + * store, for asynchronous regeneration when needed. + */ +- (void)clearContents ASDISPLAYNODE_REQUIRES_SUPER; + +/** + * @abstract Indicates that the receiver is about to display its subnodes. This method is not called if there are no + * subnodes present. + * + * @param subnode The subnode of which display is about to begin. + * + * @discussion Subclasses may override this method to be notified when subnode display (asynchronous or synchronous) is + * about to begin. + */ +- (void)subnodeDisplayWillStart:(ASDisplayNode *)subnode ASDISPLAYNODE_REQUIRES_SUPER; + +/** + * @abstract Indicates that the receiver is finished displaying its subnodes. This method is not called if there are + * no subnodes present. + * + * @param subnode The subnode of which display is about to completed. + * + * @discussion Subclasses may override this method to be notified when subnode display (asynchronous or synchronous) has + * completed. + */ +- (void)subnodeDisplayDidFinish:(ASDisplayNode *)subnode ASDISPLAYNODE_REQUIRES_SUPER; + +/** + * @abstract Marks the receiver's bounds as needing to be redrawn, with a scale value. + * + * @param contentsScale The scale at which the receiver should be drawn. + * + * @discussion Subclasses should override this if they don't want their contentsScale changed. + * + * @note This changes an internal property. + * -setNeedsDisplay is also available to trigger display without changing contentsScaleForDisplay. + * @see -setNeedsDisplay, contentsScaleForDisplay + */ +- (void)setNeedsDisplayAtScale:(CGFloat)contentsScale; + +/** + * @abstract Recursively calls setNeedsDisplayAtScale: on subnodes. + * + * @param contentsScale The scale at which the receiver's subnode hierarchy should be drawn. + * + * @discussion Subclasses may override this if they require modifying the scale set on their child nodes. + * + * @note Only the node tree is walked, not the view or layer trees. + * + * @see setNeedsDisplayAtScale: + * @see contentsScaleForDisplay + */ +- (void)recursivelySetNeedsDisplayAtScale:(CGFloat)contentsScale; + +/** + * @abstract The scale factor to apply to the rendering. + * + * @discussion Use setNeedsDisplayAtScale: to set a value and then after display, the display node will set the layer's + * contentsScale. This is to prevent jumps when re-rasterizing at a different contentsScale. + * Read this property if you need to know the future contentsScale of your layer, eg in drawParameters. + * + * @see setNeedsDisplayAtScale: + */ +@property (readonly) CGFloat contentsScaleForDisplay; + +/** + * Called as part of actionForLayer:forKey:. Gives the node a chance to provide a custom action for its layer. + * + * The default implementation returns NSNull, indicating that no action should be taken. + */ +AS_CATEGORY_IMPLEMENTABLE +- (nullable id)layerActionForKey:(NSString *)event; + +#pragma mark - Touch handling +/** @name Touch handling */ + +/** + * @abstract Tells the node when touches began in its view. + * + * @param touches A set of UITouch instances. + * @param event A UIEvent associated with the touch. + */ +- (void)touchesBegan:(NSSet *)touches withEvent:(nullable UIEvent *)event ASDISPLAYNODE_REQUIRES_SUPER; + +/** + * @abstract Tells the node when touches moved in its view. + * + * @param touches A set of UITouch instances. + * @param event A UIEvent associated with the touch. + */ +- (void)touchesMoved:(NSSet *)touches withEvent:(nullable UIEvent *)event ASDISPLAYNODE_REQUIRES_SUPER; + +/** + * @abstract Tells the node when touches ended in its view. + * + * @param touches A set of UITouch instances. + * @param event A UIEvent associated with the touch. + */ +- (void)touchesEnded:(NSSet *)touches withEvent:(nullable UIEvent *)event ASDISPLAYNODE_REQUIRES_SUPER; + +/** + * @abstract Tells the node when touches was cancelled in its view. + * + * @param touches A set of UITouch instances. + * @param event A UIEvent associated with the touch. + */ +- (void)touchesCancelled:(nullable NSSet *)touches withEvent:(nullable UIEvent *)event ASDISPLAYNODE_REQUIRES_SUPER; + + +#pragma mark - Managing Gesture Recognizers +/** @name Managing Gesture Recognizers */ + +/** + * @abstract Asks the node if a gesture recognizer should continue tracking touches. + * + * @param gestureRecognizer A gesture recognizer trying to recognize a gesture. + */ +- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer; + + +#pragma mark - Hit Testing + +/** @name Hit Testing */ + +/** + * @abstract Returns the view that contains the point. + * + * @discussion Override to make this node respond differently to touches: (e.g. hide touches from subviews, send all + * touches to certain subviews (hit area maximizing), etc.) + * + * @param point A point specified in the node's local coordinate system (bounds). + * @param event The event that warranted a call to this method. + * + * @return Returns a UIView, not ASDisplayNode, for two reasons: + * 1) allows sending events to plain UIViews that don't have attached nodes, + * 2) hitTest: is never called before the views are created. + */ +- (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event; + + +#pragma mark - Placeholders +/** @name Placeholders */ + +/** + * @abstract Optionally provide an image to serve as the placeholder for the backing store while the contents are being + * displayed. + * + * @discussion + * Subclasses may override this method and return an image to use as the placeholder. Take caution as there may be a + * time and place where this method is called on a background thread. Note that -[UIImage imageNamed:] is not thread + * safe when using image assets. + * + * To retrieve the CGSize to do any image drawing, use the node's calculatedSize property. + * + * Defaults to nil. + * + * @note Called on the display queue and/or main queue (MUST BE THREAD SAFE) + */ +- (nullable UIImage *)placeholderImage; + + +#pragma mark - Description +/** @name Description */ + +/** + * @abstract Return a description of the node + * + * @discussion The function that gets called for each display node in -recursiveDescription + */ +- (NSString *)descriptionForRecursiveDescription; + +@end + + +// Check that at most a layoutSpecBlock or one of the three layout methods is overridden +#define __ASDisplayNodeCheckForLayoutMethodOverrides \ + ASDisplayNodeAssert(_layoutSpecBlock != NULL || \ + ((ASDisplayNodeSubclassOverridesSelector(self.class, @selector(calculateSizeThatFits:)) ? 1 : 0) \ + + (ASDisplayNodeSubclassOverridesSelector(self.class, @selector(layoutSpecThatFits:)) ? 1 : 0) \ + + (ASDisplayNodeSubclassOverridesSelector(self.class, @selector(calculateLayoutThatFits:)) ? 1 : 0)) <= 1, \ + @"Subclass %@ must at least provide a layoutSpecBlock or override at most one of the three layout methods: calculateLayoutThatFits:, layoutSpecThatFits:, or calculateSizeThatFits:", NSStringFromClass(self.class)) + +#define ASDisplayNodeAssertThreadAffinity(viewNode) ASDisplayNodeAssert(!viewNode || ASMainThreadAssertionsAreDisabled() || ASDisplayNodeThreadIsMain() || !(viewNode).nodeLoaded, @"Incorrect display node thread affinity - this method should not be called off the main thread after the ASDisplayNode's view or layer have been created") +#define ASDisplayNodeCAssertThreadAffinity(viewNode) ASDisplayNodeCAssert(!viewNode || ASMainThreadAssertionsAreDisabled() || ASDisplayNodeThreadIsMain() || !(viewNode).nodeLoaded, @"Incorrect display node thread affinity - this method should not be called off the main thread after the ASDisplayNode's view or layer have been created") + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDisplayNode+Yoga.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDisplayNode+Yoga.h new file mode 100644 index 0000000..000bb90 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDisplayNode+Yoga.h @@ -0,0 +1,103 @@ +// +// ASDisplayNode+Yoga.h +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#if YOGA + +NS_ASSUME_NONNULL_BEGIN + +@class ASLayout; + +ASDK_EXTERN void ASDisplayNodePerformBlockOnEveryYogaChild(ASDisplayNode * _Nullable node, void(^block)(ASDisplayNode *node)); + +@interface ASDisplayNode (Yoga) + +@property (copy) NSArray *yogaChildren; + +- (void)addYogaChild:(ASDisplayNode *)child; +- (void)removeYogaChild:(ASDisplayNode *)child; +- (void)insertYogaChild:(ASDisplayNode *)child atIndex:(NSUInteger)index; + +- (void)semanticContentAttributeDidChange:(UISemanticContentAttribute)attribute; + +@property BOOL yogaLayoutInProgress; +// TODO: Make this atomic (lock). +@property (nullable, nonatomic) ASLayout *yogaCalculatedLayout; +@property (nonatomic) BOOL willApplyNextYogaCalculatedLayout; + +// Will walk up the Yoga tree and returns the root node +- (ASDisplayNode *)yogaRoot; + + +@end + +@interface ASDisplayNode (YogaLocking) +/** + * @discussion Attempts(spinning) to lock all node up to root node when yoga is enabled. + * This will lock self when yoga is not enabled; + */ +- (ASLockSet)lockToRootIfNeededForLayout; + +@end + + +// These methods are intended to be used internally to Texture, and should not be called directly. +@interface ASDisplayNode (YogaInternal) + +/// For internal usage only +- (BOOL)shouldHaveYogaMeasureFunc; +/// For internal usage only +- (ASLayout *)calculateLayoutYoga:(ASSizeRange)constrainedSize; +/// For internal usage only +- (void)calculateLayoutFromYogaRoot:(ASSizeRange)rootConstrainedSize willApply:(BOOL)willApply; +/// For internal usage only +- (void)invalidateCalculatedYogaLayout; +/** + * @discussion return true only when yoga enabled and the node is in yoga tree and the node is + * not leaf that implemented measure function. + */ +- (BOOL)locked_shouldLayoutFromYogaRoot; + +@end + +@interface ASDisplayNode (YogaDebugging) + +- (NSString *)yogaTreeDescription; + +@end + +@interface ASLayoutElementStyle (Yoga) + +- (YGNodeRef)yogaNodeCreateIfNeeded; +- (void)destroyYogaNode; + +@property (readonly) YGNodeRef yogaNode; + +@property ASStackLayoutDirection flexDirection; +@property YGDirection direction; +@property ASStackLayoutJustifyContent justifyContent; +@property ASStackLayoutAlignItems alignItems; +@property YGPositionType positionType; +@property ASEdgeInsets position; +@property ASEdgeInsets margin; +@property ASEdgeInsets padding; +@property ASEdgeInsets border; +@property CGFloat aspectRatio; +@property YGWrap flexWrap; + +@end + +NS_ASSUME_NONNULL_END + +// When Yoga is enabled, there are several points where we want to lock the tree to the root but otherwise (without Yoga) +// will want to simply lock self. +#define ASScopedLockSelfOrToRoot() ASScopedLockSet lockSet = [self lockToRootIfNeededForLayout] +#else +#define ASScopedLockSelfOrToRoot() ASLockScopeSelf() +#endif diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDisplayNode+Yoga.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDisplayNode+Yoga.mm new file mode 100644 index 0000000..619f5a7 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDisplayNode+Yoga.mm @@ -0,0 +1,489 @@ +// +// ASDisplayNode+Yoga.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#if YOGA /* YOGA */ + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + +#import + +#define YOGA_LAYOUT_LOGGING 0 + +#pragma mark - ASDisplayNode+Yoga + +@interface ASDisplayNode (YogaPrivate) +@property (nonatomic, weak) ASDisplayNode *yogaParent; +- (ASSizeRange)_locked_constrainedSizeForLayoutPass; +@end + +@implementation ASDisplayNode (Yoga) + +- (ASDisplayNode *)yogaRoot +{ + ASDisplayNode *yogaRoot = self; + ASDisplayNode *yogaParent = nil; + while ((yogaParent = yogaRoot.yogaParent)) { + yogaRoot = yogaParent; + } + return yogaRoot; +} + +- (void)setYogaChildren:(NSArray *)yogaChildren +{ + ASScopedLockSelfOrToRoot(); + for (ASDisplayNode *child in [_yogaChildren copy]) { + // Make sure to un-associate the YGNodeRef tree before replacing _yogaChildren + // If this becomes a performance bottleneck, it can be optimized by not doing the NSArray removals here. + [self _locked_removeYogaChild:child]; + } + _yogaChildren = nil; + for (ASDisplayNode *child in yogaChildren) { + [self _locked_addYogaChild:child]; + } +} + +- (NSArray *)yogaChildren +{ + ASLockScope(self.yogaRoot); + return [_yogaChildren copy] ?: @[]; +} + +- (void)addYogaChild:(ASDisplayNode *)child +{ + ASScopedLockSelfOrToRoot(); + [self _locked_addYogaChild:child]; +} + +- (void)_locked_addYogaChild:(ASDisplayNode *)child +{ + [self insertYogaChild:child atIndex:_yogaChildren.count]; +} + +- (void)removeYogaChild:(ASDisplayNode *)child +{ + ASScopedLockSelfOrToRoot(); + [self _locked_removeYogaChild:child]; +} + +- (void)_locked_removeYogaChild:(ASDisplayNode *)child +{ + if (child == nil) { + return; + } + + [_yogaChildren removeObjectIdenticalTo:child]; + + // YGNodeRef removal is done in setParent: + child.yogaParent = nil; + [self setNeedsLayout]; +} + +- (void)insertYogaChild:(ASDisplayNode *)child atIndex:(NSUInteger)index +{ + ASScopedLockSelfOrToRoot(); + [self _locked_insertYogaChild:child atIndex:index]; +} + +- (void)_locked_insertYogaChild:(ASDisplayNode *)child atIndex:(NSUInteger)index +{ + if (child == nil) { + return; + } + if (_yogaChildren == nil) { + _yogaChildren = [[NSMutableArray alloc] init]; + } + + // Clean up state in case this child had another parent. + [self _locked_removeYogaChild:child]; + + [_yogaChildren insertObject:child atIndex:index]; + + // YGNodeRef insertion is done in setParent: + child.yogaParent = self; + [self setNeedsLayout]; +} + +#pragma mark - Subclass Hooks + +- (void)semanticContentAttributeDidChange:(UISemanticContentAttribute)attribute +{ + UIUserInterfaceLayoutDirection layoutDirection = + [UIView userInterfaceLayoutDirectionForSemanticContentAttribute:attribute]; + self.style.direction = (layoutDirection == UIUserInterfaceLayoutDirectionLeftToRight + ? YGDirectionLTR : YGDirectionRTL); +} + +- (void)setYogaParent:(ASDisplayNode *)yogaParent +{ + ASLockScopeSelf(); + if (_yogaParent == yogaParent) { + return; + } + + YGNodeRef yogaNode = [self.style yogaNodeCreateIfNeeded]; + YGNodeRef oldParentRef = YGNodeGetParent(yogaNode); + if (oldParentRef != NULL) { + YGNodeRemoveChild(oldParentRef, yogaNode); + } + + _yogaParent = yogaParent; + if (yogaParent) { + YGNodeRef newParentRef = [yogaParent.style yogaNodeCreateIfNeeded]; + YGNodeInsertChild(newParentRef, yogaNode, YGNodeGetChildCount(newParentRef)); + } +} + +- (ASDisplayNode *)yogaParent +{ + return _yogaParent; +} + +- (void)setYogaCalculatedLayout:(ASLayout *)yogaCalculatedLayout +{ + _yogaCalculatedLayout = yogaCalculatedLayout; +} + +- (ASLayout *)yogaCalculatedLayout +{ + return _yogaCalculatedLayout; +} + +- (BOOL)willApplyNextYogaCalculatedLayout { + return _flags.willApplyNextYogaCalculatedLayout; +} + +- (void)setWillApplyNextYogaCalculatedLayout:(BOOL)willApplyNextYogaCalculatedLayout { + _flags.willApplyNextYogaCalculatedLayout = willApplyNextYogaCalculatedLayout; +} + +- (void)setYogaLayoutInProgress:(BOOL)yogaLayoutInProgress +{ + setFlag(YogaLayoutInProgress, yogaLayoutInProgress); + [self updateYogaMeasureFuncIfNeeded]; +} + +- (BOOL)yogaLayoutInProgress +{ + return checkFlag(YogaLayoutInProgress); +} + +- (ASLayout *)layoutForYogaNode +{ + YGNodeRef yogaNode = self.style.yogaNode; + + CGSize size = CGSizeMake(YGNodeLayoutGetWidth(yogaNode), YGNodeLayoutGetHeight(yogaNode)); + CGPoint position = CGPointMake(YGNodeLayoutGetLeft(yogaNode), YGNodeLayoutGetTop(yogaNode)); + + if (!ASIsCGSizeValidForSize(size)) { + size = CGSizeZero; + } + + if (!ASIsCGPositionValidForLayout(position)) { + position = CGPointZero; + } + return [ASLayout layoutWithLayoutElement:self size:size position:position sublayouts:nil]; +} + +- (void)setupYogaCalculatedLayoutAndSetNeedsLayoutForChangedNodes:(BOOL)setNeedsLayoutForChangedNodes +{ + ASScopedLockSelfOrToRoot(); + + YGNodeRef yogaNode = self.style.yogaNode; + uint32_t childCount = YGNodeGetChildCount(yogaNode); + ASDisplayNodeAssert(childCount == _yogaChildren.count, + @"Yoga tree should always be in sync with .yogaNodes array! %@", + _yogaChildren); + + ASLayout *rawSublayouts[childCount]; + int i = 0; + for (ASDisplayNode *subnode in _yogaChildren) { + rawSublayouts[i++] = [subnode layoutForYogaNode]; + } + const auto sublayouts = [NSArray arrayByTransferring:rawSublayouts count:childCount]; + + // The layout for self should have position CGPointNull, but include the calculated size. + CGSize size = CGSizeMake(YGNodeLayoutGetWidth(yogaNode), YGNodeLayoutGetHeight(yogaNode)); + if (!ASIsCGSizeValidForSize(size)) { + size = CGSizeZero; + } + ASLayout *layout = [ASLayout layoutWithLayoutElement:self size:size sublayouts:sublayouts]; + +#if ASDISPLAYNODE_ASSERTIONS_ENABLED + // Assert that the sublayout is already flattened. + for (ASLayout *sublayout in layout.sublayouts) { + if (sublayout.sublayouts.count > 0 || ASDynamicCast(sublayout.layoutElement, ASDisplayNode) == nil) { + ASDisplayNodeAssert(NO, @"Yoga sublayout is not flattened! %@, %@", self, sublayout); + } + } +#endif + + // Because this layout won't go through the rest of the logic in calculateLayoutThatFits:, flatten it now. + layout = [layout filteredNodeLayoutTree]; + + if ([self.yogaCalculatedLayout isEqual:layout] == NO) { + if (setNeedsLayoutForChangedNodes && !self.willApplyNextYogaCalculatedLayout) { + // This flag will be set when this layout is intended for immediate display. In this case, we + // want to ensure that we call setNeedsLayout on any other nodes. Note that we skip any nodes + // whose willApplyNextYogaCalculatedLayout flags are set, as those are the nodes that are + // already being laid out. + [self setNeedsLayout]; + } + self.yogaCalculatedLayout = layout; + } else { + layout = self.yogaCalculatedLayout; + ASYogaLog("-setupYogaCalculatedLayout: applying identical ASLayout: %@", layout); + } + + // Setup _pendingDisplayNodeLayout to reference the Yoga-calculated ASLayout, *unless* we are a leaf node. + // Leaf yoga nodes may have their own .sublayouts, if they use a layout spec (such as ASButtonNode). + // Their _pending variable is set after passing the Yoga checks at the start of -calculateLayoutThatFits: + + // For other Yoga nodes, there is no code that will set _pending unless we do it here. Why does it need to be set? + // When CALayer triggers the -[ASDisplayNode __layout] call, we will check if our current _pending layout + // has a size which matches our current bounds size. If it does, that layout will be used without recomputing it. + + // NOTE: Yoga does not make the constrainedSize available to intermediate nodes in the tree (e.g. not root or leaves). + // Although the size range provided here is not accurate, this will only affect caching of calls to layoutThatFits: + // These calls will behave as if they are not cached, starting a new Yoga layout pass, but this will tap into Yoga's + // own internal cache. + + if ([self shouldHaveYogaMeasureFunc] == NO) { + YGNodeRef parentNode = YGNodeGetParent(yogaNode); + CGSize parentSize = CGSizeZero; + if (parentNode) { + parentSize.width = YGNodeLayoutGetWidth(parentNode); + parentSize.height = YGNodeLayoutGetHeight(parentNode); + } + // For the root node in a Yoga tree, make sure to preserve the constrainedSize originally provided. + // This will be used for all relayouts triggered by children, since they escalate to root. + ASSizeRange range = parentNode ? ASSizeRangeUnconstrained : self.constrainedSizeForCalculatedLayout; + _pendingDisplayNodeLayout = ASDisplayNodeLayout(layout, range, parentSize, _layoutVersion); + } +} + +- (BOOL)shouldHaveYogaMeasureFunc +{ + ASLockScopeSelf(); + // Size calculation via calculateSizeThatFits: or layoutSpecThatFits: + // For these nodes, we assume they may need custom Baseline calculation too. + // This will be used for ASTextNode, as well as any other node that has no Yoga children + BOOL isLeafNode = (_yogaChildren.count == 0); + BOOL definesCustomLayout = [self implementsLayoutMethod]; + return (isLeafNode && definesCustomLayout); +} + +- (void)updateYogaMeasureFuncIfNeeded +{ + // We set the measure func only during layout. Otherwise, a cycle is created: + // The YGNodeRef Context will retain the ASDisplayNode, which retains the style, which owns the YGNodeRef. + BOOL shouldHaveMeasureFunc = ([self shouldHaveYogaMeasureFunc] && checkFlag(YogaLayoutInProgress)); + + ASLayoutElementYogaUpdateMeasureFunc(self.style.yogaNode, shouldHaveMeasureFunc ? self : nil); +} + +- (void)invalidateCalculatedYogaLayout +{ + ASLockScopeSelf(); + YGNodeRef yogaNode = self.style.yogaNode; + if (yogaNode && [self shouldHaveYogaMeasureFunc]) { + // Yoga internally asserts that MarkDirty() may only be called on nodes with a measurement function. + BOOL needsTemporaryMeasureFunc = (YGNodeGetMeasureFunc(yogaNode) == NULL); + if (needsTemporaryMeasureFunc) { + ASDisplayNodeAssert(self.yogaLayoutInProgress == NO, + @"shouldHaveYogaMeasureFunc == YES, and inside a layout pass, but no measure func pointer! %@", self); + YGNodeSetMeasureFunc(yogaNode, &ASLayoutElementYogaMeasureFunc); + } + YGNodeMarkDirty(yogaNode); + if (needsTemporaryMeasureFunc) { + YGNodeSetMeasureFunc(yogaNode, NULL); + } + } + self.yogaCalculatedLayout = nil; +} + +- (ASLayout *)calculateLayoutYoga:(ASSizeRange)constrainedSize +{ + AS::UniqueLock l(__instanceLock__); + + // There are several cases where Yoga could arrive here: + // - This node is not in a Yoga tree: it has neither a yogaParent nor yogaChildren. + // - This node is a Yoga tree root: it has no yogaParent, but has yogaChildren. + // - This node is a Yoga tree node: it has both a yogaParent and yogaChildren. + // - This node is a Yoga tree leaf: it has a yogaParent, but no yogaChidlren. + if ([self locked_shouldLayoutFromYogaRoot]) { + // If we're a yoga root, tree node, or leaf with no measure func (e.g. spacer), then + // initiate a new Yoga calculation pass from root. + as_activity_create_for_scope("Yoga layout calculation"); + if (self.yogaLayoutInProgress == NO) { + ASYogaLog("Calculating yoga layout from root %@, %@", self, + NSStringFromASSizeRange(constrainedSize)); + [self calculateLayoutFromYogaRoot:constrainedSize willApply:self.willApplyNextYogaCalculatedLayout]; + } else { + ASYogaLog("Reusing existing yoga layout %@", _yogaCalculatedLayout); + } + ASDisplayNodeAssert(_yogaCalculatedLayout, + @"Yoga node should have a non-nil layout at this stage: %@", self); + return _yogaCalculatedLayout; + } else { + // If we're a yoga leaf node with custom measurement function, proceed with normal layout so + // layoutSpecs can run (e.g. ASButtonNode). + ASYogaLog("PROCEEDING past Yoga check to calculate ASLayout for: %@", self); + } + + // Delegate to layout spec layout for nodes that do not support Yoga + return [self calculateLayoutLayoutSpec:constrainedSize]; +} + +- (void)calculateLayoutFromYogaRoot:(ASSizeRange)rootConstrainedSize willApply:(BOOL)willApply +{ + ASScopedLockSet lockSet = [self lockToRootIfNeededForLayout]; + ASDisplayNode *yogaRoot = self.yogaRoot; + + if (self != yogaRoot) { + ASYogaLog("ESCALATING to Yoga root: %@", self); + // TODO(appleguy): Consider how to get the constrainedSize for the yogaRoot when escalating manually. + [yogaRoot calculateLayoutFromYogaRoot:ASSizeRangeUnconstrained willApply:willApply]; + return; + } + + if (ASSizeRangeEqualToSizeRange(rootConstrainedSize, ASSizeRangeUnconstrained)) { + rootConstrainedSize = [self _locked_constrainedSizeForLayoutPass]; + } + + [self willCalculateLayout:rootConstrainedSize]; + [self enumerateInterfaceStateDelegates:^(id _Nonnull delegate) { + if ([delegate respondsToSelector:@selector(nodeWillCalculateLayout:)]) { + [delegate nodeWillCalculateLayout:rootConstrainedSize]; + } + }]; + + // Prepare all children for the layout pass with the current Yoga tree configuration. + ASDisplayNodePerformBlockOnEveryYogaChild(self, ^(ASDisplayNode *_Nonnull node) { + node.yogaLayoutInProgress = YES; + ASDisplayNode *yogaParent = node.yogaParent; + if (yogaParent) { + node.style.parentAlignStyle = yogaParent.style.alignItems; + } else { + node.style.parentAlignStyle = ASStackLayoutAlignItemsNotSet; + }; + }); + + ASYogaLog("CALCULATING at Yoga root with constraint = {%@, %@}: %@", + NSStringFromCGSize(rootConstrainedSize.min), NSStringFromCGSize(rootConstrainedSize.max), self); + + YGNodeRef rootYogaNode = self.style.yogaNode; + + // Apply the constrainedSize as a base, known frame of reference. + // If the root node also has style.*Size set, these will be overridden below. + // YGNodeCalculateLayout currently doesn't offer the ability to pass a minimum size (max is passed there). + + // TODO(appleguy): Reconcile the self.style.*Size properties with rootConstrainedSize + YGNodeStyleSetMinWidth (rootYogaNode, yogaFloatForCGFloat(rootConstrainedSize.min.width)); + YGNodeStyleSetMinHeight(rootYogaNode, yogaFloatForCGFloat(rootConstrainedSize.min.height)); + + // It is crucial to use yogaFloat... to convert CGFLOAT_MAX into YGUndefined here. + YGNodeCalculateLayout(rootYogaNode, + yogaFloatForCGFloat(rootConstrainedSize.max.width), + yogaFloatForCGFloat(rootConstrainedSize.max.height), + YGDirectionInherit); + + // Reset accessible elements, since layout may have changed. + ASPerformBlockOnMainThread(^{ + if (self.nodeLoaded && !self.isSynchronous) { + self.view.accessibilityElements = nil; + } + }); + + ASDisplayNodePerformBlockOnEveryYogaChild(self, ^(ASDisplayNode * _Nonnull node) { + [node setupYogaCalculatedLayoutAndSetNeedsLayoutForChangedNodes:willApply]; + node.yogaLayoutInProgress = NO; + }); + +#if YOGA_LAYOUT_LOGGING /* YOGA_LAYOUT_LOGGING */ + // Concurrent layouts will interleave the NSLog messages unless we serialize. + // Use @synchornize rather than trampolining to the main thread so the tree state isn't changed. + @synchronized ([ASDisplayNode class]) { + NSLog(@"****************************************************************************"); + NSLog(@"******************** STARTING YOGA -> ASLAYOUT CREATION ********************"); + NSLog(@"****************************************************************************"); + ASDisplayNodePerformBlockOnEveryYogaChild(self, ^(ASDisplayNode * _Nonnull node) { + NSLog(@"node = %@", node); + YGNodePrint(node.style.yogaNode, (YGPrintOptions)(YGPrintOptionsStyle | YGPrintOptionsLayout)); + NSCAssert(ASIsCGSizeValidForSize(node.yogaCalculatedLayout.size), @"Yoga layout returned an invalid size"); + NSLog(@" "); // Newline + }); + } +#endif /* YOGA_LAYOUT_LOGGING */ +} + +@end + +#pragma mark - ASDisplayNode (YogaLocking) + +@implementation ASDisplayNode (YogaLocking) + +- (ASLockSet)lockToRootIfNeededForLayout { + ASLockSet lockSet = ASLockSequence(^BOOL(ASAddLockBlock addLock) { + if (!addLock(self)) { + return NO; + } +#if YOGA + if (![self locked_shouldLayoutFromYogaRoot]) { + return YES; + } + if (self.nodeController && !addLock(self.nodeController)) { + return NO; + } + ASDisplayNode *parent = _supernode; + while (parent) { + if (!addLock(parent)) { + return NO; + } + if (parent.nodeController && !addLock(parent.nodeController)) { + return NO; + } + parent = parent->_supernode; + } +#endif + return true; + }); + return lockSet; +} + +@end + +@implementation ASDisplayNode (YogaDebugging) + +- (NSString *)yogaTreeDescription { + return [self _yogaTreeDescription:@""]; +} + +- (NSString *)_yogaTreeDescription:(NSString *)indent { + auto subtree = [NSMutableString stringWithFormat:@"%@%@\n", indent, self.description]; + for (ASDisplayNode *n in self.yogaChildren) { + [subtree appendString:[n _yogaTreeDescription:[indent stringByAppendingString:@"| "]]]; + } + return subtree; +} + +@end + +#endif /* YOGA */ diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDisplayNode.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDisplayNode.h new file mode 100644 index 0000000..eff930c --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDisplayNode.h @@ -0,0 +1,995 @@ +// +// ASDisplayNode.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#pragma once + +#import + +#import +#import +#import +#import +#import +#import +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +#define ASDisplayNodeLoggingEnabled 0 + +#ifndef AS_MAX_INTERFACE_STATE_DELEGATES +#define AS_MAX_INTERFACE_STATE_DELEGATES 4 +#endif + +@class ASDisplayNode; +@protocol ASContextTransitioning; + +/** + * UIView creation block. Used to create the backing view of a new display node. + */ +typedef UIView * _Nonnull(^ASDisplayNodeViewBlock)(void); + +/** + * UIView creation block. Used to create the backing view of a new display node. + */ +typedef UIViewController * _Nonnull(^ASDisplayNodeViewControllerBlock)(void); + +/** + * CALayer creation block. Used to create the backing layer of a new display node. + */ +typedef CALayer * _Nonnull(^ASDisplayNodeLayerBlock)(void); + +/** + * ASDisplayNode loaded callback block. This block is called BEFORE the -didLoad method and is always called on the main thread. + */ +typedef void (^ASDisplayNodeDidLoadBlock)(__kindof ASDisplayNode * node); + +/** + * ASDisplayNode will / did render node content in context. + */ +typedef void (^ASDisplayNodeContextModifier)(CGContextRef context, id _Nullable drawParameters); + +/** + * ASDisplayNode layout spec block. This block can be used instead of implementing layoutSpecThatFits: in subclass + */ +typedef ASLayoutSpec * _Nonnull(^ASLayoutSpecBlock)(__kindof ASDisplayNode *node, ASSizeRange constrainedSize); + +/** + * AsyncDisplayKit non-fatal error block. This block can be used for handling non-fatal errors. Useful for reporting + * errors that happens in production. + */ +typedef void (^ASDisplayNodeNonFatalErrorBlock)(NSError *error); + +typedef NS_ENUM(unsigned char, ASCornerRoundingType) { + ASCornerRoundingTypeDefaultSlowCALayer, + ASCornerRoundingTypePrecomposited, + ASCornerRoundingTypeClipping +}; + +/** + * Default drawing priority for display node + */ +ASDK_EXTERN NSInteger const ASDefaultDrawingPriority; + +/** + * An `ASDisplayNode` is an abstraction over `UIView` and `CALayer` that allows you to perform calculations about a view + * hierarchy off the main thread, and could do rendering off the main thread as well. + * + * The node API is designed to be as similar as possible to `UIView`. See the README for examples. + * + * ## Subclassing + * + * `ASDisplayNode` can be subclassed to create a new UI element. The subclass header `ASDisplayNode+Subclasses` provides + * necessary declarations and conveniences. + * + * Commons reasons to subclass includes making a `UIView` property available and receiving a callback after async + * display. + * + */ + +@interface ASDisplayNode : NSObject { +@public + /** + * The _displayNodeContext ivar is unused by Texture, but provided to enable advanced clients to make powerful extensions to base class functionality. + * For example, _displayNodeContext can be used to implement category methods on ASDisplayNode that add functionality to all node subclass types. + * Code demonstrating this technique can be found in the CatDealsCollectionView example. + */ + void *_displayNodeContext; +} + +/** @name Initializing a node object */ + + +/** + * @abstract Designated initializer. + * + * @return An ASDisplayNode instance whose view will be a subclass that enables asynchronous rendering, and passes + * through -layout and touch handling methods. + */ +- (instancetype)init NS_DESIGNATED_INITIALIZER; + + +/** + * @abstract Alternative initializer with a block to create the backing view. + * + * @param viewBlock The block that will be used to create the backing view. + * + * @return An ASDisplayNode instance that loads its view with the given block that is guaranteed to run on the main + * queue. The view will render synchronously and -layout and touch handling methods on the node will not be called. + */ +- (instancetype)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock; + +/** + * @abstract Alternative initializer with a block to create the backing view. + * + * @param viewBlock The block that will be used to create the backing view. + * @param didLoadBlock The block that will be called after the view created by the viewBlock is loaded + * + * @return An ASDisplayNode instance that loads its view with the given block that is guaranteed to run on the main + * queue. The view will render synchronously and -layout and touch handling methods on the node will not be called. + */ +- (instancetype)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock didLoadBlock:(nullable ASDisplayNodeDidLoadBlock)didLoadBlock; + +/** + * @abstract Alternative initializer with a block to create the backing layer. + * + * @param layerBlock The block that will be used to create the backing layer. + * + * @return An ASDisplayNode instance that loads its layer with the given block that is guaranteed to run on the main + * queue. The layer will render synchronously and -layout and touch handling methods on the node will not be called. + */ +- (instancetype)initWithLayerBlock:(ASDisplayNodeLayerBlock)layerBlock; + +/** + * @abstract Alternative initializer with a block to create the backing layer. + * + * @param layerBlock The block that will be used to create the backing layer. + * @param didLoadBlock The block that will be called after the layer created by the layerBlock is loaded + * + * @return An ASDisplayNode instance that loads its layer with the given block that is guaranteed to run on the main + * queue. The layer will render synchronously and -layout and touch handling methods on the node will not be called. + */ +- (instancetype)initWithLayerBlock:(ASDisplayNodeLayerBlock)layerBlock didLoadBlock:(nullable ASDisplayNodeDidLoadBlock)didLoadBlock; + +/** + * @abstract Add a block of work to be performed on the main thread when the node's view or layer is loaded. Thread safe. + * @warning Be careful not to retain self in `body`. Change the block parameter list to `^(MYCustomNode *self) {}` if you + * want to shadow self (e.g. if calling this during `init`). + * + * @param body The work to be performed when the node is loaded. + * + * @precondition The node is not already loaded. + */ +- (void)onDidLoad:(ASDisplayNodeDidLoadBlock)body; + +/** + * Set the block that should be used to load this node's view. + * + * @param viewBlock The block that creates a view for this node. + * + * @precondition The node is not yet loaded. + * + * @note You will usually NOT call this. See the limitations documented in @c initWithViewBlock: + */ +- (void)setViewBlock:(ASDisplayNodeViewBlock)viewBlock; + +/** + * Set the block that should be used to load this node's layer. + * + * @param layerBlock The block that creates a layer for this node. + * + * @precondition The node is not yet loaded. + * + * @note You will usually NOT call this. See the limitations documented in @c initWithLayerBlock: + */ +- (void)setLayerBlock:(ASDisplayNodeLayerBlock)layerBlock; + +/** + * @abstract Returns whether the node is synchronous. + * + * @return NO if the node wraps a _ASDisplayView, YES otherwise. + */ +@property (readonly, getter=isSynchronous) BOOL synchronous; + +/** @name Getting view and layer */ + +/** + * @abstract Returns a view. + * + * @discussion The view property is lazily initialized, similar to UIViewController. + * To go the other direction, use ASViewToDisplayNode() in ASDisplayNodeExtras.h. + * + * @warning The first access to it must be on the main thread, and should only be used on the main thread thereafter as + * well. + */ +@property (readonly) UIView *view; + +/** + * @abstract Returns whether a node's backing view or layer is loaded. + * + * @return YES if a view is loaded, or if layerBacked is YES and layer is not nil; NO otherwise. + */ +@property (readonly, getter=isNodeLoaded) BOOL nodeLoaded; + +/** + * @abstract Returns whether the node rely on a layer instead of a view. + * + * @return YES if the node rely on a layer, NO otherwise. + */ +@property (getter=isLayerBacked) BOOL layerBacked; + +/** + * @abstract Returns a layer. + * + * @discussion The layer property is lazily initialized, similar to the view property. + * To go the other direction, use ASLayerToDisplayNode() in ASDisplayNodeExtras.h. + * + * @warning The first access to it must be on the main thread, and should only be used on the main thread thereafter as + * well. + */ +@property (readonly) CALayer * layer; + +/** + * Returns YES if the node is – at least partially – visible in a window. + * + * @see didEnterVisibleState and didExitVisibleState + */ +@property (readonly, getter=isVisible) BOOL visible; + +/** + * Returns YES if the node is in the preloading interface state. + * + * @see didEnterPreloadState and didExitPreloadState + */ +@property (readonly, getter=isInPreloadState) BOOL inPreloadState; + +/** + * Returns YES if the node is in the displaying interface state. + * + * @see didEnterDisplayState and didExitDisplayState + */ +@property (readonly, getter=isInDisplayState) BOOL inDisplayState; + +/** + * @abstract Returns the Interface State of the node. + * + * @return The current ASInterfaceState of the node, indicating whether it is visible and other situational properties. + * + * @see ASInterfaceState + */ +@property (readonly) ASInterfaceState interfaceState; + +/** + * @abstract Adds a delegate to receive notifications on interfaceState changes. + * + * @warning This must be called from the main thread. + * There is a hard limit on the number of delegates a node can have; see + * AS_MAX_INTERFACE_STATE_DELEGATES above. + * + * @see ASInterfaceState + */ +- (void)addInterfaceStateDelegate:(id )interfaceStateDelegate; + +/** + * @abstract Removes a delegate from receiving notifications on interfaceState changes. + * + * @warning This must be called from the main thread. + * + * @see ASInterfaceState + */ +- (void)removeInterfaceStateDelegate:(id )interfaceStateDelegate; + +/** + * @abstract Class property that allows to set a block that can be called on non-fatal errors. This + * property can be useful for cases when Async Display Kit can recover from an abnormal behavior, but + * still gives the opportunity to use a reporting mechanism to catch occurrences in production. In + * development, Async Display Kit will assert instead of calling this block. + * + * @warning This method is not thread-safe. + */ +@property (class, nonatomic) ASDisplayNodeNonFatalErrorBlock nonFatalErrorBlock; + +/** @name Managing the nodes hierarchy */ + + +/** + * @abstract Add a node as a subnode to this node. + * + * @param subnode The node to be added. + * + * @discussion The subnode's view will automatically be added to this node's view, lazily if the views are not created + * yet. + */ +- (void)addSubnode:(ASDisplayNode *)subnode; + +/** + * @abstract Insert a subnode before a given subnode in the list. + * + * @param subnode The node to insert below another node. + * @param below The sibling node that will be above the inserted node. + * + * @discussion If the views are loaded, the subnode's view will be inserted below the given node's view in the hierarchy + * even if there are other non-displaynode views. + */ +- (void)insertSubnode:(ASDisplayNode *)subnode belowSubnode:(ASDisplayNode *)below; + +/** + * @abstract Insert a subnode after a given subnode in the list. + * + * @param subnode The node to insert below another node. + * @param above The sibling node that will be behind the inserted node. + * + * @discussion If the views are loaded, the subnode's view will be inserted above the given node's view in the hierarchy + * even if there are other non-displaynode views. + */ +- (void)insertSubnode:(ASDisplayNode *)subnode aboveSubnode:(ASDisplayNode *)above; + +/** + * @abstract Insert a subnode at a given index in subnodes. + * + * @param subnode The node to insert. + * @param idx The index in the array of the subnodes property at which to insert the node. Subnodes indices start at 0 + * and cannot be greater than the number of subnodes. + * + * @discussion If this node's view is loaded, ASDisplayNode insert the subnode's view after the subnode at index - 1's + * view even if there are other non-displaynode views. + */ +- (void)insertSubnode:(ASDisplayNode *)subnode atIndex:(NSInteger)idx; + +/** + * @abstract Replace subnode with replacementSubnode. + * + * @param subnode A subnode of self. + * @param replacementSubnode A node with which to replace subnode. + * + * @discussion Should both subnode and replacementSubnode already be subnodes of self, subnode is removed and + * replacementSubnode inserted in its place. + * If subnode is not a subnode of self, this method will throw an exception. + * If replacementSubnode is nil, this method will throw an exception + */ +- (void)replaceSubnode:(ASDisplayNode *)subnode withSubnode:(ASDisplayNode *)replacementSubnode; + +/** + * @abstract Remove this node from its supernode. + * + * @discussion The node's view will be automatically removed from the supernode's view. + */ +- (void)removeFromSupernode; + +/** + * @abstract The receiver's immediate subnodes. + */ +@property (nullable, readonly, copy) NSArray *subnodes; + +/** + * @abstract The receiver's supernode. + */ +@property (nullable, readonly, weak) ASDisplayNode *supernode; + + +/** @name Drawing and Updating the View */ + +/** + * @abstract Whether this node's view performs asynchronous rendering. + * + * @return Defaults to YES, except for synchronous views (ie, those created with -initWithViewBlock: / + * -initWithLayerBlock:), which are always NO. + * + * @discussion If this flag is set, then the node will participate in the current asyncdisplaykit_async_transaction and + * do its rendering on the displayQueue instead of the main thread. + * + * Asynchronous rendering proceeds as follows: + * + * When the view is initially added to the hierarchy, it has -needsDisplay true. + * After layout, Core Animation will call -display on the _ASDisplayLayer + * -display enqueues a rendering operation on the displayQueue + * When the render block executes, it calls the delegate display method (-drawRect:... or -display) + * The delegate provides contents via this method and an operation is added to the asyncdisplaykit_async_transaction + * Once all rendering is complete for the current asyncdisplaykit_async_transaction, + * the completion for the block sets the contents on all of the layers in the same frame + * + * If asynchronous rendering is disabled: + * + * When the view is initially added to the hierarchy, it has -needsDisplay true. + * After layout, Core Animation will call -display on the _ASDisplayLayer + * -display calls delegate display method (-drawRect:... or -display) immediately + * -display sets the layer contents immediately with the result + * + * Note: this has nothing to do with -[CALayer drawsAsynchronously]. + */ +@property BOOL displaysAsynchronously; + +/** + * @abstract Prevent the node's layer from displaying. + * + * @discussion A subclass may check this flag during -display or -drawInContext: to cancel a display that is already in + * progress. + * + * Defaults to NO. Does not control display for any child or descendant nodes; for that, use + * -recursivelySetDisplaySuspended:. + * + * If a setNeedsDisplay occurs while displaySuspended is YES, and displaySuspended is set to NO, then the + * layer will be automatically displayed. + */ +@property BOOL displaySuspended; + +/** + * @abstract Whether size changes should be animated. Default to YES. + */ +@property BOOL shouldAnimateSizeChanges; + +/** + * @abstract Prevent the node and its descendants' layer from displaying. + * + * @param flag YES if display should be prevented or cancelled; NO otherwise. + * + * @see displaySuspended + */ +- (void)recursivelySetDisplaySuspended:(BOOL)flag; + +/** + * @abstract Calls -clearContents on the receiver and its subnode hierarchy. + * + * @discussion Clears backing stores and other memory-intensive intermediates. + * If the node is removed from a visible hierarchy and then re-added, it will automatically trigger a new asynchronous display, + * as long as displaySuspended is not set. + * If the node remains in the hierarchy throughout, -setNeedsDisplay is required to trigger a new asynchronous display. + * + * @see displaySuspended and setNeedsDisplay + */ +- (void)recursivelyClearContents; + +/** + * @abstract Toggle displaying a placeholder over the node that covers content until the node and all subnodes are + * displayed. + * + * @discussion Defaults to NO. + */ +@property BOOL placeholderEnabled; + +/** + * @abstract Set the time it takes to fade out the placeholder when a node's contents are finished displaying. + * + * @discussion Defaults to 0 seconds. + */ +@property NSTimeInterval placeholderFadeDuration; + +/** + * @abstract Determines drawing priority of the node. Nodes with higher priority will be drawn earlier. + * + * @discussion Defaults to ASDefaultDrawingPriority. There may be multiple drawing threads, and some of them may + * decide to perform operations in queued order (regardless of drawingPriority) + */ +@property NSInteger drawingPriority; + +/** @name Hit Testing */ + + +/** + * @abstract Bounds insets for hit testing. + * + * @discussion When set to a non-zero inset, increases the bounds for hit testing to make it easier to tap or perform + * gestures on this node. Default is UIEdgeInsetsZero. + * + * This affects the default implementation of -hitTest and -pointInside, so subclasses should call super if you override + * it and want hitTestSlop applied. + */ +@property UIEdgeInsets hitTestSlop; + +/** + * @abstract Returns a Boolean value indicating whether the receiver contains the specified point. + * + * @discussion Includes the "slop" factor specified with hitTestSlop. + * + * @param point A point that is in the receiver's local coordinate system (bounds). + * @param event The event that warranted a call to this method. + * + * @return YES if point is inside the receiver's bounds; otherwise, NO. + */ +- (BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent *)event AS_WARN_UNUSED_RESULT; + + +/** @name Converting Between View Coordinate Systems */ + + +/** + * @abstract Converts a point from the receiver's coordinate system to that of the specified node. + * + * @param point A point specified in the local coordinate system (bounds) of the receiver. + * @param node The node into whose coordinate system point is to be converted. + * + * @return The point converted to the coordinate system of node. + */ +- (CGPoint)convertPoint:(CGPoint)point toNode:(nullable ASDisplayNode *)node AS_WARN_UNUSED_RESULT; + + +/** + * @abstract Converts a point from the coordinate system of a given node to that of the receiver. + * + * @param point A point specified in the local coordinate system (bounds) of node. + * @param node The node with point in its coordinate system. + * + * @return The point converted to the local coordinate system (bounds) of the receiver. + */ +- (CGPoint)convertPoint:(CGPoint)point fromNode:(nullable ASDisplayNode *)node AS_WARN_UNUSED_RESULT; + + +/** + * @abstract Converts a rectangle from the receiver's coordinate system to that of another view. + * + * @param rect A rectangle specified in the local coordinate system (bounds) of the receiver. + * @param node The node that is the target of the conversion operation. + * + * @return The converted rectangle. + */ +- (CGRect)convertRect:(CGRect)rect toNode:(nullable ASDisplayNode *)node AS_WARN_UNUSED_RESULT; + +/** + * @abstract Converts a rectangle from the coordinate system of another node to that of the receiver. + * + * @param rect A rectangle specified in the local coordinate system (bounds) of node. + * @param node The node with rect in its coordinate system. + * + * @return The converted rectangle. + */ +- (CGRect)convertRect:(CGRect)rect fromNode:(nullable ASDisplayNode *)node AS_WARN_UNUSED_RESULT; + +/** + * Whether or not the node would support having .layerBacked = YES. + */ +@property (readonly) BOOL supportsLayerBacking; + +/** + * Whether or not the node layout should be automatically updated when it receives safeAreaInsetsDidChange. + * + * Defaults to NO. + */ +@property BOOL automaticallyRelayoutOnSafeAreaChanges; + +/** + * Whether or not the node layout should be automatically updated when it receives layoutMarginsDidChange. + * + * Defaults to NO. + */ +@property BOOL automaticallyRelayoutOnLayoutMarginsChanges; + +@end + +/** + * Convenience methods for debugging. + */ +@interface ASDisplayNode (Debugging) + +/** + * Whether or not ASDisplayNode instances should store their unflattened layouts. + * + * The layout can be accessed via `-unflattenedCalculatedLayout`. + * + * Flattened layouts use less memory and are faster to lookup. On the other hand, unflattened layouts are useful for debugging + * because they preserve original information. + * + * Defaults to NO. + */ +@property (class) BOOL shouldStoreUnflattenedLayouts; + +@property (nullable, readonly) ASLayout *unflattenedCalculatedLayout; + +/** + * @abstract Return a description of the node hierarchy. + * + * @discussion For debugging: (lldb) po [node displayNodeRecursiveDescription] + */ +- (NSString *)displayNodeRecursiveDescription AS_WARN_UNUSED_RESULT; + +/** + * A detailed description of this node's layout state. This is useful when debugging. + */ +@property (copy, readonly) NSString *detailedLayoutDescription; + +@end + +/** + * ## UIView bridge + * + * ASDisplayNode provides thread-safe access to most of UIView and CALayer properties and methods, traditionally unsafe. + * + * Using them will not cause the actual view/layer to be created, and will be applied when it is created (when the view + * or layer property is accessed). + * + * - NOTE: After the view or layer is created, the properties pass through to the view or layer directly and must be called on the main thread. + * + * See UIView and CALayer for documentation on these common properties. + */ +@interface ASDisplayNode (UIViewBridge) + +/** + * Marks the view as needing display. Convenience for use whether the view / layer is loaded or not. Safe to call from a background thread. + */ +- (void)setNeedsDisplay; + +/** + * Marks the node as needing layout. Convenience for use whether the view / layer is loaded or not. Safe to call from a background thread. + * + * If the node determines its own desired layout size will change in the next layout pass, it will propagate this + * information up the tree so its parents can have a chance to consider and apply if necessary the new size onto the node. + * + * Note: ASCellNode has special behavior in that calling this method will automatically notify + * the containing ASTableView / ASCollectionView that the cell should be resized, if necessary. + */ +- (void)setNeedsLayout; + +/** + * Performs a layout pass on the node. Convenience for use whether the view / layer is loaded or not. Safe to call from a background thread. + */ +- (void)layoutIfNeeded; + +@property CGRect frame; // default=CGRectZero +@property CGRect bounds; // default=CGRectZero +@property CGPoint position; // default=CGPointZero +@property CGFloat alpha; // default=1.0f + +/* @abstract Sets the corner rounding method to use on the ASDisplayNode. + * There are three types of corner rounding provided by Texture: CALayer, Precomposited, and Clipping. + * + * - ASCornerRoundingTypeDefaultSlowCALayer: uses CALayer's inefficient .cornerRadius property. Use + * this type of corner in situations in which there is both movement through and movement underneath + * the corner (very rare). This uses only .cornerRadius. + * + * - ASCornerRoundingTypePrecomposited: corners are drawn using bezier paths to clip the content in a + * CGContext / UIGraphicsContext. This requires .backgroundColor and .cornerRadius to be set. Use opaque + * background colors when possible for optimal efficiency, but transparent colors are supported and much + * more efficient than CALayer. The only limitation of this approach is that it cannot clip children, and + * thus works best for ASImageNodes or containers showing a background around their children. + * + * - ASCornerRoundingTypeClipping: overlays 4 separate opaque corners on top of the content that needs + * corner rounding. Requires .backgroundColor and .cornerRadius to be set. Use clip corners in situations + * where there is movement through the corner, with an opaque background (no movement underneath the corner). + * Clipped corners are ideal for animating / resizing views, and still outperform CALayer. + * + * For more information and examples, see http://texturegroup.org/docs/corner-rounding.html + * + * @default ASCornerRoundingTypeDefaultSlowCALayer + */ +@property ASCornerRoundingType cornerRoundingType; // default=ASCornerRoundingTypeDefaultSlowCALayer .cornerRadius (offscreen rendering) + +/** @abstract The radius to use when rounding corners of the ASDisplayNode. + * + * @discussion This property is thread-safe and should always be preferred over CALayer's cornerRadius property, + * even if corner rounding type is ASCornerRoundingTypeDefaultSlowCALayer. + */ +@property CGFloat cornerRadius; // default=0.0 + +/** @abstract Which corners to mask when rounding corners. + * + * @note This option cannot be changed when using iOS < 11 + * and using ASCornerRoundingTypeDefaultSlowCALayer. Use a different corner rounding type to implement not-all-corners + * rounding in prior versions of iOS. + */ +@property CACornerMask maskedCorners; // default=all corners. + +@property BOOL clipsToBounds; // default==NO +@property (getter=isHidden) BOOL hidden; // default==NO +@property (getter=isOpaque) BOOL opaque; // default==YES + +@property (nullable) id contents; // default=nil +@property CGRect contentsRect; // default={0,0,1,1}. @see CALayer.h for details. +@property CGRect contentsCenter; // default={0,0,1,1}. @see CALayer.h for details. +@property CGFloat contentsScale; // default=1.0f. See @contentsScaleForDisplay for details. +@property CGFloat rasterizationScale; // default=1.0f. + +@property CGPoint anchorPoint; // default={0.5, 0.5} +@property CGFloat zPosition; // default=0.0 +@property CATransform3D transform; // default=CATransform3DIdentity +@property CATransform3D subnodeTransform; // default=CATransform3DIdentity + +@property (getter=isUserInteractionEnabled) BOOL userInteractionEnabled; // default=YES (NO for layer-backed nodes) +#if TARGET_OS_IOS +@property (getter=isExclusiveTouch) BOOL exclusiveTouch; // default=NO +#endif + +@property (nullable, copy) NSDictionary> *actions; // default = nil + +/** + * @abstract The node view's background color. + * + * @discussion In contrast to UIView, setting a transparent color will not set opaque = NO. + * This only affects nodes that implement +drawRect like ASTextNode. +*/ +@property (nullable, copy) UIColor *backgroundColor; // default=nil + +@property (null_resettable, copy) UIColor *tintColor; // default=Blue + +/** + * Notifies the node when the tintColor has changed. + * + * @note This method is guaranteed to be called if the tintColor is changed after the node loaded. + */ +- (void)tintColorDidChange; + +/** + * @abstract A flag used to determine how a node lays out its content when its bounds change. + * + * @discussion This is like UIView's contentMode property, but better. We do our own mapping to layer.contentsGravity in + * _ASDisplayView. You can set needsDisplayOnBoundsChange independently. + * Thus, UIViewContentModeRedraw is not allowed; use needsDisplayOnBoundsChange = YES instead, and pick an appropriate + * contentMode for your content while it's being re-rendered. + */ +@property UIViewContentMode contentMode; // default=UIViewContentModeScaleToFill +@property (copy) NSString *contentsGravity; // Use .contentMode in preference when possible. +@property UISemanticContentAttribute semanticContentAttribute; + +@property (nullable) CGColorRef shadowColor; // default=opaque rgb black +@property CGFloat shadowOpacity; // default=0.0 +@property CGSize shadowOffset; // default=(0, -3) +@property CGFloat shadowRadius; // default=3 +@property CGFloat borderWidth; // default=0 +@property (nullable) CGColorRef borderColor; // default=opaque rgb black + +@property BOOL allowsGroupOpacity; +@property BOOL allowsEdgeAntialiasing; +@property CAEdgeAntialiasingMask edgeAntialiasingMask; // default==all values from CAEdgeAntialiasingMask + +@property BOOL needsDisplayOnBoundsChange; // default==NO +@property BOOL autoresizesSubviews; // default==YES (undefined for layer-backed nodes) +@property UIViewAutoresizing autoresizingMask; // default==UIViewAutoresizingNone (undefined for layer-backed nodes) + +/** + * @abstract Content margins + * + * @discussion This property is bridged to its UIView counterpart. + * + * If your layout depends on this property, you should probably enable automaticallyRelayoutOnLayoutMarginsChanges to ensure + * that the layout gets automatically updated when the value of this property changes. Or you can override layoutMarginsDidChange + * and make all the necessary updates manually. + */ +@property UIEdgeInsets layoutMargins; +@property BOOL preservesSuperviewLayoutMargins; // default is NO - set to enable pass-through or cascading behavior of margins from this view’s parent to its children +- (void)layoutMarginsDidChange; + +/** + * @abstract Safe area insets + * + * @discussion This property is bridged to its UIVIew counterpart. + * + * If your layout depends on this property, you should probably enable automaticallyRelayoutOnSafeAreaChanges to ensure + * that the layout gets automatically updated when the value of this property changes. Or you can override safeAreaInsetsDidChange + * and make all the necessary updates manually. + */ +@property (readonly) UIEdgeInsets safeAreaInsets; +@property BOOL insetsLayoutMarginsFromSafeArea; // Default: YES +- (void)safeAreaInsetsDidChange; + + +// UIResponder methods +// By default these fall through to the underlying view, but can be overridden. +- (BOOL)canBecomeFirstResponder; // default==NO +- (BOOL)becomeFirstResponder; // default==NO (no-op) +- (BOOL)canResignFirstResponder; // default==YES +- (BOOL)resignFirstResponder; // default==NO (no-op) +- (BOOL)isFirstResponder; +- (BOOL)canPerformAction:(SEL)action withSender:(id)sender; + +#if TARGET_OS_TV +//Focus Engine +- (void)setNeedsFocusUpdate; +- (BOOL)canBecomeFocused; +- (void)updateFocusIfNeeded; +- (void)didUpdateFocusInContext:(UIFocusUpdateContext *)context withAnimationCoordinator:(UIFocusAnimationCoordinator *)coordinator; +- (BOOL)shouldUpdateFocusInContext:(UIFocusUpdateContext *)context; +- (nullable UIView *)preferredFocusedView; +#endif + +@end + +@interface ASDisplayNode (UIViewBridgeAccessibility) + +// Accessibility support +@property BOOL isAccessibilityElement; +@property (nullable, copy) NSString *accessibilityLabel; +@property (nullable, copy) NSAttributedString *accessibilityAttributedLabel API_AVAILABLE(ios(11.0),tvos(11.0)); +@property (nullable, copy) NSString *accessibilityHint; +@property (nullable, copy) NSAttributedString *accessibilityAttributedHint API_AVAILABLE(ios(11.0),tvos(11.0)); +@property (nullable, copy) NSString *accessibilityValue; +@property (nullable, copy) NSAttributedString *accessibilityAttributedValue API_AVAILABLE(ios(11.0),tvos(11.0)); +@property UIAccessibilityTraits accessibilityTraits; +@property CGRect accessibilityFrame; +@property (nullable, copy) UIBezierPath *accessibilityPath; +@property CGPoint accessibilityActivationPoint; +@property (nullable, copy) NSString *accessibilityLanguage; +@property BOOL accessibilityElementsHidden; +@property BOOL accessibilityViewIsModal; +@property BOOL shouldGroupAccessibilityChildren; +@property UIAccessibilityNavigationStyle accessibilityNavigationStyle; +@property (nullable, copy) NSArray *accessibilityCustomActions API_AVAILABLE(ios(8.0),tvos(9.0)); +#if TARGET_OS_TV +@property (nullable, copy) NSArray *accessibilityHeaderElements; +#endif + +// Accessibility identification support +@property (nullable, copy) NSString *accessibilityIdentifier; + +@end + +@interface ASDisplayNode (ASLayoutElement) + +/** + * @abstract Asks the node to return a layout based on given size range. + * + * @param constrainedSize The minimum and maximum sizes the receiver should fit in. + * + * @return An ASLayout instance defining the layout of the receiver (and its children, if the box layout model is used). + * + * @discussion Though this method does not set the bounds of the view, it does have side effects--caching both the + * constraint and the result. + * + * @warning Subclasses must not override this; it caches results from -calculateLayoutThatFits:. Calling this method may + * be expensive if result is not cached. + * + * @see [ASDisplayNode(Subclassing) calculateLayoutThatFits:] + */ +- (ASLayout *)layoutThatFits:(ASSizeRange)constrainedSize; + +@end + +@interface ASDisplayNode (ASLayoutElementStylability) + +@end + +typedef NS_ENUM(NSInteger, ASLayoutEngineType) { + ASLayoutEngineTypeLayoutSpec, + ASLayoutEngineTypeYoga +}; + +@interface ASDisplayNode (ASLayout) + +/** + * @abstract Returns the current layout type the node uses for layout the subtree. + */ +@property (readonly) ASLayoutEngineType layoutEngineType; + +/** + * @abstract Return the calculated size. + * + * @discussion Ideal for use by subclasses in -layout, having already prompted their subnodes to calculate their size by + * calling -layoutThatFits: on them in -calculateLayoutThatFits. + * + * @return Size already calculated by -calculateLayoutThatFits:. + * + * @warning Subclasses must not override this; it returns the last cached measurement and is never expensive. + */ +@property (readonly) CGSize calculatedSize; + +/** + * @abstract Return the constrained size range used for calculating layout. + * + * @return The minimum and maximum constrained sizes used by calculateLayoutThatFits:. + */ +@property (readonly) ASSizeRange constrainedSizeForCalculatedLayout; + +@end + +@interface ASDisplayNode (ASLayoutTransitioning) + +/** + * @abstract The amount of time it takes to complete the default transition animation. Default is 0.2. + */ +@property NSTimeInterval defaultLayoutTransitionDuration; + +/** + * @abstract The amount of time (measured in seconds) to wait before beginning the default transition animation. + * Default is 0.0. + */ +@property NSTimeInterval defaultLayoutTransitionDelay; + +/** + * @abstract A mask of options indicating how you want to perform the default transition animations. + * For a list of valid constants, see UIViewAnimationOptions. + */ +@property UIViewAnimationOptions defaultLayoutTransitionOptions; + +/** + * @discussion A place to perform your animation. New nodes have been inserted here. You can also use this time to re-order the hierarchy. + */ +- (void)animateLayoutTransition:(nonnull id)context; + +/** + * @discussion A place to clean up your nodes after the transition + */ +- (void)didCompleteLayoutTransition:(nonnull id)context; + +/** + * @abstract Transitions the current layout with a new constrained size. Must be called on main thread. + * + * @param animated Animation is optional, but will still proceed through your `animateLayoutTransition` implementation with `isAnimated == NO`. + * @param shouldMeasureAsync Measure the layout asynchronously. + * @param completion Optional completion block called only if a new layout is calculated. + * It is called on main, right after the measurement and before -animateLayoutTransition:. + * + * @discussion If the passed constrainedSize is the the same as the node's current constrained size, this method is noop. If passed YES to shouldMeasureAsync it's guaranteed that measurement is happening on a background thread, otherwise measaurement will happen on the thread that the method was called on. The measurementCompletion callback is always called on the main thread right after the measurement and before -animateLayoutTransition:. + * + * @see animateLayoutTransition: + * + */ +- (void)transitionLayoutWithSizeRange:(ASSizeRange)constrainedSize + animated:(BOOL)animated + shouldMeasureAsync:(BOOL)shouldMeasureAsync + measurementCompletion:(nullable void(^)(void))completion; + + +/** + * @abstract Invalidates the layout and begins a relayout of the node with the current `constrainedSize`. Must be called on main thread. + * + * @discussion It is called right after the measurement and before -animateLayoutTransition:. + * + * @param animated Animation is optional, but will still proceed through your `animateLayoutTransition` implementation with `isAnimated == NO`. + * @param shouldMeasureAsync Measure the layout asynchronously. + * @param completion Optional completion block called only if a new layout is calculated. + * + * @see animateLayoutTransition: + * + */ +- (void)transitionLayoutWithAnimation:(BOOL)animated + shouldMeasureAsync:(BOOL)shouldMeasureAsync + measurementCompletion:(nullable void(^)(void))completion; + +/** + * @abstract Cancels all performing layout transitions. Can be called on any thread. + */ +- (void)cancelLayoutTransition; + +@end + +/* + * ASDisplayNode support for automatic subnode management. + */ +@interface ASDisplayNode (ASAutomaticSubnodeManagement) + +/** + * @abstract A boolean that shows whether the node automatically inserts and removes nodes based on the presence or + * absence of the node and its subnodes is completely determined in its layoutSpecThatFits: method. + * + * @discussion If flag is YES the node no longer require addSubnode: or removeFromSupernode method calls. The presence + * or absence of subnodes is completely determined in its layoutSpecThatFits: method. + */ +@property BOOL automaticallyManagesSubnodes; + +@end + +/* + * ASDisplayNode participates in ASAsyncTransactions, so you can determine when your subnodes are done rendering. + * See: -(void)asyncdisplaykit_asyncTransactionContainerStateDidChange in ASDisplayNodeSubclass.h + */ +@interface ASDisplayNode (ASAsyncTransactionContainer) +@end + +/** UIVIew(AsyncDisplayKit) defines convenience method for adding sub-ASDisplayNode to an UIView. */ +@interface UIView (AsyncDisplayKit) +/** + * Convenience method, equivalent to [view addSubview:node.view] or [view.layer addSublayer:node.layer] if layer-backed. + * + * @param node The node to be added. + */ +- (void)addSubnode:(ASDisplayNode *)node; +@end + +/* + * CALayer(AsyncDisplayKit) defines convenience method for adding sub-ASDisplayNode to a CALayer. + */ +@interface CALayer (AsyncDisplayKit) +/** + * Convenience method, equivalent to [layer addSublayer:node.layer]. + * + * @param node The node to be added. + */ +- (void)addSubnode:(ASDisplayNode *)node; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDisplayNode.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDisplayNode.mm new file mode 100644 index 0000000..abb339b --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDisplayNode.mm @@ -0,0 +1,3774 @@ +// +// ASDisplayNode.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import +#import +#import +#import + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + +// Conditionally time these scopes to our debug ivars (only exist in debug/profile builds) +#if TIME_DISPLAYNODE_OPS + #define TIME_SCOPED(outVar) AS::ScopeTimer t(outVar) +#else + #define TIME_SCOPED(outVar) +#endif +// This is trying to merge non-rangeManaged with rangeManaged, so both range-managed and standalone nodes wait before firing their exit-visibility handlers, as UIViewController transitions now do rehosting at both start & end of animation. +// Enable this will mitigate interface updating state when coalescing disabled. +// TODO(wsdwsd0829): Rework enabling code to ensure that interface state behavior is not altered when ASCATransactionQueue is disabled. +#define ENABLE_NEW_EXIT_HIERARCHY_BEHAVIOR 0 + +using AS::MutexLocker; + +static ASDisplayNodeNonFatalErrorBlock _nonFatalErrorBlock = nil; + +@interface ASDisplayNode () +/** + * See ASDisplayNodeInternal.h for ivars + */ + +@end + +@implementation ASDisplayNode + +@dynamic layoutElementType; + +@synthesize threadSafeBounds = _threadSafeBounds; + +static std::atomic_bool suppressesInvalidCollectionUpdateExceptions = ATOMIC_VAR_INIT(NO); +static std::atomic_bool storesUnflattenedLayouts = ATOMIC_VAR_INIT(NO); + +BOOL ASDisplayNodeSubclassOverridesSelector(Class subclass, SEL selector) +{ + return ASSubclassOverridesSelector([ASDisplayNode class], subclass, selector); +} + +// For classes like ASTableNode, ASCollectionNode, ASScrollNode and similar - we have to be sure to set certain properties +// like setFrame: and setBackgroundColor: directly to the UIView and not apply it to the layer only. +BOOL ASDisplayNodeNeedsSpecialPropertiesHandling(BOOL isSynchronous, BOOL isLayerBacked) +{ + return isSynchronous && !isLayerBacked; +} + +_ASPendingState *ASDisplayNodeGetPendingState(ASDisplayNode *node) +{ + ASLockScope(node); + _ASPendingState *result = node->_pendingViewState; + if (result == nil) { + result = [[_ASPendingState alloc] init]; + node->_pendingViewState = result; + } + return result; +} + +void StubImplementationWithNoArgs(id receiver, SEL _cmd) {} +void StubImplementationWithSizeRange(id receiver, SEL _cmd, ASSizeRange sr) {} +void StubImplementationWithTwoInterfaceStates(id receiver, SEL _cmd, ASInterfaceState s0, ASInterfaceState s1) {} + +/// Returning nil here won't trigger unwanted default actions, because we override +/// +defaultActionForKey: to return kCFNull. +id StubLayerActionImplementation(id receiver, SEL _cmd, NSString *key) { return nil; } + +/** + * Returns ASDisplayNodeFlags for the given class/instance. instance MAY BE NIL. + * + * @param c the class, required + * @param instance the instance, which may be nil. (If so, the class is inspected instead) + * @remarks The instance value is used only if we suspect the class may be dynamic (because it overloads + * +respondsToSelector: or -respondsToSelector.) In that case we use our "slow path", calling this + * method on each -init and passing the instance value. While this may seem like an unlikely scenario, + * it turns out our own internal tests use a dynamic class, so it's worth capturing this edge case. + * + * @return ASDisplayNode flags. + */ +static struct ASDisplayNodeFlags GetASDisplayNodeFlags(Class c, ASDisplayNode *instance) +{ + ASDisplayNodeCAssertNotNil(c, @"class is required"); + + struct ASDisplayNodeFlags flags = {0}; + + flags.isInHierarchy = NO; + flags.displaysAsynchronously = YES; + flags.shouldAnimateSizeChanges = YES; + flags.implementsDrawRect = ([c respondsToSelector:@selector(drawRect:withParameters:isCancelled:isRasterizing:)] ? 1 : 0); + flags.implementsImageDisplay = ([c respondsToSelector:@selector(displayWithParameters:isCancelled:)] ? 1 : 0); + if (instance) { + flags.implementsDrawParameters = ([instance respondsToSelector:@selector(drawParametersForAsyncLayer:)] ? 1 : 0); + } else { + flags.implementsDrawParameters = ([c instancesRespondToSelector:@selector(drawParametersForAsyncLayer:)] ? 1 : 0); + } + + + return flags; +} + +/** + * Returns ASDisplayNodeMethodOverrides for the given class + * + * @param c the class, required. + * + * @return ASDisplayNodeMethodOverrides. + */ +static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) +{ + ASDisplayNodeCAssertNotNil(c, @"class is required"); + + ASDisplayNodeMethodOverrides overrides = ASDisplayNodeMethodOverrideNone; + + // Handling touches + if (ASDisplayNodeSubclassOverridesSelector(c, @selector(touchesBegan:withEvent:))) { + overrides |= ASDisplayNodeMethodOverrideTouchesBegan; + } + if (ASDisplayNodeSubclassOverridesSelector(c, @selector(touchesMoved:withEvent:))) { + overrides |= ASDisplayNodeMethodOverrideTouchesMoved; + } + if (ASDisplayNodeSubclassOverridesSelector(c, @selector(touchesCancelled:withEvent:))) { + overrides |= ASDisplayNodeMethodOverrideTouchesCancelled; + } + if (ASDisplayNodeSubclassOverridesSelector(c, @selector(touchesEnded:withEvent:))) { + overrides |= ASDisplayNodeMethodOverrideTouchesEnded; + } + + // Layout related methods + if (ASDisplayNodeSubclassOverridesSelector(c, @selector(layoutSpecThatFits:))) { + overrides |= ASDisplayNodeMethodOverrideLayoutSpecThatFits; + } + if (ASDisplayNodeSubclassOverridesSelector(c, @selector(calculateLayoutThatFits:)) || + ASDisplayNodeSubclassOverridesSelector(c, @selector(calculateLayoutThatFits: + restrictedToSize: + relativeToParentSize:))) { + overrides |= ASDisplayNodeMethodOverrideCalcLayoutThatFits; + } + if (ASDisplayNodeSubclassOverridesSelector(c, @selector(calculateSizeThatFits:))) { + overrides |= ASDisplayNodeMethodOverrideCalcSizeThatFits; + } + + return overrides; +} + ++ (void)initialize +{ +#if ASDISPLAYNODE_ASSERTIONS_ENABLED + if (self != [ASDisplayNode class]) { + + // Subclasses should never override these. Use unused to prevent warnings + __unused NSString *classString = NSStringFromClass(self); + + ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(calculatedSize)), @"Subclass %@ must not override calculatedSize method.", classString); + ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(calculatedLayout)), @"Subclass %@ must not override calculatedLayout method.", classString); + ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(layoutThatFits:)), @"Subclass %@ must not override layoutThatFits: method. Instead override calculateLayoutThatFits:.", classString); + ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(layoutThatFits:parentSize:)), @"Subclass %@ must not override layoutThatFits:parentSize method. Instead override calculateLayoutThatFits:.", classString); + ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(recursivelyClearContents)), @"Subclass %@ must not override recursivelyClearContents method.", classString); + ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(recursivelyClearPreloadedData)), @"Subclass %@ must not override recursivelyClearFetchedData method.", classString); + } else { + // Check if subnodes where modified during the creation of the layout + __block IMP originalLayoutSpecThatFitsIMP = ASReplaceMethodWithBlock(self, @selector(_locked_layoutElementThatFits:), ^(ASDisplayNode *_self, ASSizeRange sizeRange) { + NSArray *oldSubnodes = _self.subnodes; + ASLayoutSpec *layoutElement = ((ASLayoutSpec *( *)(id, SEL, ASSizeRange))originalLayoutSpecThatFitsIMP)(_self, @selector(_locked_layoutElementThatFits:), sizeRange); + NSArray *subnodes = _self.subnodes; + ASDisplayNodeAssert(oldSubnodes.count == subnodes.count, @"Adding or removing nodes in layoutSpecBlock or layoutSpecThatFits: is not allowed and can cause unexpected behavior."); + for (NSInteger i = 0; i < oldSubnodes.count; i++) { + ASDisplayNodeAssert(oldSubnodes[i] == subnodes[i], @"Adding or removing nodes in layoutSpecBlock or layoutSpecThatFits: is not allowed and can cause unexpected behavior."); + } + return layoutElement; + }); + } +#endif + + // Below we are pre-calculating values per-class and dynamically adding a method (_staticInitialize) to populate these values + // when each instance is constructed. These values don't change for each class, so there is significant performance benefit + // in doing it here. +initialize is guaranteed to be called before any instance method so it is safe to add this method here. + // Note that we take care to detect if the class overrides +respondsToSelector: or -respondsToSelector and take the slow path + // (recalculating for each instance) to make sure we are always correct. + + BOOL classOverridesRespondsToSelector = ASSubclassOverridesClassSelector([NSObject class], self, @selector(respondsToSelector:)); + BOOL instancesOverrideRespondsToSelector = ASSubclassOverridesSelector([NSObject class], self, @selector(respondsToSelector:)); + struct ASDisplayNodeFlags flags = GetASDisplayNodeFlags(self, nil); + ASDisplayNodeMethodOverrides methodOverrides = GetASDisplayNodeMethodOverrides(self); + + __unused Class initializeSelf = self; + + IMP staticInitialize = imp_implementationWithBlock(^(ASDisplayNode *node) { + ASDisplayNodeAssert(node.class == initializeSelf, @"Node class %@ does not have a matching _staticInitialize method; check to ensure [super initialize] is called within any custom +initialize implementations! Overridden methods will not be called unless they are also implemented by superclass %@", node.class, initializeSelf); + node->_flags = (classOverridesRespondsToSelector || instancesOverrideRespondsToSelector) ? GetASDisplayNodeFlags(node.class, node) : flags; + node->_methodOverrides = (classOverridesRespondsToSelector) ? GetASDisplayNodeMethodOverrides(node.class) : methodOverrides; + }); + + class_replaceMethod(self, @selector(_staticInitialize), staticInitialize, "v:@"); + + // Add stub implementations for global methods that the client didn't + // implement in a category. We do this instead of hard-coding empty + // implementations to avoid a linker warning when it merges categories. + // Note: addMethod will not do anything if a method already exists. + if (self == ASDisplayNode.class) { + IMP noArgsImp = (IMP)StubImplementationWithNoArgs; + class_addMethod(self, @selector(baseDidInit), noArgsImp, "v@:"); + class_addMethod(self, @selector(baseWillDealloc), noArgsImp, "v@:"); + class_addMethod(self, @selector(didLoad), noArgsImp, "v@:"); + class_addMethod(self, @selector(layoutDidFinish), noArgsImp, "v@:"); + class_addMethod(self, @selector(didEnterPreloadState), noArgsImp, "v@:"); + class_addMethod(self, @selector(didExitPreloadState), noArgsImp, "v@:"); + class_addMethod(self, @selector(didEnterDisplayState), noArgsImp, "v@:"); + class_addMethod(self, @selector(didExitDisplayState), noArgsImp, "v@:"); + class_addMethod(self, @selector(didEnterVisibleState), noArgsImp, "v@:"); + class_addMethod(self, @selector(didExitVisibleState), noArgsImp, "v@:"); + class_addMethod(self, @selector(hierarchyDisplayDidFinish), noArgsImp, "v@:"); + class_addMethod(self, @selector(calculatedLayoutDidChange), noArgsImp, "v@:"); + + auto type0 = "v@:" + std::string(@encode(ASSizeRange)); + class_addMethod(self, @selector(willCalculateLayout:), (IMP)StubImplementationWithSizeRange, type0.c_str()); + + auto interfaceStateType = std::string(@encode(ASInterfaceState)); + auto type1 = "v@:" + interfaceStateType + interfaceStateType; + class_addMethod(self, @selector(interfaceStateDidChange:fromState:), (IMP)StubImplementationWithTwoInterfaceStates, type1.c_str()); + + class_addMethod(self, @selector(layerActionForKey:), (IMP)StubLayerActionImplementation, "@@:@"); + } +} + +#if !AS_INITIALIZE_FRAMEWORK_MANUALLY +__attribute__((constructor)) static void ASLoadFrameworkInitializer(void) +{ + ASInitializeFrameworkMainThread(); +} +#endif + ++ (Class)viewClass +{ + return [_ASDisplayView class]; +} + ++ (Class)layerClass +{ + return [_ASDisplayLayer class]; +} + +#pragma mark - Lifecycle + +- (void)_staticInitialize +{ + ASDisplayNodeAssert(NO, @"_staticInitialize must be overridden"); +} + +- (void)_initializeInstance +{ + [self _staticInitialize]; + __instanceLock__.SetDebugNameWithObject(self); + + _viewClass = [self.class viewClass]; + _layerClass = [self.class layerClass]; + BOOL isSynchronous = ![_viewClass isSubclassOfClass:[_ASDisplayView class]] + || ![_layerClass isSubclassOfClass:[_ASDisplayLayer class]]; + setFlag(Synchronous, isSynchronous); + + + _contentsScaleForDisplay = ASScreenScale(); + _drawingPriority = ASDefaultTransactionPriority; + _maskedCorners = kASCACornerAllCorners; + + _primitiveTraitCollection = ASPrimitiveTraitCollectionMakeDefault(); + + _layoutVersion = 1; + + _defaultLayoutTransitionDuration = 0.2; + _defaultLayoutTransitionDelay = 0.0; + _defaultLayoutTransitionOptions = UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionTransitionNone; + + _flags.canClearContentsOfLayer = YES; + _flags.canCallSetNeedsDisplayOfLayer = YES; + + _fallbackSafeAreaInsets = UIEdgeInsetsZero; + _flags.fallbackInsetsLayoutMarginsFromSafeArea = YES; + _flags.isViewControllerRoot = NO; + + _flags.automaticallyRelayoutOnSafeAreaChanges = NO; + _flags.automaticallyRelayoutOnLayoutMarginsChanges = NO; + + [self baseDidInit]; +} + +- (instancetype)init +{ + if (!(self = [super init])) + return nil; + + [self _initializeInstance]; + + return self; +} + +- (instancetype)initWithViewClass:(Class)viewClass +{ + if (!(self = [self init])) + return nil; + + ASDisplayNodeAssert([viewClass isSubclassOfClass:[UIView class]], @"should initialize with a subclass of UIView"); + + _viewClass = viewClass; + setFlag(Synchronous, ![viewClass isSubclassOfClass:[_ASDisplayView class]]); + + return self; +} + +- (instancetype)initWithLayerClass:(Class)layerClass +{ + if (!(self = [self init])) { + return nil; + } + + ASDisplayNodeAssert([layerClass isSubclassOfClass:[CALayer class]], @"should initialize with a subclass of CALayer"); + + _layerClass = layerClass; + _flags.layerBacked = YES; + setFlag(Synchronous, ![layerClass isSubclassOfClass:[_ASDisplayLayer class]]); + + return self; +} + +- (instancetype)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock +{ + return [self initWithViewBlock:viewBlock didLoadBlock:nil]; +} + +- (instancetype)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock didLoadBlock:(ASDisplayNodeDidLoadBlock)didLoadBlock +{ + if (!(self = [self init])) { + return nil; + } + + [self setViewBlock:viewBlock]; + if (didLoadBlock != nil) { + [self onDidLoad:didLoadBlock]; + } + + return self; +} + +- (instancetype)initWithLayerBlock:(ASDisplayNodeLayerBlock)layerBlock +{ + return [self initWithLayerBlock:layerBlock didLoadBlock:nil]; +} + +- (instancetype)initWithLayerBlock:(ASDisplayNodeLayerBlock)layerBlock didLoadBlock:(ASDisplayNodeDidLoadBlock)didLoadBlock +{ + if (!(self = [self init])) { + return nil; + } + + [self setLayerBlock:layerBlock]; + if (didLoadBlock != nil) { + [self onDidLoad:didLoadBlock]; + } + + return self; +} + +ASSynthesizeLockingMethodsWithMutex(__instanceLock__); + +- (void)setViewBlock:(ASDisplayNodeViewBlock)viewBlock +{ + ASDisplayNodeAssertFalse(self.nodeLoaded); + ASDisplayNodeAssertNotNil(viewBlock, @"should initialize with a valid block that returns a UIView"); + + _viewBlock = viewBlock; + setFlag(Synchronous, YES); +} + +- (void)setLayerBlock:(ASDisplayNodeLayerBlock)layerBlock +{ + ASDisplayNodeAssertFalse(self.nodeLoaded); + ASDisplayNodeAssertNotNil(layerBlock, @"should initialize with a valid block that returns a CALayer"); + + _layerBlock = layerBlock; + _flags.layerBacked = YES; + setFlag(Synchronous, YES); +} + +- (ASDisplayNodeMethodOverrides)methodOverrides +{ + return _methodOverrides; +} + +- (void)onDidLoad:(ASDisplayNodeDidLoadBlock)body +{ + AS::UniqueLock l(__instanceLock__); + + if ([self _locked_isNodeLoaded]) { + ASDisplayNodeAssertThreadAffinity(self); + l.unlock(); + body(self); + return; + } else if (_onDidLoadBlocks == nil) { + _onDidLoadBlocks = [NSMutableArray arrayWithObject:body]; + } else { + [_onDidLoadBlocks addObject:body]; + } +} + + +- (void)asyncTraitCollectionDidChangeWithPreviousTraitCollection:(ASPrimitiveTraitCollection)previousTraitCollection +{ + if (@available(iOS 13.0, tvOS 10.0, *)) { + // When changing between light and dark mode, often the entire node needs to re-render. + // This change doesn't happen frequently so it's fairly safe to render nodes again + __instanceLock__.lock(); + BOOL loaded = _loaded(self); + ASPrimitiveTraitCollection primitiveTraitCollection = _primitiveTraitCollection; + __instanceLock__.unlock(); + if (primitiveTraitCollection.userInterfaceStyle != previousTraitCollection.userInterfaceStyle) { + if (loaded) { + // we need to run that on main thread, cause accessing CALayer properties. + // It seems than in iOS 13 sometimes it causes deadlock. + ASPerformBlockOnMainThread(^{ + self->__instanceLock__.lock(); + CGFloat cornerRadius = self->_cornerRadius; + ASCornerRoundingType cornerRoundingType = self->_cornerRoundingType; + UIColor *backgroundColor = self->_backgroundColor; + self->__instanceLock__.unlock(); + // TODO: we should resolve color using node's trait collection + // but Texture changes it from many places, so we may receive the wrong one. + CGColorRef cgBackgroundColor = backgroundColor.CGColor; + if (!CGColorEqualToColor(self->_layer.backgroundColor, cgBackgroundColor)) { + // Background colors do not dynamically update for layer backed nodes since they utilize CGColorRef + // instead of UIColor. Non layer backed node also receive color to the layer (see [_ASPendingState -applyToView:withSpecialPropertiesHandling:]). + // We utilize the _backgroundColor instance variable to track the full dynamic color + // and apply any changes here when trait collection updates occur. + self->_layer.backgroundColor = cgBackgroundColor; + } + + // If we have clipping corners, re-render the clipping corner layer upon user interface style change + if (cornerRoundingType == ASCornerRoundingTypeClipping && cornerRadius > 0.0f) { + [self _updateClipCornerLayerContentsWithRadius:cornerRadius backgroundColor:backgroundColor]; + } + + [self setNeedsDisplay]; + }); + } + } + } +} + +- (void)dealloc +{ + _flags.isDeallocating = YES; + [self baseWillDealloc]; + + // Synchronous nodes may not be able to call the hierarchy notifications, so only enforce for regular nodes. + ASDisplayNodeAssert(checkFlag(Synchronous) || !ASInterfaceStateIncludesVisible(_interfaceState), @"Node should always be marked invisible before deallocating. Node: %@", self); + + self.asyncLayer.asyncDelegate = nil; + _view.asyncdisplaykit_node = nil; + _layer.asyncdisplaykit_node = nil; + + // Remove any subnodes so they lose their connection to the now deallocated parent. This can happen + // because subnodes do not retain their supernode, but subnodes can legitimately remain alive if another + // thing outside the view hierarchy system (e.g. async display, controller code, etc). keeps a retained + // reference to subnodes. + + for (ASDisplayNode *subnode in _subnodes) + [subnode _setSupernode:nil]; + + [self scheduleIvarsForMainThreadDeallocation]; + + // TODO: Remove this? If supernode isn't already nil, this method isn't dealloc-safe anyway. + [self _setSupernode:nil]; +} + +#pragma mark - Loading + +- (BOOL)_locked_shouldLoadViewOrLayer +{ + DISABLED_ASAssertLocked(__instanceLock__); + return !_flags.isDeallocating && !(_hierarchyState & ASHierarchyStateRasterized); +} + +- (UIView *)_locked_viewToLoad +{ + DISABLED_ASAssertLocked(__instanceLock__); + + UIView *view = nil; + if (_viewBlock) { + view = _viewBlock(); + ASDisplayNodeAssertNotNil(view, @"View block returned nil"); + ASDisplayNodeAssert(![view isKindOfClass:[_ASDisplayView class]], @"View block should return a synchronously displayed view"); + _viewBlock = nil; + _viewClass = [view class]; + } else { + view = [[_viewClass alloc] init]; + } + + // Special handling of wrapping UIKit components + if (checkFlag(Synchronous)) { + [self checkResponderCompatibility]; + + // UIImageView layers. More details on the flags + if ([_viewClass isSubclassOfClass:[UIImageView class]]) { + _flags.canClearContentsOfLayer = NO; + _flags.canCallSetNeedsDisplayOfLayer = NO; + } + + // UIActivityIndicator + if ([_viewClass isSubclassOfClass:[UIActivityIndicatorView class]] + || [_viewClass isSubclassOfClass:[UIVisualEffectView class]]) { + self.opaque = NO; + } + + // CAEAGLLayer + if([[view.layer class] isSubclassOfClass:[CAEAGLLayer class]]){ + _flags.canClearContentsOfLayer = NO; + } + } + + return view; +} + +- (CALayer *)_locked_layerToLoad +{ + DISABLED_ASAssertLocked(__instanceLock__); + ASDisplayNodeAssert(_flags.layerBacked, @"_layerToLoad is only for layer-backed nodes"); + + CALayer *layer = nil; + if (_layerBlock) { + layer = _layerBlock(); + ASDisplayNodeAssertNotNil(layer, @"Layer block returned nil"); + ASDisplayNodeAssert(![layer isKindOfClass:[_ASDisplayLayer class]], @"Layer block should return a synchronously displayed layer"); + _layerBlock = nil; + _layerClass = [layer class]; + } else { + layer = [[_layerClass alloc] init]; + } + + return layer; +} + +- (void)_locked_loadViewOrLayer +{ + DISABLED_ASAssertLocked(__instanceLock__); + if (_flags.layerBacked) { + TIME_SCOPED(_debugTimeToCreateView); + _layer = [self _locked_layerToLoad]; + static int ASLayerDelegateAssociationKey; + + /** + * CALayer's .delegate property is documented to be weak, but the implementation is actually assign. + * Because our layer may survive longer than the node (e.g. if someone else retains it, or if the node + * begins deallocation on a background thread and it waiting for the -dealloc call to reach main), the only + * way to avoid a dangling pointer is to use a weak proxy. + */ + ASWeakProxy *instance = [ASWeakProxy weakProxyWithTarget:self]; + _layer.delegate = (id)instance; + objc_setAssociatedObject(_layer, &ASLayerDelegateAssociationKey, instance, OBJC_ASSOCIATION_RETAIN_NONATOMIC); + } else { + TIME_SCOPED(_debugTimeToCreateView); + _view = [self _locked_viewToLoad]; + _view.asyncdisplaykit_node = self; + _layer = _view.layer; + } + _layer.asyncdisplaykit_node = self; + + self._locked_asyncLayer.asyncDelegate = self; +} + +- (void)_didLoad +{ + ASDisplayNodeAssertMainThread(); + DISABLED_ASAssertUnlocked(__instanceLock__); + as_log_verbose(ASNodeLog(), "didLoad %@", self); + TIME_SCOPED(_debugTimeForDidLoad); + + [self didLoad]; + + __instanceLock__.lock(); + const auto onDidLoadBlocks = ASTransferStrong(_onDidLoadBlocks); + __instanceLock__.unlock(); + + for (ASDisplayNodeDidLoadBlock block in onDidLoadBlocks) { + block(self); + } + [self enumerateInterfaceStateDelegates:^(id del) { + [del nodeDidLoad]; + }]; +} + +- (BOOL)isNodeLoaded +{ + if (ASDisplayNodeThreadIsMain()) { + // Because the view and layer can only be created and destroyed on Main, that is also the only thread + // where the state of this property can change. As an optimization, we can avoid locking. + return _loaded(self); + } else { + MutexLocker l(__instanceLock__); + return [self _locked_isNodeLoaded]; + } +} + +- (BOOL)_locked_isNodeLoaded +{ + DISABLED_ASAssertLocked(__instanceLock__); + return _loaded(self); +} + +#pragma mark - Misc Setter / Getter + +- (UIView *)view +{ + AS::UniqueLock l(__instanceLock__); + + ASDisplayNodeAssert(!_flags.layerBacked, @"Call to -view undefined on layer-backed nodes"); + BOOL isLayerBacked = _flags.layerBacked; + if (isLayerBacked) { + return nil; + } + + if (_view != nil) { + return _view; + } + + if (![self _locked_shouldLoadViewOrLayer]) { + return nil; + } + + // Loading a view needs to happen on the main thread + ASDisplayNodeAssertMainThread(); + [self _locked_loadViewOrLayer]; + + // FIXME: Ideally we'd call this as soon as the node receives -setNeedsLayout + // but automatic subnode management would require us to modify the node tree + // in the background on a loaded node, which isn't currently supported. + if (_pendingViewState.hasSetNeedsLayout) { + // Need to unlock before calling setNeedsLayout to avoid deadlocks. + l.unlock(); + [self __setNeedsLayout]; + l.lock(); + } + + [self _locked_applyPendingStateToViewOrLayer]; + + // The following methods should not be called with a lock + l.unlock(); + + // No need for the lock as accessing the subviews or layers are always happening on main + [self _addSubnodeViewsAndLayers]; + + // A subclass hook should never be called with a lock + [self _didLoad]; + + return _view; +} + +- (CALayer *)layer +{ + AS::UniqueLock l(__instanceLock__); + if (_layer != nil) { + return _layer; + } + + if (![self _locked_shouldLoadViewOrLayer]) { + return nil; + } + + // Loading a layer needs to happen on the main thread + ASDisplayNodeAssertMainThread(); + [self _locked_loadViewOrLayer]; + CALayer *layer = _layer; + + // FIXME: Ideally we'd call this as soon as the node receives -setNeedsLayout + // but automatic subnode management would require us to modify the node tree + // in the background on a loaded node, which isn't currently supported. + if (_pendingViewState.hasSetNeedsLayout) { + // Need to unlock before calling setNeedsLayout to avoid deadlocks. + l.unlock(); + [self __setNeedsLayout]; + l.lock(); + } + + [self _locked_applyPendingStateToViewOrLayer]; + + // The following methods should not be called with a lock + l.unlock(); + + // No need for the lock as accessing the subviews or layers are always happening on main + [self _addSubnodeViewsAndLayers]; + + // A subclass hook should never be called with a lock + [self _didLoad]; + + return layer; +} + +// Returns nil if the layer is not an _ASDisplayLayer; will not create the layer if nil. +- (_ASDisplayLayer *)asyncLayer +{ + MutexLocker l(__instanceLock__); + return [self _locked_asyncLayer]; +} + +- (_ASDisplayLayer *)_locked_asyncLayer +{ + DISABLED_ASAssertLocked(__instanceLock__); + return [_layer isKindOfClass:[_ASDisplayLayer class]] ? (_ASDisplayLayer *)_layer : nil; +} + +- (BOOL)isSynchronous +{ + return checkFlag(Synchronous); +} + +- (void)setLayerBacked:(BOOL)isLayerBacked +{ + // Only call this if assertions are enabled – it could be expensive. + ASDisplayNodeAssert(!isLayerBacked || self.supportsLayerBacking, @"Node %@ does not support layer backing.", self); + + MutexLocker l(__instanceLock__); + if (_flags.layerBacked == isLayerBacked) { + return; + } + + if ([self _locked_isNodeLoaded]) { + ASDisplayNodeFailAssert(@"Cannot change layerBacked after view/layer has loaded."); + return; + } + + _flags.layerBacked = isLayerBacked; +} + +- (BOOL)isLayerBacked +{ + MutexLocker l(__instanceLock__); + return _flags.layerBacked; +} + +- (BOOL)supportsLayerBacking +{ + MutexLocker l(__instanceLock__); + return !checkFlag(Synchronous) && !_flags.viewEverHadAGestureRecognizerAttached && _viewClass == [_ASDisplayView class] && _layerClass == [_ASDisplayLayer class]; +} + +- (BOOL)shouldAnimateSizeChanges +{ + MutexLocker l(__instanceLock__); + return _flags.shouldAnimateSizeChanges; +} + +- (void)setShouldAnimateSizeChanges:(BOOL)shouldAnimateSizeChanges +{ + MutexLocker l(__instanceLock__); + _flags.shouldAnimateSizeChanges = shouldAnimateSizeChanges; +} + +- (CGRect)threadSafeBounds +{ + MutexLocker l(__instanceLock__); + return [self _locked_threadSafeBounds]; +} + +- (CGRect)_locked_threadSafeBounds +{ + DISABLED_ASAssertLocked(__instanceLock__); + return _threadSafeBounds; +} + +- (void)setThreadSafeBounds:(CGRect)newBounds +{ + MutexLocker l(__instanceLock__); + _threadSafeBounds = newBounds; +} + +- (void)nodeViewDidAddGestureRecognizer +{ + MutexLocker l(__instanceLock__); + _flags.viewEverHadAGestureRecognizerAttached = YES; +} + +- (UIEdgeInsets)fallbackSafeAreaInsets +{ + MutexLocker l(__instanceLock__); + return _fallbackSafeAreaInsets; +} + +- (void)setFallbackSafeAreaInsets:(UIEdgeInsets)insets +{ + BOOL needsManualUpdate; + BOOL updatesLayoutMargins; + + { + MutexLocker l(__instanceLock__); + ASDisplayNodeAssertThreadAffinity(self); + + if (UIEdgeInsetsEqualToEdgeInsets(insets, _fallbackSafeAreaInsets)) { + return; + } + + _fallbackSafeAreaInsets = insets; + needsManualUpdate = !AS_AT_LEAST_IOS11 || _flags.layerBacked; + updatesLayoutMargins = needsManualUpdate && [self _locked_insetsLayoutMarginsFromSafeArea]; + } + + if (needsManualUpdate) { + [self safeAreaInsetsDidChange]; + } + + if (updatesLayoutMargins) { + [self layoutMarginsDidChange]; + } +} + +- (void)_fallbackUpdateSafeAreaOnChildren +{ + ASDisplayNodeAssertThreadAffinity(self); + + UIEdgeInsets insets = self.safeAreaInsets; + CGRect bounds = self.bounds; + + for (ASDisplayNode *child in self.subnodes) { + if (AS_AT_LEAST_IOS11 && !child.layerBacked) { + // In iOS 11 view-backed nodes already know what their safe area is. + continue; + } + + if (child.viewControllerRoot) { + // Its safe area is controlled by a view controller. Don't override it. + continue; + } + + CGRect childFrame = child.frame; + UIEdgeInsets childInsets = UIEdgeInsetsMake(MAX(insets.top - (CGRectGetMinY(childFrame) - CGRectGetMinY(bounds)), 0), + MAX(insets.left - (CGRectGetMinX(childFrame) - CGRectGetMinX(bounds)), 0), + MAX(insets.bottom - (CGRectGetMaxY(bounds) - CGRectGetMaxY(childFrame)), 0), + MAX(insets.right - (CGRectGetMaxX(bounds) - CGRectGetMaxX(childFrame)), 0)); + + child.fallbackSafeAreaInsets = childInsets; + } +} + +- (BOOL)isViewControllerRoot +{ + MutexLocker l(__instanceLock__); + return _flags.isViewControllerRoot; +} + +- (void)setViewControllerRoot:(BOOL)flag +{ + MutexLocker l(__instanceLock__); + _flags.isViewControllerRoot = flag; +} + +- (BOOL)automaticallyRelayoutOnSafeAreaChanges +{ + MutexLocker l(__instanceLock__); + return _flags.automaticallyRelayoutOnSafeAreaChanges; +} + +- (void)setAutomaticallyRelayoutOnSafeAreaChanges:(BOOL)flag +{ + MutexLocker l(__instanceLock__); + _flags.automaticallyRelayoutOnSafeAreaChanges = flag; +} + +- (BOOL)automaticallyRelayoutOnLayoutMarginsChanges +{ + MutexLocker l(__instanceLock__); + return _flags.automaticallyRelayoutOnLayoutMarginsChanges; +} + +- (void)setAutomaticallyRelayoutOnLayoutMarginsChanges:(BOOL)flag +{ + MutexLocker l(__instanceLock__); + _flags.automaticallyRelayoutOnLayoutMarginsChanges = flag; +} + +- (BOOL)placeholderEnabled +{ + MutexLocker l(__instanceLock__); + return _flags.placeholderEnabled; +} + +- (void)setPlaceholderEnabled:(BOOL)flag +{ + MutexLocker l(__instanceLock__); + _flags.placeholderEnabled = flag; +} + +- (void)__setNodeController:(ASNodeController *)controller +{ + // See docs for why we don't lock. + if (controller.shouldInvertStrongReference) { + _strongNodeController = controller; + _weakNodeController = nil; + } else { + _weakNodeController = controller; + _strongNodeController = nil; + } +} + +- (void)checkResponderCompatibility +{ +#if ASDISPLAYNODE_ASSERTIONS_ENABLED + // There are certain cases we cannot handle and are not supported: + // 1. If the _view class is not a subclass of _ASDisplayView + if (checkFlag(Synchronous)) { + // 2. At least one UIResponder methods are overwritten in the node subclass + NSString *message = @"Overwritting %@ and having a backing view that is not an _ASDisplayView is not supported."; + ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self.class, @selector(canBecomeFirstResponder)), ([NSString stringWithFormat:message, @"canBecomeFirstResponder"])); + ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self.class, @selector(becomeFirstResponder)), ([NSString stringWithFormat:message, @"becomeFirstResponder"])); + ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self.class, @selector(canResignFirstResponder)), ([NSString stringWithFormat:message, @"canResignFirstResponder"])); + ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self.class, @selector(resignFirstResponder)), ([NSString stringWithFormat:message, @"resignFirstResponder"])); + ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self.class, @selector(isFirstResponder)), ([NSString stringWithFormat:message, @"isFirstResponder"])); + } +#endif +} + +#pragma mark + +- (NSString *)debugName +{ + MutexLocker l(__instanceLock__); + return _debugName; +} + +- (void)setDebugName:(NSString *)debugName +{ + MutexLocker l(__instanceLock__); + if (!ASObjectIsEqual(_debugName, debugName)) { + _debugName = [debugName copy]; + } +} + +#pragma mark - Layout + +#pragma mark + +- (BOOL)canLayoutAsynchronous +{ + return !self.isNodeLoaded; +} + +#pragma mark Layout Pass + +- (void)__setNeedsLayout +{ + [self invalidateCalculatedLayout]; +} + +- (void)invalidateCalculatedLayout +{ + MutexLocker l(__instanceLock__); + + _layoutVersion++; + + _unflattenedLayout = nil; + +#if YOGA + [self invalidateCalculatedYogaLayout]; +#endif +} + +- (void)__layout +{ + ASDisplayNodeAssertThreadAffinity(self); + DISABLED_ASAssertUnlocked(__instanceLock__); + + BOOL loaded = NO; + { + AS::UniqueLock l(__instanceLock__); + loaded = [self _locked_isNodeLoaded]; + CGRect bounds = _threadSafeBounds; + + if (CGRectEqualToRect(bounds, CGRectZero)) { + // Performing layout on a zero-bounds view often results in frame calculations + // with negative sizes after applying margins, which will cause + // layoutThatFits: on subnodes to assert. + os_log_debug(OS_LOG_DISABLED, "Warning: No size given for node before node was trying to layout itself: %@. Please provide a frame for the node.", self); + return; + } + + // If a current layout transition is in progress there is no need to do a measurement and layout pass in here as + // this is supposed to happen within the layout transition process + if (_transitionID != ASLayoutElementContextInvalidTransitionID) { + return; + } + + as_activity_create_for_scope("-[ASDisplayNode __layout]"); + + // This method will confirm that the layout is up to date (and update if needed). + // Importantly, it will also APPLY the layout to all of our subnodes if (unless parent is transitioning). + l.unlock(); + [self _u_measureNodeWithBoundsIfNecessary:bounds]; + l.lock(); + + [self _locked_layoutPlaceholderIfNecessary]; + } + + [self _layoutSublayouts]; + + // Per API contract, `-layout` and `-layoutDidFinish` are called only if the node is loaded. + if (loaded) { + ASPerformBlockOnMainThread(^{ + [self layout]; + [self _layoutClipCornersIfNeeded]; + [self _layoutDidFinish]; + }); + } + + [self _fallbackUpdateSafeAreaOnChildren]; +} + +- (void)_layoutDidFinish +{ + ASDisplayNodeAssertMainThread(); + DISABLED_ASAssertUnlocked(__instanceLock__); + ASDisplayNodeAssertTrue(self.isNodeLoaded); + [self layoutDidFinish]; +} + +#pragma mark Calculation + +- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize + restrictedToSize:(ASLayoutElementSize)size + relativeToParentSize:(CGSize)parentSize +{ + as_activity_scope_verbose(as_activity_create("Calculate node layout", AS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT)); + as_log_verbose(ASLayoutLog(), "Calculating layout for %@ sizeRange %@", self, NSStringFromASSizeRange(constrainedSize)); + +#if AS_SIGNPOST_ENABLE + // We only want one calculateLayout signpost interval per thread. + // Currently there is no fallback for profiling i386, since it's not useful. + static _Thread_local NSInteger tls_callDepth; + if (tls_callDepth++ == 0) { + ASSignpostStart(CalculateLayout, self, "%@", ASObjectDescriptionMakeTiny(self)); + } +#endif + + ASSizeRange styleAndParentSize = ASLayoutElementSizeResolve(self.style.size, parentSize); + const ASSizeRange resolvedRange = ASSizeRangeIntersect(constrainedSize, styleAndParentSize); + ASLayout *result = [self calculateLayoutThatFits:resolvedRange]; + as_log_verbose(ASLayoutLog(), "Calculated layout %@", result); + +#if AS_SIGNPOST_ENABLE + if (--tls_callDepth == 0) { + ASSignpostEnd(CalculateLayout, self, ""); + } +#endif + + return result; +} + +- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize +{ + __ASDisplayNodeCheckForLayoutMethodOverrides; + + switch (self.layoutEngineType) { + case ASLayoutEngineTypeLayoutSpec: + return [self calculateLayoutLayoutSpec:constrainedSize]; +#if YOGA + case ASLayoutEngineTypeYoga: + return [self calculateLayoutYoga:constrainedSize]; +#endif + // If YOGA is not defined but for some reason the layout type engine is Yoga + // we explicitly fallthrough here + default: + break; + } + + // If this case is reached a layout type engine was defined for a node that is currently + // not supported. + ASDisplayNodeAssert(NO, @"No layout type determined"); + return nil; +} + +- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize +{ + __ASDisplayNodeCheckForLayoutMethodOverrides; + + return ASIsCGSizeValidForSize(constrainedSize) ? constrainedSize : CGSizeZero; +} + +- (void)layout +{ + // Hook for subclasses + ASDisplayNodeAssertMainThread(); + DISABLED_ASAssertUnlocked(__instanceLock__); + ASDisplayNodeAssertTrue(self.isNodeLoaded); + [self enumerateInterfaceStateDelegates:^(id del) { + [del nodeDidLayout]; + }]; +} + +#pragma mark Layout Transition + +- (void)_layoutTransitionMeasurementDidFinish +{ + // Hook for subclasses - No-Op in ASDisplayNode +} + +#pragma mark <_ASTransitionContextCompletionDelegate> + +/** + * After completeTransition: is called on the ASContextTransitioning object in animateLayoutTransition: this + * delegate method will be called that start the completion process of the transition + */ +- (void)transitionContext:(_ASTransitionContext *)context didComplete:(BOOL)didComplete +{ + ASDisplayNodeAssertMainThread(); + + [self didCompleteLayoutTransition:context]; + + _pendingLayoutTransitionContext = nil; + + [self _pendingLayoutTransitionDidComplete]; +} + +#pragma mark - Display + +NSString * const ASRenderingEngineDidDisplayScheduledNodesNotification = @"ASRenderingEngineDidDisplayScheduledNodes"; +NSString * const ASRenderingEngineDidDisplayNodesScheduledBeforeTimestamp = @"ASRenderingEngineDidDisplayNodesScheduledBeforeTimestamp"; + +- (BOOL)displaysAsynchronously +{ + MutexLocker l(__instanceLock__); + return [self _locked_displaysAsynchronously]; +} + +/** + * Core implementation of -displaysAsynchronously. + */ +- (BOOL)_locked_displaysAsynchronously +{ + DISABLED_ASAssertLocked(__instanceLock__); + return checkFlag(Synchronous) == NO && _flags.displaysAsynchronously; +} + +- (void)setDisplaysAsynchronously:(BOOL)displaysAsynchronously +{ + ASDisplayNodeAssertThreadAffinity(self); + + MutexLocker l(__instanceLock__); + + // Can't do this for synchronous nodes (using layers that are not _ASDisplayLayer and so we can't control display prevention/cancel) + if (checkFlag(Synchronous)) { + return; + } + + if (_flags.displaysAsynchronously == displaysAsynchronously) { + return; + } + + _flags.displaysAsynchronously = displaysAsynchronously; + + self._locked_asyncLayer.displaysAsynchronously = displaysAsynchronously; +} + +- (BOOL)rasterizesSubtree +{ + MutexLocker l(__instanceLock__); + return _flags.rasterizesSubtree; +} + +- (void)enableSubtreeRasterization +{ + MutexLocker l(__instanceLock__); + // Already rasterized from self. + if (_flags.rasterizesSubtree) { + return; + } + + // If rasterized from above, bail. + if (ASHierarchyStateIncludesRasterized(_hierarchyState)) { + ASDisplayNodeFailAssert(@"Subnode of a rasterized node should not have redundant -enableSubtreeRasterization."); + return; + } + + // Ensure not loaded. + if ([self _locked_isNodeLoaded]) { + ASDisplayNodeFailAssert(@"Cannot call %@ on loaded node: %@", NSStringFromSelector(_cmd), self); + return; + } + + // Ensure no loaded subnodes + ASDisplayNode *loadedSubnode = ASDisplayNodeFindFirstSubnode(self, ^BOOL(ASDisplayNode * _Nonnull node) { + return node.nodeLoaded; + }); + if (loadedSubnode != nil) { + ASDisplayNodeFailAssert(@"Cannot call %@ on node %@ with loaded subnode %@", NSStringFromSelector(_cmd), self, loadedSubnode); + return; + } + + _flags.rasterizesSubtree = YES; + + // Tell subnodes that now they're in a rasterized hierarchy (while holding lock!) + for (ASDisplayNode *subnode in _subnodes) { + [subnode enterHierarchyState:ASHierarchyStateRasterized]; + } +} + +- (CGFloat)contentsScaleForDisplay +{ + MutexLocker l(__instanceLock__); + + return _contentsScaleForDisplay; +} + +- (void)setContentsScaleForDisplay:(CGFloat)contentsScaleForDisplay +{ + MutexLocker l(__instanceLock__); + + if (_contentsScaleForDisplay == contentsScaleForDisplay) { + return; + } + + _contentsScaleForDisplay = contentsScaleForDisplay; +} + +- (void)displayImmediately +{ + ASDisplayNodeAssertMainThread(); + ASDisplayNodeAssert(!checkFlag(Synchronous), @"this method is designed for asynchronous mode only"); + + [self.asyncLayer displayImmediately]; +} + +- (void)recursivelyDisplayImmediately +{ + for (ASDisplayNode *child in self.subnodes) { + [child recursivelyDisplayImmediately]; + } + [self displayImmediately]; +} + +- (void)__setNeedsDisplay +{ + BOOL shouldScheduleForDisplay = NO; + { + MutexLocker l(__instanceLock__); + BOOL nowDisplay = ASInterfaceStateIncludesDisplay(_interfaceState); + // FIXME: This should not need to recursively display, so create a non-recursive variant. + // The semantics of setNeedsDisplay (as defined by CALayer behavior) are not recursive. + if (_layer != nil && !checkFlag(Synchronous) && nowDisplay && [self _implementsDisplay]) { + shouldScheduleForDisplay = YES; + } + } + + if (shouldScheduleForDisplay) { + [ASDisplayNode scheduleNodeForRecursiveDisplay:self]; + } +} + ++ (void)scheduleNodeForRecursiveDisplay:(ASDisplayNode *)node +{ + static dispatch_once_t onceToken; + static ASRunLoopQueue *renderQueue; + dispatch_once(&onceToken, ^{ + renderQueue = [[ASRunLoopQueue alloc] initWithRunLoop:CFRunLoopGetMain() + retainObjects:NO + handler:^(ASDisplayNode * _Nonnull dequeuedItem, BOOL isQueueDrained) { + [dequeuedItem _recursivelyTriggerDisplayAndBlock:NO]; + if (isQueueDrained) { + CFTimeInterval timestamp = CACurrentMediaTime(); + [[NSNotificationCenter defaultCenter] postNotificationName:ASRenderingEngineDidDisplayScheduledNodesNotification + object:nil + userInfo:@{ASRenderingEngineDidDisplayNodesScheduledBeforeTimestamp: @(timestamp)}]; + } + }]; + }); + + as_log_verbose(ASDisplayLog(), "%s %@", sel_getName(_cmd), node); + [renderQueue enqueue:node]; +} + +/// Helper method to summarize whether or not the node run through the display process +- (BOOL)_implementsDisplay +{ + MutexLocker l(__instanceLock__); + + return _flags.implementsDrawRect || _flags.implementsImageDisplay || _flags.rasterizesSubtree; +} + +// Track that a node will be displayed as part of the current node hierarchy. +// The node sending the message should usually be passed as the parameter, similar to the delegation pattern. +- (void)_pendingNodeWillDisplay:(ASDisplayNode *)node +{ + ASDisplayNodeAssertMainThread(); + + // No lock needed as _pendingDisplayNodes is main thread only + if (!_pendingDisplayNodes) { + _pendingDisplayNodes = [[ASWeakSet alloc] init]; + } + + [_pendingDisplayNodes addObject:node]; +} + +// Notify that a node that was pending display finished +// The node sending the message should usually be passed as the parameter, similar to the delegation pattern. +- (void)_pendingNodeDidDisplay:(ASDisplayNode *)node +{ + ASDisplayNodeAssertMainThread(); + + // No lock for _pendingDisplayNodes needed as it's main thread only + [_pendingDisplayNodes removeObject:node]; + + if (_pendingDisplayNodes.isEmpty) { + // Reclaim object memory. + _pendingDisplayNodes = nil; + + [self hierarchyDisplayDidFinish]; + [self enumerateInterfaceStateDelegates:^(id delegate) { + [delegate hierarchyDisplayDidFinish]; + }]; + + BOOL placeholderShouldPersist = [self placeholderShouldPersist]; + + __instanceLock__.lock(); + if (_placeholderLayer.superlayer && !placeholderShouldPersist) { + void (^cleanupBlock)() = ^{ + [self->_placeholderLayer removeFromSuperlayer]; + }; + + if (_placeholderFadeDuration > 0.0 && ASInterfaceStateIncludesVisible(self.interfaceState)) { + [CATransaction begin]; + [CATransaction setCompletionBlock:cleanupBlock]; + [CATransaction setAnimationDuration:_placeholderFadeDuration]; + _placeholderLayer.opacity = 0.0; + [CATransaction commit]; + } else { + cleanupBlock(); + } + } + __instanceLock__.unlock(); + } +} + +// Helper method to determine if it's safe to call setNeedsDisplay on a layer without throwing away the content. +// For details look at the comment on the canCallSetNeedsDisplayOfLayer flag +- (BOOL)_canCallSetNeedsDisplayOfLayer +{ + MutexLocker l(__instanceLock__); + return _flags.canCallSetNeedsDisplayOfLayer; +} + +void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock) +{ + // This recursion must handle layers in various states: + // 1. Just added to hierarchy, CA hasn't yet called -display + // 2. Previously in a hierarchy (such as a working window owned by an Intelligent Preloading class, like ASTableView / ASCollectionView / ASDKViewController) + // 3. Has no content to display at all + // Specifically for case 1), we need to explicitly trigger a -display call now. + // Otherwise, there is no opportunity to block the main thread after CoreAnimation's transaction commit + // (even a runloop observer at a late call order will not stop the next frame from compositing, showing placeholders). + + ASDisplayNode *node = [layer asyncdisplaykit_node]; + + if (node.isSynchronous && [node _canCallSetNeedsDisplayOfLayer]) { + // Layers for UIKit components that are wrapped within a node needs to be set to be displayed as the contents of + // the layer get's cleared and would not be recreated otherwise. + // We do not call this for _ASDisplayLayer as an optimization. + [layer setNeedsDisplay]; + } + + if ([node _implementsDisplay]) { + // For layers that do get displayed here, this immediately kicks off the work on the concurrent -[_ASDisplayLayer displayQueue]. + // At the same time, it creates an associated _ASAsyncTransaction, which we can use to block on display completion. See ASDisplayNode+AsyncDisplay.mm. + [layer displayIfNeeded]; + } + + // Kick off the recursion first, so that all necessary display calls are sent and the displayQueue is full of parallelizable work. + // NOTE: The docs report that `sublayers` returns a copy but it actually doesn't. + for (CALayer *sublayer in [layer.sublayers copy]) { + recursivelyTriggerDisplayForLayer(sublayer, shouldBlock); + } + + if (shouldBlock) { + // As the recursion unwinds, verify each transaction is complete and block if it is not. + // While blocking on one transaction, others may be completing concurrently, so it doesn't matter which blocks first. + BOOL waitUntilComplete = (!node.shouldBypassEnsureDisplay); + if (waitUntilComplete) { + for (_ASAsyncTransaction *transaction in [layer.asyncdisplaykit_asyncLayerTransactions copy]) { + // Even if none of the layers have had a chance to start display earlier, they will still be allowed to saturate a multicore CPU while blocking main. + // This significantly reduces time on the main thread relative to UIKit. + [transaction waitUntilComplete]; + } + } + } +} + +- (void)_recursivelyTriggerDisplayAndBlock:(BOOL)shouldBlock +{ + ASDisplayNodeAssertMainThread(); + + CALayer *layer = self.layer; + // -layoutIfNeeded is recursive, and even walks up to superlayers to check if they need layout, + // so we should call it outside of starting the recursion below. If our own layer is not marked + // as dirty, we can assume layout has run on this subtree before. + if ([layer needsLayout]) { + [layer layoutIfNeeded]; + } + recursivelyTriggerDisplayForLayer(layer, shouldBlock); +} + +- (void)recursivelyEnsureDisplaySynchronously:(BOOL)synchronously +{ + [self _recursivelyTriggerDisplayAndBlock:synchronously]; +} + +- (void)setShouldBypassEnsureDisplay:(BOOL)shouldBypassEnsureDisplay +{ + MutexLocker l(__instanceLock__); + _flags.shouldBypassEnsureDisplay = shouldBypassEnsureDisplay; +} + +- (BOOL)shouldBypassEnsureDisplay +{ + MutexLocker l(__instanceLock__); + return _flags.shouldBypassEnsureDisplay; +} + +- (void)setNeedsDisplayAtScale:(CGFloat)contentsScale +{ + { + MutexLocker l(__instanceLock__); + if (contentsScale == _contentsScaleForDisplay) { + return; + } + + _contentsScaleForDisplay = contentsScale; + } + + [self setNeedsDisplay]; +} + +- (void)recursivelySetNeedsDisplayAtScale:(CGFloat)contentsScale +{ + ASDisplayNodePerformBlockOnEveryNode(nil, self, YES, ^(ASDisplayNode *node) { + [node setNeedsDisplayAtScale:contentsScale]; + }); +} + +- (void)_layoutClipCornersIfNeeded +{ + ASDisplayNodeAssertMainThread(); + if (_clipCornerLayers[0] == nil && _clipCornerLayers[1] == nil && _clipCornerLayers[2] == nil && + _clipCornerLayers[3] == nil) { + return; + } + + CGSize boundsSize = self.bounds.size; + for (int idx = 0; idx < NUM_CLIP_CORNER_LAYERS; idx++) { + BOOL isTop = (idx == 0 || idx == 1); + BOOL isRight = (idx == 1 || idx == 3); + if (_clipCornerLayers[idx]) { + _clipCornerLayers[idx].position = CGPointMake(isRight ? boundsSize.width : 0.0, isTop ? 0.0 : boundsSize.height); + [_layer addSublayer:_clipCornerLayers[idx]]; + } + } +} + +- (void)_updateClipCornerLayerContentsWithRadius:(CGFloat)radius backgroundColor:(UIColor *)backgroundColor +{ + ASPerformBlockOnMainThread(^{ + for (int idx = 0; idx < NUM_CLIP_CORNER_LAYERS; idx++) { + // Skip corners that aren't clipped (we have already set up & torn down layers based on maskedCorners.) + if (self->_clipCornerLayers[idx] == nil) { + continue; + } + + // Layers are, in order: Top Left, Top Right, Bottom Left, Bottom Right, which mirrors CACornerMask. + // anchorPoint is Bottom Left at 0,0 and Top Right at 1,1. + BOOL isTop = (idx == 0 || idx == 1); + BOOL isRight = (idx == 1 || idx == 3); + + CGSize size = CGSizeMake(radius + 1, radius + 1); + UIImage *newContents = ASGraphicsCreateImage(self.primitiveTraitCollection, size, NO, self.contentsScaleForDisplay, nil, nil, ^{ + CGContextRef ctx = UIGraphicsGetCurrentContext(); + if (isRight == YES) { + CGContextTranslateCTM(ctx, -radius + 1, 0); + } + if (isTop == NO) { + CGContextTranslateCTM(ctx, 0, -radius + 1); + } + UIBezierPath *roundedRect = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, radius * 2, radius * 2) cornerRadius:radius]; + [roundedRect setUsesEvenOddFillRule:YES]; + [roundedRect appendPath:[UIBezierPath bezierPathWithRect:CGRectMake(-1, -1, radius * 2 + 1, radius * 2 + 1)]]; + [backgroundColor setFill]; + [roundedRect fill]; + }); + + // No lock needed, as _clipCornerLayers is only modified on the main thread. + unowned CALayer *clipCornerLayer = self->_clipCornerLayers[idx]; + clipCornerLayer.contents = (id)(newContents.CGImage); + clipCornerLayer.bounds = CGRectMake(0.0, 0.0, size.width, size.height); + clipCornerLayer.anchorPoint = CGPointMake(isRight ? 1.0 : 0.0, isTop ? 0.0 : 1.0); + } + [self _layoutClipCornersIfNeeded]; + }); +} + +- (void)_setClipCornerLayersVisible:(CACornerMask)visibleCornerLayers +{ + ASPerformBlockOnMainThread(^{ + ASDisplayNodeAssertMainThread(); + for (int idx = 0; idx < NUM_CLIP_CORNER_LAYERS; idx++) { + BOOL visible = (0 != (visibleCornerLayers & (1 << idx))); + if (visible == (self->_clipCornerLayers[idx] != nil)) { + continue; + } else if (visible) { + static ASDisplayNodeCornerLayerDelegate *clipCornerLayers; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + clipCornerLayers = [[ASDisplayNodeCornerLayerDelegate alloc] init]; + }); + self->_clipCornerLayers[idx] = [[CALayer alloc] init]; + self->_clipCornerLayers[idx].zPosition = 99999; + self->_clipCornerLayers[idx].delegate = clipCornerLayers; + } else { + [self->_clipCornerLayers[idx] removeFromSuperlayer]; + self->_clipCornerLayers[idx] = nil; + } + } + [self _updateClipCornerLayerContentsWithRadius:self->_cornerRadius backgroundColor:self.backgroundColor]; + }); +} + +- (void)updateCornerRoundingWithType:(ASCornerRoundingType)newRoundingType + cornerRadius:(CGFloat)newCornerRadius + maskedCorners:(CACornerMask)newMaskedCorners +{ + __instanceLock__.lock(); + CGFloat oldCornerRadius = _cornerRadius; + ASCornerRoundingType oldRoundingType = _cornerRoundingType; + CACornerMask oldMaskedCorners = _maskedCorners; + + _cornerRadius = newCornerRadius; + _cornerRoundingType = newRoundingType; + _maskedCorners = newMaskedCorners; + __instanceLock__.unlock(); + + ASPerformBlockOnMainThread(^{ + ASDisplayNodeAssertMainThread(); + + if (oldRoundingType != newRoundingType || oldCornerRadius != newCornerRadius || oldMaskedCorners != newMaskedCorners) { + if (oldRoundingType == ASCornerRoundingTypeDefaultSlowCALayer) { + if (newRoundingType == ASCornerRoundingTypePrecomposited) { + self.layerCornerRadius = 0.0; + if (oldCornerRadius > 0.0) { + [self displayImmediately]; + } else { + [self setNeedsDisplay]; // Async display is OK if we aren't replacing an existing .cornerRadius. + } + } + else if (newRoundingType == ASCornerRoundingTypeClipping) { + self.layerCornerRadius = 0.0; + [self _setClipCornerLayersVisible:newMaskedCorners]; + } else if (newRoundingType == ASCornerRoundingTypeDefaultSlowCALayer) { + self.layerCornerRadius = newCornerRadius; + self.layerMaskedCorners = newMaskedCorners; + } + } + else if (oldRoundingType == ASCornerRoundingTypePrecomposited) { + if (newRoundingType == ASCornerRoundingTypeDefaultSlowCALayer) { + self.layerCornerRadius = newCornerRadius; + self.layerMaskedCorners = newMaskedCorners; + [self setNeedsDisplay]; + } + else if (newRoundingType == ASCornerRoundingTypePrecomposited) { + // Corners are already precomposited, but the radius has changed. + // Default to async re-display. The user may force a synchronous display if desired. + [self setNeedsDisplay]; + } + else if (newRoundingType == ASCornerRoundingTypeClipping) { + [self _setClipCornerLayersVisible:newMaskedCorners]; + [self setNeedsDisplay]; + } + } + else if (oldRoundingType == ASCornerRoundingTypeClipping) { + if (newRoundingType == ASCornerRoundingTypeDefaultSlowCALayer) { + self.layerCornerRadius = newCornerRadius; + [self _setClipCornerLayersVisible:kNilOptions]; + } + else if (newRoundingType == ASCornerRoundingTypePrecomposited) { + [self _setClipCornerLayersVisible:kNilOptions]; + [self displayImmediately]; + } + else if (newRoundingType == ASCornerRoundingTypeClipping) { + // Clip corners already exist, but the radius and/or maskedCorners have changed. + // This method will add & remove them, and subsequently redraw them. + [self _setClipCornerLayersVisible:newMaskedCorners]; + } + } + } + }); +} + +- (void)recursivelySetDisplaySuspended:(BOOL)flag +{ + _recursivelySetDisplaySuspended(self, nil, flag); +} + +// TODO: Replace this with ASDisplayNodePerformBlockOnEveryNode or a variant with a condition / test block. +static void _recursivelySetDisplaySuspended(ASDisplayNode *node, CALayer *layer, BOOL flag) +{ + // If there is no layer, but node whose its view is loaded, then we can traverse down its layer hierarchy. Otherwise we must stick to the node hierarchy to avoid loading views prematurely. Note that for nodes that haven't loaded their views, they can't possibly have subviews/sublayers, so we don't need to traverse the layer hierarchy for them. + if (!layer && node && node.nodeLoaded) { + layer = node.layer; + } + + // If we don't know the node, but the layer is an async layer, get the node from the layer. + if (!node && layer && [layer isKindOfClass:[_ASDisplayLayer class]]) { + node = layer.asyncdisplaykit_node; + } + + // Set the flag on the node. If this is a pure layer (no node) then this has no effect (plain layers don't support preventing/cancelling display). + node.displaySuspended = flag; + + if (layer && !node.rasterizesSubtree) { + // If there is a layer, recurse down the layer hierarchy to set the flag on descendants. This will cover both layer-based and node-based children. + for (CALayer *sublayer in layer.sublayers) { + _recursivelySetDisplaySuspended(nil, sublayer, flag); + } + } else { + // If there is no layer (view not loaded yet) or this node rasterizes descendants (there won't be a layer tree to traverse), recurse down the subnode hierarchy to set the flag on descendants. This covers only node-based children, but for a node whose view is not loaded it can't possibly have nodeless children. + for (ASDisplayNode *subnode in node.subnodes) { + _recursivelySetDisplaySuspended(subnode, nil, flag); + } + } +} + +- (BOOL)displaySuspended +{ + MutexLocker l(__instanceLock__); + return _flags.displaySuspended; +} + +- (void)setDisplaySuspended:(BOOL)flag +{ + ASDisplayNodeAssertThreadAffinity(self); + __instanceLock__.lock(); + + // Can't do this for synchronous nodes (using layers that are not _ASDisplayLayer and so we can't control display prevention/cancel) + if (checkFlag(Synchronous) || _flags.displaySuspended == flag) { + __instanceLock__.unlock(); + return; + } + + _flags.displaySuspended = flag; + + self._locked_asyncLayer.displaySuspended = flag; + + ASDisplayNode *supernode = _supernode; + __instanceLock__.unlock(); + + if ([self _implementsDisplay]) { + // Display start and finish methods needs to happen on the main thread + ASPerformBlockOnMainThread(^{ + if (flag) { + [supernode subnodeDisplayDidFinish:self]; + } else { + [supernode subnodeDisplayWillStart:self]; + } + }); + } +} + +#pragma mark <_ASDisplayLayerDelegate> + +- (void)willDisplayAsyncLayer:(_ASDisplayLayer *)layer asynchronously:(BOOL)asynchronously +{ + // Subclass hook. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + [self displayWillStart]; +#pragma clang diagnostic pop + + [self displayWillStartAsynchronously:asynchronously]; +} + +- (void)didDisplayAsyncLayer:(_ASDisplayLayer *)layer +{ + // Subclass hook. + [self displayDidFinish]; +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-implementations" +- (void)displayWillStart {} +#pragma clang diagnostic pop +- (void)displayWillStartAsynchronously:(BOOL)asynchronously +{ + ASDisplayNodeAssertMainThread(); + + // in case current node takes longer to display than it's subnodes, treat it as a dependent node + [self _pendingNodeWillDisplay:self]; + + __instanceLock__.lock(); + ASDisplayNode *supernode = _supernode; + __instanceLock__.unlock(); + + [supernode subnodeDisplayWillStart:self]; +} + +- (void)displayDidFinish +{ + ASDisplayNodeAssertMainThread(); + + [self _pendingNodeDidDisplay:self]; + + __instanceLock__.lock(); + ASDisplayNode *supernode = _supernode; + __instanceLock__.unlock(); + + [supernode subnodeDisplayDidFinish:self]; +} + +- (void)subnodeDisplayWillStart:(ASDisplayNode *)subnode +{ + // Subclass hook + [self _pendingNodeWillDisplay:subnode]; +} + +- (void)subnodeDisplayDidFinish:(ASDisplayNode *)subnode +{ + // Subclass hook + [self _pendingNodeDidDisplay:subnode]; +} + +#pragma mark + +- (id)actionForLayer:(CALayer *)layer forKey:(NSString *)event +{ + // Only drive __enterHierarchy and __exitHierarchy if the node is layer-backed. + // View-backed nodes handle them in _ASDisplayView's -willMoveToWindow: and -didMoveToWindow. + if (self.isLayerBacked) { + if (event == kCAOnOrderIn) { + [self __enterHierarchy]; + } else if (event == kCAOnOrderOut) { + [self __exitHierarchy]; + } + } + + return [self layerActionForKey:event]; +} + +#pragma mark - Error Handling + ++ (void)setNonFatalErrorBlock:(ASDisplayNodeNonFatalErrorBlock)nonFatalErrorBlock +{ + if (_nonFatalErrorBlock != nonFatalErrorBlock) { + _nonFatalErrorBlock = [nonFatalErrorBlock copy]; + } +} + ++ (ASDisplayNodeNonFatalErrorBlock)nonFatalErrorBlock +{ + return _nonFatalErrorBlock; +} + +#pragma mark - Converting to and from the Node's Coordinate System + +- (CATransform3D)_transformToAncestor:(ASDisplayNode *)ancestor +{ + CATransform3D transform = CATransform3DIdentity; + ASDisplayNode *currentNode = self; + while (currentNode.supernode) { + if (currentNode == ancestor) { + return transform; + } + + CGPoint anchorPoint = currentNode.anchorPoint; + CGRect bounds = currentNode.bounds; + CGPoint position = currentNode.position; + CGPoint origin = CGPointMake(position.x - bounds.size.width * anchorPoint.x, + position.y - bounds.size.height * anchorPoint.y); + + transform = CATransform3DTranslate(transform, origin.x, origin.y, 0); + transform = CATransform3DTranslate(transform, -bounds.origin.x, -bounds.origin.y, 0); + currentNode = currentNode.supernode; + } + return transform; +} + +static inline CATransform3D _calculateTransformFromReferenceToTarget(ASDisplayNode *referenceNode, ASDisplayNode *targetNode) +{ + ASDisplayNode *ancestor = ASDisplayNodeFindClosestCommonAncestor(referenceNode, targetNode); + + // Transform into global (away from reference coordinate space) + CATransform3D transformToGlobal = [referenceNode _transformToAncestor:ancestor]; + + // Transform into local (via inverse transform from target to ancestor) + CATransform3D transformToLocal = CATransform3DInvert([targetNode _transformToAncestor:ancestor]); + + return CATransform3DConcat(transformToGlobal, transformToLocal); +} + +- (CGPoint)convertPoint:(CGPoint)point fromNode:(ASDisplayNode *)node +{ + ASDisplayNodeAssertThreadAffinity(self); + + /** + * When passed node=nil, all methods in this family use the UIView-style + * behavior – that is, convert from/to window coordinates if there's a window, + * otherwise return the point untransformed. + */ + if (node == nil && self.nodeLoaded) { + CALayer *layer = self.layer; + if (UIWindow *window = ASFindWindowOfLayer(layer)) { + return [layer convertPoint:point fromLayer:window.layer]; + } else { + return point; + } + } + + // Get root node of the accessible node hierarchy, if node not specified + node = node ? : ASDisplayNodeUltimateParentOfNode(self); + + // Calculate transform to map points between coordinate spaces + CATransform3D nodeTransform = _calculateTransformFromReferenceToTarget(node, self); + CGAffineTransform flattenedTransform = CATransform3DGetAffineTransform(nodeTransform); + ASDisplayNodeAssertTrue(CATransform3DIsAffine(nodeTransform)); + + // Apply to point + return CGPointApplyAffineTransform(point, flattenedTransform); +} + +- (CGPoint)convertPoint:(CGPoint)point toNode:(ASDisplayNode *)node +{ + ASDisplayNodeAssertThreadAffinity(self); + + if (node == nil && self.nodeLoaded) { + CALayer *layer = self.layer; + if (UIWindow *window = ASFindWindowOfLayer(layer)) { + return [layer convertPoint:point toLayer:window.layer]; + } else { + return point; + } + } + + // Get root node of the accessible node hierarchy, if node not specified + node = node ? : ASDisplayNodeUltimateParentOfNode(self); + + // Calculate transform to map points between coordinate spaces + CATransform3D nodeTransform = _calculateTransformFromReferenceToTarget(self, node); + CGAffineTransform flattenedTransform = CATransform3DGetAffineTransform(nodeTransform); + ASDisplayNodeAssertTrue(CATransform3DIsAffine(nodeTransform)); + + // Apply to point + return CGPointApplyAffineTransform(point, flattenedTransform); +} + +- (CGRect)convertRect:(CGRect)rect fromNode:(ASDisplayNode *)node +{ + ASDisplayNodeAssertThreadAffinity(self); + + if (node == nil && self.nodeLoaded) { + CALayer *layer = self.layer; + if (UIWindow *window = ASFindWindowOfLayer(layer)) { + return [layer convertRect:rect fromLayer:window.layer]; + } else { + return rect; + } + } + + // Get root node of the accessible node hierarchy, if node not specified + node = node ? : ASDisplayNodeUltimateParentOfNode(self); + + // Calculate transform to map points between coordinate spaces + CATransform3D nodeTransform = _calculateTransformFromReferenceToTarget(node, self); + CGAffineTransform flattenedTransform = CATransform3DGetAffineTransform(nodeTransform); + ASDisplayNodeAssertTrue(CATransform3DIsAffine(nodeTransform)); + + // Apply to rect + return CGRectApplyAffineTransform(rect, flattenedTransform); +} + +- (CGRect)convertRect:(CGRect)rect toNode:(ASDisplayNode *)node +{ + ASDisplayNodeAssertThreadAffinity(self); + + if (node == nil && self.nodeLoaded) { + CALayer *layer = self.layer; + if (UIWindow *window = ASFindWindowOfLayer(layer)) { + return [layer convertRect:rect toLayer:window.layer]; + } else { + return rect; + } + } + + // Get root node of the accessible node hierarchy, if node not specified + node = node ? : ASDisplayNodeUltimateParentOfNode(self); + + // Calculate transform to map points between coordinate spaces + CATransform3D nodeTransform = _calculateTransformFromReferenceToTarget(self, node); + CGAffineTransform flattenedTransform = CATransform3DGetAffineTransform(nodeTransform); + ASDisplayNodeAssertTrue(CATransform3DIsAffine(nodeTransform)); + + // Apply to rect + return CGRectApplyAffineTransform(rect, flattenedTransform); +} + +#pragma mark - Managing the Node Hierarchy + +ASDISPLAYNODE_INLINE bool shouldDisableNotificationsForMovingBetweenParents(ASDisplayNode *from, ASDisplayNode *to) { + if (!from || !to) return NO; + if (from.isSynchronous) return NO; + if (to.isSynchronous) return NO; + if (from.isInHierarchy != to.isInHierarchy) return NO; + return YES; +} + +/// Returns incremented value of i if i is not NSNotFound +ASDISPLAYNODE_INLINE NSInteger incrementIfFound(NSInteger i) { + return i == NSNotFound ? NSNotFound : i + 1; +} + +/// Returns if a node is a member of a rasterized tree +ASDISPLAYNODE_INLINE BOOL canUseViewAPI(ASDisplayNode *node, ASDisplayNode *subnode) { + return (subnode.isLayerBacked == NO && node.isLayerBacked == NO); +} + +/// Returns if node is a member of a rasterized tree +ASDISPLAYNODE_INLINE BOOL subtreeIsRasterized(ASDisplayNode *node) { + return (node.rasterizesSubtree || (node.hierarchyState & ASHierarchyStateRasterized)); +} + +// NOTE: This method must be dealloc-safe (should not retain self). +- (ASDisplayNode *)supernode +{ + MutexLocker l(__instanceLock__); + return _supernode; +} + +- (void)_setSupernode:(ASDisplayNode *)newSupernode +{ + BOOL supernodeDidChange = NO; + ASDisplayNode *oldSupernode = nil; + { + MutexLocker l(__instanceLock__); + if (_supernode != newSupernode) { + oldSupernode = _supernode; // Access supernode properties outside of lock to avoid remote chance of deadlock, + // in case supernode implementation must access one of our properties. + _supernode = newSupernode; + supernodeDidChange = YES; + } + } + + if (supernodeDidChange) { + // Hierarchy state + ASHierarchyState stateToEnterOrExit = (newSupernode ? newSupernode.hierarchyState + : oldSupernode.hierarchyState); + + // Rasterized state + BOOL parentWasOrIsRasterized = (newSupernode ? newSupernode.rasterizesSubtree + : oldSupernode.rasterizesSubtree); + if (parentWasOrIsRasterized) { + stateToEnterOrExit |= ASHierarchyStateRasterized; + } + if (newSupernode) { + + // Now that we have a supernode, propagate its traits to self. + // This should be done before possibly forcing self to load so we have traits in -didLoad + ASTraitCollectionPropagateDown(self, newSupernode.primitiveTraitCollection); + + if (!parentWasOrIsRasterized && newSupernode.nodeLoaded) { + // Trigger the subnode to load its layer, which will create its view if it needs one. + // By doing this prior to the downward propagation of newSupernode's interface state, + // we can guarantee that -didEnterVisibleState is only called with .isNodeLoaded = YES. + [self layer]; + } + + [self enterHierarchyState:stateToEnterOrExit]; + + // If a node was added to a supernode, the supernode could be in a layout pending state. All of the hierarchy state + // properties related to the transition need to be copied over as well as propagated down the subtree. + // This is especially important as with automatic subnode management, adding subnodes can happen while a transition + // is in fly + if (ASHierarchyStateIncludesLayoutPending(stateToEnterOrExit)) { + int32_t pendingTransitionId = newSupernode->_pendingTransitionID; + if (pendingTransitionId != ASLayoutElementContextInvalidTransitionID) { + { + _pendingTransitionID = pendingTransitionId; + + // Propagate down the new pending transition id + ASDisplayNodePerformBlockOnEverySubnode(self, NO, ^(ASDisplayNode * _Nonnull node) { + node->_pendingTransitionID = pendingTransitionId; + }); + } + } + } + } else { + // If a node will be removed from the supernode it should go out from the layout pending state to remove all + // layout pending state related properties on the node + stateToEnterOrExit |= ASHierarchyStateLayoutPending; + + [self exitHierarchyState:stateToEnterOrExit]; + + // We only need to explicitly exit hierarchy here if we were rasterized. + // Otherwise we will exit the hierarchy when our view/layer does so + // which has some nice carry-over machinery to handle cases where we are removed from a hierarchy + // and then added into it again shortly after. + __instanceLock__.lock(); + BOOL isInHierarchy = _flags.isInHierarchy; + __instanceLock__.unlock(); + + if (parentWasOrIsRasterized && isInHierarchy) { + [self __exitHierarchy]; + } + } + } +} + +- (NSArray *)subnodes +{ + MutexLocker l(__instanceLock__); + if (_cachedSubnodes == nil) { + _cachedSubnodes = [_subnodes copy]; + } else { + ASDisplayNodeAssert(ASObjectIsEqual(_cachedSubnodes, _subnodes), @"Expected _subnodes and _cachedSubnodes to have the same contents."); + } + return _cachedSubnodes ?: @[]; +} + +/* + * Central private helper method that should eventually be called if submethods add, insert or replace subnodes + * This method is called with thread affinity and without lock held. + * + * @param subnode The subnode to insert + * @param subnodeIndex The index in _subnodes to insert it + * @param viewSublayerIndex The index in layer.sublayers (not view.subviews) at which to insert the view (use if we can use the view API) otherwise pass NSNotFound + * @param sublayerIndex The index in layer.sublayers at which to insert the layer (use if either parent or subnode is layer-backed) otherwise pass NSNotFound + * @param oldSubnode Remove this subnode before inserting; ok to be nil if no removal is desired + */ +- (void)_insertSubnode:(ASDisplayNode *)subnode atSubnodeIndex:(NSInteger)subnodeIndex sublayerIndex:(NSInteger)sublayerIndex andRemoveSubnode:(ASDisplayNode *)oldSubnode +{ + ASDisplayNodeAssertThreadAffinity(self); + // TODO: Disabled due to PR: https://github.com/TextureGroup/Texture/pull/1204 + DISABLED_ASAssertUnlocked(__instanceLock__); + + as_log_verbose(ASNodeLog(), "Insert subnode %@ at index %zd of %@ and remove subnode %@", subnode, subnodeIndex, self, oldSubnode); + + if (subnode == nil || subnode == self) { + ASDisplayNodeFailAssert(@"Cannot insert a nil subnode or self as subnode"); + return; + } + + if (subnodeIndex == NSNotFound) { + ASDisplayNodeFailAssert(@"Try to insert node on an index that was not found"); + return; + } + + if (self.layerBacked && !subnode.layerBacked) { + ASDisplayNodeFailAssert(@"Cannot add a view-backed node as a subnode of a layer-backed node. Supernode: %@, subnode: %@", self, subnode); + return; + } + + BOOL isRasterized = subtreeIsRasterized(self); + if (isRasterized && subnode.nodeLoaded) { + ASDisplayNodeFailAssert(@"Cannot add loaded node %@ to rasterized subtree of node %@", ASObjectDescriptionMakeTiny(subnode), ASObjectDescriptionMakeTiny(self)); + return; + } + + __instanceLock__.lock(); + NSUInteger subnodesCount = _subnodes.count; + __instanceLock__.unlock(); + if (subnodeIndex > subnodesCount || subnodeIndex < 0) { + ASDisplayNodeFailAssert(@"Cannot insert a subnode at index %ld. Count is %ld", (long)subnodeIndex, (long)subnodesCount); + return; + } + + // Disable appearance methods during move between supernodes, but make sure we restore their state after we do our thing + ASDisplayNode *oldParent = subnode.supernode; + BOOL disableNotifications = shouldDisableNotificationsForMovingBetweenParents(oldParent, self); + if (disableNotifications) { + [subnode __incrementVisibilityNotificationsDisabled]; + } + + [subnode removeFromSupernode]; + [oldSubnode removeFromSupernode]; + + __instanceLock__.lock(); + if (_subnodes == nil) { + _subnodes = [[NSMutableArray alloc] init]; + } + [_subnodes insertObject:subnode atIndex:subnodeIndex]; + _cachedSubnodes = nil; + __instanceLock__.unlock(); + + // This call will apply our .hierarchyState to the new subnode. + // If we are a managed hierarchy, as in ASCellNode trees, it will also apply our .interfaceState. + [subnode _setSupernode:self]; + + // If this subnode will be rasterized, enter hierarchy if needed + // TODO: Move this into _setSupernode: ? + if (isRasterized) { + if (self.inHierarchy) { + [subnode __enterHierarchy]; + } + } else if (self.nodeLoaded) { + // If not rasterizing, and node is loaded insert the subview/sublayer now. + [self _insertSubnodeSubviewOrSublayer:subnode atIndex:sublayerIndex]; + } // Otherwise we will insert subview/sublayer when we get loaded + + ASDisplayNodeAssert(disableNotifications == shouldDisableNotificationsForMovingBetweenParents(oldParent, self), @"Invariant violated"); + if (disableNotifications) { + [subnode __decrementVisibilityNotificationsDisabled]; + } +} + +/* + * Inserts the view or layer of the given node at the given index + * + * @param subnode The subnode to insert + * @param idx The index in _view.subviews or _layer.sublayers at which to insert the subnode.view or + * subnode.layer of the subnode + */ +- (void)_insertSubnodeSubviewOrSublayer:(ASDisplayNode *)subnode atIndex:(NSInteger)idx +{ + ASDisplayNodeAssertMainThread(); + ASDisplayNodeAssert(self.nodeLoaded, @"_insertSubnodeSubviewOrSublayer:atIndex: should never be called before our own view is created"); + + ASDisplayNodeAssert(idx != NSNotFound, @"Try to insert node on an index that was not found"); + if (idx == NSNotFound) { + return; + } + + // Because the view and layer can only be created and destroyed on Main, that is also the only thread + // where the view and layer can change. We can avoid locking. + + // If we can use view API, do. Due to an apple bug, -insertSubview:atIndex: actually wants a LAYER index, + // which we pass in. + if (canUseViewAPI(self, subnode)) { + [_view insertSubview:subnode.view atIndex:idx]; + } else { + [_layer insertSublayer:subnode.layer atIndex:(unsigned int)idx]; + } +} + +- (void)addSubnode:(ASDisplayNode *)subnode +{ + ASDisplayNodeAssertThreadAffinity(self); + + ASDisplayNodeAssert(subnode, @"Cannot insert a nil subnode"); + + // Don't add if it's already a subnode + ASDisplayNode *oldParent = subnode.supernode; + if (!subnode || subnode == self || oldParent == self) { + return; + } + + NSUInteger subnodesIndex; + NSUInteger sublayersIndex; + { + MutexLocker l(__instanceLock__); + subnodesIndex = _subnodes.count; + sublayersIndex = _layer.sublayers.count; + } + + [self _insertSubnode:subnode atSubnodeIndex:subnodesIndex sublayerIndex:sublayersIndex andRemoveSubnode:nil]; +} + +- (void)_addSubnodeViewsAndLayers +{ + ASDisplayNodeAssertMainThread(); + + TIME_SCOPED(_debugTimeToAddSubnodeViews); + + for (ASDisplayNode *node in self.subnodes) { + [self _addSubnodeSubviewOrSublayer:node]; + } +} + +- (void)_addSubnodeSubviewOrSublayer:(ASDisplayNode *)subnode +{ + ASDisplayNodeAssertMainThread(); + + // Due to a bug in Apple's framework we have to use the layer index to insert a subview + // so just use the count of the sublayers to add the subnode + NSInteger idx = _layer.sublayers.count; // No locking is needed as it's main thread only + [self _insertSubnodeSubviewOrSublayer:subnode atIndex:idx]; +} + +- (void)replaceSubnode:(ASDisplayNode *)oldSubnode withSubnode:(ASDisplayNode *)replacementSubnode +{ + ASDisplayNodeAssertThreadAffinity(self); + + if (replacementSubnode == nil) { + ASDisplayNodeFailAssert(@"Invalid subnode to replace"); + return; + } + + if (oldSubnode.supernode != self) { + ASDisplayNodeFailAssert(@"Old Subnode to replace must be a subnode"); + return; + } + + ASDisplayNodeAssert(!(self.nodeLoaded && !oldSubnode.nodeLoaded), @"We have view loaded, but child node does not."); + + NSInteger subnodeIndex; + NSInteger sublayerIndex = NSNotFound; + { + MutexLocker l(__instanceLock__); + ASDisplayNodeAssert(_subnodes, @"You should have subnodes if you have a subnode"); + + subnodeIndex = [_subnodes indexOfObjectIdenticalTo:oldSubnode]; + + // Don't bother figuring out the sublayerIndex if in a rasterized subtree, because there are no layers in the + // hierarchy and none of this could possibly work. + if (subtreeIsRasterized(self) == NO) { + if (_layer) { + sublayerIndex = [_layer.sublayers indexOfObjectIdenticalTo:oldSubnode.layer]; + ASDisplayNodeAssert(sublayerIndex != NSNotFound, @"Somehow oldSubnode's supernode is self, yet we could not find it in our layers to replace"); + if (sublayerIndex == NSNotFound) { + return; + } + } + } + } + + [self _insertSubnode:replacementSubnode atSubnodeIndex:subnodeIndex sublayerIndex:sublayerIndex andRemoveSubnode:oldSubnode]; +} + +- (void)insertSubnode:(ASDisplayNode *)subnode belowSubnode:(ASDisplayNode *)below +{ + ASDisplayNodeAssertThreadAffinity(self); + // TODO: Disabled due to PR: https://github.com/TextureGroup/Texture/pull/1204 + DISABLED_ASAssertUnlocked(__instanceLock__); + + if (subnode == nil) { + ASDisplayNodeFailAssert(@"Cannot insert a nil subnode"); + return; + } + + if (below.supernode != self) { + ASDisplayNodeFailAssert(@"Node to insert below must be a subnode"); + return; + } + + NSInteger belowSubnodeIndex; + NSInteger belowSublayerIndex = NSNotFound; + { + MutexLocker l(__instanceLock__); + ASDisplayNodeAssert(_subnodes, @"You should have subnodes if you have a subnode"); + + belowSubnodeIndex = [_subnodes indexOfObjectIdenticalTo:below]; + + // Don't bother figuring out the sublayerIndex if in a rasterized subtree, because there are no layers in the + // hierarchy and none of this could possibly work. + if (subtreeIsRasterized(self) == NO) { + if (_layer) { + belowSublayerIndex = [_layer.sublayers indexOfObjectIdenticalTo:below.layer]; + ASDisplayNodeAssert(belowSublayerIndex != NSNotFound, @"Somehow below's supernode is self, yet we could not find it in our layers to reference"); + if (belowSublayerIndex == NSNotFound) + return; + } + + ASDisplayNodeAssert(belowSubnodeIndex != NSNotFound, @"Couldn't find above in subnodes"); + + // If the subnode is already in the subnodes array / sublayers and it's before the below node, removing it to + // insert it will mess up our calculation + if (subnode.supernode == self) { + NSInteger currentIndexInSubnodes = [_subnodes indexOfObjectIdenticalTo:subnode]; + if (currentIndexInSubnodes < belowSubnodeIndex) { + belowSubnodeIndex--; + } + if (_layer) { + NSInteger currentIndexInSublayers = [_layer.sublayers indexOfObjectIdenticalTo:subnode.layer]; + if (currentIndexInSublayers < belowSublayerIndex) { + belowSublayerIndex--; + } + } + } + } + } + + ASDisplayNodeAssert(belowSubnodeIndex != NSNotFound, @"Couldn't find below in subnodes"); + + [self _insertSubnode:subnode atSubnodeIndex:belowSubnodeIndex sublayerIndex:belowSublayerIndex andRemoveSubnode:nil]; +} + +- (void)insertSubnode:(ASDisplayNode *)subnode aboveSubnode:(ASDisplayNode *)above +{ + ASDisplayNodeAssertThreadAffinity(self); + // TODO: Disabled due to PR: https://github.com/TextureGroup/Texture/pull/1204 + DISABLED_ASAssertUnlocked(__instanceLock__); + + if (subnode == nil) { + ASDisplayNodeFailAssert(@"Cannot insert a nil subnode"); + return; + } + + if (above.supernode != self) { + ASDisplayNodeFailAssert(@"Node to insert above must be a subnode"); + return; + } + + NSInteger aboveSubnodeIndex; + NSInteger aboveSublayerIndex = NSNotFound; + { + MutexLocker l(__instanceLock__); + ASDisplayNodeAssert(_subnodes, @"You should have subnodes if you have a subnode"); + + aboveSubnodeIndex = [_subnodes indexOfObjectIdenticalTo:above]; + + // Don't bother figuring out the sublayerIndex if in a rasterized subtree, because there are no layers in the + // hierarchy and none of this could possibly work. + if (subtreeIsRasterized(self) == NO) { + if (_layer) { + aboveSublayerIndex = [_layer.sublayers indexOfObjectIdenticalTo:above.layer]; + ASDisplayNodeAssert(aboveSublayerIndex != NSNotFound, @"Somehow above's supernode is self, yet we could not find it in our layers to replace"); + if (aboveSublayerIndex == NSNotFound) + return; + } + + ASDisplayNodeAssert(aboveSubnodeIndex != NSNotFound, @"Couldn't find above in subnodes"); + + // If the subnode is already in the subnodes array / sublayers and it's before the below node, removing it to + // insert it will mess up our calculation + if (subnode.supernode == self) { + NSInteger currentIndexInSubnodes = [_subnodes indexOfObjectIdenticalTo:subnode]; + if (currentIndexInSubnodes <= aboveSubnodeIndex) { + aboveSubnodeIndex--; + } + if (_layer) { + NSInteger currentIndexInSublayers = [_layer.sublayers indexOfObjectIdenticalTo:subnode.layer]; + if (currentIndexInSublayers <= aboveSublayerIndex) { + aboveSublayerIndex--; + } + } + } + } + } + + [self _insertSubnode:subnode atSubnodeIndex:incrementIfFound(aboveSubnodeIndex) sublayerIndex:incrementIfFound(aboveSublayerIndex) andRemoveSubnode:nil]; +} + +- (void)insertSubnode:(ASDisplayNode *)subnode atIndex:(NSInteger)idx +{ + ASDisplayNodeAssertThreadAffinity(self); + // TODO: Disabled due to PR: https://github.com/TextureGroup/Texture/pull/1204 + DISABLED_ASAssertUnlocked(__instanceLock__); + + if (subnode == nil) { + ASDisplayNodeFailAssert(@"Cannot insert a nil subnode"); + return; + } + + NSInteger sublayerIndex = NSNotFound; + { + MutexLocker l(__instanceLock__); + + if (idx > _subnodes.count || idx < 0) { + ASDisplayNodeFailAssert(@"Cannot insert a subnode at index %ld. Count is %ld", (long)idx, (long)_subnodes.count); + return; + } + + // Don't bother figuring out the sublayerIndex if in a rasterized subtree, because there are no layers in the + // hierarchy and none of this could possibly work. + if (subtreeIsRasterized(self) == NO) { + // Account for potentially having other subviews + if (_layer && idx == 0) { + sublayerIndex = 0; + } else if (_layer) { + ASDisplayNode *positionInRelationTo = (_subnodes.count > 0 && idx > 0) ? _subnodes[idx - 1] : nil; + if (positionInRelationTo) { + sublayerIndex = incrementIfFound([_layer.sublayers indexOfObjectIdenticalTo:positionInRelationTo.layer]); + } + } + } + } + + [self _insertSubnode:subnode atSubnodeIndex:idx sublayerIndex:sublayerIndex andRemoveSubnode:nil]; +} + +- (void)_removeSubnode:(ASDisplayNode *)subnode +{ + ASDisplayNodeAssertThreadAffinity(self); + // TODO: Disabled due to PR: https://github.com/TextureGroup/Texture/pull/1204 + DISABLED_ASAssertUnlocked(__instanceLock__); + + // Don't call self.supernode here because that will retain/autorelease the supernode. This method -_removeSupernode: is often called while tearing down a node hierarchy, and the supernode in question might be in the middle of its -dealloc. The supernode is never messaged, only compared by value, so this is safe. + // The particular issue that triggers this edge case is when a node calls -removeFromSupernode on a subnode from within its own -dealloc method. + if (!subnode || subnode.supernode != self) { + return; + } + + __instanceLock__.lock(); + [_subnodes removeObjectIdenticalTo:subnode]; + _cachedSubnodes = nil; + __instanceLock__.unlock(); + + [subnode _setSupernode:nil]; +} + +- (void)removeFromSupernode +{ + ASDisplayNodeAssertThreadAffinity(self); + // TODO: Disabled due to PR: https://github.com/TextureGroup/Texture/pull/1204 + DISABLED_ASAssertUnlocked(__instanceLock__); + + __instanceLock__.lock(); + __weak ASDisplayNode *supernode = _supernode; + __weak UIView *view = _view; + __weak CALayer *layer = _layer; + __instanceLock__.unlock(); + + [self _removeFromSupernode:supernode view:view layer:layer]; +} + +- (void)_removeFromSupernodeIfEqualTo:(ASDisplayNode *)supernode +{ + ASDisplayNodeAssertThreadAffinity(self); + // TODO: Disabled due to PR: https://github.com/TextureGroup/Texture/pull/1204 + DISABLED_ASAssertUnlocked(__instanceLock__); + + __instanceLock__.lock(); + + // Only remove if supernode is still the expected supernode + if (!ASObjectIsEqual(_supernode, supernode)) { + __instanceLock__.unlock(); + return; + } + + __weak UIView *view = _view; + __weak CALayer *layer = _layer; + __instanceLock__.unlock(); + + [self _removeFromSupernode:supernode view:view layer:layer]; +} + +- (void)_removeFromSupernode:(ASDisplayNode *)supernode view:(UIView *)view layer:(CALayer *)layer +{ + // Note: we continue even if supernode is nil to ensure view/layer are removed from hierarchy. + + if (supernode != nil) { + as_log_verbose(ASNodeLog(), "Remove %@ from supernode %@", self, supernode); + } + + // Clear supernode's reference to us before removing the view from the hierarchy, as _ASDisplayView + // will trigger us to clear our _supernode pointer in willMoveToSuperview:nil. + // This may result in removing the last strong reference, triggering deallocation after this method. + [supernode _removeSubnode:self]; + + if (view != nil) { + [view removeFromSuperview]; + } else if (layer != nil) { + [layer removeFromSuperlayer]; + } +} + +#pragma mark - Visibility API + +- (BOOL)__visibilityNotificationsDisabled +{ + // Currently, this method is only used by the testing infrastructure to verify this internal feature. + MutexLocker l(__instanceLock__); + return _flags.visibilityNotificationsDisabled > 0; +} + +- (BOOL)__selfOrParentHasVisibilityNotificationsDisabled +{ + MutexLocker l(__instanceLock__); + return (_hierarchyState & ASHierarchyStateTransitioningSupernodes); +} + +- (void)__incrementVisibilityNotificationsDisabled +{ + __instanceLock__.lock(); + const size_t maxVisibilityIncrement = (1ULL< 0, @"Can't decrement past 0"); + if (_flags.visibilityNotificationsDisabled > 0) { + _flags.visibilityNotificationsDisabled--; + } + BOOL visibilityNotificationsDisabled = (_flags.visibilityNotificationsDisabled == 0); + __instanceLock__.unlock(); + + if (visibilityNotificationsDisabled) { + // Must have just transitioned from 1 to 0. Notify all subnodes that we are no longer in a disabled state. + // FIXME: This system should be revisited when refactoring and consolidating the implementation of the + // addSubnode: and insertSubnode:... methods. As implemented, though logically irrelevant for expected use cases, + // multiple nodes in the subtree below may have a non-zero visibilityNotification count and still have + // the ASHierarchyState bit cleared (the only value checked when reading this state). + [self exitHierarchyState:ASHierarchyStateTransitioningSupernodes]; + } +} + +#pragma mark - Placeholder + +- (void)_locked_layoutPlaceholderIfNecessary +{ + DISABLED_ASAssertLocked(__instanceLock__); + if ([self _locked_shouldHavePlaceholderLayer]) { + [self _locked_setupPlaceholderLayerIfNeeded]; + } + // Update the placeholderLayer size in case the node size has changed since the placeholder was added. + _placeholderLayer.frame = self.threadSafeBounds; +} + +- (BOOL)_locked_shouldHavePlaceholderLayer +{ + DISABLED_ASAssertLocked(__instanceLock__); + return (_flags.placeholderEnabled && [self _implementsDisplay]); +} + +- (void)_locked_setupPlaceholderLayerIfNeeded +{ + ASDisplayNodeAssertMainThread(); + DISABLED_ASAssertLocked(__instanceLock__); + + if (!_placeholderLayer) { + _placeholderLayer = [CALayer layer]; + // do not set to CGFLOAT_MAX in the case that something needs to be overtop the placeholder + _placeholderLayer.zPosition = 9999.0; + } + + if (_placeholderLayer.contents == nil) { + if (!_placeholderImage) { + _placeholderImage = [self placeholderImage]; + } + if (_placeholderImage) { + BOOL stretchable = !UIEdgeInsetsEqualToEdgeInsets(_placeholderImage.capInsets, UIEdgeInsetsZero); + if (stretchable) { + ASDisplayNodeSetResizableContents(_placeholderLayer, _placeholderImage); + } else { + _placeholderLayer.contentsScale = self.contentsScale; + _placeholderLayer.contents = (id)_placeholderImage.CGImage; + } + } + } +} + +- (UIImage *)placeholderImage +{ + // Subclass hook + return nil; +} + +- (BOOL)placeholderShouldPersist +{ + // Subclass hook + return NO; +} + +#pragma mark - Hierarchy State + +- (BOOL)isInHierarchy +{ + MutexLocker l(__instanceLock__); + return _flags.isInHierarchy; +} + +- (void)__enterHierarchy +{ + ASDisplayNodeAssertMainThread(); + ASDisplayNodeAssert(!_flags.isEnteringHierarchy, @"Should not cause recursive __enterHierarchy"); + + // Profiling has shown that locking this method is beneficial, so each of the property accesses don't have to lock and unlock. + __instanceLock__.lock(); + + if (!_flags.isInHierarchy && !_flags.visibilityNotificationsDisabled && ![self __selfOrParentHasVisibilityNotificationsDisabled]) { + _flags.isEnteringHierarchy = YES; + _flags.isInHierarchy = YES; + + // Don't call -willEnterHierarchy while holding __instanceLock__. + // This method and subsequent ones (i.e -interfaceState and didEnter(.*)State) + // don't expect that they are called while the lock is being held. + // More importantly, didEnter(.*)State methods are meant to be overriden by clients. + // And so they can potentially walk up the node tree and cause deadlocks, or do expensive tasks and cause the lock to be held for too long. + __instanceLock__.unlock(); + [self willEnterHierarchy]; + for (ASDisplayNode *subnode in self.subnodes) { + [subnode __enterHierarchy]; + } + __instanceLock__.lock(); + + _flags.isEnteringHierarchy = NO; + + // If we don't have contents finished drawing by the time we are on screen, immediately add the placeholder (if it is enabled and we do have something to draw). + if (self.contents == nil) { + CALayer *layer = self.layer; + [layer setNeedsDisplay]; + + if ([self _locked_shouldHavePlaceholderLayer]) { + [CATransaction begin]; + [CATransaction setDisableActions:YES]; + [self _locked_setupPlaceholderLayerIfNeeded]; + _placeholderLayer.opacity = 1.0; + [CATransaction commit]; + [layer addSublayer:_placeholderLayer]; + } + } + } + + __instanceLock__.unlock(); + + [self didEnterHierarchy]; +} + +- (void)__exitHierarchy +{ + ASDisplayNodeAssertMainThread(); + ASDisplayNodeAssert(!_flags.isExitingHierarchy, @"Should not cause recursive __exitHierarchy"); + + // Profiling has shown that locking this method is beneficial, so each of the property accesses don't have to lock and unlock. + __instanceLock__.lock(); + + if (_flags.isInHierarchy && !_flags.visibilityNotificationsDisabled && ![self __selfOrParentHasVisibilityNotificationsDisabled]) { + _flags.isExitingHierarchy = YES; + _flags.isInHierarchy = NO; + + // Don't call -didExitHierarchy while holding __instanceLock__. + // This method and subsequent ones (i.e -interfaceState and didExit(.*)State) + // don't expect that they are called while the lock is being held. + // More importantly, didExit(.*)State methods are meant to be overriden by clients. + // And so they can potentially walk up the node tree and cause deadlocks, or do expensive tasks and cause the lock to be held for too long. + __instanceLock__.unlock(); + [self didExitHierarchy]; + for (ASDisplayNode *subnode in self.subnodes) { + [subnode __exitHierarchy]; + } + __instanceLock__.lock(); + + _flags.isExitingHierarchy = NO; + } + + __instanceLock__.unlock(); +} + +- (void)enterHierarchyState:(ASHierarchyState)hierarchyState +{ + if (hierarchyState == ASHierarchyStateNormal) { + return; // This method is a no-op with a 0-bitfield argument, so don't bother recursing. + } + + ASDisplayNodePerformBlockOnEveryNode(nil, self, NO, ^(ASDisplayNode *node) { + node.hierarchyState |= hierarchyState; + }); +} + +- (void)exitHierarchyState:(ASHierarchyState)hierarchyState +{ + if (hierarchyState == ASHierarchyStateNormal) { + return; // This method is a no-op with a 0-bitfield argument, so don't bother recursing. + } + ASDisplayNodePerformBlockOnEveryNode(nil, self, NO, ^(ASDisplayNode *node) { + node.hierarchyState &= (~hierarchyState); + }); +} + +- (ASHierarchyState)hierarchyState +{ + MutexLocker l(__instanceLock__); + return _hierarchyState; +} + +- (void)setHierarchyState:(ASHierarchyState)newState +{ + ASHierarchyState oldState = ASHierarchyStateNormal; + { + MutexLocker l(__instanceLock__); + if (_hierarchyState == newState) { + return; + } + oldState = _hierarchyState; + _hierarchyState = newState; + } + + // Entered rasterization state. + if (newState & ASHierarchyStateRasterized) { + ASDisplayNodeAssert(checkFlag(Synchronous) == NO, @"Node created using -initWithViewBlock:/-initWithLayerBlock: cannot be added to subtree of node with subtree rasterization enabled. Node: %@", self); + } + + // Entered or exited range managed state. + if ((newState & ASHierarchyStateRangeManaged) != (oldState & ASHierarchyStateRangeManaged)) { + if (newState & ASHierarchyStateRangeManaged) { + [self enterInterfaceState:self.supernode.pendingInterfaceState]; + } else { + // The case of exiting a range-managed state should be fairly rare. Adding or removing the node + // to a view hierarchy will cause its interfaceState to be either fully set or unset (all fields), + // but because we might be about to be added to a view hierarchy, exiting the interface state now + // would cause inefficient churn. The tradeoff is that we may not clear contents / fetched data + // for nodes that are removed from a managed state and then retained but not used (bad idea anyway!) + } + } + + if ((newState & ASHierarchyStateLayoutPending) != (oldState & ASHierarchyStateLayoutPending)) { + if (newState & ASHierarchyStateLayoutPending) { + // Entering layout pending state + } else { + // Leaving layout pending state, reset related properties + MutexLocker l(__instanceLock__); + _pendingTransitionID = ASLayoutElementContextInvalidTransitionID; + _pendingLayoutTransition = nil; + } + } + + as_log_verbose(ASNodeLog(), "%s%@ %@", sel_getName(_cmd), NSStringFromASHierarchyStateChange(oldState, newState), self); +} + +- (void)willEnterHierarchy +{ + ASDisplayNodeAssertMainThread(); + ASDisplayNodeAssert(_flags.isEnteringHierarchy, @"You should never call -willEnterHierarchy directly. Appearance is automatically managed by ASDisplayNode"); + ASDisplayNodeAssert(!_flags.isExitingHierarchy, @"ASDisplayNode inconsistency. __enterHierarchy and __exitHierarchy are mutually exclusive"); + DISABLED_ASAssertUnlocked(__instanceLock__); + + if (![self supportsRangeManagedInterfaceState]) { + self.interfaceState = ASInterfaceStateInHierarchy; + } else if (ASCATransactionQueueGet().enabled) { + __instanceLock__.lock(); + ASInterfaceState state = _preExitingInterfaceState; + _preExitingInterfaceState = ASInterfaceStateNone; + __instanceLock__.unlock(); + // Layer thrash happened, revert to before exiting. + if (state != ASInterfaceStateNone) { + self.interfaceState = state; + } + } +} + +- (void)didEnterHierarchy { + ASDisplayNodeAssertMainThread(); + ASDisplayNodeAssert(!_flags.isEnteringHierarchy, @"You should never call -didEnterHierarchy directly. Appearance is automatically managed by ASDisplayNode"); + ASDisplayNodeAssert(!_flags.isExitingHierarchy, @"ASDisplayNode inconsistency. __enterHierarchy and __exitHierarchy are mutually exclusive"); + ASDisplayNodeAssert(_flags.isInHierarchy, @"ASDisplayNode inconsistency. __enterHierarchy and __exitHierarchy are mutually exclusive"); + DISABLED_ASAssertUnlocked(__instanceLock__); + + [self enumerateInterfaceStateDelegates:^(id del) { + if ([del respondsToSelector:@selector(didEnterHierarchy)]) { + [del didEnterHierarchy]; + } + }]; +} + +- (void)didExitHierarchy +{ + ASDisplayNodeAssertMainThread(); + ASDisplayNodeAssert(_flags.isExitingHierarchy, @"You should never call -didExitHierarchy directly. Appearance is automatically managed by ASDisplayNode"); + ASDisplayNodeAssert(!_flags.isEnteringHierarchy, @"ASDisplayNode inconsistency. __enterHierarchy and __exitHierarchy are mutually exclusive"); + DISABLED_ASAssertUnlocked(__instanceLock__); + + [self enumerateInterfaceStateDelegates:^(id del) { + if ([del respondsToSelector:@selector(didExitHierarchy)]) { + [del didExitHierarchy]; + } + }]; + + // This case is important when tearing down hierarchies. We must deliver a visibileStateDidChange:NO callback, as part our API guarantee that this method can be used for + // things like data analytics about user content viewing. We cannot call the method in the dealloc as any incidental retain operations in client code would fail. + // Additionally, it may be that a Standard UIView which is containing us is moving between hierarchies, and we should not send the call if we will be re-added in the + // same runloop. Strategy: strong reference (might be the last!), wait one runloop, and confirm we are still outside the hierarchy (both layer-backed and view-backed). + // TODO: This approach could be optimized by only performing the dispatch for root elements + recursively apply the interface state change. This would require a closer + // integration with _ASDisplayLayer to ensure that the superlayer pointer has been cleared by this stage (to check if we are root or not), or a different delegate call. + +#if !ENABLE_NEW_EXIT_HIERARCHY_BEHAVIOR + if (![self supportsRangeManagedInterfaceState]) { + self.interfaceState = ASInterfaceStateNone; + return; + } +#endif + if (ASInterfaceStateIncludesVisible(self.pendingInterfaceState)) { + void(^exitVisibleInterfaceState)(void) = ^{ + // This block intentionally retains self. + self->__instanceLock__.lock(); + unsigned isStillInHierarchy = self->_flags.isInHierarchy; + BOOL isVisible = ASInterfaceStateIncludesVisible(self->_pendingInterfaceState); + ASInterfaceState newState = (self->_pendingInterfaceState & ~ASInterfaceStateVisible); + // layer may be thrashed, we need to remember the state so we can reset if it enters in same runloop later. + self->_preExitingInterfaceState = self->_pendingInterfaceState; + self->__instanceLock__.unlock(); + if (!isStillInHierarchy && isVisible) { +#if ENABLE_NEW_EXIT_HIERARCHY_BEHAVIOR + if (![self supportsRangeManagedInterfaceState]) { + newState = ASInterfaceStateNone; + } +#endif + self.interfaceState = newState; + } + }; + + if (!ASCATransactionQueueGet().enabled) { + dispatch_async(dispatch_get_main_queue(), exitVisibleInterfaceState); + } else { + exitVisibleInterfaceState(); + } + } +} + +#pragma mark - Interface State + +/** + * We currently only set interface state on nodes in table/collection views. For other nodes, if they are + * in the hierarchy we enable all ASInterfaceState types with `ASInterfaceStateInHierarchy`, otherwise `None`. + */ +- (BOOL)supportsRangeManagedInterfaceState +{ + MutexLocker l(__instanceLock__); + return ASHierarchyStateIncludesRangeManaged(_hierarchyState); +} + +- (void)enterInterfaceState:(ASInterfaceState)interfaceState +{ + if (interfaceState == ASInterfaceStateNone) { + return; // This method is a no-op with a 0-bitfield argument, so don't bother recursing. + } + ASDisplayNodePerformBlockOnEveryNode(nil, self, YES, ^(ASDisplayNode *node) { + node.interfaceState |= interfaceState; + }); +} + +- (void)exitInterfaceState:(ASInterfaceState)interfaceState +{ + if (interfaceState == ASInterfaceStateNone) { + return; // This method is a no-op with a 0-bitfield argument, so don't bother recursing. + } + ASDisplayNodePerformBlockOnEveryNode(nil, self, YES, ^(ASDisplayNode *node) { + node.interfaceState &= (~interfaceState); + }); +} + +- (void)recursivelySetInterfaceState:(ASInterfaceState)newInterfaceState +{ + as_activity_create_for_scope("Recursively set interface state"); + + // Instead of each node in the recursion assuming it needs to schedule itself for display, + // setInterfaceState: skips this when handling range-managed nodes (our whole subtree has this set). + // If our range manager intends for us to be displayed right now, and didn't before, get started! + BOOL shouldScheduleDisplay = [self supportsRangeManagedInterfaceState] && [self shouldScheduleDisplayWithNewInterfaceState:newInterfaceState]; + ASDisplayNodePerformBlockOnEveryNode(nil, self, YES, ^(ASDisplayNode *node) { + node.interfaceState = newInterfaceState; + }); + if (shouldScheduleDisplay) { + [ASDisplayNode scheduleNodeForRecursiveDisplay:self]; + } +} + +- (ASInterfaceState)interfaceState +{ + MutexLocker l(__instanceLock__); + return _interfaceState; +} + +- (void)setInterfaceState:(ASInterfaceState)newState +{ + if (!ASCATransactionQueueGet().enabled) { + [self applyPendingInterfaceState:newState]; + } else { + MutexLocker l(__instanceLock__); + if (_pendingInterfaceState != newState) { + _pendingInterfaceState = newState; + [ASCATransactionQueueGet() enqueue:self]; + } + } +} + +- (ASInterfaceState)pendingInterfaceState +{ + MutexLocker l(__instanceLock__); + return _pendingInterfaceState; +} + +- (void)applyPendingInterfaceState:(ASInterfaceState)newPendingState +{ + //This method is currently called on the main thread. The assert has been added here because all of the + //did(Enter|Exit)(Display|Visible|Preload)State methods currently guarantee calling on main. + ASDisplayNodeAssertMainThread(); + + // This method manages __instanceLock__ itself, to ensure the lock is not held while didEnter/Exit(.*)State methods are called, thus avoid potential deadlocks + DISABLED_ASAssertUnlocked(__instanceLock__); + + ASInterfaceState oldState = ASInterfaceStateNone; + ASInterfaceState newState = ASInterfaceStateNone; + { + MutexLocker l(__instanceLock__); + // newPendingState will not be used when ASCATransactionQueue is enabled + // and use _pendingInterfaceState instead for interfaceState update. + if (!ASCATransactionQueueGet().enabled) { + _pendingInterfaceState = newPendingState; + } + oldState = _interfaceState; + newState = _pendingInterfaceState; + if (newState == oldState) { + return; + } + _interfaceState = newState; + _preExitingInterfaceState = ASInterfaceStateNone; + } + + // It should never be possible for a node to be visible but not be allowed / expected to display. + ASDisplayNodeAssertFalse(ASInterfaceStateIncludesVisible(newState) && !ASInterfaceStateIncludesDisplay(newState)); + + // TODO: Trigger asynchronous measurement if it is not already cached or being calculated. + // if ((newState & ASInterfaceStateMeasureLayout) != (oldState & ASInterfaceStateMeasureLayout)) { + // } + + // For the Preload and Display ranges, we don't want to call -clear* if not being managed by a range controller. + // Otherwise we get flashing behavior from normal UIKit manipulations like navigation controller push / pop. + // Still, the interfaceState should be updated to the current state of the node; just don't act on the transition. + + // Entered or exited data loading state. + BOOL nowPreload = ASInterfaceStateIncludesPreload(newState); + BOOL wasPreload = ASInterfaceStateIncludesPreload(oldState); + + if (nowPreload != wasPreload) { + if (nowPreload) { + [self _didEnterPreloadState]; + } else { + // We don't want to call -didExitPreloadState on nodes that aren't being managed by a range controller. + // Otherwise we get flashing behavior from normal UIKit manipulations like navigation controller push / pop. + if ([self supportsRangeManagedInterfaceState]) { + [self _didExitPreloadState]; + } + } + } + + // Entered or exited contents rendering state. + BOOL nowDisplay = ASInterfaceStateIncludesDisplay(newState); + BOOL wasDisplay = ASInterfaceStateIncludesDisplay(oldState); + + if (nowDisplay != wasDisplay) { + if ([self supportsRangeManagedInterfaceState]) { + if (nowDisplay) { + // Once the working window is eliminated (ASRangeHandlerRender), trigger display directly here. + [self setDisplaySuspended:NO]; + } else { + [self setDisplaySuspended:YES]; + //schedule clear contents on next runloop + dispatch_async(dispatch_get_main_queue(), ^{ + self->__instanceLock__.lock(); + ASInterfaceState interfaceState = self->_interfaceState; + self->__instanceLock__.unlock(); + if (ASInterfaceStateIncludesDisplay(interfaceState) == NO) { + [self clearContents]; + } + }); + } + } else { + // NOTE: This case isn't currently supported as setInterfaceState: isn't exposed externally, and all + // internal use cases are range-managed. When a node is visible, don't mess with display - CA will start it. + if (!ASInterfaceStateIncludesVisible(newState)) { + // Check _implementsDisplay purely for efficiency - it's faster even than calling -asyncLayer. + if ([self _implementsDisplay]) { + if (nowDisplay) { + [ASDisplayNode scheduleNodeForRecursiveDisplay:self]; + } else { + [[self asyncLayer] cancelAsyncDisplay]; + //schedule clear contents on next runloop + dispatch_async(dispatch_get_main_queue(), ^{ + self->__instanceLock__.lock(); + ASInterfaceState interfaceState = self->_interfaceState; + self->__instanceLock__.unlock(); + if (ASInterfaceStateIncludesDisplay(interfaceState) == NO) { + [self clearContents]; + } + }); + } + } + } + } + + if (nowDisplay) { + [self _didEnterDisplayState]; + } else { + [self _didExitDisplayState]; + } + } + + // Became visible or invisible. When range-managed, this represents literal visibility - at least one pixel + // is onscreen. If not range-managed, we can't guarantee more than the node being present in an onscreen window. + BOOL nowVisible = ASInterfaceStateIncludesVisible(newState); + BOOL wasVisible = ASInterfaceStateIncludesVisible(oldState); + + if (nowVisible != wasVisible) { + if (nowVisible) { + [self _didEnterVisibleState]; + } else { + [self _didExitVisibleState]; + } + } + + // Log this change, unless it's just the node going from {} -> {Measure} because that change happens + // for all cell nodes and it isn't currently meaningful. + BOOL measureChangeOnly = ((oldState | newState) == ASInterfaceStateMeasureLayout); + if (!measureChangeOnly) { + as_log_verbose(ASNodeLog(), "%s %@ %@", sel_getName(_cmd), NSStringFromASInterfaceStateChange(oldState, newState), self); + } + + [self _interfaceStateDidChange:newState fromState:oldState]; +} + +- (void)prepareForCATransactionCommit +{ + // Apply _pendingInterfaceState actual _interfaceState, note that ASInterfaceStateNone is not used. + [self applyPendingInterfaceState:ASInterfaceStateNone]; +} + +- (void)_interfaceStateDidChange:(ASInterfaceState)newState fromState:(ASInterfaceState)oldState +{ + DISABLED_ASAssertUnlocked(__instanceLock__); + ASDisplayNodeAssertMainThread(); + [self interfaceStateDidChange:newState fromState:oldState]; + [self enumerateInterfaceStateDelegates:^(id del) { + [del interfaceStateDidChange:newState fromState:oldState]; + }]; +} + +- (BOOL)shouldScheduleDisplayWithNewInterfaceState:(ASInterfaceState)newInterfaceState +{ + BOOL willDisplay = ASInterfaceStateIncludesDisplay(newInterfaceState); + BOOL nowDisplay = ASInterfaceStateIncludesDisplay(self.interfaceState); + return willDisplay && (willDisplay != nowDisplay); +} + +- (void)addInterfaceStateDelegate:(id )interfaceStateDelegate +{ + MutexLocker l(__instanceLock__); + _flags.hasHadInterfaceStateDelegates = YES; + for (int i = 0; i < AS_MAX_INTERFACE_STATE_DELEGATES; i++) { + if (_interfaceStateDelegates[i] == nil) { + _interfaceStateDelegates[i] = interfaceStateDelegate; + return; + } + } + ASDisplayNodeFailAssert(@"Exceeded interface state delegate limit: %d", AS_MAX_INTERFACE_STATE_DELEGATES); +} + +- (void)removeInterfaceStateDelegate:(id )interfaceStateDelegate +{ + MutexLocker l(__instanceLock__); + for (int i = 0; i < AS_MAX_INTERFACE_STATE_DELEGATES; i++) { + if (_interfaceStateDelegates[i] == interfaceStateDelegate) { + _interfaceStateDelegates[i] = nil; + break; + } + } +} + +- (BOOL)isVisible +{ + MutexLocker l(__instanceLock__); + return ASInterfaceStateIncludesVisible(_interfaceState); +} + +- (void)_didEnterVisibleState +{ + ASDisplayNodeAssertMainThread(); + +#if ASDISPLAYNODE_ASSERTIONS_ENABLED + // Rasterized node's loading state is merged with root node of rasterized tree. + if (!(self.hierarchyState & ASHierarchyStateRasterized)) { + ASDisplayNodeAssert(self.isNodeLoaded, @"Node should be loaded before entering visible state."); + } +#endif + + DISABLED_ASAssertUnlocked(__instanceLock__); + [self didEnterVisibleState]; + [self enumerateInterfaceStateDelegates:^(id del) { + [del didEnterVisibleState]; + }]; + +#if AS_ENABLE_TIPS + [ASTipsController.shared nodeDidAppear:self]; +#endif +} + +- (void)_didExitVisibleState +{ + ASDisplayNodeAssertMainThread(); + DISABLED_ASAssertUnlocked(__instanceLock__); + [self didExitVisibleState]; + [self enumerateInterfaceStateDelegates:^(id del) { + [del didExitVisibleState]; + }]; +} + +- (BOOL)isInDisplayState +{ + MutexLocker l(__instanceLock__); + return ASInterfaceStateIncludesDisplay(_interfaceState); +} + +- (void)_didEnterDisplayState +{ + ASDisplayNodeAssertMainThread(); + DISABLED_ASAssertUnlocked(__instanceLock__); + [self didEnterDisplayState]; + [self enumerateInterfaceStateDelegates:^(id del) { + [del didEnterDisplayState]; + }]; +} + +- (void)_didExitDisplayState +{ + ASDisplayNodeAssertMainThread(); + DISABLED_ASAssertUnlocked(__instanceLock__); + [self didExitDisplayState]; + [self enumerateInterfaceStateDelegates:^(id del) { + [del didExitDisplayState]; + }]; +} + +- (BOOL)isInPreloadState +{ + MutexLocker l(__instanceLock__); + return ASInterfaceStateIncludesPreload(_interfaceState); +} + +- (void)setNeedsPreload +{ + if (self.isInPreloadState) { + [self recursivelyPreload]; + } +} + +- (void)recursivelyPreload +{ + ASPerformBlockOnMainThread(^{ + ASDisplayNodePerformBlockOnEveryNode(nil, self, YES, ^(ASDisplayNode * _Nonnull node) { + [node didEnterPreloadState]; + }); + }); +} + +- (void)recursivelyClearPreloadedData +{ + ASPerformBlockOnMainThread(^{ + ASDisplayNodePerformBlockOnEveryNode(nil, self, YES, ^(ASDisplayNode * _Nonnull node) { + [node didExitPreloadState]; + }); + }); +} + +- (void)_didEnterPreloadState +{ + ASDisplayNodeAssertMainThread(); + DISABLED_ASAssertUnlocked(__instanceLock__); + [self didEnterPreloadState]; + + // If this node has ASM enabled and is not yet visible, force a layout pass to apply its applicable pending layout, if any, + // so that its subnodes are inserted/deleted and start preloading right away. + // + // - If it has an up-to-date layout (and subnodes), calling -layoutIfNeeded will be fast. + // + // - If it doesn't have a calculated or pending layout that fits its current bounds, a measurement pass will occur + // (see -__layout and -_u_measureNodeWithBoundsIfNecessary:). This scenario is uncommon, + // and running a measurement pass here is a fine trade-off because preloading any time after this point would be late. + + if (self.automaticallyManagesSubnodes && !ASActivateExperimentalFeature(ASExperimentalDidEnterPreloadSkipASMLayout)) { + [self layoutIfNeeded]; + } + [self enumerateInterfaceStateDelegates:^(id del) { + [del didEnterPreloadState]; + }]; +} + +- (void)_didExitPreloadState +{ + ASDisplayNodeAssertMainThread(); + DISABLED_ASAssertUnlocked(__instanceLock__); + [self didExitPreloadState]; + [self enumerateInterfaceStateDelegates:^(id del) { + [del didExitPreloadState]; + }]; +} + +- (void)clearContents +{ + ASDisplayNodeAssertMainThread(); + DISABLED_ASAssertUnlocked(__instanceLock__); + + MutexLocker l(__instanceLock__); + if (_flags.canClearContentsOfLayer) { + // No-op if these haven't been created yet, as that guarantees they don't have contents that needs to be released. + _layer.contents = nil; + } + + _placeholderLayer.contents = nil; + _placeholderImage = nil; +} + +- (void)recursivelyClearContents +{ + ASPerformBlockOnMainThread(^{ + ASDisplayNodePerformBlockOnEveryNode(nil, self, YES, ^(ASDisplayNode * _Nonnull node) { + [node clearContents]; + }); + }); +} + +- (void)enumerateInterfaceStateDelegates:(void (NS_NOESCAPE ^)(id))block +{ + DISABLED_ASAssertUnlocked(__instanceLock__); + + id dels[AS_MAX_INTERFACE_STATE_DELEGATES]; + int count = 0; + { + ASLockScopeSelf(); + // Fast path for non-delegating nodes. + if (!_flags.hasHadInterfaceStateDelegates) { + return; + } + + for (int i = 0; i < AS_MAX_INTERFACE_STATE_DELEGATES; i++) { + if ((dels[count] = _interfaceStateDelegates[i])) { + count++; + } + } + } + for (int i = 0; i < count; i++) { + block(dels[i]); + } +} + +#pragma mark - Gesture Recognizing + +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event +{ + // Subclass hook +} + +- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event +{ + // Subclass hook +} + +- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event +{ + // Subclass hook +} + +- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event +{ + // Subclass hook +} + +- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer +{ + // This method is only implemented on UIView on iOS 6+. + ASDisplayNodeAssertMainThread(); + + // No locking needed as it's main thread only + UIView *view = _view; + if (view == nil) { + return YES; + } + + // If we reach the base implementation, forward up the view hierarchy. + UIView *superview = view.superview; + return [superview gestureRecognizerShouldBegin:gestureRecognizer]; +} + +- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event +{ + ASDisplayNodeAssertMainThread(); + return [_view hitTest:point withEvent:event]; +} + +- (void)setHitTestSlop:(UIEdgeInsets)hitTestSlop +{ + MutexLocker l(__instanceLock__); + _hitTestSlop = hitTestSlop; +} + +- (UIEdgeInsets)hitTestSlop +{ + MutexLocker l(__instanceLock__); + return _hitTestSlop; +} + +- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event +{ + ASDisplayNodeAssertMainThread(); + UIEdgeInsets slop = self.hitTestSlop; + if (_view && UIEdgeInsetsEqualToEdgeInsets(slop, UIEdgeInsetsZero)) { + // Safer to use UIView's -pointInside:withEvent: if we can. + return [_view pointInside:point withEvent:event]; + } else { + return CGRectContainsPoint(UIEdgeInsetsInsetRect(self.bounds, slop), point); + } +} + + +#pragma mark - Pending View State + +- (void)_locked_applyPendingStateToViewOrLayer +{ + ASDisplayNodeAssertMainThread(); + DISABLED_ASAssertLocked(__instanceLock__); + ASDisplayNodeAssert(self.nodeLoaded, @"must have a view or layer"); + + TIME_SCOPED(_debugTimeToApplyPendingState); + + // If no view/layer properties were set before the view/layer were created, _pendingViewState will be nil and the default values + // for the view/layer are still valid. + [self _locked_applyPendingViewState]; + + if (_flags.displaySuspended) { + self._locked_asyncLayer.displaySuspended = YES; + } + if (!_flags.displaysAsynchronously) { + self._locked_asyncLayer.displaysAsynchronously = NO; + } +} + +- (void)applyPendingViewState +{ + ASDisplayNodeAssertMainThread(); + DISABLED_ASAssertUnlocked(__instanceLock__); + + AS::UniqueLock l(__instanceLock__); + // FIXME: Ideally we'd call this as soon as the node receives -setNeedsLayout + // but automatic subnode management would require us to modify the node tree + // in the background on a loaded node, which isn't currently supported. + if (_pendingViewState.hasSetNeedsLayout) { + // Need to unlock before calling setNeedsLayout to avoid deadlocks. + l.unlock(); + [self __setNeedsLayout]; + l.lock(); + } + + [self _locked_applyPendingViewState]; +} + +- (void)_locked_applyPendingViewState +{ + ASDisplayNodeAssertMainThread(); + DISABLED_ASAssertLocked(__instanceLock__); + ASDisplayNodeAssert([self _locked_isNodeLoaded], @"Expected node to be loaded before applying pending state."); + + if (_flags.layerBacked) { + [_pendingViewState applyToLayer:_layer]; + } else { + BOOL specialPropertiesHandling = ASDisplayNodeNeedsSpecialPropertiesHandling(checkFlag(Synchronous), _flags.layerBacked); + [_pendingViewState applyToView:_view withSpecialPropertiesHandling:specialPropertiesHandling]; + } + + // _ASPendingState objects can add up very quickly when adding + // many nodes. This is especially an issue in large collection views + // and table views. This needs to be weighed against the cost of + // reallocing a _ASPendingState. So in range managed nodes we + // delete the pending state, otherwise we just clear it. + if (ASHierarchyStateIncludesRangeManaged(_hierarchyState)) { + _pendingViewState = nil; + } else { + [_pendingViewState clearChanges]; + } +} + +// This method has proved helpful in a few rare scenarios, similar to a category extension on UIView, but assumes knowledge of _ASDisplayView. +// It's considered private API for now and its use should not be encouraged. +- (ASDisplayNode *)_supernodeWithClass:(Class)supernodeClass checkViewHierarchy:(BOOL)checkViewHierarchy +{ + ASDisplayNode *supernode = self.supernode; + while (supernode) { + if ([supernode isKindOfClass:supernodeClass]) + return supernode; + supernode = supernode.supernode; + } + if (!checkViewHierarchy) { + return nil; + } + + UIView *view = self.view.superview; + while (view) { + ASDisplayNode *viewNode = ((_ASDisplayView *)view).asyncdisplaykit_node; + if (viewNode) { + if ([viewNode isKindOfClass:supernodeClass]) + return viewNode; + } + + view = view.superview; + } + + return nil; +} + +#pragma mark - Performance Measurement + +- (void)setMeasurementOptions:(ASDisplayNodePerformanceMeasurementOptions)measurementOptions +{ + MutexLocker l(__instanceLock__); + _measurementOptions = measurementOptions; +} + +- (ASDisplayNodePerformanceMeasurementOptions)measurementOptions +{ + MutexLocker l(__instanceLock__); + return _measurementOptions; +} + +- (ASDisplayNodePerformanceMeasurements)performanceMeasurements +{ + MutexLocker l(__instanceLock__); + ASDisplayNodePerformanceMeasurements measurements = { .layoutSpecNumberOfPasses = -1, .layoutSpecTotalTime = NAN, .layoutComputationNumberOfPasses = -1, .layoutComputationTotalTime = NAN }; + if (_measurementOptions & ASDisplayNodePerformanceMeasurementOptionLayoutSpec) { + measurements.layoutSpecNumberOfPasses = _layoutSpecNumberOfPasses; + measurements.layoutSpecTotalTime = _layoutSpecTotalTime; + } + if (_measurementOptions & ASDisplayNodePerformanceMeasurementOptionLayoutComputation) { + measurements.layoutComputationNumberOfPasses = _layoutComputationNumberOfPasses; + measurements.layoutComputationTotalTime = _layoutComputationTotalTime; + } + return measurements; +} + +#pragma mark - Accessibility + +- (void)setIsAccessibilityContainer:(BOOL)isAccessibilityContainer +{ + MutexLocker l(__instanceLock__); + _flags.isAccessibilityContainer = isAccessibilityContainer; +} + +- (BOOL)isAccessibilityContainer +{ + MutexLocker l(__instanceLock__); + return _flags.isAccessibilityContainer; +} + +- (NSString *)defaultAccessibilityLabel +{ + return nil; +} + +- (NSString *)defaultAccessibilityHint +{ + return nil; +} + +- (NSString *)defaultAccessibilityValue +{ + return nil; +} + +- (NSString *)defaultAccessibilityIdentifier +{ + return nil; +} + +- (UIAccessibilityTraits)defaultAccessibilityTraits +{ + return UIAccessibilityTraitNone; +} + +#pragma mark - Debugging (Private) + +- (NSMutableArray *)propertiesForDescription +{ + NSMutableArray *result = [NSMutableArray array]; + ASPushMainThreadAssertionsDisabled(); + + NSString *debugName = self.debugName; + if (debugName.length > 0) { + [result addObject:@{ (id)kCFNull : ASStringWithQuotesIfMultiword(debugName) }]; + } + + NSString *axId = self.accessibilityIdentifier; + if (axId.length > 0) { + [result addObject:@{ (id)kCFNull : ASStringWithQuotesIfMultiword(axId) }]; + } + + ASPopMainThreadAssertionsDisabled(); + return result; +} + +- (NSMutableArray *)propertiesForDebugDescription +{ + NSMutableArray *result = [NSMutableArray array]; + + if (self.debugName.length > 0) { + [result addObject:@{ @"debugName" : ASStringWithQuotesIfMultiword(self.debugName)}]; + } + if (self.accessibilityIdentifier.length > 0) { + [result addObject:@{ @"axId": ASStringWithQuotesIfMultiword(self.accessibilityIdentifier) }]; + } + + CGRect windowFrame = [self _frameInWindow]; + if (CGRectIsNull(windowFrame) == NO) { + [result addObject:@{ @"frameInWindow" : [NSValue valueWithCGRect:windowFrame] }]; + } + + // Attempt to find view controller. + // Note that the convenience method asdk_associatedViewController has an assertion + // that it's run on main. Since this is a debug method, let's bypass the assertion + // and run up the chain ourselves. + if (_view != nil) { + for (UIResponder *responder in [_view asdk_responderChainEnumerator]) { + UIViewController *vc = ASDynamicCast(responder, UIViewController); + if (vc) { + [result addObject:@{ @"viewController" : ASObjectDescriptionMakeTiny(vc) }]; + break; + } + } + } + + if (_view != nil) { + [result addObject:@{ @"alpha" : @(_view.alpha) }]; + [result addObject:@{ @"frame" : [NSValue valueWithCGRect:_view.frame] }]; + } else if (_layer != nil) { + [result addObject:@{ @"alpha" : @(_layer.opacity) }]; + [result addObject:@{ @"frame" : [NSValue valueWithCGRect:_layer.frame] }]; + } else if (_pendingViewState != nil) { + [result addObject:@{ @"alpha" : @(_pendingViewState.alpha) }]; + [result addObject:@{ @"frame" : [NSValue valueWithCGRect:_pendingViewState.frame] }]; + } + + // Check supernode so that if we are a cell node we don't find self. + ASCellNode *cellNode = [self supernodeOfClass:[ASCellNode class] includingSelf:NO]; + if (cellNode != nil) { + [result addObject:@{ @"cellNode" : ASObjectDescriptionMakeTiny(cellNode) }]; + } + + [result addObject:@{ @"interfaceState" : NSStringFromASInterfaceState(self.interfaceState)} ]; + + if (_view != nil) { + [result addObject:@{ @"view" : ASObjectDescriptionMakeTiny(_view) }]; + } else if (_layer != nil) { + [result addObject:@{ @"layer" : ASObjectDescriptionMakeTiny(_layer) }]; + } else if (_viewClass != nil) { + [result addObject:@{ @"viewClass" : _viewClass }]; + } else if (_layerClass != nil) { + [result addObject:@{ @"layerClass" : _layerClass }]; + } else if (_viewBlock != nil) { + [result addObject:@{ @"viewBlock" : _viewBlock }]; + } else if (_layerBlock != nil) { + [result addObject:@{ @"layerBlock" : _layerBlock }]; + } + +#if TIME_DISPLAYNODE_OPS + NSString *creationTypeString = [NSString stringWithFormat:@"cr8:%.2lfms dl:%.2lfms ap:%.2lfms ad:%.2lfms", 1000 * _debugTimeToCreateView, 1000 * _debugTimeForDidLoad, 1000 * _debugTimeToApplyPendingState, 1000 * _debugTimeToAddSubnodeViews]; + [result addObject:@{ @"creationTypeString" : creationTypeString }]; +#endif + + return result; +} + +- (NSString *)description +{ + return ASObjectDescriptionMake(self, [self propertiesForDescription]); +} + +- (NSString *)debugDescription +{ + ASPushMainThreadAssertionsDisabled(); + const auto result = ASObjectDescriptionMake(self, [self propertiesForDebugDescription]); + ASPopMainThreadAssertionsDisabled(); + return result; +} + +// This should only be called for debugging. It's not thread safe and it doesn't assert. +// NOTE: Returns CGRectNull if the node isn't in a hierarchy. +- (CGRect)_frameInWindow +{ + if (self.isNodeLoaded == NO || self.isInHierarchy == NO) { + return CGRectNull; + } + + if (self.layerBacked) { + CALayer *rootLayer = _layer; + CALayer *nextLayer = nil; + while ((nextLayer = rootLayer.superlayer) != nil) { + rootLayer = nextLayer; + } + + return [_layer convertRect:self.threadSafeBounds toLayer:rootLayer]; + } else { + return [_view convertRect:self.threadSafeBounds toView:nil]; + } +} + +@end + +#pragma mark - ASDisplayNode (Debugging) + +@implementation ASDisplayNode (Debugging) + ++ (void)setShouldStoreUnflattenedLayouts:(BOOL)shouldStore +{ + storesUnflattenedLayouts.store(shouldStore); +} + ++ (BOOL)shouldStoreUnflattenedLayouts +{ + return storesUnflattenedLayouts.load(); +} + +- (ASLayout *)unflattenedCalculatedLayout +{ + MutexLocker l(__instanceLock__); + return _unflattenedLayout; +} + ++ (void)setSuppressesInvalidCollectionUpdateExceptions:(BOOL)suppresses +{ + suppressesInvalidCollectionUpdateExceptions.store(suppresses); +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-implementations" ++ (BOOL)suppressesInvalidCollectionUpdateExceptions +{ + return suppressesInvalidCollectionUpdateExceptions.load(); +} +#pragma clang diagnostic pop + +- (NSString *)displayNodeRecursiveDescription +{ + return [self _recursiveDescriptionHelperWithIndent:@""]; +} + +- (NSString *)_recursiveDescriptionHelperWithIndent:(NSString *)indent +{ + NSMutableString *subtree = [[[indent stringByAppendingString:self.debugDescription] stringByAppendingString:@"\n"] mutableCopy]; + for (ASDisplayNode *n in self.subnodes) { + [subtree appendString:[n _recursiveDescriptionHelperWithIndent:[indent stringByAppendingString:@" | "]]]; + } + return subtree; +} + +- (NSString *)detailedLayoutDescription +{ + ASPushMainThreadAssertionsDisabled(); + MutexLocker l(__instanceLock__); + const auto props = [[NSMutableArray alloc] init]; + + [props addObject:@{ @"layoutVersion": @(_layoutVersion.load()) }]; + [props addObject:@{ @"bounds": [NSValue valueWithCGRect:self.bounds] }]; + + if (_calculatedDisplayNodeLayout.layout) { + [props addObject:@{ @"calculatedLayout": _calculatedDisplayNodeLayout.layout }]; + [props addObject:@{ @"calculatedVersion": @(_calculatedDisplayNodeLayout.version) }]; + [props addObject:@{ @"calculatedConstrainedSize" : NSStringFromASSizeRange(_calculatedDisplayNodeLayout.constrainedSize) }]; + if (_calculatedDisplayNodeLayout.requestedLayoutFromAbove) { + [props addObject:@{ @"calculatedRequestedLayoutFromAbove": @"YES" }]; + } + } + if (_pendingDisplayNodeLayout.layout) { + [props addObject:@{ @"pendingLayout": _pendingDisplayNodeLayout.layout }]; + [props addObject:@{ @"pendingVersion": @(_pendingDisplayNodeLayout.version) }]; + [props addObject:@{ @"pendingConstrainedSize" : NSStringFromASSizeRange(_pendingDisplayNodeLayout.constrainedSize) }]; + if (_pendingDisplayNodeLayout.requestedLayoutFromAbove) { + [props addObject:@{ @"pendingRequestedLayoutFromAbove": (id)kCFNull }]; + } + } + + ASPopMainThreadAssertionsDisabled(); + return ASObjectDescriptionMake(self, props); +} + +@end + +#pragma mark - ASDisplayNode UIKit / CA Categories + +// We use associated objects as a last resort if our view is not a _ASDisplayView ie it doesn't have the _node ivar to write to + +static const char *ASDisplayNodeAssociatedNodeKey = "ASAssociatedNode"; + +@implementation UIView (ASDisplayNodeInternal) + +- (void)setAsyncdisplaykit_node:(ASDisplayNode *)node +{ + ASWeakProxy *weakProxy = [ASWeakProxy weakProxyWithTarget:node]; + objc_setAssociatedObject(self, ASDisplayNodeAssociatedNodeKey, weakProxy, OBJC_ASSOCIATION_RETAIN); // Weak reference to avoid cycle, since the node retains the view. +} + +- (ASDisplayNode *)asyncdisplaykit_node +{ + ASWeakProxy *weakProxy = objc_getAssociatedObject(self, ASDisplayNodeAssociatedNodeKey); + return weakProxy.target; +} + +@end + +@implementation CALayer (ASDisplayNodeInternal) + +- (void)setAsyncdisplaykit_node:(ASDisplayNode *)node +{ + ASWeakProxy *weakProxy = [ASWeakProxy weakProxyWithTarget:node]; + objc_setAssociatedObject(self, ASDisplayNodeAssociatedNodeKey, weakProxy, OBJC_ASSOCIATION_RETAIN); // Weak reference to avoid cycle, since the node retains the layer. +} + +- (ASDisplayNode *)asyncdisplaykit_node +{ + ASWeakProxy *weakProxy = objc_getAssociatedObject(self, ASDisplayNodeAssociatedNodeKey); + return weakProxy.target; +} + +@end + +@implementation UIView (AsyncDisplayKit) + +- (void)addSubnode:(ASDisplayNode *)subnode +{ + if (subnode.layerBacked) { + // Call -addSubnode: so that we use the asyncdisplaykit_node path if possible. + [self.layer addSubnode:subnode]; + } else { + ASDisplayNode *selfNode = self.asyncdisplaykit_node; + if (selfNode) { + [selfNode addSubnode:subnode]; + } else { + if (subnode.supernode) { + [subnode removeFromSupernode]; + } + [self addSubview:subnode.view]; + } + } +} + +@end + +@implementation CALayer (AsyncDisplayKit) + +- (void)addSubnode:(ASDisplayNode *)subnode +{ + ASDisplayNode *selfNode = self.asyncdisplaykit_node; + if (selfNode) { + [selfNode addSubnode:subnode]; + } else { + if (subnode.supernode) { + [subnode removeFromSupernode]; + } + [self addSublayer:subnode.layer]; + } +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDisplayNodeExtras.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDisplayNodeExtras.h new file mode 100644 index 0000000..03f2940 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDisplayNodeExtras.h @@ -0,0 +1,213 @@ +// +// ASDisplayNodeExtras.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +#import +#import + +/** + * Sets the debugName field for these nodes to the given symbol names, within the domain of "self.class" + * For instance, in `MYButtonNode` if you call `ASSetDebugNames(self.titleNode, _countNode)` the debug names + * for the nodes will be set to `MYButtonNode.titleNode` and `MYButtonNode.countNode`. + */ +#if DEBUG + #define ASSetDebugName(node, format, ...) node.debugName = [NSString stringWithFormat:format, __VA_ARGS__] + #define ASSetDebugNames(...) _ASSetDebugNames(self.class, @"" # __VA_ARGS__, __VA_ARGS__, nil) +#else + #define ASSetDebugName(node, format, ...) + #define ASSetDebugNames(...) +#endif + +NS_ASSUME_NONNULL_BEGIN + +/// For deallocation of objects on the main thread across multiple run loops. +ASDK_EXTERN void ASPerformMainThreadDeallocation(id _Nullable __strong * _Nonnull objectPtr); + +// Because inline methods can't be extern'd and need to be part of the translation unit of code +// that compiles with them to actually inline, we both declare and define these in the header. +ASDISPLAYNODE_INLINE BOOL ASInterfaceStateIncludesVisible(ASInterfaceState interfaceState) +{ + return ((interfaceState & ASInterfaceStateVisible) == ASInterfaceStateVisible); +} + +ASDISPLAYNODE_INLINE BOOL ASInterfaceStateIncludesDisplay(ASInterfaceState interfaceState) +{ + return ((interfaceState & ASInterfaceStateDisplay) == ASInterfaceStateDisplay); +} + +ASDISPLAYNODE_INLINE BOOL ASInterfaceStateIncludesPreload(ASInterfaceState interfaceState) +{ + return ((interfaceState & ASInterfaceStatePreload) == ASInterfaceStatePreload); +} + +ASDISPLAYNODE_INLINE BOOL ASInterfaceStateIncludesMeasureLayout(ASInterfaceState interfaceState) +{ + return ((interfaceState & ASInterfaceStateMeasureLayout) == ASInterfaceStateMeasureLayout); +} + +__unused static NSString * NSStringFromASInterfaceState(ASInterfaceState interfaceState) +{ + NSMutableArray *states = [NSMutableArray array]; + if (interfaceState == ASInterfaceStateNone) { + [states addObject:@"No state"]; + } + if (ASInterfaceStateIncludesMeasureLayout(interfaceState)) { + [states addObject:@"MeasureLayout"]; + } + if (ASInterfaceStateIncludesPreload(interfaceState)) { + [states addObject:@"Preload"]; + } + if (ASInterfaceStateIncludesDisplay(interfaceState)) { + [states addObject:@"Display"]; + } + if (ASInterfaceStateIncludesVisible(interfaceState)) { + [states addObject:@"Visible"]; + } + return [NSString stringWithFormat:@"{ %@ }", [states componentsJoinedByString:@" | "]]; +} + +#define INTERFACE_STATE_DELTA(Name) ({ \ + if ((oldState & ASInterfaceState##Name) != (newState & ASInterfaceState##Name)) { \ + [changes appendFormat:@"%c%s ", (newState & ASInterfaceState##Name ? '+' : '-'), #Name]; \ + } \ +}) + +/// e.g. { +Visible, -Preload } (although that should never actually happen.) +/// NOTE: Changes to MeasureLayout state don't really mean anything so we omit them for now. +__unused static NSString *NSStringFromASInterfaceStateChange(ASInterfaceState oldState, ASInterfaceState newState) +{ + if (oldState == newState) { + return @"{ }"; + } + + NSMutableString *changes = [NSMutableString stringWithString:@"{ "]; + INTERFACE_STATE_DELTA(Preload); + INTERFACE_STATE_DELTA(Display); + INTERFACE_STATE_DELTA(Visible); + [changes appendString:@"}"]; + return changes; +} + +#undef INTERFACE_STATE_DELTA + +/** + Returns the appropriate interface state for a given ASDisplayNode and window + */ +ASDK_EXTERN ASInterfaceState ASInterfaceStateForDisplayNode(ASDisplayNode *displayNode, UIWindow *window) AS_WARN_UNUSED_RESULT; + +/** + Given a layer, returns the associated display node, if any. + */ +ASDK_EXTERN ASDisplayNode * _Nullable ASLayerToDisplayNode(CALayer * _Nullable layer) AS_WARN_UNUSED_RESULT; + +/** + Given a view, returns the associated display node, if any. + */ +ASDK_EXTERN ASDisplayNode * _Nullable ASViewToDisplayNode(UIView * _Nullable view) AS_WARN_UNUSED_RESULT; + +/** + Given a node, returns the root of the node hierarchy (where supernode == nil) + */ +ASDK_EXTERN ASDisplayNode *ASDisplayNodeUltimateParentOfNode(ASDisplayNode *node) AS_WARN_UNUSED_RESULT; + +/** + If traverseSublayers == YES, this function will walk the layer hierarchy, spanning discontinuous sections of the node hierarchy\ + (e.g. the layers of UIKit intermediate views in UIViewControllers, UITableView, UICollectionView). + In the event that a node's backing layer is not created yet, the function will only walk the direct subnodes instead + of forcing the layer hierarchy to be created. + */ +ASDK_EXTERN void ASDisplayNodePerformBlockOnEveryNode(CALayer * _Nullable layer, ASDisplayNode * _Nullable node, BOOL traverseSublayers, void(^block)(ASDisplayNode *node)); + +/** + This function will walk the node hierarchy in a breadth first fashion. It does run the block on the node provided + directly to the function call. It does NOT traverse sublayers. + */ +ASDK_EXTERN void ASDisplayNodePerformBlockOnEveryNodeBFS(ASDisplayNode *node, void(^block)(ASDisplayNode *node)); + +/** + Identical to ASDisplayNodePerformBlockOnEveryNode, except it does not run the block on the + node provided directly to the function call - only on all descendants. + */ +ASDK_EXTERN void ASDisplayNodePerformBlockOnEverySubnode(ASDisplayNode *node, BOOL traverseSublayers, void(^block)(ASDisplayNode *node)); + +/** + Given a display node, traverses up the layer tree hierarchy, returning the first display node that passes block. + */ +ASDK_EXTERN ASDisplayNode * _Nullable ASDisplayNodeFindFirstSupernode(ASDisplayNode * _Nullable node, BOOL (^block)(ASDisplayNode *node)) AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED_MSG("Use the `supernodes` property instead."); + +/** + Given a display node, traverses up the layer tree hierarchy, returning the first display node of kind class. + */ +ASDK_EXTERN __kindof ASDisplayNode * _Nullable ASDisplayNodeFindFirstSupernodeOfClass(ASDisplayNode *start, Class c) AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED_MSG("Use the `supernodeOfClass:includingSelf:` method instead."); + +/** + * Given a layer, find the window it lives in, if any. + */ +ASDK_EXTERN UIWindow * _Nullable ASFindWindowOfLayer(CALayer *layer) AS_WARN_UNUSED_RESULT; + +/** + * Given a layer, find the closest view it lives in, if any. + */ +ASDK_EXTERN UIView * _Nullable ASFindClosestViewOfLayer(CALayer *layer) AS_WARN_UNUSED_RESULT; + +/** + * Given two nodes, finds their most immediate common parent. Used for geometry conversion methods. + * NOTE: It is an error to try to convert between nodes which do not share a common ancestor. This behavior is + * disallowed in UIKit documentation and the behavior is left undefined. The output does not have a rigorously defined + * failure mode (i.e. returning CGPointZero or returning the point exactly as passed in). Rather than track the internal + * undefined and undocumented behavior of UIKit in ASDisplayNode, this operation is defined to be incorrect in all + * circumstances and must be fixed wherever encountered. + */ +ASDK_EXTERN ASDisplayNode * _Nullable ASDisplayNodeFindClosestCommonAncestor(ASDisplayNode *node1, ASDisplayNode *node2) AS_WARN_UNUSED_RESULT; + +/** + Given a display node, collects all descendants. This is a specialization of ASCollectContainer() that walks the Core Animation layer tree as opposed to the display node tree, thus supporting non-continues display node hierarchies. + */ +ASDK_EXTERN NSArray *ASCollectDisplayNodes(ASDisplayNode *node) AS_WARN_UNUSED_RESULT; + +/** + Given a display node, traverses down the node hierarchy, returning all the display nodes that pass the block. + */ +ASDK_EXTERN NSArray *ASDisplayNodeFindAllSubnodes(ASDisplayNode *start, BOOL (^block)(ASDisplayNode *node)) AS_WARN_UNUSED_RESULT; + +/** + Given a display node, traverses down the node hierarchy, returning all the display nodes of kind class. + */ +ASDK_EXTERN NSArray<__kindof ASDisplayNode *> *ASDisplayNodeFindAllSubnodesOfClass(ASDisplayNode *start, Class c) AS_WARN_UNUSED_RESULT; + +/** + Given a display node, traverses down the node hierarchy, returning the depth-first display node, including the start node that pass the block. + */ +ASDK_EXTERN __kindof ASDisplayNode * _Nullable ASDisplayNodeFindFirstNode(ASDisplayNode *start, BOOL (^block)(ASDisplayNode *node)) AS_WARN_UNUSED_RESULT; + +/** + Given a display node, traverses down the node hierarchy, returning the depth-first display node, excluding the start node, that pass the block + */ +ASDK_EXTERN __kindof ASDisplayNode * _Nullable ASDisplayNodeFindFirstSubnode(ASDisplayNode *start, BOOL (^block)(ASDisplayNode *node)) AS_WARN_UNUSED_RESULT; + +/** + Given a display node, traverses down the node hierarchy, returning the depth-first display node of kind class. + */ +ASDK_EXTERN __kindof ASDisplayNode * _Nullable ASDisplayNodeFindFirstSubnodeOfClass(ASDisplayNode *start, Class c) AS_WARN_UNUSED_RESULT; + +ASDK_EXTERN UIColor *ASDisplayNodeDefaultPlaceholderColor(void) AS_WARN_UNUSED_RESULT; +ASDK_EXTERN UIColor *ASDisplayNodeDefaultTintColor(void) AS_WARN_UNUSED_RESULT; + +/** + Disable willAppear / didAppear / didDisappear notifications for a sub-hierarchy, then re-enable when done. Nested calls are supported. + */ +ASDK_EXTERN void ASDisplayNodeDisableHierarchyNotifications(ASDisplayNode *node); +ASDK_EXTERN void ASDisplayNodeEnableHierarchyNotifications(ASDisplayNode *node); + +// Not to be called directly. +ASDK_EXTERN void _ASSetDebugNames(Class owningClass, NSString *names, ASDisplayNode * _Nullable object, ...); + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDisplayNodeExtras.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDisplayNodeExtras.mm new file mode 100644 index 0000000..2cebbb5 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASDisplayNodeExtras.mm @@ -0,0 +1,337 @@ +// +// ASDisplayNodeExtras.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import + +#import +#import + +void ASPerformMainThreadDeallocation(id _Nullable __strong * _Nonnull objectPtr) { + /** + * UIKit components must be deallocated on the main thread. We use this shared + * run loop queue to gradually deallocate them across many turns of the main run loop. + */ + static ASRunLoopQueue *queue; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + queue = [[ASRunLoopQueue alloc] initWithRunLoop:CFRunLoopGetMain() retainObjects:YES handler:nil]; + queue.batchSize = 10; + }); + + if (objectPtr != NULL && *objectPtr != nil) { + // TODO: If ASRunLoopQueue supported an "unsafe_unretained" mode, we could + // transfer the caller's +1 into it and save the retain/release pair. + + // Lock queue while enqueuing and releasing, so that there's no risk + // that the queue will release before we get a chance to release. + [queue lock]; + [queue enqueue:*objectPtr]; // Retain, +1 + *objectPtr = nil; // Release, +0 + [queue unlock]; // (After queue drains), release, -1 + } +} + +void _ASSetDebugNames(Class _Nonnull owningClass, NSString * _Nonnull names, ASDisplayNode * _Nullable object, ...) +{ + NSString *owningClassName = NSStringFromClass(owningClass); + NSArray *nameArray = [names componentsSeparatedByString:@", "]; + va_list args; + va_start(args, object); + NSInteger i = 0; + for (ASDisplayNode *node = object; node != nil; node = va_arg(args, id), i++) { + NSMutableString *symbolName = [nameArray[i] mutableCopy]; + // Remove any `self.` or `_` prefix + [symbolName replaceOccurrencesOfString:@"self." withString:@"" options:NSAnchoredSearch range:NSMakeRange(0, symbolName.length)]; + [symbolName replaceOccurrencesOfString:@"_" withString:@"" options:NSAnchoredSearch range:NSMakeRange(0, symbolName.length)]; + node.debugName = [NSString stringWithFormat:@"%@.%@", owningClassName, symbolName]; + } + ASDisplayNodeCAssert(nameArray.count == i, @"Malformed call to ASSetDebugNames: %@", names); + va_end(args); +} + +ASInterfaceState ASInterfaceStateForDisplayNode(ASDisplayNode *displayNode, UIWindow *window) +{ + ASDisplayNodeCAssert(![displayNode isLayerBacked], @"displayNode must not be layer backed as it may have a nil window"); + if (displayNode && [displayNode supportsRangeManagedInterfaceState]) { + // Directly clear the visible bit if we are not in a window. This means that the interface state is, + // if not already, about to be set to invisible as it is not possible for an element to be visible + // while outside of a window. + ASInterfaceState interfaceState = displayNode.pendingInterfaceState; + return (window == nil ? (interfaceState &= (~ASInterfaceStateVisible)) : interfaceState); + } else { + // For not range managed nodes we might be on our own to try to guess if we're visible. + return (window == nil ? ASInterfaceStateNone : (ASInterfaceStateVisible | ASInterfaceStateDisplay)); + } +} + +ASDisplayNode *ASLayerToDisplayNode(CALayer *layer) +{ + return layer.asyncdisplaykit_node; +} + +ASDisplayNode *ASViewToDisplayNode(UIView *view) +{ + return view.asyncdisplaykit_node; +} + +void ASDisplayNodePerformBlockOnEveryNode(CALayer * _Nullable layer, ASDisplayNode * _Nullable node, BOOL traverseSublayers, void(^block)(ASDisplayNode *node)) +{ + if (!node) { + ASDisplayNodeCAssertNotNil(layer, @"Cannot recursively perform with nil node and nil layer"); + ASDisplayNodeCAssertMainThread(); + node = ASLayerToDisplayNode(layer); + } + + if (node) { + block(node); + } + if (traverseSublayers && !layer && [node isNodeLoaded] && ASDisplayNodeThreadIsMain()) { + layer = node.layer; + } + + if (traverseSublayers && layer && node.rasterizesSubtree == NO) { + /// NOTE: The docs say `sublayers` returns a copy, but it does not. + /// See: http://stackoverflow.com/questions/14854480/collection-calayerarray-0x1ed8faa0-was-mutated-while-being-enumerated + for (CALayer *sublayer in [[layer sublayers] copy]) { + ASDisplayNodePerformBlockOnEveryNode(sublayer, nil, traverseSublayers, block); + } + } else if (node) { + for (ASDisplayNode *subnode in [node subnodes]) { + ASDisplayNodePerformBlockOnEveryNode(nil, subnode, traverseSublayers, block); + } + } +} + +void ASDisplayNodePerformBlockOnEveryNodeBFS(ASDisplayNode *node, void(^block)(ASDisplayNode *node)) +{ + // Queue used to keep track of subnodes while traversing this layout in a BFS fashion. + std::queue queue; + queue.push(node); + + while (!queue.empty()) { + node = queue.front(); + queue.pop(); + + block(node); + + // Add all subnodes to process in next step + for (ASDisplayNode *subnode in node.subnodes) { + queue.push(subnode); + } + } +} + +void ASDisplayNodePerformBlockOnEverySubnode(ASDisplayNode *node, BOOL traverseSublayers, void(^block)(ASDisplayNode *node)) +{ + for (ASDisplayNode *subnode in node.subnodes) { + ASDisplayNodePerformBlockOnEveryNode(nil, subnode, YES, block); + } +} + +ASDisplayNode *ASDisplayNodeFindFirstSupernode(ASDisplayNode *node, BOOL (^block)(ASDisplayNode *node)) +{ + // This function has historically started with `self` but the name suggests + // that it wouldn't. Perhaps we should change the behavior. + for (ASDisplayNode *ancestor in node.supernodesIncludingSelf) { + if (block(ancestor)) { + return ancestor; + } + } + return nil; +} + +__kindof ASDisplayNode *ASDisplayNodeFindFirstSupernodeOfClass(ASDisplayNode *start, Class c) +{ + // This function has historically started with `self` but the name suggests + // that it wouldn't. Perhaps we should change the behavior. + return [start supernodeOfClass:c includingSelf:YES]; +} + +static void _ASCollectDisplayNodes(NSMutableArray *array, CALayer *layer) +{ + ASDisplayNode *node = ASLayerToDisplayNode(layer); + + if (nil != node) { + [array addObject:node]; + } + + for (CALayer *sublayer in layer.sublayers) + _ASCollectDisplayNodes(array, sublayer); +} + +NSArray *ASCollectDisplayNodes(ASDisplayNode *node) +{ + NSMutableArray *list = [[NSMutableArray alloc] init]; + for (CALayer *sublayer in node.layer.sublayers) { + _ASCollectDisplayNodes(list, sublayer); + } + return list; +} + +#pragma mark - Find all subnodes + +static void _ASDisplayNodeFindAllSubnodes(NSMutableArray *array, ASDisplayNode *node, BOOL (^block)(ASDisplayNode *node)) +{ + if (!node) + return; + + for (ASDisplayNode *subnode in node.subnodes) { + if (block(subnode)) { + [array addObject:subnode]; + } + + _ASDisplayNodeFindAllSubnodes(array, subnode, block); + } +} + +NSArray *ASDisplayNodeFindAllSubnodes(ASDisplayNode *start, BOOL (^block)(ASDisplayNode *node)) +{ + NSMutableArray *list = [[NSMutableArray alloc] init]; + _ASDisplayNodeFindAllSubnodes(list, start, block); + return list; +} + +NSArray<__kindof ASDisplayNode *> *ASDisplayNodeFindAllSubnodesOfClass(ASDisplayNode *start, Class c) +{ + return ASDisplayNodeFindAllSubnodes(start, ^(ASDisplayNode *n) { + return [n isKindOfClass:c]; + }); +} + +#pragma mark - Find first subnode + +static ASDisplayNode *_ASDisplayNodeFindFirstNode(ASDisplayNode *startNode, BOOL includeStartNode, BOOL (^block)(ASDisplayNode *node)) +{ + for (ASDisplayNode *subnode in startNode.subnodes) { + ASDisplayNode *foundNode = _ASDisplayNodeFindFirstNode(subnode, YES, block); + if (foundNode) { + return foundNode; + } + } + + if (includeStartNode && block(startNode)) + return startNode; + + return nil; +} + +__kindof ASDisplayNode *ASDisplayNodeFindFirstNode(ASDisplayNode *startNode, BOOL (^block)(ASDisplayNode *node)) +{ + return _ASDisplayNodeFindFirstNode(startNode, YES, block); +} + +__kindof ASDisplayNode *ASDisplayNodeFindFirstSubnode(ASDisplayNode *startNode, BOOL (^block)(ASDisplayNode *node)) +{ + return _ASDisplayNodeFindFirstNode(startNode, NO, block); +} + +__kindof ASDisplayNode *ASDisplayNodeFindFirstSubnodeOfClass(ASDisplayNode *start, Class c) +{ + return ASDisplayNodeFindFirstSubnode(start, ^(ASDisplayNode *n) { + return [n isKindOfClass:c]; + }); +} + +static inline BOOL _ASDisplayNodeIsAncestorOfDisplayNode(ASDisplayNode *possibleAncestor, ASDisplayNode *possibleDescendant) +{ + ASDisplayNode *supernode = possibleDescendant; + while (supernode) { + if (supernode == possibleAncestor) { + return YES; + } + supernode = supernode.supernode; + } + + return NO; +} + +UIWindow * _Nullable ASFindWindowOfLayer(CALayer *layer) +{ + UIView *view = ASFindClosestViewOfLayer(layer); + if (UIWindow *window = ASDynamicCast(view, UIWindow)) { + return window; + } else { + return view.window; + } +} + +UIView * _Nullable ASFindClosestViewOfLayer(CALayer *layer) +{ + while (layer != nil) { + if (UIView *view = ASDynamicCast(layer.delegate, UIView)) { + return view; + } + layer = layer.superlayer; + } + return nil; +} + +ASDisplayNode *ASDisplayNodeFindClosestCommonAncestor(ASDisplayNode *node1, ASDisplayNode *node2) +{ + ASDisplayNode *possibleAncestor = node1; + while (possibleAncestor) { + if (_ASDisplayNodeIsAncestorOfDisplayNode(possibleAncestor, node2)) { + break; + } + possibleAncestor = possibleAncestor.supernode; + } + + ASDisplayNodeCAssertNotNil(possibleAncestor, @"Could not find a common ancestor between node1: %@ and node2: %@", node1, node2); + return possibleAncestor; +} + +ASDisplayNode *ASDisplayNodeUltimateParentOfNode(ASDisplayNode *node) +{ + // node <- supernode on each loop + // previous <- node on each loop where node is not nil + // previous is the final non-nil value of supernode, i.e. the root node + ASDisplayNode *previousNode = node; + while ((node = [node supernode])) { + previousNode = node; + } + return previousNode; +} + +#pragma mark - Placeholders + +UIColor *ASDisplayNodeDefaultPlaceholderColor() +{ + static UIColor *defaultPlaceholderColor; + + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + defaultPlaceholderColor = [UIColor colorWithWhite:0.95 alpha:1.0]; + }); + return defaultPlaceholderColor; +} + +UIColor *ASDisplayNodeDefaultTintColor() +{ + static UIColor *defaultTintColor; + + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + defaultTintColor = [UIColor colorWithRed:0.0 green:0.478 blue:1.0 alpha:1.0]; + }); + return defaultTintColor; +} + +#pragma mark - Hierarchy Notifications + +void ASDisplayNodeDisableHierarchyNotifications(ASDisplayNode *node) +{ + [node __incrementVisibilityNotificationsDisabled]; +} + +void ASDisplayNodeEnableHierarchyNotifications(ASDisplayNode *node) +{ + [node __decrementVisibilityNotificationsDisabled]; +} diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASEditableTextNode.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASEditableTextNode.h new file mode 100644 index 0000000..e660eb1 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASEditableTextNode.h @@ -0,0 +1,203 @@ +// +// ASEditableTextNode.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@protocol ASEditableTextNodeDelegate; +@class ASTextKitComponents; + +/** + @abstract Implements a node that supports text editing. + @discussion Does not support layer backing. + */ +@interface ASEditableTextNode : ASDisplayNode + +/** + * @abstract Initializes an editable text node using default TextKit components. + * + * @return An initialized ASEditableTextNode. + */ +- (instancetype)init; + +/** + * @abstract Initializes an editable text node using the provided TextKit components. + * + * @param textKitComponents The TextKit stack used to render text. + * @param placeholderTextKitComponents The TextKit stack used to render placeholder text. + * + * @return An initialized ASEditableTextNode. + */ +- (instancetype)initWithTextKitComponents:(ASTextKitComponents *)textKitComponents + placeholderTextKitComponents:(ASTextKitComponents *)placeholderTextKitComponents; + +//! @abstract The text node's delegate, which must conform to the protocol. +@property (nullable, weak) id delegate; + +#pragma mark - Configuration + +/** + @abstract Enable scrolling on the textView + @default true + */ +@property (nonatomic) BOOL scrollEnabled; + +/** + @abstract Access to underlying UITextView for more configuration options. + @warning This property should only be used on the main thread and should not be accessed before the editable text node's view is created. + */ +@property (nonatomic, readonly) UITextView *textView; + +//! @abstract The attributes to apply to new text being entered by the user. +@property (nullable, nonatomic, copy) NSDictionary *typingAttributes; + +//! @abstract The range of text currently selected. If length is zero, the range is the cursor location. +@property NSRange selectedRange; + +#pragma mark - Placeholder +/** + @abstract Indicates if the receiver is displaying the placeholder text. + @discussion To update the placeholder, see the property. + @result YES if the placeholder is currently displayed; NO otherwise. + */ +- (BOOL)isDisplayingPlaceholder AS_WARN_UNUSED_RESULT; + +/** + @abstract The styled placeholder text displayed by the text node while no text is entered + @discussion The placeholder is displayed when the user has not entered any text and the keyboard is not visible. + */ +@property (nullable, nonatomic, copy) NSAttributedString *attributedPlaceholderText; + +#pragma mark - Modifying User Text +/** + @abstract The styled text displayed by the receiver. + @discussion When the placeholder is displayed (as indicated by -isDisplayingPlaceholder), this value is nil. Otherwise, this value is the attributed text the user has entered. This value can be modified regardless of whether the receiver is the first responder (and thus, editing) or not. Changing this value from nil to non-nil will result in the placeholder being hidden, and the new value being displayed. + */ +@property (nullable, nonatomic, copy) NSAttributedString *attributedText; + +#pragma mark - Managing The Keyboard +//! @abstract The text input mode used by the receiver's keyboard, if it is visible. This value is undefined if the receiver is not the first responder. +@property (nonatomic, readonly) UITextInputMode *textInputMode; + +/** + @abstract The textContainerInset of both the placeholder and typed textView. This value defaults to UIEdgeInsetsZero. + */ +@property (nonatomic) UIEdgeInsets textContainerInset; + +/** + @abstract The maximum number of lines to display. Additional lines will require scrolling. + @default 0 (No limit) + */ +@property (nonatomic) NSUInteger maximumLinesToDisplay; + +/** + @abstract Indicates whether the receiver's text view is the first responder, and thus has the keyboard visible and is prepared for editing by the user. + @result YES if the receiver's text view is the first-responder; NO otherwise. + */ +- (BOOL)isFirstResponder AS_WARN_UNUSED_RESULT; + +//! @abstract Makes the receiver's text view the first responder. +- (BOOL)becomeFirstResponder; + +//! @abstract Resigns the receiver's text view from first-responder status, if it has it. +- (BOOL)resignFirstResponder; + +#pragma mark - Geometry +/** + @abstract Returns the frame of the given range of characters. + @param textRange A range of characters. + @discussion This method raises an exception if `textRange` is not a valid range of characters within the receiver's attributed text. + @result A CGRect that is the bounding box of the glyphs covered by the given range of characters, in the coordinate system of the receiver. + */ +- (CGRect)frameForTextRange:(NSRange)textRange AS_WARN_UNUSED_RESULT; + +/** + @abstract properties. + */ +@property (nonatomic) UITextAutocapitalizationType autocapitalizationType; // default is UITextAutocapitalizationTypeSentences +@property (nonatomic) UITextAutocorrectionType autocorrectionType; // default is UITextAutocorrectionTypeDefault +@property (nonatomic) UITextSpellCheckingType spellCheckingType; // default is UITextSpellCheckingTypeDefault; +@property (nonatomic) UIKeyboardType keyboardType; // default is UIKeyboardTypeDefault +@property (nonatomic) UIKeyboardAppearance keyboardAppearance; // default is UIKeyboardAppearanceDefault +@property (nonatomic) UIReturnKeyType returnKeyType; // default is UIReturnKeyDefault (See note under UIReturnKeyType enum) +@property (nonatomic) BOOL enablesReturnKeyAutomatically; // default is NO (when YES, will automatically disable return key when text widget has zero-length contents, and will automatically enable when text widget has non-zero-length contents) +@property (nonatomic, getter=isSecureTextEntry) BOOL secureTextEntry; // default is NO + +@end + +@interface ASEditableTextNode (Unavailable) + +- (instancetype)initWithLayerBlock:(ASDisplayNodeLayerBlock)viewBlock didLoadBlock:(nullable ASDisplayNodeDidLoadBlock)didLoadBlock NS_UNAVAILABLE; + +- (instancetype)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock didLoadBlock:(nullable ASDisplayNodeDidLoadBlock)didLoadBlock NS_UNAVAILABLE; + +@end + +#pragma mark - +/** + * The methods declared by the ASEditableTextNodeDelegate protocol allow the adopting delegate to + * respond to notifications such as began and finished editing, selection changed and text updated; + * and manage whether a specified text should be replaced. + */ +@protocol ASEditableTextNodeDelegate + +@optional +/** + @abstract Asks the delegate if editing should begin for the text node. + @param editableTextNode An editable text node. + @discussion YES if editing should begin; NO if editing should not begin -- the default returns YES. + */ +- (BOOL)editableTextNodeShouldBeginEditing:(ASEditableTextNode *)editableTextNode; + +/** + @abstract Indicates to the delegate that the text node began editing. + @param editableTextNode An editable text node. + @discussion The invocation of this method coincides with the keyboard animating to become visible. + */ +- (void)editableTextNodeDidBeginEditing:(ASEditableTextNode *)editableTextNode; + +/** + @abstract Asks the delegate whether the specified text should be replaced in the editable text node. + @param editableTextNode An editable text node. + @param range The current selection range. If the length of the range is 0, range reflects the current insertion point. If the user presses the Delete key, the length of the range is 1 and an empty string object replaces that single character. + @param text The text to insert. + @discussion YES if the old text should be replaced by the new text; NO if the replacement operation should be aborted. + @result The text node calls this method whenever the user types a new character or deletes an existing character. Implementation of this method is optional -- the default implementation returns YES. + */ +- (BOOL)editableTextNode:(ASEditableTextNode *)editableTextNode shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text; + +/** + @abstract Indicates to the delegate that the text node's selection has changed. + @param editableTextNode An editable text node. + @param fromSelectedRange The previously selected range. + @param toSelectedRange The current selected range. Equivalent to the property. + @param dueToEditing YES if the selection change was due to editing; NO otherwise. + @discussion You can access the selection of the receiver via . + */ +- (void)editableTextNodeDidChangeSelection:(ASEditableTextNode *)editableTextNode fromSelectedRange:(NSRange)fromSelectedRange toSelectedRange:(NSRange)toSelectedRange dueToEditing:(BOOL)dueToEditing; + +/** + @abstract Indicates to the delegate that the text node's text was updated. + @param editableTextNode An editable text node. + @discussion This method is called each time the user updated the text node's text. It is not called for programmatic changes made to the text via the property. + */ +- (void)editableTextNodeDidUpdateText:(ASEditableTextNode *)editableTextNode; + +/** + @abstract Indicates to the delegate that the text node has finished editing. + @param editableTextNode An editable text node. + @discussion The invocation of this method coincides with the keyboard animating to become hidden. + */ +- (void)editableTextNodeDidFinishEditing:(ASEditableTextNode *)editableTextNode; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASEditableTextNode.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASEditableTextNode.mm new file mode 100644 index 0000000..60be21f --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASEditableTextNode.mm @@ -0,0 +1,880 @@ +// +// ASEditableTextNode.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import + +#import +#import +#import +#import +#import + +/** + @abstract Object to hold UITextView's pending UITextInputTraits +**/ +@interface _ASTextInputTraitsPendingState : NSObject + +@property UITextAutocapitalizationType autocapitalizationType; +@property UITextAutocorrectionType autocorrectionType; +@property UITextSpellCheckingType spellCheckingType; +@property UIKeyboardAppearance keyboardAppearance; +@property UIKeyboardType keyboardType; +@property UIReturnKeyType returnKeyType; +@property BOOL enablesReturnKeyAutomatically; +@property (getter=isSecureTextEntry) BOOL secureTextEntry; + +@end + +@implementation _ASTextInputTraitsPendingState + +- (instancetype)init +{ + if (!(self = [super init])) + return nil; + + // set default values, as defined in Apple's comments in UITextInputTraits.h + _autocapitalizationType = UITextAutocapitalizationTypeSentences; + _autocorrectionType = UITextAutocorrectionTypeDefault; + _spellCheckingType = UITextSpellCheckingTypeDefault; + _keyboardAppearance = UIKeyboardAppearanceDefault; + _keyboardType = UIKeyboardTypeDefault; + _returnKeyType = UIReturnKeyDefault; + + return self; +} + +@end + +/** + @abstract As originally reported in rdar://14729288, when scrollEnabled = NO, + UITextView does not calculate its contentSize. This makes it difficult + for a client to embed a UITextView inside a different scroll view with + other content (setting scrollEnabled = NO on the UITextView itself, + because the containing scroll view will handle the gesture)... + because accessing contentSize is typically necessary to perform layout. + Apple later closed the issue as expected behavior. This works around + the issue by ensuring that contentSize is always calculated, while + still providing control over the UITextView's scrolling. + + See issue: https://github.com/facebook/AsyncDisplayKit/issues/1063 + */ +@interface ASPanningOverriddenUITextView : ASTextKitComponentsTextView +{ + BOOL _shouldBlockPanGesture; +} +@end + +@implementation ASPanningOverriddenUITextView + +#if TARGET_OS_IOS + // tvOS doesn't support self.scrollsToTop +- (BOOL)scrollEnabled +{ + return _shouldBlockPanGesture; +} + +- (void)setScrollEnabled:(BOOL)scrollEnabled +{ + _shouldBlockPanGesture = !scrollEnabled; + self.scrollsToTop = scrollEnabled; + + [super setScrollEnabled:YES]; +} +#endif + +- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer +{ + // Never allow our pans to begin when _shouldBlockPanGesture is true. + if (_shouldBlockPanGesture && gestureRecognizer == self.panGestureRecognizer) + return NO; + + // Otherwise, proceed as usual. + if ([UITextView instancesRespondToSelector:_cmd]) + return [super gestureRecognizerShouldBegin:gestureRecognizer]; + return YES; +} + +@end + +#pragma mark - +@interface ASEditableTextNode () +{ + @private + // Configuration. + NSDictionary *_typingAttributes; + + // Core. + id __weak _delegate; + + // TextKit. + AS::RecursiveMutex _textKitLock; + ASTextKitComponents *_textKitComponents; + ASTextKitComponents *_placeholderTextKitComponents; + // Forwards NSLayoutManagerDelegate methods related to word kerning + ASTextNodeWordKerner *_wordKerner; + + // UITextInputTraits + AS::RecursiveMutex _textInputTraitsLock; + _ASTextInputTraitsPendingState *_textInputTraits; + + // Misc. State. + BOOL _displayingPlaceholder; // Defaults to YES. + BOOL _isPreservingSelection; + BOOL _selectionChangedForEditedText; + BOOL _delegateDidUpdateEnqueued; + BOOL _scrollEnabled; + + NSRange _previousSelectedRange; +} + +@property (nonatomic, readonly) _ASTextInputTraitsPendingState *textInputTraits; + +@end + +@implementation ASEditableTextNode + +#pragma mark - NSObject Overrides +- (instancetype)init +{ + return [self initWithTextKitComponents:[ASTextKitComponents componentsWithAttributedSeedString:nil textContainerSize:CGSizeZero] + placeholderTextKitComponents:[ASTextKitComponents componentsWithAttributedSeedString:nil textContainerSize:CGSizeZero]]; +} + +- (instancetype)initWithTextKitComponents:(ASTextKitComponents *)textKitComponents + placeholderTextKitComponents:(ASTextKitComponents *)placeholderTextKitComponents +{ + if (!(self = [super init])) + return nil; + + _displayingPlaceholder = YES; + _scrollEnabled = YES; + + // Create the scaffolding for the text view. + _textKitComponents = textKitComponents; + _textKitComponents.layoutManager.delegate = self; + _wordKerner = [[ASTextNodeWordKerner alloc] init]; + _textContainerInset = UIEdgeInsetsZero; + + // Create the placeholder scaffolding. + _placeholderTextKitComponents = placeholderTextKitComponents; + _placeholderTextKitComponents.layoutManager.delegate = self; + + return self; +} + +#pragma mark - ASDisplayNode Overrides +- (void)didLoad +{ + [super didLoad]; + + void (^configureTextView)(UITextView *) = ^(UITextView *textView) { + if (!self->_displayingPlaceholder || textView != self->_textKitComponents.textView) { + // If showing the placeholder, don't propagate backgroundColor/opaque to the editable textView. It is positioned over the placeholder to accept taps to begin editing, and if it's opaque/colored then it'll obscure the placeholder. + textView.backgroundColor = self.backgroundColor; + textView.opaque = self.opaque; + } else if (self->_displayingPlaceholder && textView == self->_textKitComponents.textView) { + // The default backgroundColor for a textView is white. Due to the reason described above, make sure the editable textView starts out transparent. + textView.backgroundColor = nil; + textView.opaque = NO; + } + textView.textContainerInset = self.textContainerInset; + + // Configure textView with UITextInputTraits + { + AS::MutexLocker l(self->_textInputTraitsLock); + if (self->_textInputTraits) { + textView.autocapitalizationType = self->_textInputTraits.autocapitalizationType; + textView.autocorrectionType = self->_textInputTraits.autocorrectionType; + textView.spellCheckingType = self->_textInputTraits.spellCheckingType; + textView.keyboardType = self->_textInputTraits.keyboardType; + textView.keyboardAppearance = self->_textInputTraits.keyboardAppearance; + textView.returnKeyType = self->_textInputTraits.returnKeyType; + textView.enablesReturnKeyAutomatically = self->_textInputTraits.enablesReturnKeyAutomatically; + textView.secureTextEntry = self->_textInputTraits.isSecureTextEntry; + } + } + + [self.view addSubview:textView]; + }; + + AS::MutexLocker l(_textKitLock); + + // Create and configure the placeholder text view. + _placeholderTextKitComponents.textView = [[ASTextKitComponentsTextView alloc] initWithFrame:CGRectZero textContainer:_placeholderTextKitComponents.textContainer]; + _placeholderTextKitComponents.textView.userInteractionEnabled = NO; + _placeholderTextKitComponents.textView.accessibilityElementsHidden = YES; + configureTextView(_placeholderTextKitComponents.textView); + + // Create and configure our text view. + _textKitComponents.textView = [[ASPanningOverriddenUITextView alloc] initWithFrame:CGRectZero textContainer:_textKitComponents.textContainer]; + _textKitComponents.textView.scrollEnabled = _scrollEnabled; + _textKitComponents.textView.delegate = self; + #if TARGET_OS_IOS + _textKitComponents.textView.editable = YES; + #endif + _textKitComponents.textView.typingAttributes = _typingAttributes; + _textKitComponents.textView.accessibilityHint = _placeholderTextKitComponents.textStorage.string; + configureTextView(_textKitComponents.textView); + + [self _updateDisplayingPlaceholder]; + + // once view is loaded, setters set directly on view + _textInputTraits = nil; +} + +- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize +{ + ASTextKitComponents *displayedComponents = [self isDisplayingPlaceholder] ? _placeholderTextKitComponents : _textKitComponents; + + CGSize textSize; + + if (_maximumLinesToDisplay > 0) { + textSize = [displayedComponents sizeForConstrainedWidth:constrainedSize.width + forMaxNumberOfLines: _maximumLinesToDisplay]; + } else { + textSize = [displayedComponents sizeForConstrainedWidth:constrainedSize.width]; + } + + CGFloat width = std::ceil(textSize.width + _textContainerInset.left + _textContainerInset.right); + CGFloat height = std::ceil(textSize.height + _textContainerInset.top + _textContainerInset.bottom); + return CGSizeMake(std::fmin(width, constrainedSize.width), std::fmin(height, constrainedSize.height)); +} + +- (void)layout +{ + ASDisplayNodeAssertMainThread(); + + [super layout]; + [self _layoutTextView]; +} + +- (void)setBackgroundColor:(UIColor *)backgroundColor +{ + [super setBackgroundColor:backgroundColor]; + + AS::MutexLocker l(_textKitLock); + + // If showing the placeholder, don't propagate backgroundColor/opaque to the editable textView. It is positioned over the placeholder to accept taps to begin editing, and if it's opaque/colored then it'll obscure the placeholder. + // The backgroundColor/opaque will be propagated to the editable textView when editing begins. + if (!_displayingPlaceholder) { + _textKitComponents.textView.backgroundColor = backgroundColor; + } + _placeholderTextKitComponents.textView.backgroundColor = backgroundColor; +} + +- (void)setTextContainerInset:(UIEdgeInsets)textContainerInset +{ + AS::MutexLocker l(_textKitLock); + + _textContainerInset = textContainerInset; + _textKitComponents.textView.textContainerInset = textContainerInset; + _placeholderTextKitComponents.textView.textContainerInset = textContainerInset; +} + +- (void)setOpaque:(BOOL)opaque +{ + [super setOpaque:opaque]; + + AS::MutexLocker l(_textKitLock); + + // If showing the placeholder, don't propagate backgroundColor/opaque to the editable textView. It is positioned over the placeholder to accept taps to begin editing, and if it's opaque/colored then it'll obscure the placeholder. + // The backgroundColor/opaque will be propagated to the editable textView when editing begins. + if (!_displayingPlaceholder) { + _textKitComponents.textView.opaque = opaque; + } + _placeholderTextKitComponents.textView.opaque = opaque; +} + +- (void)setLayerBacked:(BOOL)layerBacked +{ + ASDisplayNodeAssert(!layerBacked, @"Cannot set layerBacked to YES on ASEditableTextNode – instances must be view-backed in order to ensure touch events can be passed to the internal UITextView during editing."); + [super setLayerBacked:layerBacked]; +} + +- (BOOL)supportsLayerBacking +{ + return NO; +} + +#pragma mark - Configuration +@synthesize delegate = _delegate; + +- (void)setScrollEnabled:(BOOL)scrollEnabled +{ + AS::MutexLocker l(_textKitLock); + _scrollEnabled = scrollEnabled; + [_textKitComponents.textView setScrollEnabled:_scrollEnabled]; +} + +- (UITextView *)textView +{ + ASDisplayNodeAssertMainThread(); + [self view]; + ASDisplayNodeAssert(_textKitComponents.textView != nil, @"UITextView must be created in -[ASEditableTextNode didLoad]"); + return _textKitComponents.textView; +} + +- (void)setMaximumLinesToDisplay:(NSUInteger)maximumLines +{ + _maximumLinesToDisplay = maximumLines; + [self setNeedsLayout]; +} + +#pragma mark - +@dynamic typingAttributes; + +- (NSDictionary *)typingAttributes +{ + return _typingAttributes; +} + +- (void)setTypingAttributes:(NSDictionary *)typingAttributes +{ + if (ASObjectIsEqual(typingAttributes, _typingAttributes)) + return; + + _typingAttributes = [typingAttributes copy]; + + AS::MutexLocker l(_textKitLock); + + _textKitComponents.textView.typingAttributes = _typingAttributes; +} + +#pragma mark - +@dynamic selectedRange; + +- (NSRange)selectedRange +{ + AS::MutexLocker l(_textKitLock); + return _textKitComponents.textView.selectedRange; +} + +- (void)setSelectedRange:(NSRange)selectedRange +{ + AS::MutexLocker l(_textKitLock); + _textKitComponents.textView.selectedRange = selectedRange; +} + +#pragma mark - Placeholder +- (BOOL)isDisplayingPlaceholder +{ + return _displayingPlaceholder; +} + +#pragma mark - +@dynamic attributedPlaceholderText; +- (NSAttributedString *)attributedPlaceholderText +{ + AS::MutexLocker l(_textKitLock); + + return [_placeholderTextKitComponents.textStorage copy]; +} + +- (void)setAttributedPlaceholderText:(NSAttributedString *)attributedPlaceholderText +{ + AS::MutexLocker l(_textKitLock); + + if (ASObjectIsEqual(_placeholderTextKitComponents.textStorage, attributedPlaceholderText)) + return; + + [_placeholderTextKitComponents.textStorage setAttributedString:attributedPlaceholderText ? : [[NSAttributedString alloc] initWithString:@""]]; + _textKitComponents.textView.accessibilityHint = attributedPlaceholderText.string; +} + +#pragma mark - Modifying User Text +@dynamic attributedText; +- (NSAttributedString *)attributedText +{ + // Per contract in our header, this value is nil when the placeholder is displayed. + if ([self isDisplayingPlaceholder]) + return nil; + + AS::MutexLocker l(_textKitLock); + + return [_textKitComponents.textStorage copy]; +} + +- (void)setAttributedText:(NSAttributedString *)attributedText +{ + AS::MutexLocker l(_textKitLock); + + // If we (_cmd) are called while the text view itself is updating (-textViewDidUpdate:), you cannot update the text storage and expect perfect propagation to the text view. + // Thus, we always update the textview directly if it's been created already. + if (ASObjectIsEqual((_textKitComponents.textView.attributedText ? : _textKitComponents.textStorage), attributedText)) + return; + + // If the cursor isn't at the end of the text, we need to preserve the selected range to avoid moving the cursor. + NSRange selectedRange = _textKitComponents.textView.selectedRange; + BOOL preserveSelectedRange = (selectedRange.location != _textKitComponents.textStorage.length); + + NSAttributedString *attributedStringToDisplay = nil; + + if (attributedText) + attributedStringToDisplay = attributedText; + // Otherwise, note that we don't simply nil out attributed text. Because the insertion point is guided by the attributes at index 0, we need to attribute an empty string to ensure the insert point obeys our typing attributes. + else + attributedStringToDisplay = [[NSAttributedString alloc] initWithString:@"" attributes:self.typingAttributes]; + + // Always prefer updating the text view directly if it's been created (see above). + if (_textKitComponents.textView) + [_textKitComponents.textView setAttributedText:attributedStringToDisplay]; + else + [_textKitComponents.textStorage setAttributedString:attributedStringToDisplay]; + + // Calculated size depends on the seeded text. + [self setNeedsLayout]; + + // Update if placeholder is shown. + [self _updateDisplayingPlaceholder]; + + // Preserve cursor range, if necessary. + if (preserveSelectedRange) { + _isPreservingSelection = YES; // Used in -textViewDidChangeSelection: to avoid informing our delegate about our preservation. + [_textKitComponents.textView setSelectedRange:selectedRange]; + _isPreservingSelection = NO; + } +} + +#pragma mark - Core +- (void)_updateDisplayingPlaceholder +{ + AS::MutexLocker l(_textKitLock); + + // Show the placeholder if necessary. + _displayingPlaceholder = (_textKitComponents.textStorage.length == 0); + _placeholderTextKitComponents.textView.hidden = !_displayingPlaceholder; + + // If hiding the placeholder, propagate backgroundColor/opaque to the editable textView. It is positioned over the placeholder to accept taps to begin editing, and was kept transparent so it doesn't obscure the placeholder text. Now that we're editing it and the placeholder is hidden, we can make it opaque to avoid unnecessary blending. + if (!_displayingPlaceholder) { + _textKitComponents.textView.opaque = self.isOpaque; + _textKitComponents.textView.backgroundColor = self.backgroundColor; + } else { + _textKitComponents.textView.opaque = NO; + _textKitComponents.textView.backgroundColor = nil; + } +} + +- (void)_layoutTextView +{ + AS::MutexLocker l(_textKitLock); + + // Layout filling our bounds. + _textKitComponents.textView.frame = self.bounds; + _placeholderTextKitComponents.textView.frame = self.bounds; + + // Note that both of these won't be necessary once we can disable scrolling, pending rdar://14729288 + // When we resize to fit (above) the prior layout becomes invalid. For whatever reason, UITextView doesn't invalidate its layout when its frame changes on its own, so we have to do so ourselves. + [_textKitComponents.layoutManager invalidateLayoutForCharacterRange:NSMakeRange(0, [_textKitComponents.textStorage length]) actualCharacterRange:NULL]; + + // When you type beyond UITextView's bounds it scrolls you down a line. We need to remain at the top. + [_textKitComponents.textView setContentOffset:CGPointZero animated:NO]; +} + +#pragma mark - Keyboard +@dynamic textInputMode; +- (UITextInputMode *)textInputMode +{ + AS::MutexLocker l(_textKitLock); + return [_textKitComponents.textView textInputMode]; +} + +- (BOOL)isFirstResponder +{ + AS::MutexLocker l(_textKitLock); + return [_textKitComponents.textView isFirstResponder]; +} + +- (BOOL)canBecomeFirstResponder { + AS::MutexLocker l(_textKitLock); + return [_textKitComponents.textView canBecomeFirstResponder]; +} + +- (BOOL)becomeFirstResponder +{ + AS::MutexLocker l(_textKitLock); + return [_textKitComponents.textView becomeFirstResponder]; +} + +- (BOOL)canResignFirstResponder { + AS::MutexLocker l(_textKitLock); + return [_textKitComponents.textView canResignFirstResponder]; +} + +- (BOOL)resignFirstResponder +{ + AS::MutexLocker l(_textKitLock); + return [_textKitComponents.textView resignFirstResponder]; +} + +#pragma mark - UITextInputTraits + +- (_ASTextInputTraitsPendingState *)textInputTraits +{ + if (!_textInputTraits) { + _textInputTraits = [[_ASTextInputTraitsPendingState alloc] init]; + } + return _textInputTraits; +} + +- (void)setAutocapitalizationType:(UITextAutocapitalizationType)autocapitalizationType +{ + AS::MutexLocker l(_textInputTraitsLock); + if (self.isNodeLoaded) { + [self.textView setAutocapitalizationType:autocapitalizationType]; + } else { + [self.textInputTraits setAutocapitalizationType:autocapitalizationType]; + } +} + +- (UITextAutocapitalizationType)autocapitalizationType +{ + AS::MutexLocker l(_textInputTraitsLock); + if (self.isNodeLoaded) { + return [self.textView autocapitalizationType]; + } else { + return [self.textInputTraits autocapitalizationType]; + } +} + +- (void)setAutocorrectionType:(UITextAutocorrectionType)autocorrectionType +{ + AS::MutexLocker l(_textInputTraitsLock); + if (self.isNodeLoaded) { + [self.textView setAutocorrectionType:autocorrectionType]; + } else { + [self.textInputTraits setAutocorrectionType:autocorrectionType]; + } +} + +- (UITextAutocorrectionType)autocorrectionType +{ + AS::MutexLocker l(_textInputTraitsLock); + if (self.isNodeLoaded) { + return [self.textView autocorrectionType]; + } else { + return [self.textInputTraits autocorrectionType]; + } +} + +- (void)setSpellCheckingType:(UITextSpellCheckingType)spellCheckingType +{ + AS::MutexLocker l(_textInputTraitsLock); + if (self.isNodeLoaded) { + [self.textView setSpellCheckingType:spellCheckingType]; + } else { + [self.textInputTraits setSpellCheckingType:spellCheckingType]; + } +} + +- (UITextSpellCheckingType)spellCheckingType +{ + AS::MutexLocker l(_textInputTraitsLock); + if (self.isNodeLoaded) { + return [self.textView spellCheckingType]; + } else { + return [self.textInputTraits spellCheckingType]; + } +} + +- (void)setEnablesReturnKeyAutomatically:(BOOL)enablesReturnKeyAutomatically +{ + AS::MutexLocker l(_textInputTraitsLock); + if (self.isNodeLoaded) { + [self.textView setEnablesReturnKeyAutomatically:enablesReturnKeyAutomatically]; + } else { + [self.textInputTraits setEnablesReturnKeyAutomatically:enablesReturnKeyAutomatically]; + } +} + +- (BOOL)enablesReturnKeyAutomatically +{ + AS::MutexLocker l(_textInputTraitsLock); + if (self.isNodeLoaded) { + return [self.textView enablesReturnKeyAutomatically]; + } else { + return [self.textInputTraits enablesReturnKeyAutomatically]; + } +} + +- (void)setKeyboardAppearance:(UIKeyboardAppearance)setKeyboardAppearance +{ + AS::MutexLocker l(_textInputTraitsLock); + if (self.isNodeLoaded) { + [self.textView setKeyboardAppearance:setKeyboardAppearance]; + } else { + [self.textInputTraits setKeyboardAppearance:setKeyboardAppearance]; + } +} + +- (UIKeyboardAppearance)keyboardAppearance +{ + AS::MutexLocker l(_textInputTraitsLock); + if (self.isNodeLoaded) { + return [self.textView keyboardAppearance]; + } else { + return [self.textInputTraits keyboardAppearance]; + } +} + +- (void)setKeyboardType:(UIKeyboardType)keyboardType +{ + AS::MutexLocker l(_textInputTraitsLock); + if (self.isNodeLoaded) { + [self.textView setKeyboardType:keyboardType]; + } else { + [self.textInputTraits setKeyboardType:keyboardType]; + } +} + +- (UIKeyboardType)keyboardType +{ + AS::MutexLocker l(_textInputTraitsLock); + if (self.isNodeLoaded) { + return [self.textView keyboardType]; + } else { + return [self.textInputTraits keyboardType]; + } +} + +- (void)setReturnKeyType:(UIReturnKeyType)returnKeyType +{ + AS::MutexLocker l(_textInputTraitsLock); + if (self.isNodeLoaded) { + [self.textView setReturnKeyType:returnKeyType]; + } else { + [self.textInputTraits setReturnKeyType:returnKeyType]; + } +} + +- (UIReturnKeyType)returnKeyType +{ + AS::MutexLocker l(_textInputTraitsLock); + if (self.isNodeLoaded) { + return [self.textView returnKeyType]; + } else { + return [self.textInputTraits returnKeyType]; + } +} + +- (void)setSecureTextEntry:(BOOL)secureTextEntry +{ + AS::MutexLocker l(_textInputTraitsLock); + if (self.isNodeLoaded) { + [self.textView setSecureTextEntry:secureTextEntry]; + } else { + [self.textInputTraits setSecureTextEntry:secureTextEntry]; + } +} + +- (BOOL)isSecureTextEntry +{ + AS::MutexLocker l(_textInputTraitsLock); + if (self.isNodeLoaded) { + return [self.textView isSecureTextEntry]; + } else { + return [self.textInputTraits isSecureTextEntry]; + } +} + +#pragma mark - UITextView Delegate +- (BOOL)textViewShouldBeginEditing:(UITextView *)textView +{ + // Delegateify. + return [self _delegateShouldBeginEditing]; +} + +- (void)textViewDidBeginEditing:(UITextView *)textView +{ + // Delegateify. + [self _delegateDidBeginEditing]; +} + +- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text +{ + // Delegateify. + return [self _delegateShouldChangeTextInRange:range replacementText:text]; +} + +- (void)textViewDidChange:(UITextView *)textView +{ + AS::MutexLocker l(_textKitLock); + + // Note we received a text changed event. + // This is used by _delegateDidChangeSelectionFromSelectedRange:toSelectedRange: to distinguish between selection changes that happen because of editing or pure selection changes. + _selectionChangedForEditedText = YES; + + // Update if the placeholder is visible. + [self _updateDisplayingPlaceholder]; + + // Invalidate, as our calculated size depends on the textview's seeded text. + [self invalidateCalculatedLayout]; + + // Delegateify. + [self _delegateDidUpdateText]; +} + +- (void)textViewDidChangeSelection:(UITextView *)textView +{ + // Typing attributes get reset when selection changes. Reapply them so they actually obey our header. + _textKitComponents.textView.typingAttributes = _typingAttributes; + + // If we're only changing selection to preserve it, don't notify about anything. + if (_isPreservingSelection) + return; + + // Note if we receive a -textDidChange: between now and when we delegatify. + // This is used by _delegateDidChangeSelectionFromSelectedRange:toSelectedRange: to distinguish between selection changes that happen because of editing or pure selection changes. + _selectionChangedForEditedText = NO; + + NSRange fromSelectedRange = _previousSelectedRange; + NSRange toSelectedRange = self.selectedRange; + _previousSelectedRange = toSelectedRange; + + // Delegateify. + [self _delegateDidChangeSelectionFromSelectedRange:fromSelectedRange toSelectedRange:toSelectedRange]; +} + +- (void)textViewDidEndEditing:(UITextView *)textView +{ + // Delegateify. + [self _delegateDidFinishEditing]; +} + +#pragma mark - NSLayoutManager Delegate + +- (NSUInteger)layoutManager:(NSLayoutManager *)layoutManager shouldGenerateGlyphs:(const CGGlyph *)glyphs properties:(const NSGlyphProperty *)properties characterIndexes:(const NSUInteger *)characterIndexes font:(UIFont *)aFont forGlyphRange:(NSRange)glyphRange +{ + return [_wordKerner layoutManager:layoutManager shouldGenerateGlyphs:glyphs properties:properties characterIndexes:characterIndexes font:aFont forGlyphRange:glyphRange]; +} + +- (NSControlCharacterAction)layoutManager:(NSLayoutManager *)layoutManager shouldUseAction:(NSControlCharacterAction)defaultAction forControlCharacterAtIndex:(NSUInteger)characterIndex +{ + return [_wordKerner layoutManager:layoutManager shouldUseAction:defaultAction forControlCharacterAtIndex:characterIndex]; +} + +- (CGRect)layoutManager:(NSLayoutManager *)layoutManager boundingBoxForControlGlyphAtIndex:(NSUInteger)glyphIndex forTextContainer:(NSTextContainer *)textContainer proposedLineFragment:(CGRect)proposedRect glyphPosition:(CGPoint)glyphPosition characterIndex:(NSUInteger)characterIndex +{ + return [_wordKerner layoutManager:layoutManager boundingBoxForControlGlyphAtIndex:glyphIndex forTextContainer:textContainer proposedLineFragment:proposedRect glyphPosition:glyphPosition characterIndex:characterIndex]; +} + +#pragma mark - Geometry +- (CGRect)frameForTextRange:(NSRange)textRange +{ + AS::MutexLocker l(_textKitLock); + + // Bail on invalid range. + if (NSMaxRange(textRange) > [_textKitComponents.textStorage length]) { + ASDisplayNodeAssert(NO, @"Invalid range"); + return CGRectZero; + } + + // Force glyph generation and layout. + [_textKitComponents.layoutManager ensureLayoutForTextContainer:_textKitComponents.textContainer]; + + NSRange glyphRange = [_textKitComponents.layoutManager glyphRangeForCharacterRange:textRange actualCharacterRange:NULL]; + CGRect textRect = [_textKitComponents.layoutManager boundingRectForGlyphRange:glyphRange inTextContainer:_textKitComponents.textContainer]; + return [_textKitComponents.textView convertRect:textRect toView:self.view]; +} + +#pragma mark - +- (BOOL)_delegateShouldBeginEditing +{ + if ([_delegate respondsToSelector:@selector(editableTextNodeShouldBeginEditing:)]) { + return [_delegate editableTextNodeShouldBeginEditing:self]; + } + return YES; +} + +- (void)_delegateDidBeginEditing +{ + if ([_delegate respondsToSelector:@selector(editableTextNodeDidBeginEditing:)]) + [_delegate editableTextNodeDidBeginEditing:self]; +} + +- (BOOL)_delegateShouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text +{ + if ([_delegate respondsToSelector:@selector(editableTextNode:shouldChangeTextInRange:replacementText:)]) { + return [_delegate editableTextNode:self shouldChangeTextInRange:range replacementText:text]; + } + + return YES; +} + +- (void)_delegateDidChangeSelectionFromSelectedRange:(NSRange)fromSelectedRange toSelectedRange:(NSRange)toSelectedRange +{ + // There are two reasons we're invoking the delegate on the next run of the runloop. + // 1. UITextView invokes its delegate methods when it's in the middle of text-processing. For example, -textViewDidChange: is invoked before you can truly rely on the changes being propagated throughout the Text Kit hierarchy. + // 2. This delegate method (-textViewDidChangeSelection:) is called both before -textViewDidChange: and before the layout manager/etc. has necessarily generated+laid out its glyphs. Because of the former, we need to wait until -textViewDidChange: has had an opportunity to be called so can accurately determine whether this selection change is due to editing (_selectionChangedForEditedText). + // Thus, to avoid calling out to client code in the middle of UITextView's processing, we call the delegate on the next run of the runloop, when all such internal processing is surely done. + dispatch_async(dispatch_get_main_queue(), ^{ + if ([self->_delegate respondsToSelector:@selector(editableTextNodeDidChangeSelection:fromSelectedRange:toSelectedRange:dueToEditing:)]) + [self->_delegate editableTextNodeDidChangeSelection:self fromSelectedRange:fromSelectedRange toSelectedRange:toSelectedRange dueToEditing:self->_selectionChangedForEditedText]; + }); +} + +- (void)_delegateDidUpdateText +{ + // Note that because -editableTextNodeDidUpdateText: passes no state, the current state of the receiver will be accessed. Thus, it's not useful to enqueue a second delegation call if the first hasn't happened yet -- doing so will result in the delegate receiving -editableTextNodeDidUpdateText: when the "updated text" has already been processed. This may sound innocuous, but because our delegation may cause additional updates to the textview's string, and because such updates discard spelling suggestions and autocompletions (like double-space to `.`), it can actually be quite dangerous! + if (_delegateDidUpdateEnqueued) + return; + + _delegateDidUpdateEnqueued = YES; + + // UITextView invokes its delegate methods when it's in the middle of text-processing. For example, -textViewDidChange: is invoked before you can truly rely on the changes being propagated throughout the Text Kit hierarchy. + // Thus, to avoid calling out to client code in the middle of UITextView's processing, we call the delegate on the next run of the runloop, when all such internal processing is surely done. + dispatch_async(dispatch_get_main_queue(), ^{ + self->_delegateDidUpdateEnqueued = NO; + if ([self->_delegate respondsToSelector:@selector(editableTextNodeDidUpdateText:)]) + [self->_delegate editableTextNodeDidUpdateText:self]; + }); +} + +- (void)_delegateDidFinishEditing +{ + if ([_delegate respondsToSelector:@selector(editableTextNodeDidFinishEditing:)]) + [_delegate editableTextNodeDidFinishEditing:self]; +} + +#pragma mark - UIAccessibilityContainer + +- (NSInteger)accessibilityElementCount +{ + if (!self.isNodeLoaded) { + ASDisplayNodeFailAssert(@"Cannot access accessibilityElementCount since ASEditableTextNode is not loaded"); + return 0; + } + return 1; +} + +- (NSArray *)accessibilityElements +{ + if (!self.isNodeLoaded) { + ASDisplayNodeFailAssert(@"Cannot access accessibilityElements since ASEditableTextNode is not loaded"); + return @[]; + } + return @[self.textView]; +} + +- (id)accessibilityElementAtIndex:(NSInteger)index +{ + if (!self.isNodeLoaded) { + ASDisplayNodeFailAssert(@"Cannot access accessibilityElementAtIndex: since ASEditableTextNode is not loaded"); + return nil; + } + return self.textView; +} + +- (NSInteger)indexOfAccessibilityElement:(id)element +{ + return 0; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASExperimentalFeatures.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASExperimentalFeatures.h new file mode 100644 index 0000000..7729e2e --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASExperimentalFeatures.h @@ -0,0 +1,41 @@ +// +// ASExperimentalFeatures.h +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * A bit mask of features. Make sure to update configuration.json when you add entries. + */ +typedef NS_OPTIONS(NSUInteger, ASExperimentalFeatures) { + // If AS_ENABLE_TEXTNODE=0 or TextNode2 subspec is used this setting is a no op and ASTextNode2 + // will be used in all cases + ASExperimentalTextNode = 1 << 0, // exp_text_node + ASExperimentalInterfaceStateCoalescing = 1 << 1, // exp_interface_state_coalesce + ASExperimentalLayerDefaults = 1 << 2, // exp_infer_layer_defaults + ASExperimentalCollectionTeardown = 1 << 3, // exp_collection_teardown + ASExperimentalFramesetterCache = 1 << 4, // exp_framesetter_cache + ASExperimentalSkipClearData = 1 << 5, // exp_skip_clear_data + ASExperimentalDidEnterPreloadSkipASMLayout = 1 << 6, // exp_did_enter_preload_skip_asm_layout + ASExperimentalDispatchApply = 1 << 7, // exp_dispatch_apply + ASExperimentalDrawingGlobal = 1 << 8, // exp_drawing_global + ASExperimentalOptimizeDataControllerPipeline = 1 << 9, // exp_optimize_data_controller_pipeline + ASExperimentalDoNotCacheAccessibilityElements = 1 << 10, // exp_do_not_cache_accessibility_elements + ASExperimentalFeatureAll = 0xFFFFFFFF +}; + +/// Convert flags -> name array. +ASDK_EXTERN NSArray *ASExperimentalFeaturesGetNames(ASExperimentalFeatures flags); + +/// Convert name array -> flags. +ASDK_EXTERN ASExperimentalFeatures ASExperimentalFeaturesFromArray(NSArray *array); + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASExperimentalFeatures.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASExperimentalFeatures.mm new file mode 100644 index 0000000..fef124b --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASExperimentalFeatures.mm @@ -0,0 +1,50 @@ +// +// ASExperimentalFeatures.mm +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import + +NSArray *ASExperimentalFeaturesGetNames(ASExperimentalFeatures flags) +{ + NSArray *allNames = ASCreateOnce((@[@"exp_text_node", + @"exp_interface_state_coalesce", + @"exp_infer_layer_defaults", + @"exp_collection_teardown", + @"exp_framesetter_cache", + @"exp_skip_clear_data", + @"exp_did_enter_preload_skip_asm_layout", + @"exp_dispatch_apply", + @"exp_drawing_global", + @"exp_optimize_data_controller_pipeline", + @"exp_do_not_cache_accessibility_elements"])); + if (flags == ASExperimentalFeatureAll) { + return allNames; + } + + // Go through all names, testing each bit. + NSUInteger i = 0; + return ASArrayByFlatMapping(allNames, NSString *name, ({ + (flags & (1 << i++)) ? name : nil; + })); +} + +// O(N^2) but with counts this small, it's probably faster +// than hashing the strings. +ASExperimentalFeatures ASExperimentalFeaturesFromArray(NSArray *array) +{ + NSArray *allNames = ASExperimentalFeaturesGetNames(ASExperimentalFeatureAll); + ASExperimentalFeatures result = kNilOptions; + for (NSString *str in array) { + NSUInteger i = [allNames indexOfObject:str]; + if (i != NSNotFound) { + result |= (1 << i); + } + } + return result; +} diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASImageNode+AnimatedImage.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASImageNode+AnimatedImage.mm new file mode 100644 index 0000000..9716f85 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASImageNode+AnimatedImage.mm @@ -0,0 +1,400 @@ +// +// ASImageNode+AnimatedImage.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + +#define ASAnimatedImageDebug 0 + +@interface ASNetworkImageNode (Private) +- (void)_locked_setDefaultImage:(UIImage *)image; +@end + + +@implementation ASImageNode (AnimatedImage) + +#pragma mark - GIF support + +- (void)setAnimatedImage:(id )animatedImage +{ + ASLockScopeSelf(); + [self _locked_setAnimatedImage:animatedImage]; +} + +- (void)_locked_setAnimatedImage:(id )animatedImage +{ + DISABLED_ASAssertLocked(__instanceLock__); + + if (ASObjectIsEqual(_animatedImage, animatedImage) && (animatedImage == nil || animatedImage.playbackReady)) { + return; + } + + __block id previousAnimatedImage = _animatedImage; + + _animatedImage = animatedImage; + + if (animatedImage != nil) { + __weak ASImageNode *weakSelf = self; + if ([animatedImage respondsToSelector:@selector(setCoverImageReadyCallback:)]) { + animatedImage.coverImageReadyCallback = ^(UIImage *coverImage) { + // In this case the lock is already gone we have to call the unlocked version therefore + [weakSelf setCoverImageCompleted:coverImage]; + }; + } + + animatedImage.playbackReadyCallback = ^{ + // In this case the lock is already gone we have to call the unlocked version therefore + [weakSelf setShouldAnimate:YES]; + }; + if (animatedImage.playbackReady) { + [self _locked_setShouldAnimate:YES]; + } + } else { + // Clean up after ourselves. + + // Don't bother using a `_locked` version for setting contnst as it should be pretty safe calling it with + // reaquire the lock and would add overhead to introduce this version + self.contents = nil; + [self _locked_setCoverImage:nil]; + } + + // Push calling subclass to the next runloop cycle + // We have to schedule the block on the common modes otherwise the tracking mode will not be included and it will + // not fire e.g. while scrolling down + CFRunLoopPerformBlock(CFRunLoopGetCurrent(), kCFRunLoopCommonModes, ^(void) { + [self animatedImageSet:animatedImage previousAnimatedImage:previousAnimatedImage]; + }); + // Don't need to wakeup the runloop as the current is already running + // CFRunLoopWakeUp(runLoop); // Should not be necessary +} + +- (void)animatedImageSet:(id )newAnimatedImage previousAnimatedImage:(id )previousAnimatedImage +{ + // Subclass hook should not be called with the lock held + DISABLED_ASAssertUnlocked(__instanceLock__); + + // Subclasses may override +} + +- (id )animatedImage +{ + ASLockScopeSelf(); + return _animatedImage; +} + +- (void)setAnimatedImagePaused:(BOOL)animatedImagePaused +{ + ASLockScopeSelf(); + + _imageNodeFlags.animatedImagePaused = animatedImagePaused; + + [self _locked_setShouldAnimate:!animatedImagePaused]; +} + +- (BOOL)animatedImagePaused +{ + ASLockScopeSelf(); + return _imageNodeFlags.animatedImagePaused; +} + +- (void)setCoverImageCompleted:(UIImage *)coverImage +{ + if (ASInterfaceStateIncludesDisplay(self.interfaceState)) { + ASLockScopeSelf(); + [self _locked_setCoverImageCompleted:coverImage]; + } +} + +- (void)_locked_setCoverImageCompleted:(UIImage *)coverImage +{ + DISABLED_ASAssertLocked(__instanceLock__); + + _displayLinkLock.lock(); + BOOL setCoverImage = (_displayLink == nil) || _displayLink.paused; + _displayLinkLock.unlock(); + + if (setCoverImage) { + [self _locked_setCoverImage:coverImage]; + } +} + +- (void)setCoverImage:(UIImage *)coverImage +{ + ASLockScopeSelf(); + [self _locked_setCoverImage:coverImage]; +} + +- (void)_locked_setCoverImage:(UIImage *)coverImage +{ + DISABLED_ASAssertLocked(__instanceLock__); + + //If we're a network image node, we want to set the default image so + //that it will correctly be restored if it exits the range. +#if ASAnimatedImageDebug + NSLog(@"setting cover image: %p", self); +#endif + if ([self isKindOfClass:[ASNetworkImageNode class]]) { + [(ASNetworkImageNode *)self _locked_setDefaultImage:coverImage]; + } else if (_displayLink == nil || _displayLink.paused == YES) { + [self _locked_setImage:coverImage]; + } +} + +- (NSString *)animatedImageRunLoopMode +{ + AS::MutexLocker l(_displayLinkLock); + return _animatedImageRunLoopMode; +} + +- (void)setAnimatedImageRunLoopMode:(NSString *)runLoopMode +{ + AS::MutexLocker l(_displayLinkLock); + + if (runLoopMode == nil) { + runLoopMode = ASAnimatedImageDefaultRunLoopMode; + } + + if (_displayLink != nil) { + [_displayLink removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:_animatedImageRunLoopMode]; + [_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:runLoopMode]; + } + _animatedImageRunLoopMode = [runLoopMode copy]; +} + +- (void)setShouldAnimate:(BOOL)shouldAnimate +{ + ASLockScopeSelf(); + [self _locked_setShouldAnimate:shouldAnimate]; +} + +- (void)_locked_setShouldAnimate:(BOOL)shouldAnimate +{ + DISABLED_ASAssertLocked(__instanceLock__); + + // This test is explicitly done and not ASPerformBlockOnMainThread as this would perform the block immediately + // on main if called on main thread and we have to call methods locked or unlocked based on which thread we are on + if (ASDisplayNodeThreadIsMain()) { + if (shouldAnimate) { + [self _locked_startAnimating]; + } else { + [self _locked_stopAnimating]; + } + } else { + // We have to dispatch to the main thread and call the regular methods as the lock is already gone if the + // block is called + dispatch_async(dispatch_get_main_queue(), ^{ + if (shouldAnimate) { + [self startAnimating]; + } else { + [self stopAnimating]; + } + }); + } +} + +#pragma mark - Animating + +- (void)startAnimating +{ + ASDisplayNodeAssertMainThread(); + + ASLockScopeSelf(); + [self _locked_startAnimating]; +} + +- (void)_locked_startAnimating +{ + DISABLED_ASAssertLocked(__instanceLock__); + + // It should be safe to call self.interfaceState in this case as it will only grab the lock of the superclass + if (!ASInterfaceStateIncludesVisible(self.interfaceState)) { + return; + } + + if (_imageNodeFlags.animatedImagePaused) { + return; + } + + if (_animatedImage.playbackReady == NO) { + return; + } + +#if ASAnimatedImageDebug + NSLog(@"starting animation: %p", self); +#endif + + AS::MutexLocker l(_displayLinkLock); + if (_displayLink == nil) { + _playHead = 0; + _displayLink = [CADisplayLink displayLinkWithTarget:[ASWeakProxy weakProxyWithTarget:self] selector:@selector(displayLinkFired:)]; + _lastSuccessfulFrameIndex = NSUIntegerMax; + + [_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:_animatedImageRunLoopMode]; + } else { + _displayLink.paused = NO; + } +} + +- (void)stopAnimating +{ + ASDisplayNodeAssertMainThread(); + + ASLockScopeSelf(); + [self _locked_stopAnimating]; +} + +- (void)_locked_stopAnimating +{ + ASDisplayNodeAssertMainThread(); + DISABLED_ASAssertLocked(__instanceLock__); + +#if ASAnimatedImageDebug + NSLog(@"stopping animation: %p", self); +#endif + ASDisplayNodeAssertMainThread(); + AS::MutexLocker l(_displayLinkLock); + _displayLink.paused = YES; + self.lastDisplayLinkFire = 0; + + [_animatedImage clearAnimatedImageCache]; +} + +#pragma mark - ASDisplayNode + +- (void)didEnterVisibleState +{ + ASDisplayNodeAssertMainThread(); + [super didEnterVisibleState]; + + if (self.animatedImage.coverImageReady) { + [self setCoverImage:self.animatedImage.coverImage]; + } + if (self.animatedImage.playbackReady) { + [self startAnimating]; + } +} + +- (void)didExitVisibleState +{ + ASDisplayNodeAssertMainThread(); + [super didExitVisibleState]; + + [self stopAnimating]; +} + +- (void)didExitDisplayState +{ + ASDisplayNodeAssertMainThread(); +#if ASAnimatedImageDebug + NSLog(@"exiting display state: %p", self); +#endif + + // Check to see if we're an animated image before calling super in case someone + // decides they want to clear out the animatedImage itself on exiting the display + // state + BOOL isAnimatedImage = self.animatedImage != nil; + [super didExitDisplayState]; + + // Also clear out the contents we've set to be good citizens, we'll put it back in when we become visible. + if (isAnimatedImage) { + self.contents = nil; + [self setCoverImage:nil]; + } +} + +#pragma mark - Display Link Callbacks + +- (void)displayLinkFired:(CADisplayLink *)displayLink +{ + ASDisplayNodeAssertMainThread(); + + CFTimeInterval timeBetweenLastFire; + if (self.lastDisplayLinkFire == 0) { + timeBetweenLastFire = 0; + } else if (AS_AVAILABLE_IOS_TVOS(10, 10)) { + timeBetweenLastFire = displayLink.targetTimestamp - displayLink.timestamp; + } else { + timeBetweenLastFire = CACurrentMediaTime() - self.lastDisplayLinkFire; + } + self.lastDisplayLinkFire = CACurrentMediaTime(); + + _playHead += timeBetweenLastFire; + + while (_playHead > self.animatedImage.totalDuration) { + // Set playhead to zero to keep from showing different frames on different playthroughs + _playHead = 0; + _playedLoops++; + } + + if (self.animatedImage.loopCount > 0 && _playedLoops >= self.animatedImage.loopCount) { + [self stopAnimating]; + return; + } + + NSUInteger frameIndex = [self frameIndexAtPlayHeadPosition:_playHead]; + if (frameIndex == _lastSuccessfulFrameIndex) { + return; + } + CGImageRef frameImage = [self.animatedImage imageAtIndex:frameIndex]; + + if (frameImage == nil) { + //Pause the display link until we get a file ready notification + displayLink.paused = YES; + self.lastDisplayLinkFire = 0; + } else { + self.contents = (__bridge id)frameImage; + _lastSuccessfulFrameIndex = frameIndex; + [self displayDidFinish]; + } +} + +- (NSUInteger)frameIndexAtPlayHeadPosition:(CFTimeInterval)playHead +{ + ASDisplayNodeAssertMainThread(); + NSUInteger frameIndex = 0; + for (NSUInteger durationIndex = 0; durationIndex < self.animatedImage.frameCount; durationIndex++) { + playHead -= [self.animatedImage durationAtIndex:durationIndex]; + if (playHead < 0) { + return frameIndex; + } + frameIndex++; + } + + return frameIndex; +} + +@end + +#pragma mark - ASImageNode(AnimatedImageInvalidation) + +@implementation ASImageNode(AnimatedImageInvalidation) + +- (void)invalidateAnimatedImage +{ + AS::MutexLocker l(_displayLinkLock); +#if ASAnimatedImageDebug + if (_displayLink) { + NSLog(@"invalidating display link"); + } +#endif + [_displayLink invalidate]; + _displayLink = nil; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASImageNode.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASImageNode.h new file mode 100644 index 0000000..dcdb110 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASImageNode.h @@ -0,0 +1,212 @@ +// +// ASImageNode.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@protocol ASAnimatedImageProtocol; + +/** + * Image modification block. Use to transform an image before display. + * + * @param image The image to be displayed. + * + * @return A transformed image. + */ +typedef UIImage * _Nullable (^asimagenode_modification_block_t)(UIImage *image, ASPrimitiveTraitCollection traitCollection); + +/** + * @abstract Draws images. + * @discussion Supports cropping, tinting, and arbitrary image modification blocks. + */ +@interface ASImageNode : ASControlNode + +/** + * @abstract The image to display. + * + * @discussion The node will efficiently display stretchable images by using + * the layer's contentsCenter property. Non-stretchable images work too, of + * course. + */ +@property (nullable) UIImage *image; + +/** + @abstract The placeholder color. + */ +@property (nullable, copy) UIColor *placeholderColor; + +/** + * @abstract Indicates whether efficient cropping of the receiver is enabled. + * + * @discussion Defaults to YES. See -setCropEnabled:recropImmediately:inBounds: for more + * information. + */ +@property (getter=isCropEnabled) BOOL cropEnabled; + +/** + * @abstract Indicates that efficient downsizing of backing store should *not* be enabled. + * + * @discussion Defaults to NO. @see ASCroppedImageBackingSizeAndDrawRectInBounds for more + * information. + */ +@property BOOL forceUpscaling; + +/** + * @abstract Forces image to be rendered at forcedSize. + * @discussion Defaults to CGSizeZero to indicate that the forcedSize should not be used. + * Setting forcedSize to non-CGSizeZero will force the backing of the layer contents to + * be forcedSize (automatically adjusted for contentsSize). + */ +@property CGSize forcedSize; + +/** + * @abstract Enables or disables efficient cropping. + * + * @param cropEnabled YES to efficiently crop the receiver's contents such that + * contents outside of its bounds are not included; NO otherwise. + * + * @param recropImmediately If the receiver has an image, YES to redisplay the + * receiver immediately; NO otherwise. + * + * @param cropBounds The bounds into which the receiver will be cropped. Useful + * if bounds are to change in response to cropping (but have not yet done so). + * + * @discussion Efficient cropping is only performed when the receiver's view's + * contentMode is UIViewContentModeScaleAspectFill. By default, cropping is + * enabled. The crop alignment may be controlled via cropAlignmentFactor. + */ +- (void)setCropEnabled:(BOOL)cropEnabled recropImmediately:(BOOL)recropImmediately inBounds:(CGRect)cropBounds; + +/** + * @abstract A value that controls how the receiver's efficient cropping is aligned. + * + * @discussion This value defines a rectangle that is to be featured by the + * receiver. The rectangle is specified as a "unit rectangle," using + * fractions of the source image's width and height, e.g. CGRectMake(0.5, 0, + * 0.5, 1.0) will feature the full right half a photo. If the cropRect is + * empty, the content mode of the receiver will be used to determine its + * dimensions, and only the cropRect's origin will be used for positioning. The + * default value of this property is CGRectMake(0.5, 0.5, 0.0, 0.0). + */ +@property CGRect cropRect; + +/** + * @abstract An optional block which can perform drawing operations on image + * during the display phase. + * + * @discussion Can be used to add image effects (such as rounding, adding + * borders, or other pattern overlays) without extraneous display calls. + */ +@property (nullable) asimagenode_modification_block_t imageModificationBlock; + +/** + * @abstract Marks the receiver as needing display and performs a block after + * display has finished. + * + * @param displayCompletionBlock The block to be performed after display has + * finished. Its `canceled` property will be YES if display was prevented or + * canceled (via displaySuspended); NO otherwise. + * + * @discussion displayCompletionBlock will be performed on the main-thread. If + * `displaySuspended` is YES, `displayCompletionBlock` is will be + * performed immediately and `YES` will be passed for `canceled`. + */ +- (void)setNeedsDisplayWithCompletion:(nullable void (^)(BOOL canceled))displayCompletionBlock; + +#if TARGET_OS_TV +/** + * A bool to track if the current appearance of the node + * is the default focus appearance. + * Exposed here so the category methods can set it. + */ +@property BOOL isDefaultFocusAppearance; +#endif + +@end + +#if TARGET_OS_TV +@interface ASImageNode (tvOS) +@end +#endif + +@interface ASImageNode (AnimatedImage) + +/** + * @abstract The animated image to playback + * + * @discussion Set this to an object which conforms to ASAnimatedImageProtocol + * to have the ASImageNode playback an animated image. + * @warning this method should not be overridden, it may not always be called as + * another method is used internally. If you need to know when the animatedImage + * is set, override @c animatedImageSet:previousAnimatedImage: + */ +@property (nullable) id animatedImage; + +/** + * @abstract Pause the playback of an animated image. + * + * @discussion Set to YES to pause playback of an animated image and NO to resume + * playback. + */ +@property BOOL animatedImagePaused; + +/** + * @abstract The runloop mode used to animate the image. + * + * @discussion Defaults to NSRunLoopCommonModes. Another commonly used mode is NSDefaultRunLoopMode. + * Setting NSDefaultRunLoopMode will cause animation to pause while scrolling (if the ASImageNode is + * in a scroll view), which may improve scroll performance in some use cases. + */ +@property (copy) NSString *animatedImageRunLoopMode; + +/** + * @abstract Method called when animated image has been set + * + * @discussion This method is for subclasses to override so they can know if an animated image + * has been set on the node. + */ +- (void)animatedImageSet:(nullable id )newAnimatedImage previousAnimatedImage:(nullable id )previousAnimatedImage ASDISPLAYNODE_REQUIRES_SUPER; + +@end + +@interface ASImageNode (Unavailable) + +- (instancetype)initWithLayerBlock:(ASDisplayNodeLayerBlock)viewBlock didLoadBlock:(nullable ASDisplayNodeDidLoadBlock)didLoadBlock AS_UNAVAILABLE(); + +- (instancetype)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock didLoadBlock:(nullable ASDisplayNodeDidLoadBlock)didLoadBlock AS_UNAVAILABLE(); + +@end + +/** + * @abstract Image modification block that rounds (and optionally adds a border to) an image. + * + * @param borderWidth The width of the round border to draw, or zero if no border is desired. + * @param borderColor What colour border to draw. + * + * @see + * + * @return An ASImageNode image modification block. + */ +ASDK_EXTERN asimagenode_modification_block_t ASImageNodeRoundBorderModificationBlock(CGFloat borderWidth, UIColor * _Nullable borderColor); + +/** + * @abstract Image modification block that applies a tint color à la UIImage configured with + * renderingMode set to UIImageRenderingModeAlwaysTemplate. + * + * @param color The color to tint the image. + * + * @see + * + * @return An ASImageNode image modification block. + */ +ASDK_EXTERN asimagenode_modification_block_t ASImageNodeTintColorModificationBlock(UIColor *color); + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASImageNode.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASImageNode.mm new file mode 100644 index 0000000..95ac201 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASImageNode.mm @@ -0,0 +1,837 @@ +// +// ASImageNode.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + +// TODO: It would be nice to remove this dependency; it's the only subclass using more than +FrameworkSubclasses.h +#import + +typedef void (^ASImageNodeDrawParametersBlock)(ASWeakMapEntry *entry); + +@interface ASImageNodeDrawParameters : NSObject { +@package + UIImage *_image; + BOOL _opaque; + CGRect _bounds; + CGFloat _contentsScale; + UIColor *_backgroundColor; + UIColor *_tintColor; + UIViewContentMode _contentMode; + BOOL _cropEnabled; + BOOL _forceUpscaling; + CGSize _forcedSize; + CGRect _cropRect; + CGRect _cropDisplayBounds; + asimagenode_modification_block_t _imageModificationBlock; + ASDisplayNodeContextModifier _willDisplayNodeContentWithRenderingContext; + ASDisplayNodeContextModifier _didDisplayNodeContentWithRenderingContext; + ASImageNodeDrawParametersBlock _didDrawBlock; + ASPrimitiveTraitCollection _traitCollection; +} + +@end + +@implementation ASImageNodeDrawParameters + +@end + +/** + * Contains all data that is needed to generate the content bitmap. + */ +@interface ASImageNodeContentsKey : NSObject + +@property (nonatomic) UIImage *image; +@property CGSize backingSize; +@property CGRect imageDrawRect; +@property BOOL isOpaque; +@property (nonatomic, copy) UIColor *backgroundColor; +@property (nonatomic, copy) UIColor *tintColor; +@property (nonatomic) ASDisplayNodeContextModifier willDisplayNodeContentWithRenderingContext; +@property (nonatomic) ASDisplayNodeContextModifier didDisplayNodeContentWithRenderingContext; +@property (nonatomic) asimagenode_modification_block_t imageModificationBlock; +@property UIUserInterfaceStyle userInterfaceStyle API_AVAILABLE(tvos(10.0), ios(12.0)); +@end + +@implementation ASImageNodeContentsKey + +- (BOOL)isEqual:(id)object +{ + if (self == object) { + return YES; + } + + // Optimization opportunity: The `isKindOfClass` call here could be avoided by not using the NSObject `isEqual:` + // convention and instead using a custom comparison function that assumes all items are heterogeneous. + // However, profiling shows that our entire `isKindOfClass` expression is only ~1/40th of the total + // overheard of our caching, so it's likely not high-impact. + if ([object isKindOfClass:[ASImageNodeContentsKey class]]) { + ASImageNodeContentsKey *other = (ASImageNodeContentsKey *)object; + BOOL areKeysEqual = [_image isEqual:other.image] + && CGSizeEqualToSize(_backingSize, other.backingSize) + && CGRectEqualToRect(_imageDrawRect, other.imageDrawRect) + && _isOpaque == other.isOpaque + && [_backgroundColor isEqual:other.backgroundColor] + && [_tintColor isEqual:other.tintColor] + && _willDisplayNodeContentWithRenderingContext == other.willDisplayNodeContentWithRenderingContext + && _didDisplayNodeContentWithRenderingContext == other.didDisplayNodeContentWithRenderingContext + && _imageModificationBlock == other.imageModificationBlock; + if (AS_AVAILABLE_IOS_TVOS(12, 10)) { + // iOS 12, tvOS 10 and later (userInterfaceStyle only available in iOS12+) + areKeysEqual = areKeysEqual && _userInterfaceStyle == other.userInterfaceStyle; + } + return areKeysEqual; + } else { + return NO; + } +} + +- (NSUInteger)hash +{ +#pragma clang diagnostic push +#pragma clang diagnostic warning "-Wpadded" + struct { + NSUInteger imageHash; + CGSize backingSize; + CGRect imageDrawRect; + NSInteger isOpaque; + NSUInteger backgroundColorHash; + NSUInteger tintColorHash; + void *willDisplayNodeContentWithRenderingContext; + void *didDisplayNodeContentWithRenderingContext; + void *imageModificationBlock; +#pragma clang diagnostic pop + } data = { + _image.hash, + _backingSize, + _imageDrawRect, + _isOpaque, + _backgroundColor.hash, + _tintColor.hash, + (void *)_willDisplayNodeContentWithRenderingContext, + (void *)_didDisplayNodeContentWithRenderingContext, + (void *)_imageModificationBlock + }; + return ASHashBytes(&data, sizeof(data)); +} + +@end + +@implementation ASImageNode +{ +@private + UIImage *_image; + ASWeakMapEntry *_weakCacheEntry; // Holds a reference that keeps our contents in cache. + UIColor *_placeholderColor; + + void (^_displayCompletionBlock)(BOOL canceled); + + // Drawing + ASTextNode *_debugLabelNode; + + // Cropping. + CGSize _forcedSize; //Defaults to CGSizeZero, indicating no forced size. + CGRect _cropRect; // Defaults to CGRectMake(0.5, 0.5, 0, 0) + CGRect _cropDisplayBounds; // Defaults to CGRectNull +} + +@synthesize image = _image; +@synthesize imageModificationBlock = _imageModificationBlock; + +#pragma mark - Lifecycle + +- (instancetype)init +{ + if (!(self = [super init])) + return nil; + + // TODO can this be removed? + self.contentsScale = ASScreenScale(); + self.contentMode = UIViewContentModeScaleAspectFill; + self.opaque = NO; + self.clipsToBounds = YES; + + // If no backgroundColor is set to the image node and it's a subview of UITableViewCell, UITableView is setting + // the opaque value of all subviews to YES if highlighting / selection is happening and does not set it back to the + // initial value. With setting a explicit backgroundColor we can prevent that change. + self.backgroundColor = [UIColor clearColor]; + + _imageNodeFlags.cropEnabled = YES; + _imageNodeFlags.forceUpscaling = NO; + _imageNodeFlags.regenerateFromImageAsset = NO; + _cropRect = CGRectMake(0.5, 0.5, 0, 0); + _cropDisplayBounds = CGRectNull; + _placeholderColor = ASDisplayNodeDefaultPlaceholderColor(); + _animatedImageRunLoopMode = ASAnimatedImageDefaultRunLoopMode; + + return self; +} + +- (void)dealloc +{ + // Invalidate all components around animated images + [self invalidateAnimatedImage]; +} + +#pragma mark - Placeholder + +- (UIImage *)placeholderImage +{ + // FIXME: Replace this implementation with reusable CALayers that have .backgroundColor set. + // This would completely eliminate the memory and performance cost of the backing store. + CGSize size = self.calculatedSize; + if ((size.width * size.height) < CGFLOAT_EPSILON) { + return nil; + } + + __instanceLock__.lock(); + ASPrimitiveTraitCollection tc = _primitiveTraitCollection; + __instanceLock__.unlock(); + return ASGraphicsCreateImage(tc, size, NO, 1, nil, nil, ^{ + AS::MutexLocker l(__instanceLock__); + [_placeholderColor setFill]; + UIRectFill(CGRectMake(0, 0, size.width, size.height)); + }); +} + +#pragma mark - Layout and Sizing + +- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize +{ + const auto image = ASLockedSelf(_image); + + if (image == nil) { + return [super calculateSizeThatFits:constrainedSize]; + } + + return image.size; +} + +#pragma mark - Setter / Getter + +- (void)setImage:(UIImage *)image +{ + AS::MutexLocker l(__instanceLock__); + [self _locked_setImage:image]; +} + +- (void)_locked_setImage:(UIImage *)image +{ + DISABLED_ASAssertLocked(__instanceLock__); + if (ASObjectIsEqual(_image, image)) { + return; + } + + _image = image; + + if (image != nil) { + // We explicitly call setNeedsDisplay in this case, although we know setNeedsDisplay will be called with lock held. + // Therefore we have to be careful in methods that are involved with setNeedsDisplay to not run into a deadlock + [self setNeedsDisplay]; + + // For debugging purposes we don't care about locking for now + if ([ASImageNode shouldShowImageScalingOverlay] && _debugLabelNode == nil) { + // do not use ASPerformBlockOnMainThread here, if it performs the block synchronously it will continue + // holding the lock while calling addSubnode. + dispatch_async(dispatch_get_main_queue(), ^{ + self->_debugLabelNode = [[ASTextNode alloc] init]; + self->_debugLabelNode.layerBacked = YES; + [self addSubnode:self->_debugLabelNode]; + }); + } + } else { + self.contents = nil; + } +} + +- (UIImage *)image +{ + return ASLockedSelf(_image); +} + +- (UIColor *)placeholderColor +{ + return ASLockedSelf(_placeholderColor); +} + +- (void)setPlaceholderColor:(UIColor *)placeholderColor +{ + ASLockScopeSelf(); + if (ASCompareAssignCopy(_placeholderColor, placeholderColor)) { + _flags.placeholderEnabled = (placeholderColor != nil); + } +} + +#pragma mark - Drawing + +- (NSObject *)drawParametersForAsyncLayer:(_ASDisplayLayer *)layer +{ + ASImageNodeDrawParameters *drawParameters = [[ASImageNodeDrawParameters alloc] init]; + + { + ASLockScopeSelf(); + UIImage *drawImage = _image; + if (AS_AVAILABLE_IOS_TVOS(13, 10)) { + if (_imageNodeFlags.regenerateFromImageAsset && drawImage != nil) { + _imageNodeFlags.regenerateFromImageAsset = NO; + UITraitCollection *tc = [UITraitCollection traitCollectionWithUserInterfaceStyle:_primitiveTraitCollection.userInterfaceStyle]; + UIImage *generatedImage = [drawImage.imageAsset imageWithTraitCollection:tc]; + if ( generatedImage != nil ) { + drawImage = generatedImage; + } + } + } + + drawParameters->_image = drawImage; + drawParameters->_contentsScale = _contentsScaleForDisplay; + drawParameters->_cropEnabled = _imageNodeFlags.cropEnabled; + drawParameters->_forceUpscaling = _imageNodeFlags.forceUpscaling; + drawParameters->_forcedSize = _forcedSize; + drawParameters->_cropRect = _cropRect; + drawParameters->_cropDisplayBounds = _cropDisplayBounds; + drawParameters->_imageModificationBlock = _imageModificationBlock; + drawParameters->_willDisplayNodeContentWithRenderingContext = _willDisplayNodeContentWithRenderingContext; + drawParameters->_didDisplayNodeContentWithRenderingContext = _didDisplayNodeContentWithRenderingContext; + drawParameters->_traitCollection = _primitiveTraitCollection; + + // Hack for now to retain the weak entry that was created while this drawing happened + drawParameters->_didDrawBlock = ^(ASWeakMapEntry *entry){ + ASLockScopeSelf(); + self->_weakCacheEntry = entry; + }; + } + + // We need to unlock before we access the other accessor. + // Especially tintColor because it needs to walk up the view hierarchy + drawParameters->_bounds = [self threadSafeBounds]; + drawParameters->_opaque = self.opaque; + drawParameters->_backgroundColor = self.backgroundColor; + drawParameters->_contentMode = self.contentMode; + drawParameters->_tintColor = self.tintColor; + + return drawParameters; +} + ++ (UIImage *)displayWithParameters:(id)parameter isCancelled:(NS_NOESCAPE asdisplaynode_iscancelled_block_t)isCancelled +{ + ASImageNodeDrawParameters *drawParameter = (ASImageNodeDrawParameters *)parameter; + + UIImage *image = drawParameter->_image; + if (image == nil) { + return nil; + } + + CGRect drawParameterBounds = drawParameter->_bounds; + BOOL forceUpscaling = drawParameter->_forceUpscaling; + CGSize forcedSize = drawParameter->_forcedSize; + BOOL cropEnabled = drawParameter->_cropEnabled; + BOOL isOpaque = drawParameter->_opaque; + UIColor *backgroundColor = drawParameter->_backgroundColor; + UIColor *tintColor = drawParameter->_tintColor; + UIViewContentMode contentMode = drawParameter->_contentMode; + CGFloat contentsScale = drawParameter->_contentsScale; + CGRect cropDisplayBounds = drawParameter->_cropDisplayBounds; + CGRect cropRect = drawParameter->_cropRect; + asimagenode_modification_block_t imageModificationBlock = drawParameter->_imageModificationBlock; + ASDisplayNodeContextModifier willDisplayNodeContentWithRenderingContext = drawParameter->_willDisplayNodeContentWithRenderingContext; + ASDisplayNodeContextModifier didDisplayNodeContentWithRenderingContext = drawParameter->_didDisplayNodeContentWithRenderingContext; + + BOOL hasValidCropBounds = cropEnabled && !CGRectIsEmpty(cropDisplayBounds); + CGRect bounds = (hasValidCropBounds ? cropDisplayBounds : drawParameterBounds); + + + ASDisplayNodeAssert(contentsScale > 0, @"invalid contentsScale at display time"); + + // if the image is resizable, bail early since the image has likely already been configured + BOOL stretchable = !UIEdgeInsetsEqualToEdgeInsets(image.capInsets, UIEdgeInsetsZero); + if (stretchable) { + if (imageModificationBlock != NULL) { + image = imageModificationBlock(image, drawParameter->_traitCollection); + } + return image; + } + + CGSize imageSize = image.size; + CGSize imageSizeInPixels = CGSizeMake(imageSize.width * image.scale, imageSize.height * image.scale); + CGSize boundsSizeInPixels = CGSizeMake(std::floor(bounds.size.width * contentsScale), std::floor(bounds.size.height * contentsScale)); + + BOOL contentModeSupported = contentMode == UIViewContentModeScaleAspectFill || + contentMode == UIViewContentModeScaleAspectFit || + contentMode == UIViewContentModeCenter; + + CGSize backingSize = CGSizeZero; + CGRect imageDrawRect = CGRectZero; + + if (boundsSizeInPixels.width * contentsScale < 1.0f || boundsSizeInPixels.height * contentsScale < 1.0f || + imageSizeInPixels.width < 1.0f || imageSizeInPixels.height < 1.0f) { + return nil; + } + + + // If we're not supposed to do any cropping, just decode image at original size + if (!cropEnabled || !contentModeSupported) { + backingSize = imageSizeInPixels; + imageDrawRect = (CGRect){.size = backingSize}; + } else { + if (CGSizeEqualToSize(CGSizeZero, forcedSize) == NO) { + //scale forced size + forcedSize.width *= contentsScale; + forcedSize.height *= contentsScale; + } + ASCroppedImageBackingSizeAndDrawRectInBounds(imageSizeInPixels, + boundsSizeInPixels, + contentMode, + cropRect, + forceUpscaling, + forcedSize, + &backingSize, + &imageDrawRect); + } + + if (backingSize.width <= 0.0f || backingSize.height <= 0.0f || + imageDrawRect.size.width <= 0.0f || imageDrawRect.size.height <= 0.0f) { + return nil; + } + + ASImageNodeContentsKey *contentsKey = [[ASImageNodeContentsKey alloc] init]; + contentsKey.image = image; + contentsKey.backingSize = backingSize; + contentsKey.imageDrawRect = imageDrawRect; + contentsKey.isOpaque = isOpaque; + contentsKey.backgroundColor = backgroundColor; + contentsKey.tintColor = tintColor; + contentsKey.willDisplayNodeContentWithRenderingContext = willDisplayNodeContentWithRenderingContext; + contentsKey.didDisplayNodeContentWithRenderingContext = didDisplayNodeContentWithRenderingContext; + contentsKey.imageModificationBlock = imageModificationBlock; + + if (AS_AVAILABLE_IOS_TVOS(12, 10)) { + contentsKey.userInterfaceStyle = drawParameter->_traitCollection.userInterfaceStyle; + } + + if (isCancelled()) { + return nil; + } + + ASWeakMapEntry *entry = [self.class contentsForkey:contentsKey + drawParameters:parameter + isCancelled:isCancelled]; + // If nil, we were cancelled. + if (entry == nil) { + return nil; + } + + if (drawParameter->_didDrawBlock) { + drawParameter->_didDrawBlock(entry); + } + + return entry.value; +} + +static ASWeakMap *cache = nil; + ++ (ASWeakMapEntry *)contentsForkey:(ASImageNodeContentsKey *)key drawParameters:(id)drawParameters isCancelled:(asdisplaynode_iscancelled_block_t)isCancelled +{ + static dispatch_once_t onceToken; + static AS::Mutex *cacheLock = nil; + dispatch_once(&onceToken, ^{ + cacheLock = new AS::Mutex(); + }); + + { + AS::MutexLocker l(*cacheLock); + if (!cache) { + cache = [[ASWeakMap alloc] init]; + } + ASWeakMapEntry *entry = [cache entryForKey:key]; + if (entry != nil) { + return entry; + } + } + + // cache miss + UIImage *contents = [self createContentsForkey:key drawParameters:drawParameters isCancelled:isCancelled]; + if (contents == nil) { // If nil, we were cancelled + return nil; + } + + { + AS::MutexLocker l(*cacheLock); + return [cache setObject:contents forKey:key]; + } +} + ++ (UIImage *)createContentsForkey:(ASImageNodeContentsKey *)key drawParameters:(id)parameter isCancelled:(asdisplaynode_iscancelled_block_t)isCancelled +{ + // The following `ASGraphicsCreateImage` call will sometimes take take longer than 5ms on an + // A5 processor for a 400x800 backingSize. + // Check for cancellation before we call it. + if (isCancelled()) { + return nil; + } + + ASImageNodeDrawParameters *drawParameters = (ASImageNodeDrawParameters *)parameter; + + // Use contentsScale of 1.0 and do the contentsScale handling in boundsSizeInPixels so ASCroppedImageBackingSizeAndDrawRectInBounds + // will do its rounding on pixel instead of point boundaries + UIImage *result = ASGraphicsCreateImage(drawParameters->_traitCollection, key.backingSize, key.isOpaque, 1.0, key.image, isCancelled, ^{ + BOOL contextIsClean = YES; + + CGContextRef context = UIGraphicsGetCurrentContext(); + if (context && key.willDisplayNodeContentWithRenderingContext) { + key.willDisplayNodeContentWithRenderingContext(context, drawParameters); + contextIsClean = NO; + } + + // if view is opaque, fill the context with background color + if (key.isOpaque && key.backgroundColor) { + [key.backgroundColor setFill]; + UIRectFill({ .size = key.backingSize }); + contextIsClean = NO; + } + + // iOS 9 appears to contain a thread safety regression when drawing the same CGImageRef on + // multiple threads concurrently. In fact, instead of crashing, it appears to deadlock. + // The issue is present in Mac OS X El Capitan and has been seen hanging Pro apps like Adobe Premiere, + // as well as iOS games, and a small number of ASDK apps that provide the same image reference + // to many separate ASImageNodes. A workaround is to set .displaysAsynchronously = NO for the nodes + // that may get the same pointer for a given UI asset image, etc. + // FIXME: We should replace @synchronized here, probably using a global, locked NSMutableSet, and + // only if the object already exists in the set we should create a semaphore to signal waiting threads + // upon removal of the object from the set when the operation completes. + // Another option is to have ASDisplayNode+AsyncDisplay coordinate these cases, and share the decoded buffer. + // Details tracked in https://github.com/facebook/AsyncDisplayKit/issues/1068 + + UIImage *image = key.image; + BOOL canUseCopy = (contextIsClean || ASImageAlphaInfoIsOpaque(CGImageGetAlphaInfo(image.CGImage))); + CGBlendMode blendMode = canUseCopy ? kCGBlendModeCopy : kCGBlendModeNormal; + UIImageRenderingMode renderingMode = [image renderingMode]; + if (renderingMode == UIImageRenderingModeAlwaysTemplate && key.tintColor) { + [key.tintColor setFill]; + } + + @synchronized(image) { + [image drawInRect:key.imageDrawRect blendMode:blendMode alpha:1]; + } + + if (context && key.didDisplayNodeContentWithRenderingContext) { + key.didDisplayNodeContentWithRenderingContext(context, drawParameters); + } + }); + + // if the original image was stretchy, keep it stretchy + UIImage *originalImage = key.image; + if (!UIEdgeInsetsEqualToEdgeInsets(originalImage.capInsets, UIEdgeInsetsZero)) { + result = [result resizableImageWithCapInsets:originalImage.capInsets resizingMode:originalImage.resizingMode]; + } + + if (key.imageModificationBlock) { + result = key.imageModificationBlock(result, drawParameters->_traitCollection); + } + + return result; +} + +- (void)displayDidFinish +{ + [super displayDidFinish]; + + __instanceLock__.lock(); + UIImage *image = _image; + void (^displayCompletionBlock)(BOOL canceled) = _displayCompletionBlock; + BOOL shouldPerformDisplayCompletionBlock = (image && displayCompletionBlock); + + // Clear the ivar now. The block is retained and will be executed shortly. + if (shouldPerformDisplayCompletionBlock) { + _displayCompletionBlock = nil; + } + + BOOL hasDebugLabel = (_debugLabelNode != nil); + __instanceLock__.unlock(); + + // Update the debug label if necessary + if (hasDebugLabel) { + // For debugging purposes we don't care about locking for now + CGSize imageSize = image.size; + CGSize imageSizeInPixels = CGSizeMake(imageSize.width * image.scale, imageSize.height * image.scale); + CGSize boundsSizeInPixels = CGSizeMake(std::floor(self.bounds.size.width * self.contentsScale), std::floor(self.bounds.size.height * self.contentsScale)); + CGFloat pixelCountRatio = (imageSizeInPixels.width * imageSizeInPixels.height) / (boundsSizeInPixels.width * boundsSizeInPixels.height); + if (pixelCountRatio != 1.0) { + NSString *scaleString = [NSString stringWithFormat:@"%.2fx", pixelCountRatio]; + _debugLabelNode.attributedText = [[NSAttributedString alloc] initWithString:scaleString attributes:[self debugLabelAttributes]]; + _debugLabelNode.hidden = NO; + } else { + _debugLabelNode.hidden = YES; + _debugLabelNode.attributedText = nil; + } + } + + // If we've got a block to perform after displaying, do it. + if (shouldPerformDisplayCompletionBlock) { + displayCompletionBlock(NO); + } +} + +- (void)setNeedsDisplayWithCompletion:(void (^ _Nullable)(BOOL canceled))displayCompletionBlock +{ + if (self.displaySuspended) { + if (displayCompletionBlock) + displayCompletionBlock(YES); + return; + } + + // Stash the block and call-site queue. We'll invoke it in -displayDidFinish. + { + AS::MutexLocker l(__instanceLock__); + if (_displayCompletionBlock != displayCompletionBlock) { + _displayCompletionBlock = displayCompletionBlock; + } + } + + [self setNeedsDisplay]; +} + +- (void)_setNeedsDisplayOnTemplatedImages +{ + BOOL isTemplateImage = NO; + { + AS::MutexLocker l(__instanceLock__); + isTemplateImage = (_image.renderingMode == UIImageRenderingModeAlwaysTemplate); + } + + if (isTemplateImage) { + [self setNeedsDisplay]; + } +} + +- (void)tintColorDidChange +{ + [super tintColorDidChange]; + + [self _setNeedsDisplayOnTemplatedImages]; +} + +#pragma mark Interface State + +- (void)didEnterHierarchy +{ + [super didEnterHierarchy]; + + [self _setNeedsDisplayOnTemplatedImages]; +} + +- (void)clearContents +{ + [super clearContents]; + + AS::MutexLocker l(__instanceLock__); + _weakCacheEntry = nil; // release contents from the cache. +} + +#pragma mark - Cropping + +- (BOOL)isCropEnabled +{ + AS::MutexLocker l(__instanceLock__); + return _imageNodeFlags.cropEnabled; +} + +- (void)setCropEnabled:(BOOL)cropEnabled +{ + [self setCropEnabled:cropEnabled recropImmediately:NO inBounds:self.bounds]; +} + +- (void)setCropEnabled:(BOOL)cropEnabled recropImmediately:(BOOL)recropImmediately inBounds:(CGRect)cropBounds +{ + __instanceLock__.lock(); + if (_imageNodeFlags.cropEnabled == cropEnabled) { + __instanceLock__.unlock(); + return; + } + + _imageNodeFlags.cropEnabled = cropEnabled; + _cropDisplayBounds = cropBounds; + + UIImage *image = _image; + __instanceLock__.unlock(); + + // If we have an image to display, display it, respecting our recrop flag. + if (image != nil) { + ASPerformBlockOnMainThread(^{ + if (recropImmediately) + [self displayImmediately]; + else + [self setNeedsDisplay]; + }); + } +} + +- (CGRect)cropRect +{ + AS::MutexLocker l(__instanceLock__); + return _cropRect; +} + +- (void)setCropRect:(CGRect)cropRect +{ + { + AS::MutexLocker l(__instanceLock__); + if (CGRectEqualToRect(_cropRect, cropRect)) { + return; + } + + _cropRect = cropRect; + } + + // TODO: this logic needs to be updated to respect cropRect. + CGSize boundsSize = self.bounds.size; + CGSize imageSize = self.image.size; + + BOOL isCroppingImage = ((boundsSize.width < imageSize.width) || (boundsSize.height < imageSize.height)); + + // Re-display if we need to. + ASPerformBlockOnMainThread(^{ + if (self.nodeLoaded && self.contentMode == UIViewContentModeScaleAspectFill && isCroppingImage) + [self setNeedsDisplay]; + }); +} + +- (BOOL)forceUpscaling +{ + AS::MutexLocker l(__instanceLock__); + return _imageNodeFlags.forceUpscaling; +} + +- (void)setForceUpscaling:(BOOL)forceUpscaling +{ + AS::MutexLocker l(__instanceLock__); + _imageNodeFlags.forceUpscaling = forceUpscaling; +} + +- (CGSize)forcedSize +{ + AS::MutexLocker l(__instanceLock__); + return _forcedSize; +} + +- (void)setForcedSize:(CGSize)forcedSize +{ + AS::MutexLocker l(__instanceLock__); + _forcedSize = forcedSize; +} + +- (asimagenode_modification_block_t)imageModificationBlock +{ + AS::MutexLocker l(__instanceLock__); + return _imageModificationBlock; +} + +- (void)setImageModificationBlock:(asimagenode_modification_block_t)imageModificationBlock +{ + AS::MutexLocker l(__instanceLock__); + _imageModificationBlock = imageModificationBlock; +} + +#pragma mark - Debug + +- (void)layout +{ + [super layout]; + + if (_debugLabelNode) { + CGSize boundsSize = self.bounds.size; + CGSize debugLabelSize = [_debugLabelNode layoutThatFits:ASSizeRangeMake(CGSizeZero, boundsSize)].size; + CGPoint debugLabelOrigin = CGPointMake(boundsSize.width - debugLabelSize.width, + boundsSize.height - debugLabelSize.height); + _debugLabelNode.frame = (CGRect) {debugLabelOrigin, debugLabelSize}; + } +} + +- (NSDictionary *)debugLabelAttributes +{ + return @{ + NSFontAttributeName: [UIFont systemFontOfSize:15.0], + NSForegroundColorAttributeName: [UIColor redColor] + }; +} + +- (void)asyncTraitCollectionDidChangeWithPreviousTraitCollection:(ASPrimitiveTraitCollection)previousTraitCollection { + [super asyncTraitCollectionDidChangeWithPreviousTraitCollection:previousTraitCollection]; + + if (AS_AVAILABLE_IOS_TVOS(13, 10)) { + AS::MutexLocker l(__instanceLock__); + // update image if userInterfaceStyle was changed (dark mode) + if (_image != nil + && _primitiveTraitCollection.userInterfaceStyle != previousTraitCollection.userInterfaceStyle) { + _imageNodeFlags.regenerateFromImageAsset = YES; + } + } +} + + +@end + +#pragma mark - Extras + +asimagenode_modification_block_t ASImageNodeRoundBorderModificationBlock(CGFloat borderWidth, UIColor *borderColor) +{ + return ^(UIImage *originalImage, ASPrimitiveTraitCollection traitCollection) { + return ASGraphicsCreateImage(traitCollection, originalImage.size, NO, originalImage.scale, originalImage, nil, ^{ + UIBezierPath *roundOutline = [UIBezierPath bezierPathWithOvalInRect:(CGRect){CGPointZero, originalImage.size}]; + + // Make the image round + [roundOutline addClip]; + + // Draw the original image + [originalImage drawAtPoint:CGPointZero blendMode:kCGBlendModeCopy alpha:1]; + + // Draw a border on top. + if (borderWidth > 0.0) { + [borderColor setStroke]; + [roundOutline setLineWidth:borderWidth]; + [roundOutline stroke]; + } + }); + }; +} + +asimagenode_modification_block_t ASImageNodeTintColorModificationBlock(UIColor *color) +{ + return ^(UIImage *originalImage, ASPrimitiveTraitCollection traitCollection) { + UIImage *modifiedImage = ASGraphicsCreateImage(traitCollection, originalImage.size, NO, originalImage.scale, originalImage, nil, ^{ + // Set color and render template + [color setFill]; + UIImage *templateImage = [originalImage imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; + [templateImage drawAtPoint:CGPointZero blendMode:kCGBlendModeCopy alpha:1]; + }); + + // if the original image was stretchy, keep it stretchy + if (!UIEdgeInsetsEqualToEdgeInsets(originalImage.capInsets, UIEdgeInsetsZero)) { + modifiedImage = [modifiedImage resizableImageWithCapInsets:originalImage.capInsets resizingMode:originalImage.resizingMode]; + } + + return modifiedImage; + }; +} diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASLocking.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASLocking.h new file mode 100644 index 0000000..3e284dc --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASLocking.h @@ -0,0 +1,158 @@ +// +// ASLocking.h +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +#import + +NS_ASSUME_NONNULL_BEGIN + +#define kLockSetCapacity 32 + +/** + * An extension of NSLocking that supports -tryLock. + */ +@protocol ASLocking + +/// Try to take lock without blocking. Returns whether the lock was taken. +- (BOOL)tryLock; + +@end + +/** + * A set of locks acquired during ASLockSequence. + */ +typedef struct { + unsigned count; + CFTypeRef _Nullable locks[kLockSetCapacity]; +} ASLockSet; + +/** + * Declare a lock set that is automatically unlocked at the end of scope. + * + * We use this instead of a scope-locking macro because we want to be able + * to step through the lock sequence block in the debugger. + */ +#define ASScopedLockSet __unused ASLockSet __attribute__((cleanup(ASUnlockSet))) + +/** + * A block that attempts to add a lock to a lock sequence. + * Such a block is provided to the caller of ASLockSequence. + * + * Returns whether the lock was added. You should return + * NO from your lock sequence body if it returns NO. + * + * For instance, you might write `return addLock(l1) && addLock(l2)`. + * + * @param lock The lock to attempt to add. + * @return YES if the lock was added, NO otherwise. + */ +typedef BOOL(^ASAddLockBlock)(id lock); + +/** + * A block that attempts to lock multiple locks in sequence. + * Such a block is provided by the caller of ASLockSequence. + * + * The block may be run multiple times, if not all locks are immediately + * available. Therefore the block should be idempotent. + * + * The block should attempt to invoke addLock multiple times with + * different locks. It should return NO as soon as any addLock + * operation fails. + * + * For instance, you might write `return addLock(l1) && addLock(l2)`. + * + * @param addLock A block you can call to attempt to add a lock. + * @return YES if all locks were added, NO otherwise. + */ +typedef BOOL(^ASLockSequenceBlock)(NS_NOESCAPE ASAddLockBlock addLock); + +/** + * Unlock and release all of the locks in this lock set. + */ +NS_INLINE void ASUnlockSet(ASLockSet *lockSet) { + for (unsigned i = 0; i < lockSet->count; i++) { + CFTypeRef lock = lockSet->locks[i]; + [(__bridge id)lock unlock]; + CFRelease(lock); + } +} + +/** + * Take multiple locks "simultaneously," avoiding deadlocks + * caused by lock ordering. + * + * The block you provide should attempt to take a series of locks, + * using the provided `addLock` block. As soon as any addLock fails, + * you should return NO. + * + * For example: + * ASLockSequence(^(ASAddLockBlock addLock) ^{ + * return addLock(l0) && addLock(l1); + * }); + * + * Note: This function doesn't protect from lock ordering deadlocks if + * one of the locks is already locked (recursive.) Only locks taken + * inside this function are guaranteed not to cause a deadlock. + */ +NS_INLINE ASLockSet ASLockSequence(NS_NOESCAPE ASLockSequenceBlock body) +{ + __block ASLockSet locks = (ASLockSet){0, {}}; + BOOL (^addLock)(id) = ^(id obj) { + + // nil lock = ignore. + if (!obj) { + return YES; + } + + // If they go over capacity, assert and return YES. + // If we return NO, they will enter an infinite loop. + if (locks.count == kLockSetCapacity) { + ASDisplayNodeCFailAssert(@"Locking more than %d locks at once is not supported.", kLockSetCapacity); + return YES; + } + + if ([obj tryLock]) { + locks.locks[locks.count++] = (__bridge_retained CFTypeRef)obj; + return YES; + } + return NO; + }; + + /** + * Repeatedly try running their block, passing in our `addLock` + * until it succeeds. If it fails, unlock all and yield the thread + * to reduce spinning. + */ + while (true) { + if (body(addLock)) { + // Success + return locks; + } else { + ASUnlockSet(&locks); + locks.count = 0; + sched_yield(); + } + } +} + +/** + * These Foundation classes already implement -tryLock. + */ + +@interface NSLock (ASLocking) +@end + +@interface NSRecursiveLock (ASLocking) +@end + +@interface NSConditionLock (ASLocking) +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASMainThreadDeallocation.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASMainThreadDeallocation.h new file mode 100644 index 0000000..391b6bb --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASMainThreadDeallocation.h @@ -0,0 +1,56 @@ +// +// ASMainThreadDeallocation.h +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface NSObject (ASMainThreadIvarTeardown) + +/** + * Call this from -dealloc to schedule this instance's + * ivars for main thread deallocation as needed. + * + * This method includes a check for whether it's on the main thread, + * and it will do nothing in that case. + */ +- (void)scheduleIvarsForMainThreadDeallocation; + +@end + +@interface NSObject (ASNeedsMainThreadDeallocation) + +/** + * Override this property to indicate that instances of this + * class need to be deallocated on the main thread. + * You do not access this property yourself. + * + * The NSObject implementation returns YES if the class name has + * a prefix UI, AV, or CA. This property is also overridden to + * return fixed values for other common classes, such as UIImage, + * UIGestureRecognizer, and UIResponder. + */ +@property (class, readonly) BOOL needsMainThreadDeallocation; + +@end + +@interface NSProxy (ASNeedsMainThreadDeallocation) + +/** + * Override this property to indicate that instances of this + * class need to be deallocated on the main thread. + * You do not access this property yourself. + * + * The NSProxy implementation returns NO because + * proxies almost always hold weak references. + */ +@property (class, readonly) BOOL needsMainThreadDeallocation; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASMainThreadDeallocation.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASMainThreadDeallocation.mm new file mode 100644 index 0000000..8b732a5 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASMainThreadDeallocation.mm @@ -0,0 +1,202 @@ +// +// ASMainThreadDeallocation.mm +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import +#import +#import +#import + +@implementation NSObject (ASMainThreadIvarTeardown) + +- (void)scheduleIvarsForMainThreadDeallocation +{ + if (ASDisplayNodeThreadIsMain()) { + return; + } + + NSValue *ivarsObj = [[self class] _ivarsThatMayNeedMainDeallocation]; + + // Unwrap the ivar array + unsigned int count = 0; + // Will be unused if assertions are disabled. + __unused int scanResult = sscanf(ivarsObj.objCType, "[%u^{objc_ivar}]", &count); + ASDisplayNodeAssert(scanResult == 1, @"Unexpected type in NSValue: %s", ivarsObj.objCType); + Ivar ivars[count]; + [ivarsObj getValue:ivars]; + + for (Ivar ivar : ivars) { + id value = object_getIvar(self, ivar); + if (value == nil) { + continue; + } + + if ([object_getClass(value) needsMainThreadDeallocation]) { + os_log_debug(ASMainThreadDeallocationLog(), "%@: Trampolining ivar '%s' value %@ for main deallocation.", self, ivar_getName(ivar), value); + + // Release the ivar's reference before handing the object to the queue so we + // don't risk holding onto it longer than the queue does. + object_setIvar(self, ivar, nil); + + ASPerformMainThreadDeallocation(&value); + } else { + os_log_debug(ASMainThreadDeallocationLog(), "%@: Not trampolining ivar '%s' value %@.", self, ivar_getName(ivar), value); + } + } +} + +/** + * Returns an NSValue-wrapped array of all the ivars in this class or its superclasses + * up through ASDisplayNode, that we expect may need to be deallocated on main. + * + * This method caches its results. + * + * Result is of type NSValue<[Ivar]> + */ ++ (NSValue * _Nonnull)_ivarsThatMayNeedMainDeallocation NS_RETURNS_RETAINED +{ + static NSCache *ivarsCache; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + ivarsCache = [[NSCache alloc] init]; + }); + + NSValue *result = [ivarsCache objectForKey:self]; + if (result != nil) { + return result; + } + + // Cache miss. + unsigned int resultCount = 0; + static const int kMaxDealloc2MainIvarsPerClassTree = 64; + Ivar resultIvars[kMaxDealloc2MainIvarsPerClassTree]; + + // Get superclass results first. + Class c = class_getSuperclass(self); + if (c != [NSObject class]) { + NSValue *ivarsObj = [c _ivarsThatMayNeedMainDeallocation]; + // Unwrap the ivar array and append it to our working array + unsigned int count = 0; + // Will be unused if assertions are disabled. + __unused int scanResult = sscanf(ivarsObj.objCType, "[%u^{objc_ivar}]", &count); + ASDisplayNodeAssert(scanResult == 1, @"Unexpected type in NSValue: %s", ivarsObj.objCType); + ASDisplayNodeCAssert(resultCount + count < kMaxDealloc2MainIvarsPerClassTree, @"More than %d dealloc2main ivars are not supported. Count: %d", kMaxDealloc2MainIvarsPerClassTree, resultCount + count); + [ivarsObj getValue:resultIvars + resultCount]; + resultCount += count; + } + + // Now gather ivars from this particular class. + unsigned int allMyIvarsCount; + Ivar *allMyIvars = class_copyIvarList(self, &allMyIvarsCount); + + for (NSUInteger i = 0; i < allMyIvarsCount; i++) { + Ivar ivar = allMyIvars[i]; + + // NOTE: Would be great to exclude weak/unowned ivars, since we don't + // release them. Unfortunately the objc_ivar_management access is private and + // class_getWeakIvarLayout does not have a well-defined structure. + + const char *type = ivar_getTypeEncoding(ivar); + + if (type != NULL && strcmp(type, @encode(id)) == 0) { + // If it's `id` we have to include it just in case. + resultIvars[resultCount] = ivar; + resultCount += 1; + as_log_verbose(ASMainThreadDeallocationLog(), "%@: Marking ivar '%s' for possible main deallocation due to type id", self, ivar_getName(ivar)); + } else { + // If it's an ivar with a static type, check the type. + Class c = ASGetClassFromType(type); + if ([c needsMainThreadDeallocation]) { + resultIvars[resultCount] = ivar; + resultCount += 1; + as_log_verbose(ASMainThreadDeallocationLog(), "%@: Marking ivar '%s' for main deallocation due to class %@", self, ivar_getName(ivar), c); + } else { + as_log_verbose(ASMainThreadDeallocationLog(), "%@: Skipping ivar '%s' for main deallocation.", self, ivar_getName(ivar)); + } + } + } + free(allMyIvars); + + // Encode the type (array of Ivars) into a string and wrap it in an NSValue + char arrayType[32]; + snprintf(arrayType, 32, "[%u^{objc_ivar}]", resultCount); + result = [NSValue valueWithBytes:resultIvars objCType:arrayType]; + + [ivarsCache setObject:result forKey:self]; + return result; +} + +@end + +@implementation NSObject (ASNeedsMainThreadDeallocation) + ++ (BOOL)needsMainThreadDeallocation +{ + const auto name = class_getName(self); + if (0 == strncmp(name, "AV", 2) || 0 == strncmp(name, "UI", 2) || 0 == strncmp(name, "CA", 2)) { + return YES; + } + return NO; +} + +@end + +@implementation CALayer (ASNeedsMainThreadDeallocation) + ++ (BOOL)needsMainThreadDeallocation +{ + return YES; +} + +@end + +@implementation UIColor (ASNeedsMainThreadDeallocation) + ++ (BOOL)needsMainThreadDeallocation +{ + return NO; +} + +@end + +@implementation UIGestureRecognizer (ASNeedsMainThreadDeallocation) + ++ (BOOL)needsMainThreadDeallocation +{ + return YES; +} + +@end + +@implementation UIImage (ASNeedsMainThreadDeallocation) + ++ (BOOL)needsMainThreadDeallocation +{ + return NO; +} + +@end + +@implementation UIResponder (ASNeedsMainThreadDeallocation) + ++ (BOOL)needsMainThreadDeallocation +{ + return YES; +} + +@end + +@implementation NSProxy (ASNeedsMainThreadDeallocation) + ++ (BOOL)needsMainThreadDeallocation +{ + return NO; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASMapNode.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASMapNode.h new file mode 100644 index 0000000..9ddc2b6 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASMapNode.h @@ -0,0 +1,92 @@ +// +// ASMapNode.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +#if TARGET_OS_IOS && AS_USE_MAPKIT +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * Map Annotation options. + * The default behavior is to ignore the annotations' positions, use the region or options specified instead. + * Swift: to select the default behavior, use []. + */ +typedef NS_OPTIONS(NSUInteger, ASMapNodeShowAnnotationsOptions) +{ + /** The annotations' positions are ignored, use the region or options specified instead. */ + ASMapNodeShowAnnotationsOptionsIgnored = 0, + /** The annotations' positions are used to calculate the region to show in the map, equivalent to showAnnotations:animated. */ + ASMapNodeShowAnnotationsOptionsZoomed = 1 << 0, + /** This will only have an effect if combined with the Zoomed state with liveMap turned on.*/ + ASMapNodeShowAnnotationsOptionsAnimated = 1 << 1 +}; + +@interface ASMapNode : ASImageNode + +/** + The current options of ASMapNode. This can be set at any time and ASMapNode will animate the change.

This property may be set from a background thread before the node is loaded, and will automatically be applied to define the behavior of the static snapshot (if .liveMap = NO) or the internal MKMapView (otherwise).

Changes to the region and camera options will only be animated when when the liveMap mode is enabled, otherwise these options will be applied statically to the new snapshot.

The options object is used to specify properties even when the liveMap mode is enabled, allowing seamless transitions between the snapshot and liveMap (as well as back to the snapshot). + */ +@property (nonatomic) MKMapSnapshotOptions *options; + +/** The region is simply the sub-field on the options object. If the objects object is reset, + this will in effect be overwritten and become the value of the .region property on that object. + Defaults to MKCoordinateRegionForMapRect(MKMapRectWorld). + */ +@property (nonatomic) MKCoordinateRegion region; + +/** + This is the MKMapView that is the live map part of ASMapNode. This will be nil if .liveMap = NO. Note, MKMapView is *not* thread-safe. + */ +@property (nullable, readonly) MKMapView *mapView; + +/** + Set this to YES to turn the snapshot into an interactive MKMapView and vice versa. Defaults to NO. This property may be set on a background thread before the node is loaded, and will automatically be actioned, once the node is loaded. + */ +@property (getter=isLiveMap) BOOL liveMap; + +/** + @abstract Whether ASMapNode should automatically request a new map snapshot to correspond to the new node size. + @default Default value is YES. + @discussion If mapSize is set then this will be set to NO, since the size will be the same in all orientations. + */ +@property BOOL needsMapReloadOnBoundsChange; + +/** + Set the delegate of the MKMapView. This can be set even before mapView is created and will be set on the map in the case that the liveMap mode is engaged. + + If the live map view has been created, this may only be set on the main thread. + */ +@property (nonatomic, weak) id mapDelegate; + +/** + * @abstract The annotations to display on the map. + */ +@property (copy) NSArray> *annotations; + +/** + * @abstract This property specifies how to show the annotations. + * @default Default value is ASMapNodeShowAnnotationsIgnored + */ +@property ASMapNodeShowAnnotationsOptions showAnnotationsOptions; + +/** + * @abstract The block which should return annotation image for static map based on provided annotation. + * @discussion This block is executed on an arbitrary serial queue. If this block is nil, standard pin is used. + */ +@property (nullable) UIImage * _Nullable (^imageForStaticMapAnnotationBlock)(id annotation, CGPoint *centerOffset); + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASMapNode.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASMapNode.mm new file mode 100644 index 0000000..599851e --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASMapNode.mm @@ -0,0 +1,440 @@ +// +// ASMapNode.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#if TARGET_OS_IOS && AS_USE_MAPKIT + +#import + +#import +#import +#import +#import +#import +#import + +@interface ASMapNode() +{ + MKMapSnapshotter *_snapshotter; + BOOL _snapshotAfterLayout; + NSArray *_annotations; +} +@end + +@implementation ASMapNode + +@synthesize needsMapReloadOnBoundsChange = _needsMapReloadOnBoundsChange; +@synthesize mapDelegate = _mapDelegate; +@synthesize options = _options; +@synthesize liveMap = _liveMap; +@synthesize showAnnotationsOptions = _showAnnotationsOptions; + +#pragma mark - Lifecycle +- (instancetype)init +{ + if (!(self = [super init])) { + return nil; + } + self.backgroundColor = ASDisplayNodeDefaultPlaceholderColor(); + self.clipsToBounds = YES; + self.userInteractionEnabled = YES; + + _needsMapReloadOnBoundsChange = YES; + _liveMap = NO; + _annotations = @[]; + _showAnnotationsOptions = ASMapNodeShowAnnotationsOptionsIgnored; + return self; +} + +- (void)didLoad +{ + [super didLoad]; + if (self.isLiveMap) { + [self addLiveMap]; + } +} + +- (void)dealloc +{ + [self destroySnapshotter]; +} + +- (void)setLayerBacked:(BOOL)layerBacked +{ + ASDisplayNodeAssert(!self.isLiveMap, @"ASMapNode can not be layer backed whilst .liveMap = YES, set .liveMap = NO to use layer backing."); + [super setLayerBacked:layerBacked]; +} + +- (void)didEnterPreloadState +{ + [super didEnterPreloadState]; + ASPerformBlockOnMainThread(^{ + if (self.isLiveMap) { + [self addLiveMap]; + } else { + [self takeSnapshot]; + } + }); +} + +- (void)didExitPreloadState +{ + [super didExitPreloadState]; + ASPerformBlockOnMainThread(^{ + if (self.isLiveMap) { + [self removeLiveMap]; + } + }); +} + +#pragma mark - Settings + +- (BOOL)isLiveMap +{ + ASLockScopeSelf(); + return _liveMap; +} + +- (void)setLiveMap:(BOOL)liveMap +{ + ASDisplayNodeAssert(!self.isLayerBacked, @"ASMapNode can not use the interactive map feature whilst .isLayerBacked = YES, set .layerBacked = NO to use the interactive map feature."); + ASLockScopeSelf(); + if (liveMap == _liveMap) { + return; + } + _liveMap = liveMap; + if (self.nodeLoaded) { + liveMap ? [self addLiveMap] : [self removeLiveMap]; + } +} + +- (BOOL)needsMapReloadOnBoundsChange +{ + ASLockScopeSelf(); + return _needsMapReloadOnBoundsChange; +} + +- (void)setNeedsMapReloadOnBoundsChange:(BOOL)needsMapReloadOnBoundsChange +{ + ASLockScopeSelf(); + _needsMapReloadOnBoundsChange = needsMapReloadOnBoundsChange; +} + +- (MKMapSnapshotOptions *)options +{ + ASLockScopeSelf(); + if (!_options) { + _options = [[MKMapSnapshotOptions alloc] init]; + _options.region = MKCoordinateRegionForMapRect(MKMapRectWorld); + CGSize calculatedSize = self.calculatedSize; + if (!CGSizeEqualToSize(calculatedSize, CGSizeZero)) { + _options.size = calculatedSize; + } + } + return _options; +} + +- (void)setOptions:(MKMapSnapshotOptions *)options +{ + ASLockScopeSelf(); + if (!_options || ![options isEqual:_options]) { + _options = options; + if (self.isLiveMap) { + [self applySnapshotOptions]; + } else if (_snapshotter) { + [self destroySnapshotter]; + [self takeSnapshot]; + } + } +} + +- (MKCoordinateRegion)region +{ + return self.options.region; +} + +- (void)setRegion:(MKCoordinateRegion)region +{ + MKMapSnapshotOptions * options = [self.options copy]; + options.region = region; + self.options = options; +} + +- (id)mapDelegate +{ + return ASLockedSelf(_mapDelegate); +} + +- (void)setMapDelegate:(id)mapDelegate { + ASLockScopeSelf(); + _mapDelegate = mapDelegate; + + if (_mapView) { + ASDisplayNodeAssertMainThread(); + _mapView.delegate = mapDelegate; + } +} + +#pragma mark - Snapshotter + +- (void)takeSnapshot +{ + // If our size is zero, we want to avoid calling a default sized snapshot. Set _snapshotAfterLayout to YES + // so if layout changes in the future, we'll try snapshotting again. + ASLayout *layout = self.calculatedLayout; + if (layout == nil || CGSizeEqualToSize(CGSizeZero, layout.size)) { + _snapshotAfterLayout = YES; + return; + } + + _snapshotAfterLayout = NO; + + if (!_snapshotter) { + [self setUpSnapshotter]; + } + + if (_snapshotter.isLoading) { + return; + } + + __weak __typeof__(self) weakSelf = self; + [_snapshotter startWithQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) + completionHandler:^(MKMapSnapshot *snapshot, NSError *error) { + __typeof__(self) strongSelf = weakSelf; + if (!strongSelf) { + return; + } + + if (!error) { + UIImage *image = snapshot.image; + NSArray *annotations = strongSelf.annotations; + if (annotations.count > 0) { + // Only create a graphics context if we have annotations to draw. + // The MKMapSnapshotter is currently not capable of rendering annotations automatically. + + CGRect finalImageRect = CGRectMake(0, 0, image.size.width, image.size.height); + + image = ASGraphicsCreateImage(strongSelf.primitiveTraitCollection, image.size, YES, image.scale, image, nil, ^{ + [image drawAtPoint:CGPointZero]; + + UIImage *pinImage; + CGPoint pinCenterOffset = CGPointZero; + + // Get a standard annotation view pin if there is no custom annotation block. + if (!strongSelf.imageForStaticMapAnnotationBlock) { + pinImage = [strongSelf.class defaultPinImageWithCenterOffset:&pinCenterOffset]; + } + + for (id annotation in annotations) { + if (strongSelf.imageForStaticMapAnnotationBlock) { + // Get custom annotation image from custom annotation block. + pinImage = strongSelf.imageForStaticMapAnnotationBlock(annotation, &pinCenterOffset); + if (!pinImage) { + // just for case block returned nil, which can happen + pinImage = [strongSelf.class defaultPinImageWithCenterOffset:&pinCenterOffset]; + } + } + + CGPoint point = [snapshot pointForCoordinate:annotation.coordinate]; + if (CGRectContainsPoint(finalImageRect, point)) { + CGSize pinSize = pinImage.size; + point.x -= pinSize.width / 2.0; + point.y -= pinSize.height / 2.0; + point.x += pinCenterOffset.x; + point.y += pinCenterOffset.y; + [pinImage drawAtPoint:point]; + } + } + }); + } + + strongSelf.image = image; + } + }]; +} + ++ (UIImage *)defaultPinImageWithCenterOffset:(CGPoint *)centerOffset NS_RETURNS_RETAINED +{ + static MKAnnotationView *pin; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + pin = [[MKPinAnnotationView alloc] initWithAnnotation:nil reuseIdentifier:@""]; + }); + *centerOffset = pin.centerOffset; + return pin.image; +} + +- (void)setUpSnapshotter +{ + _snapshotter = [[MKMapSnapshotter alloc] initWithOptions:self.options]; +} + +- (void)destroySnapshotter +{ + [_snapshotter cancel]; + _snapshotter = nil; +} + +- (void)applySnapshotOptions +{ + MKMapSnapshotOptions *options = self.options; + [_mapView setCamera:options.camera animated:YES]; + [_mapView setRegion:options.region animated:YES]; + [_mapView setMapType:options.mapType]; + _mapView.showsBuildings = options.showsBuildings; + _mapView.showsPointsOfInterest = options.showsPointsOfInterest; +} + +#pragma mark - Actions +- (void)addLiveMap +{ + ASDisplayNodeAssertMainThread(); + if (!_mapView) { + __weak ASMapNode *weakSelf = self; + _mapView = [[MKMapView alloc] initWithFrame:CGRectZero]; + _mapView.delegate = weakSelf.mapDelegate; + [weakSelf applySnapshotOptions]; + [_mapView addAnnotations:_annotations]; + [weakSelf setNeedsLayout]; + [weakSelf.view addSubview:_mapView]; + + ASMapNodeShowAnnotationsOptions showAnnotationsOptions = self.showAnnotationsOptions; + if (showAnnotationsOptions & ASMapNodeShowAnnotationsOptionsZoomed) { + BOOL const animated = showAnnotationsOptions & ASMapNodeShowAnnotationsOptionsAnimated; + [_mapView showAnnotations:_mapView.annotations animated:animated]; + } + } +} + +- (void)removeLiveMap +{ + [_mapView removeFromSuperview]; + _mapView = nil; +} + +- (NSArray *)annotations +{ + ASLockScopeSelf(); + return _annotations; +} + +- (void)setAnnotations:(NSArray *)annotations +{ + annotations = [annotations copy] ? : @[]; + + ASLockScopeSelf(); + _annotations = annotations; + ASMapNodeShowAnnotationsOptions showAnnotationsOptions = self.showAnnotationsOptions; + if (self.isLiveMap) { + [_mapView removeAnnotations:_mapView.annotations]; + [_mapView addAnnotations:annotations]; + + if (showAnnotationsOptions & ASMapNodeShowAnnotationsOptionsZoomed) { + BOOL const animated = showAnnotationsOptions & ASMapNodeShowAnnotationsOptionsAnimated; + [_mapView showAnnotations:_mapView.annotations animated:animated]; + } + } else { + if (showAnnotationsOptions & ASMapNodeShowAnnotationsOptionsZoomed) { + self.region = [self regionToFitAnnotations:annotations]; + } + else { + [self takeSnapshot]; + } + } +} + +- (MKCoordinateRegion)regionToFitAnnotations:(NSArray> *)annotations +{ + if([annotations count] == 0) + return MKCoordinateRegionForMapRect(MKMapRectWorld); + + CLLocationCoordinate2D topLeftCoord = CLLocationCoordinate2DMake(-90, 180); + CLLocationCoordinate2D bottomRightCoord = CLLocationCoordinate2DMake(90, -180); + + for (id annotation in annotations) { + topLeftCoord = CLLocationCoordinate2DMake(std::fmax(topLeftCoord.latitude, annotation.coordinate.latitude), + std::fmin(topLeftCoord.longitude, annotation.coordinate.longitude)); + bottomRightCoord = CLLocationCoordinate2DMake(std::fmin(bottomRightCoord.latitude, annotation.coordinate.latitude), + std::fmax(bottomRightCoord.longitude, annotation.coordinate.longitude)); + } + + MKCoordinateRegion region = MKCoordinateRegionMake(CLLocationCoordinate2DMake(topLeftCoord.latitude - (topLeftCoord.latitude - bottomRightCoord.latitude) * 0.5, + topLeftCoord.longitude + (bottomRightCoord.longitude - topLeftCoord.longitude) * 0.5), + MKCoordinateSpanMake(std::fabs(topLeftCoord.latitude - bottomRightCoord.latitude) * 2, + std::fabs(bottomRightCoord.longitude - topLeftCoord.longitude) * 2)); + + return region; +} + +-(ASMapNodeShowAnnotationsOptions)showAnnotationsOptions { + return ASLockedSelf(_showAnnotationsOptions); +} + +-(void)setShowAnnotationsOptions:(ASMapNodeShowAnnotationsOptions)showAnnotationsOptions { + ASLockScopeSelf(); + _showAnnotationsOptions = showAnnotationsOptions; +} + +#pragma mark - Layout +- (void)setSnapshotSizeWithReloadIfNeeded:(CGSize)snapshotSize +{ + if (snapshotSize.height > 0 && snapshotSize.width > 0 && !CGSizeEqualToSize(self.options.size, snapshotSize)) { + _options.size = snapshotSize; + if (_snapshotter) { + [self destroySnapshotter]; + [self takeSnapshot]; + } + } +} + +- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize +{ + // FIXME: Need a better way to allow maps to take up the right amount of space in a layout (sizeRange, etc) + // These fallbacks protect against inheriting a constrainedSize that contains a CGFLOAT_MAX value. + if (!ASIsCGSizeValidForLayout(constrainedSize)) { + //ASDisplayNodeAssert(NO, @"Invalid width or height in ASMapNode"); + constrainedSize = CGSizeZero; + } + [self setSnapshotSizeWithReloadIfNeeded:constrainedSize]; + return constrainedSize; +} + +- (void)calculatedLayoutDidChange +{ + [super calculatedLayoutDidChange]; + + if (_snapshotAfterLayout) { + [self takeSnapshot]; + } +} + +// -layout isn't usually needed over -layoutSpecThatFits, but this way we can avoid a needless node wrapper for MKMapView. +- (void)layout +{ + [super layout]; + if (self.isLiveMap) { + _mapView.frame = CGRectMake(0.0f, 0.0f, self.calculatedSize.width, self.calculatedSize.height); + } else { + // If our bounds.size is different from our current snapshot size, then let's request a new image from MKMapSnapshotter. + if (_needsMapReloadOnBoundsChange) { + [self setSnapshotSizeWithReloadIfNeeded:self.bounds.size]; + // FIXME: Adding a check for Preload here seems to cause intermittent map load failures, but shouldn't. + // if (ASInterfaceStateIncludesPreload(self.interfaceState)) { + } + } +} + +- (BOOL)supportsLayerBacking +{ + return NO; +} + +@end +#endif // TARGET_OS_IOS && AS_USE_MAPKIT diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASMultiplexImageNode.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASMultiplexImageNode.h new file mode 100644 index 0000000..72a913a --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASMultiplexImageNode.h @@ -0,0 +1,282 @@ +// +// ASMultiplexImageNode.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +#if AS_USE_PHOTOS +#import +#else +@class PHAsset; +@class PHImageManager; +@class PHImageRequestOptions; +#endif + +NS_ASSUME_NONNULL_BEGIN + +@protocol ASMultiplexImageNodeDelegate; +@protocol ASMultiplexImageNodeDataSource; + +typedef id ASImageIdentifier; + +ASDK_EXTERN NSString *const ASMultiplexImageNodeErrorDomain; + +/** + * ASMultiplexImageNode error codes. + */ +typedef NS_ENUM(NSUInteger, ASMultiplexImageNodeErrorCode) { + /** + * Indicates that the data source didn't provide a source for an image identifier. + */ + ASMultiplexImageNodeErrorCodeNoSourceForImage = 0, + + /** + * Indicates that the best image identifier changed before a download for a worse identifier began. + */ + ASMultiplexImageNodeErrorCodeBestImageIdentifierChanged, + + /** + * Indicates that the Photos framework returned no image and no error. + * This may happen if the image is in iCloud and the user did not specify `allowsNetworkAccess` + * in their image request. + */ + ASMultiplexImageNodeErrorCodePhotosImageManagerFailedWithoutError, + + /** + * Indicates that the image node could not retrieve the PHAsset for a given asset identifier. + * This typically means that the user has not given Photos framework permissions yet or the asset + * has been removed from the device. + */ + ASMultiplexImageNodeErrorCodePHAssetIsUnavailable +}; + + +/** + * @abstract ASMultiplexImageNode is an image node that can load and display multiple versions of an image. For + * example, it can display a low-resolution version of an image while the high-resolution version is loading. + * + * @discussion ASMultiplexImageNode begins loading images when its resource can either return a UIImage directly, or a URL the image node should load. + */ +@interface ASMultiplexImageNode : ASImageNode + +/** + * @abstract The designated initializer. + * @param cache The object that implements a cache of images for the image node. + * @param downloader The object that implements image downloading for the image node. + * @discussion If `cache` is nil, the receiver will not attempt to retrieve images from a cache before downloading them. + * @return An initialized ASMultiplexImageNode. + */ +- (instancetype)initWithCache:(nullable id)cache downloader:(nullable id)downloader NS_DESIGNATED_INITIALIZER; + +/** + * @abstract The delegate, which must conform to the protocol. + */ +@property (nonatomic, weak) id delegate; + +/** + * @abstract The data source, which must conform to the protocol. + * @discussion This value is required for ASMultiplexImageNode to load images. + */ +@property (nonatomic, weak) id dataSource; + +/** + * @abstract Whether the receiver should download more than just its highest-quality image. Defaults to NO. + * + * @discussion ASMultiplexImageNode immediately loads and displays the first image specified in (its + * highest-quality image). If that image is not immediately available or cached, the node can download and display + * lesser-quality images. Set `downloadsIntermediateImages` to YES to enable this behaviour. + */ +@property (nonatomic) BOOL downloadsIntermediateImages; + +/** + * @abstract An array of identifiers representing various versions of an image for ASMultiplexImageNode to display. + * + * @discussion An identifier can be any object that conforms to NSObject and NSCopying. The array should be in + * decreasing order of image quality -- that is, the first identifier in the array represents the best version. + * + * @see for more information on the image loading process. + */ +@property (nonatomic, copy) NSArray *imageIdentifiers; + +/** + * @abstract Notify the receiver SSAA that its data source has new UIImages or NSURLs available for . + * + * @discussion If a higher-quality image than is currently displayed is now available, it will be loaded. + */ +- (void)reloadImageIdentifierSources; + +/** + * @abstract The identifier for the last image that the receiver loaded, or nil. + * + * @discussion This value may differ from if the image hasn't yet been displayed. + */ +@property (nullable, nonatomic, readonly) ASImageIdentifier loadedImageIdentifier; + +/** + * @abstract The identifier for the image that the receiver is currently displaying, or nil. + */ +@property (nullable, nonatomic, readonly) ASImageIdentifier displayedImageIdentifier; + +/** + * @abstract If the downloader implements progressive image rendering and this value is YES progressive renders of the + * image will be displayed as the image downloads. Regardless of this properties value, progress renders will + * only occur when the node is visible. Defaults to YES. + */ +@property (nonatomic) BOOL shouldRenderProgressImages; + +/** + * @abstract The image manager that this image node should use when requesting images from the Photos framework. If this is `nil` (the default), then `PHImageManager.defaultManager` is used. + + * @see `+[NSURL URLWithAssetLocalIdentifier:targetSize:contentMode:options:]` below. + */ +@property (nullable, nonatomic) PHImageManager *imageManager API_AVAILABLE(ios(8.0), tvos(10.0)); + +@end + + +#pragma mark - +/** + * The methods declared by the ASMultiplexImageNodeDelegate protocol allow the adopting delegate to respond to + * notifications such as began, progressed and finished downloading, updated and displayed an image. + */ +@protocol ASMultiplexImageNodeDelegate + +@optional +/** + * @abstract Notification that the image node began downloading an image. + * @param imageNode The sender. + * @param imageIdentifier The identifier for the image that is downloading. + */ +- (void)multiplexImageNode:(ASMultiplexImageNode *)imageNode didStartDownloadOfImageWithIdentifier:(id)imageIdentifier; + +/** + * @abstract Notification that the image node's download progressed. + * @param imageNode The sender. + * @param downloadProgress The progress of the download. Value is between 0.0 and 1.0. + * @param imageIdentifier The identifier for the image that is downloading. + */ +- (void)multiplexImageNode:(ASMultiplexImageNode *)imageNode + didUpdateDownloadProgress:(CGFloat)downloadProgress + forImageWithIdentifier:(ASImageIdentifier)imageIdentifier; + +/** + * @abstract Notification that the image node's download has finished. + * @param imageNode The sender. + * @param imageIdentifier The identifier for the image that finished downloading. + * @param error The error that occurred while downloading, if one occurred; nil otherwise. + */ +- (void)multiplexImageNode:(ASMultiplexImageNode *)imageNode +didFinishDownloadingImageWithIdentifier:(ASImageIdentifier)imageIdentifier + error:(nullable NSError *)error; + +/** + * @abstract Notification that the image node's image was updated. + * @param imageNode The sender. + * @param image The new image, ready for display. + * @param imageIdentifier The identifier for `image`. + * @param previousImage The old, previously-loaded image. + * @param previousImageIdentifier The identifier for `previousImage`. + * @note This method does not indicate that `image` has been displayed. + * @see <[ASMultiplexImageNodeDelegate multiplexImageNode:didDisplayUpdatedImage:withIdentifier:]>. + */ +- (void)multiplexImageNode:(ASMultiplexImageNode *)imageNode + didUpdateImage:(nullable UIImage *)image + withIdentifier:(nullable ASImageIdentifier)imageIdentifier + fromImage:(nullable UIImage *)previousImage + withIdentifier:(nullable ASImageIdentifier)previousImageIdentifier; + +/** + * @abstract Notification that the image node displayed a new image. + * @param imageNode The sender. + * @param image The new image, now being displayed. + * @param imageIdentifier The identifier for `image`. + * @discussion This method is only called when `image` changes, and not on subsequent redisplays of the same image. + */ +- (void)multiplexImageNode:(ASMultiplexImageNode *)imageNode + didDisplayUpdatedImage:(nullable UIImage *)image + withIdentifier:(nullable ASImageIdentifier)imageIdentifier; + +/** + * @abstract Notification that the image node finished displaying an image. + * @param imageNode The sender. + * @discussion This method is called every time an image is displayed, whether or not it has changed. + */ +- (void)multiplexImageNodeDidFinishDisplay:(ASMultiplexImageNode *)imageNode; + +@end + + +#pragma mark - +/** + * The ASMultiplexImageNodeDataSource protocol is adopted by an object that provides the multiplex image node, + * for each image identifier, an image or a URL the image node should load. + */ +@protocol ASMultiplexImageNodeDataSource + +@optional +/** + * @abstract An image for the specified identifier. + * @param imageNode The sender. + * @param imageIdentifier The identifier for the image that should be returned. + * @discussion If the image is already available to the data source, this method should be used in lieu of providing the + * URL to the image via -multiplexImageNode:URLForImageIdentifier:. + * @return A UIImage corresponding to `imageIdentifier`, or nil if none is available. + */ +- (nullable UIImage *)multiplexImageNode:(ASMultiplexImageNode *)imageNode imageForImageIdentifier:(ASImageIdentifier)imageIdentifier; + +/** + * @abstract An image URL for the specified identifier. + * @param imageNode The sender. + * @param imageIdentifier The identifier for the image that will be downloaded. + * @discussion Supported URLs include HTTP, HTTPS, AssetsLibrary, and FTP URLs as well as Photos framework URLs (see note). + * + * If the image is already available to the data source, it should be provided via <[ASMultiplexImageNodeDataSource + * multiplexImageNode:imageForImageIdentifier:]> instead. + * @return An NSURL for the image identified by `imageIdentifier`, or nil if none is available. + * @see `+[NSURL URLWithAssetLocalIdentifier:targetSize:contentMode:options:]` below. + */ +- (nullable NSURL *)multiplexImageNode:(ASMultiplexImageNode *)imageNode URLForImageIdentifier:(ASImageIdentifier)imageIdentifier; + +/** + * @abstract A PHAsset for the specific asset local identifier + * @param imageNode The sender. + * @param assetLocalIdentifier The local identifier for a PHAsset that this image node is loading. + * + * @discussion This optional method can improve image performance if your data source already has the PHAsset available. + * If this method is not implemented, or returns nil, the image node will request the asset from the Photos framework. + * @note This method may be called from any thread. + * @return A PHAsset corresponding to `assetLocalIdentifier`, or nil if none is available. + */ +- (nullable PHAsset *)multiplexImageNode:(ASMultiplexImageNode *)imageNode assetForLocalIdentifier:(NSString *)assetLocalIdentifier API_AVAILABLE(ios(8.0), tvos(10.0)); +@end + +#pragma mark - + +#if AS_USE_PHOTOS + +@interface NSURL (ASPhotosFrameworkURLs) + +/** + * @abstract Create an NSURL that specifies an image from the Photos framework. + * + * @discussion When implementing `-multiplexImageNode:URLForImageIdentifier:`, you can return a URL + * created by this method and the image node will attempt to load the image from the Photos framework. + * @note The `synchronous` flag in `options` is ignored. + * @note The `Opportunistic` delivery mode is not supported and will be treated as `HighQualityFormat`. + */ ++ (NSURL *)URLWithAssetLocalIdentifier:(NSString *)assetLocalIdentifier + targetSize:(CGSize)targetSize + contentMode:(PHImageContentMode)contentMode + options:(PHImageRequestOptions *)options NS_RETURNS_RETAINED AS_WARN_UNUSED_RESULT API_AVAILABLE(ios(8.0), tvos(10.0)); + +@end + +#endif + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASMultiplexImageNode.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASMultiplexImageNode.mm new file mode 100644 index 0000000..55de28d --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASMultiplexImageNode.mm @@ -0,0 +1,940 @@ +// +// ASMultiplexImageNode.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#if TARGET_OS_IOS && AS_USE_ASSETS_LIBRARY +#import +#endif + +#import +#import +#import +#import +#import +#import +#import + +#if AS_USE_PHOTOS +#import +#endif + +#if AS_PIN_REMOTE_IMAGE +#import +#else +#import +#endif + +using AS::MutexLocker; + +NSString *const ASMultiplexImageNodeErrorDomain = @"ASMultiplexImageNodeErrorDomain"; + +#if AS_USE_ASSETS_LIBRARY +static NSString *const kAssetsLibraryURLScheme = @"assets-library"; +#endif + +/** + @abstract Signature for the block to be performed after an image has loaded. + @param image The image that was loaded, or nil if no image was loaded. + @param imageIdentifier The identifier of the image that was loaded, or nil if no image was loaded. + @param error An error describing why an image couldn't be loaded, if it failed to load; nil otherwise. + */ +typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdentifier, NSError *error); + +@interface ASMultiplexImageNode () +{ +@private + // Core. + id _cache; + + id _downloader; + struct { + unsigned int downloaderImplementsSetProgress:1; + unsigned int downloaderImplementsSetPriority:1; + unsigned int downloaderImplementsDownloadWithPriority:1; + } _downloaderFlags; + + __weak id _delegate; + struct { + unsigned int downloadStart:1; + unsigned int downloadProgress:1; + unsigned int downloadFinish:1; + unsigned int updatedImageDisplayFinish:1; + unsigned int updatedImage:1; + unsigned int displayFinish:1; + } _delegateFlags; + + __weak id _dataSource; + struct { + unsigned int image:1; + unsigned int URL:1; + unsigned int asset:1; + } _dataSourceFlags; + + // Image flags. + BOOL _downloadsIntermediateImages; // Defaults to NO. + AS::Mutex _imageIdentifiersLock; + NSArray *_imageIdentifiers; + id _loadedImageIdentifier; + id _loadingImageIdentifier; + id _displayedImageIdentifier; + __weak NSOperation *_phImageRequestOperation; + + // Networking. + AS::RecursiveMutex _downloadIdentifierLock; + id _downloadIdentifier; + + // Properties + BOOL _shouldRenderProgressImages; + + //set on init only + BOOL _cacheSupportsClearing; +} + +//! @abstract Read-write redeclaration of property declared in ASMultiplexImageNode.h. +@property (nonatomic, copy) id loadedImageIdentifier; + +//! @abstract The image identifier that's being loaded by _loadNextImageWithCompletion:. +@property (nonatomic, copy) id loadingImageIdentifier; + +/** + @abstract Returns the next image identifier that should be downloaded. + @discussion This method obeys and reflects the value of `downloadsIntermediateImages`. + @result The next image identifier, from `_imageIdentifiers`, that should be downloaded, or nil if no image should be downloaded next. + */ +- (id)_nextImageIdentifierToDownload; + +/** + @abstract Returns the best image that is immediately available from our datasource without downloading or hitting the cache. + @param imageIdentifierOut Upon return, the image identifier for the returned image; nil otherwise. + @discussion This method exclusively uses the data source's -multiplexImageNode:imageForIdentifier: method to return images. It does not fetch from the cache or kick off downloading. + @result The best UIImage available immediately; nil if no image is immediately available. + */ +- (UIImage *)_bestImmediatelyAvailableImageFromDataSource:(id *)imageIdentifierOut; + +/** + @abstract Loads and displays the next image in the receiver's loading sequence. + @discussion This method obeys `downloadsIntermediateImages`. This method has no effect if nothing further should be loaded, as indicated by `_nextImageIdentifierToDownload`. This method will load the next image from the data-source, if possible; otherwise, the session's image cache will be queried for the desired image, and as a last resort, the image will be downloaded. + */ +- (void)_loadNextImage; + +/** + @abstract Fetches the image corresponding to the given imageIdentifier from the given URL from the session's image cache. + @param imageIdentifier The identifier for the image to be fetched. May not be nil. + @param imageURL The URL of the image to fetch. May not be nil. + @param completionBlock The block to be performed when the image has been fetched from the cache, if possible. May not be nil. + @discussion This method queries both the session's in-memory and on-disk caches (with preference for the in-memory cache). + */ +- (void)_fetchImageWithIdentifierFromCache:(id)imageIdentifier URL:(NSURL *)imageURL completion:(void (^)(UIImage *image))completionBlock; + +#if TARGET_OS_IOS && AS_USE_ASSETS_LIBRARY +/** + @abstract Loads the image corresponding to the given assetURL from the device's Assets Library. + @param imageIdentifier The identifier for the image to be loaded. May not be nil. + @param assetURL The assets-library URL (e.g., "assets-library://identifier") of the image to load, from ALAsset. May not be nil. + @param completionBlock The block to be performed when the image has been loaded, if possible. May not be nil. + */ +- (void)_loadALAssetWithIdentifier:(id)imageIdentifier URL:(NSURL *)assetURL completion:(void (^)(UIImage *image, NSError *error))completionBlock; +#endif + +#if AS_USE_PHOTOS +/** + @abstract Loads the image corresponding to the given image request from the Photos framework. + @param imageIdentifier The identifier for the image to be loaded. May not be nil. + @param request The photos image request to load. May not be nil. + @param completionBlock The block to be performed when the image has been loaded, if possible. May not be nil. + */ +- (void)_loadPHAssetWithRequest:(ASPhotosFrameworkImageRequest *)request identifier:(id)imageIdentifier completion:(void (^)(UIImage *image, NSError *error))completionBlock API_AVAILABLE(ios(8.0), tvos(10.0)); +#endif + +/** + @abstract Downloads the image corresponding to the given imageIdentifier from the given URL. + @param imageIdentifier The identifier for the image to be downloaded. May not be nil. + @param imageURL The URL of the image to downloaded. May not be nil. + @param completionBlock The block to be performed when the image has been downloaded, if possible. May not be nil. + */ +- (void)_downloadImageWithIdentifier:(id)imageIdentifier URL:(NSURL *)imageURL completion:(void (^)(UIImage *image, NSError *error))completionBlock; + +@end + +@implementation ASMultiplexImageNode + +#pragma mark - Getting Started / Tearing Down +- (instancetype)initWithCache:(id)cache downloader:(id)downloader +{ + if (!(self = [super init])) + return nil; + + _cache = (id)cache; + _downloader = (id)downloader; + + _downloaderFlags.downloaderImplementsSetProgress = [downloader respondsToSelector:@selector(setProgressImageBlock:callbackQueue:withDownloadIdentifier:)]; + _downloaderFlags.downloaderImplementsSetPriority = [downloader respondsToSelector:@selector(setPriority:withDownloadIdentifier:)]; + _downloaderFlags.downloaderImplementsDownloadWithPriority = [downloader respondsToSelector:@selector(downloadImageWithURL:priority:callbackQueue:downloadProgress:completion:)]; + + _cacheSupportsClearing = [cache respondsToSelector:@selector(clearFetchedImageFromCacheWithURL:)]; + + _shouldRenderProgressImages = YES; + + self.shouldBypassEnsureDisplay = YES; + + return self; +} + +- (instancetype)init +{ +#if AS_PIN_REMOTE_IMAGE + return [self initWithCache:[ASPINRemoteImageDownloader sharedDownloader] downloader:[ASPINRemoteImageDownloader sharedDownloader]]; +#else + return [self initWithCache:nil downloader:[ASBasicImageDownloader sharedImageDownloader]]; +#endif +} + +- (void)dealloc +{ + [_phImageRequestOperation cancel]; +} + +#pragma mark - ASDisplayNode Overrides + +- (void)clearContents +{ + [super clearContents]; // This actually clears the contents, so we need to do this first for our displayedImageIdentifier to be meaningful. + [self _setDisplayedImageIdentifier:nil withImage:nil]; + + // NOTE: We intentionally do not cancel image downloads until `clearPreloadedData`. +} + +- (void)didExitPreloadState +{ + [super didExitPreloadState]; + + [_phImageRequestOperation cancel]; + + [self _setDownloadIdentifier:nil]; + + if (_cacheSupportsClearing && self.loadedImageIdentifier != nil) { + NSURL *URL = [_dataSource multiplexImageNode:self URLForImageIdentifier:self.loadedImageIdentifier]; + if (URL != nil) { + [_cache clearFetchedImageFromCacheWithURL:URL]; + } + } + + // setting this to nil makes the node fetch images the next time its display starts + _loadedImageIdentifier = nil; + [self _setImage:nil]; +} + +- (void)didEnterPreloadState +{ + [super didEnterPreloadState]; + + [self _loadImageIdentifiers]; +} + +- (void)displayDidFinish +{ + [super displayDidFinish]; + + // We may now be displaying the loaded identifier, if they're different. + UIImage *displayedImage = self.image; + if (displayedImage) { + if (!ASObjectIsEqual(_displayedImageIdentifier, _loadedImageIdentifier)) + [self _setDisplayedImageIdentifier:_loadedImageIdentifier withImage:displayedImage]; + + // Delegateify + if (_delegateFlags.displayFinish) { + if (ASDisplayNodeThreadIsMain()) + [_delegate multiplexImageNodeDidFinishDisplay:self]; + else { + __weak __typeof__(self) weakSelf = self; + dispatch_async(dispatch_get_main_queue(), ^{ + __typeof__(self) strongSelf = weakSelf; + if (!strongSelf) + return; + [strongSelf.delegate multiplexImageNodeDidFinishDisplay:strongSelf]; + }); + } + } + } +} + +- (BOOL)placeholderShouldPersist +{ + return (self.image == nil && self.animatedImage == nil && self.imageIdentifiers.count > 0); +} + +/* displayWillStartAsynchronously in ASNetworkImageNode has a very similar implementation. Changes here are likely necessary + in ASNetworkImageNode as well. */ +- (void)displayWillStartAsynchronously:(BOOL)asynchronously +{ + [super displayWillStartAsynchronously:asynchronously]; + [self didEnterPreloadState]; + [self _updatePriorityOnDownloaderIfNeeded]; +} + +/* didEnterVisibleState / didExitVisibleState in ASNetworkImageNode has a very similar implementation. Changes here are likely necessary + in ASNetworkImageNode as well. */ +- (void)didEnterVisibleState +{ + [super didEnterVisibleState]; + [self _updatePriorityOnDownloaderIfNeeded]; + [self _updateProgressImageBlockOnDownloaderIfNeeded]; +} + +- (void)didExitVisibleState +{ + [super didExitVisibleState]; + [self _updatePriorityOnDownloaderIfNeeded]; + [self _updateProgressImageBlockOnDownloaderIfNeeded]; +} + +- (void)didExitDisplayState +{ + [super didExitDisplayState]; + [self _updatePriorityOnDownloaderIfNeeded]; +} + +#pragma mark - Core + +- (void)setImage:(UIImage *)image +{ + ASDisplayNodeAssert(NO, @"Setting the image directly on an ASMultiplexImageNode is unsafe. It will be cleared in didExitPreloadRange and will have no way to restore in didEnterPreloadRange"); + super.image = image; +} + +- (void)_setImage:(UIImage *)image +{ + super.image = image; +} + +- (void)setDelegate:(id )delegate +{ + if (_delegate == delegate) + return; + + _delegate = delegate; + _delegateFlags.downloadStart = [_delegate respondsToSelector:@selector(multiplexImageNode:didStartDownloadOfImageWithIdentifier:)]; + _delegateFlags.downloadProgress = [_delegate respondsToSelector:@selector(multiplexImageNode:didUpdateDownloadProgress:forImageWithIdentifier:)]; + _delegateFlags.downloadFinish = [_delegate respondsToSelector:@selector(multiplexImageNode:didFinishDownloadingImageWithIdentifier:error:)]; + _delegateFlags.updatedImageDisplayFinish = [_delegate respondsToSelector:@selector(multiplexImageNode:didDisplayUpdatedImage:withIdentifier:)]; + _delegateFlags.updatedImage = [_delegate respondsToSelector:@selector(multiplexImageNode:didUpdateImage:withIdentifier:fromImage:withIdentifier:)]; + _delegateFlags.displayFinish = [_delegate respondsToSelector:@selector(multiplexImageNodeDidFinishDisplay:)]; +} + + +- (void)setDataSource:(id )dataSource +{ + if (_dataSource == dataSource) + return; + + _dataSource = dataSource; + _dataSourceFlags.image = [_dataSource respondsToSelector:@selector(multiplexImageNode:imageForImageIdentifier:)]; + _dataSourceFlags.URL = [_dataSource respondsToSelector:@selector(multiplexImageNode:URLForImageIdentifier:)]; + if (AS_AVAILABLE_IOS_TVOS(9, 10)) { + _dataSourceFlags.asset = [_dataSource respondsToSelector:@selector(multiplexImageNode:assetForLocalIdentifier:)]; + } +} + + +- (void)setShouldRenderProgressImages:(BOOL)shouldRenderProgressImages +{ + [self lock]; + if (shouldRenderProgressImages == _shouldRenderProgressImages) { + [self unlock]; + return; + } + + _shouldRenderProgressImages = shouldRenderProgressImages; + + [self unlock]; + [self _updateProgressImageBlockOnDownloaderIfNeeded]; +} + +- (BOOL)shouldRenderProgressImages +{ + return ASLockedSelf(_shouldRenderProgressImages); +} + +#pragma mark - + +#pragma mark - + +- (NSArray *)imageIdentifiers +{ + MutexLocker l(_imageIdentifiersLock); + return _imageIdentifiers; +} + +- (void)setImageIdentifiers:(NSArray *)imageIdentifiers +{ + { + MutexLocker l(_imageIdentifiersLock); + if (ASObjectIsEqual(_imageIdentifiers, imageIdentifiers)) { + return; + } + + _imageIdentifiers = [[NSArray alloc] initWithArray:imageIdentifiers copyItems:YES]; + } + + [self setNeedsPreload]; +} + +- (void)reloadImageIdentifierSources +{ + // setting this to nil makes the node think it has not downloaded any images + _loadedImageIdentifier = nil; + [self _loadImageIdentifiers]; +} + +#pragma mark - + + +#pragma mark - Core Internal +- (void)_setDisplayedImageIdentifier:(id)displayedImageIdentifier withImage:(UIImage *)image +{ + ASDisplayNodeAssertMainThread(); + + if (ASObjectIsEqual(_displayedImageIdentifier, displayedImageIdentifier)) { + return; + } + + _displayedImageIdentifier = displayedImageIdentifier; + + // Delegateify. + // Note that we're using the params here instead of self.image and _displayedImageIdentifier because those can change before the async block below executes. + if (_delegateFlags.updatedImageDisplayFinish) { + if (ASDisplayNodeThreadIsMain()) + [_delegate multiplexImageNode:self didDisplayUpdatedImage:image withIdentifier:displayedImageIdentifier]; + else { + __weak __typeof__(self) weakSelf = self; + dispatch_async(dispatch_get_main_queue(), ^{ + __typeof__(self) strongSelf = weakSelf; + if (!strongSelf) + return; + [strongSelf.delegate multiplexImageNode:strongSelf didDisplayUpdatedImage:image withIdentifier:displayedImageIdentifier]; + }); + } + } +} + +- (void)_setDownloadIdentifier:(id)downloadIdentifier +{ + MutexLocker l(_downloadIdentifierLock); + if (ASObjectIsEqual(downloadIdentifier, _downloadIdentifier)) + return; + + if (_downloadIdentifier) { + [_downloader cancelImageDownloadForIdentifier:_downloadIdentifier]; + } + _downloadIdentifier = downloadIdentifier; +} + +#pragma mark - Image Loading Machinery + +- (void)_loadImageIdentifiers +{ + // Grab the best possible image we can load right now. + id bestImmediatelyAvailableImageIdentifier = nil; + UIImage *bestImmediatelyAvailableImage = [self _bestImmediatelyAvailableImageFromDataSource:&bestImmediatelyAvailableImageIdentifier]; + as_log_verbose(ASImageLoadingLog(), "%@ Best immediately available image identifier is %@", self, bestImmediatelyAvailableImageIdentifier); + + // Load it. This kicks off cache fetching/downloading, as appropriate. + [self _finishedLoadingImage:bestImmediatelyAvailableImage forIdentifier:bestImmediatelyAvailableImageIdentifier error:nil]; +} + +- (UIImage *)_bestImmediatelyAvailableImageFromDataSource:(id *)imageIdentifierOut +{ + MutexLocker l(_imageIdentifiersLock); + + // If we don't have any identifiers to load or don't implement the image DS method, bail. + if ([_imageIdentifiers count] == 0 || !_dataSourceFlags.image) { + return nil; + } + + // Grab the best available image from the data source. + UIImage *existingImage = self.image; + for (id imageIdentifier in _imageIdentifiers) { + // If this image is already loaded, don't request it from the data source again because + // the data source may generate a new instance of UIImage that returns NO for isEqual: + // and we'll end up in an infinite loading loop. + UIImage *image = ASObjectIsEqual(imageIdentifier, _loadedImageIdentifier) ? existingImage : [_dataSource multiplexImageNode:self imageForImageIdentifier:imageIdentifier]; + if (image) { + if (imageIdentifierOut) { + *imageIdentifierOut = imageIdentifier; + } + + return image; + } + } + + return nil; +} + +#pragma mark - + +- (void)_updatePriorityOnDownloaderIfNeeded +{ + DISABLED_ASAssertUnlocked(_downloadIdentifierLock); + + if (_downloaderFlags.downloaderImplementsSetPriority) { + // Read our interface state before locking so that we don't lock super while holding our lock. + ASInterfaceState interfaceState = self.interfaceState; + MutexLocker l(_downloadIdentifierLock); + + if (_downloadIdentifier != nil) { + ASImageDownloaderPriority priority = ASImageDownloaderPriorityWithInterfaceState(interfaceState); + [_downloader setPriority:priority withDownloadIdentifier:_downloadIdentifier]; + } + } +} + +- (void)_updateProgressImageBlockOnDownloaderIfNeeded +{ + DISABLED_ASAssertUnlocked(_downloadIdentifierLock); + + BOOL shouldRenderProgressImages = self.shouldRenderProgressImages; + + // Read our interface state before locking so that we don't lock super while holding our lock. + ASInterfaceState interfaceState = self.interfaceState; + MutexLocker l(_downloadIdentifierLock); + + if (!_downloaderFlags.downloaderImplementsSetProgress || _downloadIdentifier == nil) { + return; + } + + ASImageDownloaderProgressImage progress = nil; + if (shouldRenderProgressImages && ASInterfaceStateIncludesVisible(interfaceState)) { + __weak __typeof__(self) weakSelf = self; + progress = ^(UIImage * _Nonnull progressImage, CGFloat progress, id _Nullable downloadIdentifier) { + __typeof__(self) strongSelf = weakSelf; + if (strongSelf == nil) { + return; + } + + MutexLocker l(strongSelf->_downloadIdentifierLock); + //Getting a result back for a different download identifier, download must not have been successfully canceled + if (ASObjectIsEqual(strongSelf->_downloadIdentifier, downloadIdentifier) == NO && downloadIdentifier != nil) { + return; + } + [strongSelf _setImage:progressImage]; + }; + } + [_downloader setProgressImageBlock:progress callbackQueue:dispatch_get_main_queue() withDownloadIdentifier:_downloadIdentifier]; +} + +- (void)_clearImage +{ + [self _setImage:nil]; +} + +#pragma mark - +- (id)_nextImageIdentifierToDownload +{ + MutexLocker l(_imageIdentifiersLock); + + // If we've already loaded the best identifier, we've got nothing else to do. + id bestImageIdentifier = _imageIdentifiers.firstObject; + if (!bestImageIdentifier || ASObjectIsEqual(_loadedImageIdentifier, bestImageIdentifier)) { + return nil; + } + + id nextImageIdentifierToDownload = nil; + + // If we're not supposed to download intermediate images, load the best identifier we've got. + if (!_downloadsIntermediateImages) { + nextImageIdentifierToDownload = bestImageIdentifier; + } + // Otherwise, load progressively. + else { + NSUInteger loadedIndex = [_imageIdentifiers indexOfObject:_loadedImageIdentifier]; + + // If nothing has loaded yet, load the worst identifier. + if (loadedIndex == NSNotFound) { + nextImageIdentifierToDownload = [_imageIdentifiers lastObject]; + } + // Otherwise, load the next best identifier (if there is one) + else if (loadedIndex > 0) { + nextImageIdentifierToDownload = _imageIdentifiers[loadedIndex - 1]; + } + } + + return nextImageIdentifierToDownload; +} + +- (void)_loadNextImage +{ + // Determine the next identifier to load (if any). + id nextImageIdentifier = [self _nextImageIdentifierToDownload]; + if (!nextImageIdentifier) { + [self _finishedLoadingImage:nil forIdentifier:nil error:nil]; + return; + } + + as_activity_create_for_scope("Load next image for multiplex image node"); + as_log_verbose(ASImageLoadingLog(), "Loading image for %@ ident: %@", self, nextImageIdentifier); + self.loadingImageIdentifier = nextImageIdentifier; + + __weak __typeof__(self) weakSelf = self; + ASMultiplexImageLoadCompletionBlock finishedLoadingBlock = ^(UIImage *image, id imageIdentifier, NSError *error) { + __typeof__(self) strongSelf = weakSelf; + if (!strongSelf) + return; + + // Only nil out the loading identifier if the loading identifier hasn't changed. + if (ASObjectIsEqual(strongSelf.loadingImageIdentifier, nextImageIdentifier)) { + strongSelf.loadingImageIdentifier = nil; + } + [strongSelf _finishedLoadingImage:image forIdentifier:imageIdentifier error:error]; + }; + + // Ask our data-source if it's got this image. + if (_dataSourceFlags.image) { + UIImage *image = [_dataSource multiplexImageNode:self imageForImageIdentifier:nextImageIdentifier]; + if (image) { + as_log_verbose(ASImageLoadingLog(), "Acquired image from data source for %@ ident: %@", self, nextImageIdentifier); + finishedLoadingBlock(image, nextImageIdentifier, nil); + return; + } + } + + NSURL *nextImageURL = (_dataSourceFlags.URL) ? [_dataSource multiplexImageNode:self URLForImageIdentifier:nextImageIdentifier] : nil; + // If we fail to get a URL for the image, we have no source and can't proceed. + if (!nextImageURL) { + os_log_error(ASImageLoadingLog(), "Could not acquire URL %@ ident: (%@)", self, nextImageIdentifier); + finishedLoadingBlock(nil, nil, [NSError errorWithDomain:ASMultiplexImageNodeErrorDomain code:ASMultiplexImageNodeErrorCodeNoSourceForImage userInfo:nil]); + return; + } + +#if TARGET_OS_IOS && AS_USE_ASSETS_LIBRARY + // If it's an assets-library URL, we need to fetch it from the assets library. + if ([[nextImageURL scheme] isEqualToString:kAssetsLibraryURLScheme]) { + // Load the asset. + [self _loadALAssetWithIdentifier:nextImageIdentifier URL:nextImageURL completion:^(UIImage *downloadedImage, NSError *error) { + as_log_verbose(ASImageLoadingLog(), "Acquired image from assets library for %@ %@", weakSelf, nextImageIdentifier); + finishedLoadingBlock(downloadedImage, nextImageIdentifier, error); + }]; + + return; + } +#endif + +#if AS_USE_PHOTOS + if (AS_AVAILABLE_IOS_TVOS(9, 10)) { + // Likewise, if it's a Photos asset, we need to fetch it accordingly. + if (ASPhotosFrameworkImageRequest *request = [ASPhotosFrameworkImageRequest requestWithURL:nextImageURL]) { + [self _loadPHAssetWithRequest:request identifier:nextImageIdentifier completion:^(UIImage *image, NSError *error) { + as_log_verbose(ASImageLoadingLog(), "Acquired image from Photos for %@ %@", weakSelf, nextImageIdentifier); + finishedLoadingBlock(image, nextImageIdentifier, error); + }]; + + return; + } + } +#endif + + // Otherwise, it's a web URL that we can download. + // First, check the cache. + [self _fetchImageWithIdentifierFromCache:nextImageIdentifier URL:nextImageURL completion:^(UIImage *imageFromCache) { + __typeof__(self) strongSelf = weakSelf; + if (!strongSelf) + return; + + // If we had a cache-hit, we're done. + if (imageFromCache) { + as_log_verbose(ASImageLoadingLog(), "Acquired image from cache for %@ id: %@ img: %@", strongSelf, nextImageIdentifier, imageFromCache); + finishedLoadingBlock(imageFromCache, nextImageIdentifier, nil); + return; + } + + // If the next image to load has changed, bail. + if (!ASObjectIsEqual([strongSelf _nextImageIdentifierToDownload], nextImageIdentifier)) { + finishedLoadingBlock(nil, nil, [NSError errorWithDomain:ASMultiplexImageNodeErrorDomain code:ASMultiplexImageNodeErrorCodeBestImageIdentifierChanged userInfo:nil]); + return; + } + + // Otherwise, we've got to download it. + [strongSelf _downloadImageWithIdentifier:nextImageIdentifier URL:nextImageURL completion:^(UIImage *downloadedImage, NSError *error) { + __typeof__(self) strongSelf = weakSelf; + if (downloadedImage) { + as_log_verbose(ASImageLoadingLog(), "Acquired image from download for %@ id: %@ img: %@", strongSelf, nextImageIdentifier, downloadedImage); + } else { + os_log_error(ASImageLoadingLog(), "Error downloading image for %@ id: %@ err: %@", strongSelf, nextImageIdentifier, error); + } + finishedLoadingBlock(downloadedImage, nextImageIdentifier, error); + }]; + }]; +} +#if TARGET_OS_IOS && AS_USE_ASSETS_LIBRARY +- (void)_loadALAssetWithIdentifier:(id)imageIdentifier URL:(NSURL *)assetURL completion:(void (^)(UIImage *image, NSError *error))completionBlock +{ + ASDisplayNodeAssertNotNil(imageIdentifier, @"imageIdentifier is required"); + ASDisplayNodeAssertNotNil(assetURL, @"assetURL is required"); + ASDisplayNodeAssertNotNil(completionBlock, @"completionBlock is required"); + + // ALAssetsLibrary was replaced in iOS 8 and deprecated in iOS 9. + // We'll drop support very soon. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + ALAssetsLibrary *assetLibrary = [[ALAssetsLibrary alloc] init]; + + [assetLibrary assetForURL:assetURL resultBlock:^(ALAsset *asset) { + ALAssetRepresentation *representation = [asset defaultRepresentation]; + CGImageRef coreGraphicsImage = [representation fullScreenImage]; + + UIImage *downloadedImage = (coreGraphicsImage ? [UIImage imageWithCGImage:coreGraphicsImage] : nil); + completionBlock(downloadedImage, nil); + } failureBlock:^(NSError *error) { + completionBlock(nil, error); + }]; +#pragma clang diagnostic pop +} +#endif + +#if AS_USE_PHOTOS +- (void)_loadPHAssetWithRequest:(ASPhotosFrameworkImageRequest *)request identifier:(id)imageIdentifier completion:(void (^)(UIImage *image, NSError *error))completionBlock +{ + ASDisplayNodeAssertNotNil(imageIdentifier, @"imageIdentifier is required"); + ASDisplayNodeAssertNotNil(request, @"request is required"); + ASDisplayNodeAssertNotNil(completionBlock, @"completionBlock is required"); + + /* + * Locking rationale: + * As of iOS 9, Photos.framework will eventually deadlock if you hit it with concurrent fetch requests. rdar://22984886 + * Concurrent image requests are OK, but metadata requests aren't, so we limit ourselves to one at a time. + */ + static NSLock *phRequestLock; + static NSOperationQueue *phImageRequestQueue; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + phRequestLock = [NSLock new]; + phImageRequestQueue = [NSOperationQueue new]; + phImageRequestQueue.maxConcurrentOperationCount = 10; + phImageRequestQueue.name = @"org.AsyncDisplayKit.MultiplexImageNode.phImageRequestQueue"; + }); + + // Each ASMultiplexImageNode can have max 1 inflight Photos image request operation + [_phImageRequestOperation cancel]; + + __weak __typeof(self) weakSelf = self; + NSOperation *newImageRequestOp = [NSBlockOperation blockOperationWithBlock:^{ + __strong __typeof(weakSelf) strongSelf = weakSelf; + if (strongSelf == nil) { return; } + + PHAsset *imageAsset = nil; + + // Try to get the asset immediately from the data source. + if (strongSelf->_dataSourceFlags.asset) { + imageAsset = [strongSelf.dataSource multiplexImageNode:strongSelf assetForLocalIdentifier:request.assetIdentifier]; + } + + // Fall back to locking and getting the PHAsset. + if (imageAsset == nil) { + [phRequestLock lock]; + // -[PHFetchResult dealloc] plays a role in the deadlock mentioned above, so we make sure the PHFetchResult is deallocated inside the critical section + @autoreleasepool { + imageAsset = [PHAsset fetchAssetsWithLocalIdentifiers:@[request.assetIdentifier] options:nil].firstObject; + } + [phRequestLock unlock]; + } + + if (imageAsset == nil) { + NSError *error = [NSError errorWithDomain:ASMultiplexImageNodeErrorDomain code:ASMultiplexImageNodeErrorCodePHAssetIsUnavailable userInfo:nil]; + completionBlock(nil, error); + return; + } + + PHImageRequestOptions *options = [request.options copy]; + + // We don't support opportunistic delivery – one request, one image. + if (options.deliveryMode == PHImageRequestOptionsDeliveryModeOpportunistic) { + options.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat; + } + + if (options.deliveryMode == PHImageRequestOptionsDeliveryModeHighQualityFormat) { + // Without this flag the result will be delivered on the main queue, which is pointless + // But synchronous -> HighQualityFormat so we only use it if high quality format is specified + options.synchronous = YES; + } + + PHImageManager *imageManager = strongSelf.imageManager ? : PHImageManager.defaultManager; + [imageManager requestImageForAsset:imageAsset targetSize:request.targetSize contentMode:request.contentMode options:options resultHandler:^(UIImage *image, NSDictionary *info) { + NSError *error = info[PHImageErrorKey]; + + if (error == nil && image == nil) { + error = [NSError errorWithDomain:ASMultiplexImageNodeErrorDomain code:ASMultiplexImageNodeErrorCodePhotosImageManagerFailedWithoutError userInfo:nil]; + } + + if (NSThread.isMainThread) { + dispatch_async(dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0), ^{ + completionBlock(image, error); + }); + } else { + completionBlock(image, error); + } + }]; + }]; + // If you don't set this, iOS will sometimes infer NSQualityOfServiceUserInteractive and promote the entire queue to that level, damaging system responsiveness + newImageRequestOp.qualityOfService = NSQualityOfServiceUserInitiated; + _phImageRequestOperation = newImageRequestOp; + [phImageRequestQueue addOperation:newImageRequestOp]; +} +#endif + +- (void)_fetchImageWithIdentifierFromCache:(id)imageIdentifier URL:(NSURL *)imageURL completion:(void (^)(UIImage *image))completionBlock +{ + ASDisplayNodeAssertNotNil(imageIdentifier, @"imageIdentifier is required"); + ASDisplayNodeAssertNotNil(imageURL, @"imageURL is required"); + ASDisplayNodeAssertNotNil(completionBlock, @"completionBlock is required"); + + if (_cache) { + [_cache cachedImageWithURL:imageURL callbackQueue:dispatch_get_main_queue() completion:^(id imageContainer, __unused ASImageCacheType cacheType ) { + completionBlock([imageContainer asdk_image]); + }]; + } + // If we don't have a cache, just fail immediately. + else { + completionBlock(nil); + } +} + +- (void)_downloadImageWithIdentifier:(id)imageIdentifier URL:(NSURL *)imageURL completion:(void (^)(UIImage *image, NSError *error))completionBlock +{ + ASDisplayNodeAssertNotNil(imageIdentifier, @"imageIdentifier is required"); + ASDisplayNodeAssertNotNil(imageURL, @"imageURL is required"); + ASDisplayNodeAssertNotNil(completionBlock, @"completionBlock is required"); + + // Delegate (start) + if (_delegateFlags.downloadStart) + [_delegate multiplexImageNode:self didStartDownloadOfImageWithIdentifier:imageIdentifier]; + + __weak __typeof__(self) weakSelf = self; + ASImageDownloaderProgress downloadProgressBlock = NULL; + if (_delegateFlags.downloadProgress) { + downloadProgressBlock = ^(CGFloat progress) { + __typeof__(self) strongSelf = weakSelf; + if (!strongSelf) + return; + [strongSelf.delegate multiplexImageNode:strongSelf didUpdateDownloadProgress:progress forImageWithIdentifier:imageIdentifier]; + }; + } + + ASImageDownloaderCompletion completion = ^(id imageContainer, NSError *error, id downloadIdentifier, id userInfo) { + // We dereference iVars directly, so we can't have weakSelf going nil on us. + __typeof__(self) strongSelf = weakSelf; + if (!strongSelf) + return; + + MutexLocker l(strongSelf->_downloadIdentifierLock); + //Getting a result back for a different download identifier, download must not have been successfully canceled + if (ASObjectIsEqual(strongSelf->_downloadIdentifier, downloadIdentifier) == NO && downloadIdentifier != nil) { + return; + } + + completionBlock([imageContainer asdk_image], error); + + // Delegateify. + if (strongSelf->_delegateFlags.downloadFinish) + [strongSelf->_delegate multiplexImageNode:weakSelf didFinishDownloadingImageWithIdentifier:imageIdentifier error:error]; + }; + + // Download! + ASPerformBlockOnBackgroundThread(^{ + __typeof__(self) strongSelf = weakSelf; + if (!strongSelf) + return; + + dispatch_queue_t callbackQueue = dispatch_get_main_queue(); + + id downloadIdentifier; + if (strongSelf->_downloaderFlags.downloaderImplementsDownloadWithPriority) { + /* + Decide a priority based on the current interface state of this node. + It can happen that this method was called when the node entered preload state + but the interface state, at this point, tells us that the node is (going to be) visible, + If that's the case, we jump to a higher priority directly. + */ + ASImageDownloaderPriority priority = ASImageDownloaderPriorityWithInterfaceState(strongSelf.interfaceState); + downloadIdentifier = [strongSelf->_downloader downloadImageWithURL:imageURL + priority:priority + callbackQueue:callbackQueue + downloadProgress:downloadProgressBlock + completion:completion]; + } else { + /* + Kick off a download with default priority. + The actual "default" value is decided by the downloader. + ASBasicImageDownloader and ASPINRemoteImageDownloader both use ASImageDownloaderPriorityImminent + which is mapped to NSURLSessionTaskPriorityDefault. + + This means that preload and display nodes use the same priority + and their requests are put into the same pool. + */ + downloadIdentifier = [strongSelf->_downloader downloadImageWithURL:imageURL + callbackQueue:callbackQueue + downloadProgress:downloadProgressBlock + completion:completion]; + } + + [strongSelf _setDownloadIdentifier:downloadIdentifier]; + [strongSelf _updateProgressImageBlockOnDownloaderIfNeeded]; + }); +} + +#pragma mark - +- (void)_finishedLoadingImage:(UIImage *)image forIdentifier:(id)imageIdentifier error:(NSError *)error +{ + // If we failed to load, we stop the loading process. + // Note that if we bailed before we began downloading because the best identifier changed, we don't bail, but rather just begin loading the best image identifier. + if (error && !([error.domain isEqual:ASMultiplexImageNodeErrorDomain] && error.code == ASMultiplexImageNodeErrorCodeBestImageIdentifierChanged)) + return; + + + _imageIdentifiersLock.lock(); + NSUInteger imageIdentifierCount = [_imageIdentifiers count]; + _imageIdentifiersLock.unlock(); + + // Update our image if we got one, or if we're not supposed to display one at all. + // We explicitly perform this check because our datasource often doesn't give back immediately available images, even though we might have downloaded one already. + // Because we seed this call with bestImmediatelyAvailableImageFromDataSource, we must be careful not to trample an existing image. + if (image || imageIdentifierCount == 0) { + as_log_verbose(ASImageLoadingLog(), "[%p] loaded -> displaying (%@, %@)", self, imageIdentifier, image); + id previousIdentifier = self.loadedImageIdentifier; + UIImage *previousImage = self.image; + + self.loadedImageIdentifier = imageIdentifier; + [self _setImage:image]; + + if (_delegateFlags.updatedImage) { + [_delegate multiplexImageNode:self didUpdateImage:image withIdentifier:imageIdentifier fromImage:previousImage withIdentifier:previousIdentifier]; + } + + } + + // Load our next image, if we have one to load. + if ([self _nextImageIdentifierToDownload]) + [self _loadNextImage]; +} + +@end + +#if AS_USE_PHOTOS +@implementation NSURL (ASPhotosFrameworkURLs) + ++ (NSURL *)URLWithAssetLocalIdentifier:(NSString *)assetLocalIdentifier targetSize:(CGSize)targetSize contentMode:(PHImageContentMode)contentMode options:(PHImageRequestOptions *)options NS_RETURNS_RETAINED +{ + ASPhotosFrameworkImageRequest *request = [[ASPhotosFrameworkImageRequest alloc] initWithAssetIdentifier:assetLocalIdentifier]; + request.options = options; + request.contentMode = contentMode; + request.targetSize = targetSize; + return request.url; +} + +@end +#endif diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASNavigationController.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASNavigationController.h new file mode 100644 index 0000000..0d259b2 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASNavigationController.h @@ -0,0 +1,30 @@ +// +// ASNavigationController.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * ASNavigationController + * + * @discussion ASNavigationController is a drop in replacement for UINavigationController + * which improves memory efficiency by implementing the @c ASManagesChildVisibilityDepth protocol. + * You can use ASNavigationController with regular UIViewControllers, as well as ASDKViewControllers. + * It is safe to subclass or use even where AsyncDisplayKit is not adopted. + * + * @see ASManagesChildVisibilityDepth + */ +@interface ASNavigationController : UINavigationController + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASNavigationController.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASNavigationController.mm new file mode 100644 index 0000000..2fbc488 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASNavigationController.mm @@ -0,0 +1,113 @@ +// +// ASNavigationController.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import + +@implementation ASNavigationController +{ + BOOL _parentManagesVisibilityDepth; + NSInteger _visibilityDepth; +} + +ASVisibilityDidMoveToParentViewController; + +ASVisibilityViewWillAppear; + +ASVisibilityViewDidDisappearImplementation; + +ASVisibilitySetVisibilityDepth; + +ASVisibilityDepthImplementation; + +- (void)visibilityDepthDidChange +{ + for (UIViewController *viewController in self.viewControllers) { + if ([viewController conformsToProtocol:@protocol(ASVisibilityDepth)]) { + [(id )viewController visibilityDepthDidChange]; + } + } +} + +- (NSInteger)visibilityDepthOfChildViewController:(UIViewController *)childViewController +{ + NSUInteger viewControllerIndex = [self.viewControllers indexOfObjectIdenticalTo:childViewController]; + if (viewControllerIndex == NSNotFound) { + //If childViewController is not actually a child, return NSNotFound which is also a really large number. + return NSNotFound; + } + + if (viewControllerIndex == self.viewControllers.count - 1) { + //view controller is at the top, just return our own visibility depth. + return [self visibilityDepth]; + } else if (viewControllerIndex == 0) { + //view controller is the root view controller. Can be accessed by holding the back button. + return [self visibilityDepth] + 1; + } + + return [self visibilityDepth] + self.viewControllers.count - 1 - viewControllerIndex; +} + +#pragma mark - UIKit overrides + +- (NSArray *)popToViewController:(UIViewController *)viewController animated:(BOOL)animated +{ + as_activity_create_for_scope("Pop multiple from ASNavigationController"); + NSArray *viewControllers = [super popToViewController:viewController animated:animated]; + os_log_info(ASNodeLog(), "Popped %@ to %@, removing %@", self, viewController, ASGetDescriptionValueString(viewControllers)); + + [self visibilityDepthDidChange]; + return viewControllers; +} + +- (NSArray *)popToRootViewControllerAnimated:(BOOL)animated +{ + as_activity_create_for_scope("Pop to root of ASNavigationController"); + NSArray *viewControllers = [super popToRootViewControllerAnimated:animated]; + os_log_info(ASNodeLog(), "Popped view controllers %@ from %@", ASGetDescriptionValueString(viewControllers), self); + + [self visibilityDepthDidChange]; + return viewControllers; +} + +- (void)setViewControllers:(NSArray *)viewControllers +{ + // NOTE: As of now this method calls through to setViewControllers:animated: so no need to log/activity here. + + [super setViewControllers:viewControllers]; + [self visibilityDepthDidChange]; +} + +- (void)setViewControllers:(NSArray *)viewControllers animated:(BOOL)animated +{ + as_activity_create_for_scope("Set view controllers of ASNavigationController"); + os_log_info(ASNodeLog(), "Set view controllers of %@ to %@ animated: %d", self, ASGetDescriptionValueString(viewControllers), animated); + [super setViewControllers:viewControllers animated:animated]; + [self visibilityDepthDidChange]; +} + +- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated +{ + as_activity_create_for_scope("Push view controller on ASNavigationController"); + os_log_info(ASNodeLog(), "Pushing %@ onto %@", viewController, self); + [super pushViewController:viewController animated:animated]; + [self visibilityDepthDidChange]; +} + +- (UIViewController *)popViewControllerAnimated:(BOOL)animated +{ + as_activity_create_for_scope("Pop view controller from ASNavigationController"); + UIViewController *viewController = [super popViewControllerAnimated:animated]; + os_log_info(ASNodeLog(), "Popped %@ from %@", viewController, self); + [self visibilityDepthDidChange]; + return viewController; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASNetworkImageLoadInfo.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASNetworkImageLoadInfo.h new file mode 100644 index 0000000..55c4b49 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASNetworkImageLoadInfo.h @@ -0,0 +1,39 @@ +// +// ASNetworkImageLoadInfo.h +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +typedef NS_ENUM(NSInteger, ASNetworkImageSourceType) { + ASNetworkImageSourceUnspecified = 0, + ASNetworkImageSourceSynchronousCache, + ASNetworkImageSourceAsynchronousCache, + ASNetworkImageSourceFileURL, + ASNetworkImageSourceDownload, +}; + +AS_SUBCLASSING_RESTRICTED +@interface ASNetworkImageLoadInfo : NSObject + +/// The type of source from which the image was loaded. +@property (readonly) ASNetworkImageSourceType sourceType; + +/// The image URL that was downloaded. +@property (readonly) NSURL *url; + +/// The download identifier, if one was provided. +@property (nullable, readonly) id downloadIdentifier; + +/// The userInfo object provided by the downloader, if one was provided. +@property (nullable, readonly) id userInfo; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASNetworkImageLoadInfo.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASNetworkImageLoadInfo.mm new file mode 100644 index 0000000..64ec547 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASNetworkImageLoadInfo.mm @@ -0,0 +1,31 @@ +// +// ASNetworkImageLoadInfo.mm +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +@implementation ASNetworkImageLoadInfo + +- (instancetype)initWithURL:(NSURL *)url sourceType:(ASNetworkImageSourceType)sourceType downloadIdentifier:(id)downloadIdentifier userInfo:(id)userInfo +{ + if (self = [super init]) { + _url = [url copy]; + _sourceType = sourceType; + _downloadIdentifier = downloadIdentifier; + _userInfo = userInfo; + } + return self; +} + +#pragma mark - NSCopying + +- (id)copyWithZone:(NSZone *)zone +{ + return self; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASNetworkImageNode.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASNetworkImageNode.h new file mode 100644 index 0000000..fa07a3d --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASNetworkImageNode.h @@ -0,0 +1,253 @@ +// +// ASNetworkImageNode.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@protocol ASNetworkImageNodeDelegate, ASImageCacheProtocol, ASImageDownloaderProtocol; +@class ASNetworkImageLoadInfo; + + +/** + * ASNetworkImageNode is a simple image node that can download and display an image from the network, with support for a + * placeholder image (). The currently-displayed image is always available in the inherited ASImageNode + * property. + * + * @see ASMultiplexImageNode for a more powerful counterpart to this class. + */ +@interface ASNetworkImageNode : ASImageNode + +/** + * The designated initializer. Cache and Downloader are WEAK references. + * + * @param cache The object that implements a cache of images for the image node. Weak reference. + * @param downloader The object that implements image downloading for the image node. Must not be nil. Weak reference. + * + * @discussion If `cache` is nil, the receiver will not attempt to retrieve images from a cache before downloading them. + * + * @return An initialized ASNetworkImageNode. + */ +- (instancetype)initWithCache:(nullable id)cache downloader:(id)downloader NS_DESIGNATED_INITIALIZER; + +/** + * Convenience initializer. + * + * @return An ASNetworkImageNode configured to use the NSURLSession-powered ASBasicImageDownloader, and no extra cache. + */ +- (instancetype)init; + +/** + * The delegate, which must conform to the protocol. + */ +@property (nullable, weak) id delegate; + +/** + * The delegate will receive callbacks on main thread. Default to YES. + */ +@property (class) BOOL useMainThreadDelegateCallbacks; + +/** + * The image to display. + * + * @discussion By setting an image to the image property the ASNetworkImageNode will act like a plain ASImageNode. + * As soon as the URL is set the ASNetworkImageNode will act like an ASNetworkImageNode and the image property + * will be managed internally. This means the image property will be cleared out and replaced by the placeholder + * () image while loading and the final image after the new image data was downloaded and processed. + * If you want to use a placholder image functionality use the defaultImage property instead. + */ +@property (nullable) UIImage *image; + +/** + * A placeholder image to display while the URL is loading. This is slightly different than placeholderImage in the + * ASDisplayNode superclass as defaultImage will *not* be displayed synchronously. If you wish to have the image + * displayed synchronously, use @c placeholderImage. + */ +@property (nullable) UIImage *defaultImage; + +/** + * The URL of a new image to download and display. + * + * @discussion By setting an URL, the image property of this node will be managed internally. This means previously + * directly set images to the image property will be cleared out and replaced by the placeholder () image + * while loading and the final image after the new image data was downloaded and processed. + */ +@property (nullable, copy) NSURL *URL; + +/** + * An array of URLs of increasing cost to download. + * + * @discussion By setting an array of URLs, the image property of this node will be managed internally. This means previously + * directly set images to the image property will be cleared out and replaced by the placeholder () image + * while loading and the final image after the new image data was downloaded and processed. + * + * @deprecated This API has been removed for now due to the increased complexity to the class that it brought. + * Please use .URL instead. + */ +@property (nullable, copy) NSArray *URLs ASDISPLAYNODE_DEPRECATED_MSG("Please use URL instead."); + +/** + * Download and display a new image. + * + * @param URL The URL of a new image to download and display. + * @param reset Whether to display a placeholder () while loading the new image. + * + * @discussion By setting an URL, the image property of this node will be managed internally. This means previously + * directly set images to the image property will be cleared out and replaced by the placeholder () image + * while loading and the final image after the new image data was downloaded and processed. + */ +- (void)setURL:(nullable NSURL *)URL resetToDefault:(BOOL)reset; + +/** + * If is a local file, set this property to YES to take advantage of UIKit's image caching. Defaults to YES. + */ +@property BOOL shouldCacheImage; + +/** + * If the downloader implements progressive image rendering and this value is YES progressive renders of the + * image will be displayed as the image downloads. Regardless of this properties value, progress renders will + * only occur when the node is visible. Defaults to YES. + */ +@property BOOL shouldRenderProgressImages; + +/** + * The image quality of the current image. + * + * If the URL is set, this is a number between 0 and 1 and can be used to track + * progressive progress. Calculated by dividing number of bytes / expected number of total bytes. + * This is zero until the first progressive render or the final display. + * + * If the URL is unset, this is 1 if defaultImage or image is set to non-nil. + * + */ +@property (readonly) CGFloat currentImageQuality; + +/** + * The currentImageQuality (value between 0 and 1) of the last image that completed displaying. + */ +@property (readonly) CGFloat renderedImageQuality; + +/** + * Download progress of the current image. + * When downloading a network image, this value would be updated to track download progress (value between 0 and 1) + * This is 1 if image load from cache or network successfully. + */ +@property (readonly) CGFloat downloadProgress; + +@end + + +#pragma mark - + +/** + * The methods declared by the ASNetworkImageNodeDelegate protocol allow the adopting delegate to respond to + * notifications such as finished decoding and downloading an image. + */ +@protocol ASNetworkImageNodeDelegate +@optional + +/** + * Notification that the image node started to load + * + * @param imageNode The sender. + * + * @discussion Called on the main thread. + */ +- (void)imageNodeDidStartFetchingData:(ASNetworkImageNode *)imageNode; + +/** + * Notification that the image node will load image from cache + * + * @param imageNode The sender. + * + * @discussion Called on the main thread. + */ +- (void)imageNodeWillLoadImageFromCache:(ASNetworkImageNode *)imageNode; + +/** + * Notification that the image node finished loading image from cache + * + * @param imageNode The sender. + * + * @discussion Called on the main thread. + */ +- (void)imageNodeDidLoadImageFromCache:(ASNetworkImageNode *)imageNode; + +/** + * Notification that the image node failed to load image from cache + * + * @param imageNode The sender. + * + * @discussion Called on the main thread. + */ +- (void)imageNodeDidFailToLoadImageFromCache:(ASNetworkImageNode *)imageNode; + +/** + * Notification that the image node will load image from network + * + * @param imageNode The sender. + * + * @discussion Called on the main thread. + */ +- (void)imageNodeWillLoadImageFromNetwork:(ASNetworkImageNode *)imageNode; + +/** + * Notification that the image node will start display + * + * @param imageNode The sender. + * + * @discussion Called on the main thread. + */ +- (void)imageNodeWillStartDisplayAsynchronously:(ASNetworkImageNode *)imageNode; + +/** + * Notification that the image node finished downloading an image, with additional info. + * If implemented, this method will be called instead of `imageNode:didLoadImage:`. + * + * @param imageNode The sender. + * @param image The newly-loaded image. + * @param info Additional information about the image load. + * + * @discussion Called on the main thread if useMainThreadDelegateCallbacks=YES (the default), otherwise on a background thread. + */ +- (void)imageNode:(ASNetworkImageNode *)imageNode didLoadImage:(UIImage *)image info:(ASNetworkImageLoadInfo *)info; + +/** + * Notification that the image node finished downloading an image. + * + * @param imageNode The sender. + * @param image The newly-loaded image. + * + * @discussion Called on the main thread if useMainThreadDelegateCallbacks=YES (the default), otherwise on a background thread. + */ +- (void)imageNode:(ASNetworkImageNode *)imageNode didLoadImage:(UIImage *)image; + +/** + * Notification that the image node failed to download the image. + * + * @param imageNode The sender. + * @param error The error with details. + * + * @discussion Called on the main thread if useMainThreadDelegateCallbacks=YES (the default), otherwise on a background thread. + */ +- (void)imageNode:(ASNetworkImageNode *)imageNode didFailWithError:(NSError *)error; + +/** + * Notification that the image node finished decoding an image. + * + * @param imageNode The sender. + * + * @discussion Called on the main thread. + */ +- (void)imageNodeDidFinishDecoding:(ASNetworkImageNode *)imageNode; + + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASNetworkImageNode.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASNetworkImageNode.mm new file mode 100644 index 0000000..085fc06 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASNetworkImageNode.mm @@ -0,0 +1,945 @@ +// +// ASNetworkImageNode.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + +#if AS_PIN_REMOTE_IMAGE +#import +#endif + +@interface ASNetworkImageNode () +{ + // Only access any of these while locked. + __weak id _delegate; + + NSURL *_URL; + UIImage *_defaultImage; + + NSInteger _cacheSentinel; + id _downloadIdentifier; + // The download identifier that we have set a progress block on, if any. + id _downloadIdentifierForProgressBlock; + + CGFloat _currentImageQuality; + CGFloat _renderedImageQuality; + CGFloat _downloadProgress; + + // Immutable and set on init only. We don't need to lock in this case. + __weak id _downloader; + + // Immutable and set on init only. We don't need to lock in this case. + __weak id _cache; + + // Group all of these BOOLs into a shared bitfield struct in order to save on memory used. + struct { + unsigned int delegateWillStartDisplayAsynchronously:1; + unsigned int delegateWillLoadImageFromCache:1; + unsigned int delegateWillLoadImageFromNetwork:1; + unsigned int delegateDidStartFetchingData:1; + unsigned int delegateDidFailWithError:1; + unsigned int delegateDidFinishDecoding:1; + unsigned int delegateDidLoadImage:1; + unsigned int delegateDidLoadImageFromCache:1; + unsigned int delegateDidLoadImageWithInfo:1; + unsigned int delegateDidFailToLoadImageFromCache:1; + + unsigned int downloaderImplementsSetProgress:1; + unsigned int downloaderImplementsSetPriority:1; + unsigned int downloaderImplementsAnimatedImage:1; + unsigned int downloaderImplementsCancelWithResume:1; + unsigned int downloaderImplementsDownloadWithPriority:1; + + unsigned int cacheSupportsClearing:1; + unsigned int cacheSupportsSynchronousFetch:1; + + unsigned int imageLoaded:1; + unsigned int imageWasSetExternally:1; + unsigned int shouldRenderProgressImages:1; + unsigned int shouldCacheImage:1; + } _networkImageNodeFlags; +} + +@end + +@implementation ASNetworkImageNode + +static std::atomic_bool _useMainThreadDelegateCallbacks(true); + +@dynamic image; + +- (instancetype)initWithCache:(id)cache downloader:(id)downloader +{ + if (!(self = [super init])) + return nil; + + _cache = (id)cache; + _downloader = (id)downloader; + + _networkImageNodeFlags.downloaderImplementsSetProgress = [downloader respondsToSelector:@selector(setProgressImageBlock:callbackQueue:withDownloadIdentifier:)]; + _networkImageNodeFlags.downloaderImplementsSetPriority = [downloader respondsToSelector:@selector(setPriority:withDownloadIdentifier:)]; + _networkImageNodeFlags.downloaderImplementsAnimatedImage = [downloader respondsToSelector:@selector(animatedImageWithData:)]; + _networkImageNodeFlags.downloaderImplementsCancelWithResume = [downloader respondsToSelector:@selector(cancelImageDownloadWithResumePossibilityForIdentifier:)]; + _networkImageNodeFlags.downloaderImplementsDownloadWithPriority = [downloader respondsToSelector:@selector(downloadImageWithURL:priority:callbackQueue:downloadProgress:completion:)]; + + _networkImageNodeFlags.cacheSupportsClearing = [cache respondsToSelector:@selector(clearFetchedImageFromCacheWithURL:)]; + _networkImageNodeFlags.cacheSupportsSynchronousFetch = [cache respondsToSelector:@selector(synchronouslyFetchedCachedImageWithURL:)]; + + _networkImageNodeFlags.shouldCacheImage = YES; + _networkImageNodeFlags.shouldRenderProgressImages = YES; + self.shouldBypassEnsureDisplay = YES; + + return self; +} + +- (instancetype)init +{ +#if AS_PIN_REMOTE_IMAGE + return [self initWithCache:[ASPINRemoteImageDownloader sharedDownloader] downloader:[ASPINRemoteImageDownloader sharedDownloader]]; +#else + return [self initWithCache:nil downloader:[ASBasicImageDownloader sharedImageDownloader]]; +#endif +} + +- (void)dealloc +{ + [self _cancelImageDownloadWithResumePossibility:NO]; +} + +- (dispatch_queue_t)callbackQueue +{ + return dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); +} + +#pragma mark - Public methods -- must lock + +/// Setter for public image property. It has the side effect of setting an internal _networkImageNodeFlags.imageWasSetExternally that prevents setting an image internally. Setting an image internally should happen with the _setImage: method +- (void)setImage:(UIImage *)image +{ + ASLockScopeSelf(); + [self _locked_setImage:image]; +} + +- (void)_locked_setImage:(UIImage *)image +{ + DISABLED_ASAssertLocked(__instanceLock__); + + BOOL imageWasSetExternally = (image != nil); + BOOL shouldCancelAndClear = imageWasSetExternally && (imageWasSetExternally != _networkImageNodeFlags.imageWasSetExternally); + _networkImageNodeFlags.imageWasSetExternally = imageWasSetExternally; + if (shouldCancelAndClear) { + ASDisplayNodeAssertNil(_URL, @"Directly setting an image on an ASNetworkImageNode causes it to behave like an ASImageNode instead of an ASNetworkImageNode. If this is what you want, set the URL to nil first."); + _URL = nil; + [self _locked_cancelDownloadAndClearImageWithResumePossibility:NO]; + } + + // If our image is being set externally, the image quality is 100% + if (imageWasSetExternally) { + [self _setCurrentImageQuality:1.0]; + [self _setDownloadProgress:1.0]; + } + + [self _locked__setImage:image]; +} + +/// Setter for private image property. See @c _locked_setImage why this is needed +- (void)_setImage:(UIImage *)image +{ + ASLockScopeSelf(); + [self _locked__setImage:image]; +} + +- (void)_locked__setImage:(UIImage *)image +{ + DISABLED_ASAssertLocked(__instanceLock__); + [super _locked_setImage:image]; +} + +// Deprecated +- (void)setURLs:(NSArray *)URLs +{ + [self setURL:[URLs firstObject]]; +} + +// Deprecated +- (NSArray *)URLs +{ + return @[self.URL]; +} + +- (void)setURL:(NSURL *)URL +{ + [self setURL:URL resetToDefault:YES]; +} + +- (void)setURL:(NSURL *)URL resetToDefault:(BOOL)reset +{ + { + ASLockScopeSelf(); + + if (ASObjectIsEqual(URL, _URL)) { + return; + } + + URL = [URL copy]; + + ASDisplayNodeAssert(_networkImageNodeFlags.imageWasSetExternally == NO, @"Setting a URL to an ASNetworkImageNode after setting an image changes its behavior from an ASImageNode to an ASNetworkImageNode. If this is what you want, set the image to nil first."); + + _networkImageNodeFlags.imageWasSetExternally = NO; + + [self _locked_cancelImageDownloadWithResumePossibility:NO]; + + [self _setDownloadProgress:0.0]; + + _networkImageNodeFlags.imageLoaded = NO; + + _URL = URL; + + // If URL is nil and URL was not equal to _URL (checked at the top), then we previously had a URL but it's been nil'd out. + BOOL hadURL = (URL == nil); + if (reset || hadURL) { + [self _setCurrentImageQuality:(hadURL ? 0.0 : 1.0)]; + [self _locked__setImage:_defaultImage]; + [self _locked_setAnimatedImage:nil]; + } + } + + [self setNeedsPreload]; +} + +- (NSURL *)URL +{ + return ASLockedSelf(_URL); +} + +- (void)setDefaultImage:(UIImage *)defaultImage +{ + ASLockScopeSelf(); + + [self _locked_setDefaultImage:defaultImage]; +} + +- (void)_locked_setDefaultImage:(UIImage *)defaultImage +{ + if (ASObjectIsEqual(defaultImage, _defaultImage)) { + return; + } + + _defaultImage = defaultImage; + + if (!_networkImageNodeFlags.imageLoaded) { + [self _setCurrentImageQuality:((_URL == nil) ? 0.0 : 1.0)]; + [self _locked__setImage:defaultImage]; + } +} + +- (UIImage *)defaultImage +{ + return ASLockedSelf(_defaultImage); +} + +- (void)setCurrentImageQuality:(CGFloat)currentImageQuality +{ + ASLockScopeSelf(); + _currentImageQuality = currentImageQuality; +} + +- (CGFloat)currentImageQuality +{ + return ASLockedSelf(_currentImageQuality); +} + +/** + * Always use these methods internally to update the current image quality + * We want to maintain the order that currentImageQuality is set regardless of the calling thread, + * so we always have to dispatch to the main thread to ensure that we queue the operations in the correct order. + * (see comment in displayDidFinish) + */ +- (void)_setCurrentImageQuality:(CGFloat)imageQuality +{ + dispatch_async(dispatch_get_main_queue(), ^{ + self.currentImageQuality = imageQuality; + }); +} + +- (void)setDownloadProgress:(CGFloat)downloadProgress +{ + ASLockScopeSelf(); + _downloadProgress = downloadProgress; +} + +- (CGFloat)downloadProgress +{ + return ASLockedSelf(_downloadProgress); +} + +/** + * Always use these methods internally to update the current download progress + * We want to maintain the order that downloadProgress is set regardless of the calling thread, + * so we always have to dispatch to the main thread to ensure that we queue the operations in the correct order. + * (see comment in displayDidFinish) + */ +- (void)_setDownloadProgress:(CGFloat)downloadProgress +{ + dispatch_async(dispatch_get_main_queue(), ^{ + self.downloadProgress = downloadProgress; + }); +} + +- (void)setRenderedImageQuality:(CGFloat)renderedImageQuality +{ + ASLockScopeSelf(); + _renderedImageQuality = renderedImageQuality; +} + +- (CGFloat)renderedImageQuality +{ + ASLockScopeSelf(); + return _renderedImageQuality; +} + +- (void)setDelegate:(id)delegate +{ + ASLockScopeSelf(); + _delegate = delegate; + + _networkImageNodeFlags.delegateWillStartDisplayAsynchronously = [delegate respondsToSelector:@selector(imageNodeWillStartDisplayAsynchronously:)]; + _networkImageNodeFlags.delegateWillLoadImageFromCache = [delegate respondsToSelector:@selector(imageNodeWillLoadImageFromCache:)]; + _networkImageNodeFlags.delegateWillLoadImageFromNetwork = [delegate respondsToSelector:@selector(imageNodeWillLoadImageFromNetwork:)]; + _networkImageNodeFlags.delegateDidStartFetchingData = [delegate respondsToSelector:@selector(imageNodeDidStartFetchingData:)]; + _networkImageNodeFlags.delegateDidFailWithError = [delegate respondsToSelector:@selector(imageNode:didFailWithError:)]; + _networkImageNodeFlags.delegateDidFinishDecoding = [delegate respondsToSelector:@selector(imageNodeDidFinishDecoding:)]; + _networkImageNodeFlags.delegateDidLoadImage = [delegate respondsToSelector:@selector(imageNode:didLoadImage:)]; + _networkImageNodeFlags.delegateDidLoadImageFromCache = [delegate respondsToSelector:@selector(imageNodeDidLoadImageFromCache:)]; + _networkImageNodeFlags.delegateDidLoadImageWithInfo = [delegate respondsToSelector:@selector(imageNode:didLoadImage:info:)]; + _networkImageNodeFlags.delegateDidFailToLoadImageFromCache = [delegate respondsToSelector:@selector(imageNodeDidFailToLoadImageFromCache:)]; +} + +- (id)delegate +{ + ASLockScopeSelf(); + return _delegate; +} + +- (void)setShouldRenderProgressImages:(BOOL)shouldRenderProgressImages +{ + if (ASLockedSelfCompareAssign(_networkImageNodeFlags.shouldRenderProgressImages, shouldRenderProgressImages)) { + [self _updateProgressImageBlockOnDownloaderIfNeeded]; + } +} + +- (BOOL)shouldRenderProgressImages +{ + ASLockScopeSelf(); + return _networkImageNodeFlags.shouldRenderProgressImages; +} + +- (void)setShouldCacheImage:(BOOL)shouldCacheImage +{ + ASLockedSelfCompareAssign(_networkImageNodeFlags.shouldCacheImage, shouldCacheImage); +} + +- (BOOL)shouldCacheImage +{ + ASLockScopeSelf(); + return _networkImageNodeFlags.shouldCacheImage; +} + +- (BOOL)placeholderShouldPersist +{ + ASLockScopeSelf(); + return (self.image == nil && self.animatedImage == nil && _URL != nil); +} + +/* displayWillStartAsynchronously: in ASMultiplexImageNode has a very similar implementation. Changes here are likely necessary + in ASMultiplexImageNode as well. */ +- (void)displayWillStartAsynchronously:(BOOL)asynchronously +{ + [super displayWillStartAsynchronously:asynchronously]; + + id delegate; + BOOL notifyDelegate; + { + ASLockScopeSelf(); + notifyDelegate = _networkImageNodeFlags.delegateWillStartDisplayAsynchronously; + delegate = _delegate; + } + if (notifyDelegate) { + [delegate imageNodeWillStartDisplayAsynchronously:self]; + } + + if (asynchronously == NO && _networkImageNodeFlags.cacheSupportsSynchronousFetch) { + ASLockScopeSelf(); + + NSURL *url = _URL; + if (_networkImageNodeFlags.imageLoaded == NO && url && _downloadIdentifier == nil) { + UIImage *result = [[_cache synchronouslyFetchedCachedImageWithURL:url] asdk_image]; + if (result) { + [self _setCurrentImageQuality:1.0]; + [self _setDownloadProgress:1.0]; + [self _locked__setImage:result]; + _networkImageNodeFlags.imageLoaded = YES; + + // Call out to the delegate. + if (_networkImageNodeFlags.delegateDidLoadImageWithInfo) { + ASUnlockScope(self); + const auto info = [[ASNetworkImageLoadInfo alloc] initWithURL:url sourceType:ASNetworkImageSourceSynchronousCache downloadIdentifier:nil userInfo:nil]; + [delegate imageNode:self didLoadImage:result info:info]; + } else if (_networkImageNodeFlags.delegateDidLoadImage) { + ASUnlockScope(self); + [delegate imageNode:self didLoadImage:result]; + } + } + } + } + + if (self.image == nil) { + [self _updatePriorityOnDownloaderIfNeeded]; + } +} + +/* visibileStateDidChange in ASMultiplexImageNode has a very similar implementation. Changes here are likely necessary + in ASMultiplexImageNode as well. */ +- (void)didEnterVisibleState +{ + [super didEnterVisibleState]; + [self _updatePriorityOnDownloaderIfNeeded]; + [self _updateProgressImageBlockOnDownloaderIfNeeded]; +} + +- (void)didExitVisibleState +{ + [super didExitVisibleState]; + [self _updatePriorityOnDownloaderIfNeeded]; + [self _updateProgressImageBlockOnDownloaderIfNeeded]; +} + +- (void)didExitDisplayState +{ + [super didExitDisplayState]; + [self _updatePriorityOnDownloaderIfNeeded]; +} + +- (void)didExitPreloadState +{ + [super didExitPreloadState]; + + // If the image was set explicitly we don't want to remove it while exiting the preload state + if (ASLockedSelf(_networkImageNodeFlags.imageWasSetExternally)) { + return; + } + + [self _cancelDownloadAndClearImageWithResumePossibility:YES]; +} + +- (void)didEnterPreloadState +{ + [super didEnterPreloadState]; + + // Image was set externally no need to load an image + [self _lazilyLoadImageIfNecessary]; +} + ++ (void)setUseMainThreadDelegateCallbacks:(BOOL)useMainThreadDelegateCallbacks +{ + _useMainThreadDelegateCallbacks = useMainThreadDelegateCallbacks; +} + ++ (BOOL)useMainThreadDelegateCallbacks +{ + return _useMainThreadDelegateCallbacks; +} + +#pragma mark - Progress + +- (void)_updateDownloadedProgress:(CGFloat)progress + downloadIdentifier:(nullable id)downloadIdentifier +{ + ASLockScopeSelf(); + // Getting a result back for a different download identifier, download must not have been successfully canceled + if (ASObjectIsEqual(_downloadIdentifier, downloadIdentifier) == NO && downloadIdentifier != nil) { + return; + } + [self _setDownloadProgress:progress]; +} + +- (void)handleProgressImage:(UIImage *)progressImage progress:(CGFloat)progress downloadIdentifier:(nullable id)downloadIdentifier +{ + ASLockScopeSelf(); + + // Getting a result back for a different download identifier, download must not have been successfully canceled + if (ASObjectIsEqual(_downloadIdentifier, downloadIdentifier) == NO && downloadIdentifier != nil) { + return; + } + + as_log_verbose(ASImageLoadingLog(), "Received progress image for %@ q: %.2g id: %@", self, progress, progressImage); + [self _setCurrentImageQuality:progress]; + [self _locked__setImage:progressImage]; +} + +- (void)_updatePriorityOnDownloaderIfNeeded +{ + if (_networkImageNodeFlags.downloaderImplementsSetPriority) { + ASLockScopeSelf(); + + if (_downloadIdentifier != nil) { + ASImageDownloaderPriority priority = ASImageDownloaderPriorityWithInterfaceState(_interfaceState); + [_downloader setPriority:priority withDownloadIdentifier:_downloadIdentifier]; + } + } +} + +- (void)_updateProgressImageBlockOnDownloaderIfNeeded +{ + // If the downloader doesn't do progress, we are done. + if (_networkImageNodeFlags.downloaderImplementsSetProgress == NO) { + return; + } + + // Read state. + [self lock]; + BOOL shouldRender = _networkImageNodeFlags.shouldRenderProgressImages && ASInterfaceStateIncludesVisible(_interfaceState); + id oldDownloadIDForProgressBlock = _downloadIdentifierForProgressBlock; + id newDownloadIDForProgressBlock = shouldRender ? _downloadIdentifier : nil; + BOOL clearAndReattempt = NO; + [self unlock]; + + // If we're already bound to the correct download, we're done. + if (ASObjectIsEqual(oldDownloadIDForProgressBlock, newDownloadIDForProgressBlock)) { + return; + } + + // Unbind from the previous download. + if (oldDownloadIDForProgressBlock != nil) { + as_log_verbose(ASImageLoadingLog(), "Disabled progress images for %@ id: %@", self, oldDownloadIDForProgressBlock); + [_downloader setProgressImageBlock:nil callbackQueue:[self callbackQueue] withDownloadIdentifier:oldDownloadIDForProgressBlock]; + } + + // Bind to the current download. + if (newDownloadIDForProgressBlock != nil) { + __weak __typeof(self) weakSelf = self; + as_log_verbose(ASImageLoadingLog(), "Enabled progress images for %@ id: %@", self, newDownloadIDForProgressBlock); + [_downloader setProgressImageBlock:^(UIImage * _Nonnull progressImage, CGFloat progress, id _Nullable downloadIdentifier) { + [weakSelf handleProgressImage:progressImage progress:progress downloadIdentifier:downloadIdentifier]; + } callbackQueue:[self callbackQueue] withDownloadIdentifier:newDownloadIDForProgressBlock]; + } + + // Update state local state with lock held. + { + ASLockScopeSelf(); + // Check if the oldDownloadIDForProgressBlock still is the same as the _downloadIdentifierForProgressBlock + if (_downloadIdentifierForProgressBlock == oldDownloadIDForProgressBlock) { + _downloadIdentifierForProgressBlock = newDownloadIDForProgressBlock; + } else if (newDownloadIDForProgressBlock != nil) { + // If this is not the case another thread did change the _downloadIdentifierForProgressBlock already so + // we have to deregister the newDownloadIDForProgressBlock that we registered above + clearAndReattempt = YES; + } + } + + if (clearAndReattempt) { + // In this case another thread changed the _downloadIdentifierForProgressBlock before we finished registering + // the new progress block for newDownloadIDForProgressBlock ID. Let's clear it now and reattempt to register + [_downloader setProgressImageBlock:nil callbackQueue:[self callbackQueue] withDownloadIdentifier:newDownloadIDForProgressBlock]; + [self _updateProgressImageBlockOnDownloaderIfNeeded]; + } +} + +- (void)_cancelDownloadAndClearImageWithResumePossibility:(BOOL)storeResume +{ + ASLockScopeSelf(); + [self _locked_cancelDownloadAndClearImageWithResumePossibility:storeResume]; +} + +- (void)_locked_cancelDownloadAndClearImageWithResumePossibility:(BOOL)storeResume +{ + DISABLED_ASAssertLocked(__instanceLock__); + + [self _locked_cancelImageDownloadWithResumePossibility:storeResume]; + + [self _locked_setAnimatedImage:nil]; + [self _setCurrentImageQuality:0.0]; + [self _setDownloadProgress:0.0]; + [self _locked__setImage:_defaultImage]; + + _networkImageNodeFlags.imageLoaded = NO; + + if (_networkImageNodeFlags.cacheSupportsClearing) { + if (_URL != nil) { + as_log_verbose(ASImageLoadingLog(), "Clearing cached image for %@ url: %@", self, _URL); + [_cache clearFetchedImageFromCacheWithURL:_URL]; + } + } +} + +- (void)_cancelImageDownloadWithResumePossibility:(BOOL)storeResume +{ + ASLockScopeSelf(); + [self _locked_cancelImageDownloadWithResumePossibility:storeResume]; +} + +- (void)_locked_cancelImageDownloadWithResumePossibility:(BOOL)storeResume +{ + DISABLED_ASAssertLocked(__instanceLock__); + + if (!_downloadIdentifier) { + return; + } + + if (_downloadIdentifier) { + if (storeResume && _networkImageNodeFlags.downloaderImplementsCancelWithResume) { + as_log_verbose(ASImageLoadingLog(), "Canceling image download with resume for %@ id: %@", self, _downloadIdentifier); + [_downloader cancelImageDownloadWithResumePossibilityForIdentifier:_downloadIdentifier]; + } else { + as_log_verbose(ASImageLoadingLog(), "Canceling image download no resume for %@ id: %@", self, _downloadIdentifier); + [_downloader cancelImageDownloadForIdentifier:_downloadIdentifier]; + } + } + _downloadIdentifier = nil; + _cacheSentinel++; +} + +- (void)_downloadImageWithCompletion:(void (^)(id imageContainer, NSError*, id downloadIdentifier, id userInfo))finished +{ + ASPerformBlockOnBackgroundThread(^{ + NSURL *url; + id downloadIdentifier; + BOOL cancelAndReattempt = NO; + ASInterfaceState interfaceState; + + // Below, to avoid performance issues, we're calling downloadImageWithURL without holding the lock. This is a bit ugly because + // We need to reobtain the lock after and ensure that the task we've kicked off still matches our URL. If not, we need to cancel + // it and try again. + { + ASLockScopeSelf(); + url = self->_URL; + interfaceState = self->_interfaceState; + } + + dispatch_queue_t callbackQueue = [self callbackQueue]; + __weak __typeof__(self) weakSelf = self; + ASImageDownloaderProgress downloadProgress = ^(CGFloat progress){ + __typeof__(self) strongSelf = weakSelf; + if (strongSelf) { + [strongSelf _updateDownloadedProgress:progress downloadIdentifier:downloadIdentifier]; + } + }; + ASImageDownloaderCompletion completion = ^(id _Nullable imageContainer, NSError * _Nullable error, id _Nullable downloadIdentifier, id _Nullable userInfo) { + if (finished != NULL) { + finished(imageContainer, error, downloadIdentifier, userInfo); + } + }; + + if (self->_networkImageNodeFlags.downloaderImplementsDownloadWithPriority) { + /* + Decide a priority based on the current interface state of this node. + It can happen that this method was called when the node entered preload state + but the interface state, at this point, tells us that the node is (going to be) visible. + If that's the case, we jump to a higher priority directly. + */ + ASImageDownloaderPriority priority = ASImageDownloaderPriorityWithInterfaceState(interfaceState); + + downloadIdentifier = [self->_downloader downloadImageWithURL:url + + priority:priority + callbackQueue:callbackQueue + downloadProgress:downloadProgress + completion:completion]; + } else { + /* + Kick off a download with default priority. + The actual "default" value is decided by the downloader. + ASBasicImageDownloader and ASPINRemoteImageDownloader both use ASImageDownloaderPriorityImminent + which is mapped to NSURLSessionTaskPriorityDefault. + + This means that preload and display nodes use the same priority + and their requests are put into the same pool. + */ + downloadIdentifier = [self->_downloader downloadImageWithURL:url + callbackQueue:callbackQueue + downloadProgress:downloadProgress + completion:completion]; + } + as_log_verbose(ASImageLoadingLog(), "Downloading image for %@ url: %@", self, url); + + { + ASLockScopeSelf(); + if (ASObjectIsEqual(self->_URL, url)) { + // The download we kicked off is correct, no need to do any more work. + self->_downloadIdentifier = downloadIdentifier; + } else { + // The URL changed since we kicked off our download task. This shouldn't happen often so we'll pay the cost and + // cancel that request and kick off a new one. + cancelAndReattempt = YES; + } + } + + if (cancelAndReattempt) { + if (downloadIdentifier != nil) { + as_log_verbose(ASImageLoadingLog(), "Canceling image download no resume for %@ id: %@", self, downloadIdentifier); + [self->_downloader cancelImageDownloadForIdentifier:downloadIdentifier]; + } + [self _downloadImageWithCompletion:finished]; + return; + } + + [self _updateProgressImageBlockOnDownloaderIfNeeded]; + }); +} + +- (void)_lazilyLoadImageIfNecessary +{ + ASDisplayNodeAssertMainThread(); + + [self lock]; + __weak id delegate = _delegate; + BOOL delegateDidStartFetchingData = _networkImageNodeFlags.delegateDidStartFetchingData; + BOOL delegateWillLoadImageFromCache = _networkImageNodeFlags.delegateWillLoadImageFromCache; + BOOL delegateWillLoadImageFromNetwork = _networkImageNodeFlags.delegateWillLoadImageFromNetwork; + BOOL delegateDidLoadImageFromCache = _networkImageNodeFlags.delegateDidLoadImageFromCache; + BOOL delegateDidFailToLoadImageFromCache = _networkImageNodeFlags.delegateDidFailToLoadImageFromCache; + BOOL isImageLoaded = _networkImageNodeFlags.imageLoaded; + __block NSURL *URL = _URL; + id currentDownloadIdentifier = _downloadIdentifier; + [self unlock]; + + if (!isImageLoaded && URL != nil && currentDownloadIdentifier == nil) { + if (delegateDidStartFetchingData) { + [delegate imageNodeDidStartFetchingData:self]; + } + + if (URL.isFileURL) { + dispatch_async(dispatch_get_main_queue(), ^{ + ASLockScopeSelf(); + + // Bail out if not the same URL anymore + if (!ASObjectIsEqual(URL, self->_URL)) { + return; + } + + if (self->_networkImageNodeFlags.shouldCacheImage) { + [self _locked__setImage:[UIImage imageNamed:URL.path.lastPathComponent]]; + } else { + // First try to load the path directly, for efficiency assuming a developer who + // doesn't want caching is trying to be as minimal as possible. + auto nonAnimatedImage = [[UIImage alloc] initWithContentsOfFile:URL.path]; + if (nonAnimatedImage == nil) { + // If we couldn't find it, execute an -imageNamed:-like search so we can find resources even if the + // extension is not provided in the path. This allows the same path to work regardless of shouldCacheImage. + NSString *filename = [[NSBundle mainBundle] pathForResource:URL.path.lastPathComponent ofType:nil]; + if (filename != nil) { + nonAnimatedImage = [[UIImage alloc] initWithContentsOfFile:filename]; + // Update URL to point to newly-resolved file URL for animated image load. + URL = nonAnimatedImage ? [NSURL URLWithString:filename] : URL; + } + } + + // If the file may be an animated gif and then created an animated image. + id animatedImage = nil; + if (self->_networkImageNodeFlags.downloaderImplementsAnimatedImage) { + const auto data = [[NSData alloc] initWithContentsOfURL:URL]; + if (data != nil) { + animatedImage = [self->_downloader animatedImageWithData:data]; + + if ([animatedImage respondsToSelector:@selector(isDataSupported:)] && [animatedImage isDataSupported:data] == NO) { + animatedImage = nil; + } + } + } + + if (animatedImage != nil) { + [self _locked_setAnimatedImage:animatedImage]; + } else { + [self _locked__setImage:nonAnimatedImage]; + } + } + + self->_networkImageNodeFlags.imageLoaded = YES; + + [self _setCurrentImageQuality:1.0]; + [self _setDownloadProgress:1.0]; + + if (self->_networkImageNodeFlags.delegateDidLoadImageWithInfo) { + ASUnlockScope(self); + const auto info = [[ASNetworkImageLoadInfo alloc] initWithURL:URL sourceType:ASNetworkImageSourceFileURL downloadIdentifier:nil userInfo:nil]; + [delegate imageNode:self didLoadImage:self.image info:info]; + } else if (self->_networkImageNodeFlags.delegateDidLoadImage) { + ASUnlockScope(self); + [delegate imageNode:self didLoadImage:self.image]; + } + }); + } else { + __weak __typeof__(self) weakSelf = self; + const auto finished = ^(id imageContainer, NSError *error, id downloadIdentifier, ASNetworkImageSourceType imageSource, id userInfo) { + ASPerformBlockOnBackgroundThread(^{ + __typeof__(self) strongSelf = weakSelf; + if (strongSelf == nil) { + return; + } + + as_log_verbose(ASImageLoadingLog(), "Downloaded image for %@ img: %@ url: %@", self, [imageContainer asdk_image], URL); + + // Grab the lock for the rest of the block + ASLockScope(strongSelf); + + //Getting a result back for a different download identifier, download must not have been successfully canceled + if (ASObjectIsEqual(strongSelf->_downloadIdentifier, downloadIdentifier) == NO && downloadIdentifier != nil) { + return; + } + + //No longer in preload range, no point in setting the results (they won't be cleared in exit preload range) + if (ASInterfaceStateIncludesPreload(strongSelf->_interfaceState) == NO) { + strongSelf->_downloadIdentifier = nil; + strongSelf->_cacheSentinel++; + return; + } + + UIImage *newImage; + if (imageContainer != nil) { + [strongSelf _setCurrentImageQuality:1.0]; + [strongSelf _setDownloadProgress:1.0]; + NSData *animatedImageData = [imageContainer asdk_animatedImageData]; + if (animatedImageData && strongSelf->_networkImageNodeFlags.downloaderImplementsAnimatedImage) { + id animatedImage = [strongSelf->_downloader animatedImageWithData:animatedImageData]; + [strongSelf _locked_setAnimatedImage:animatedImage]; + } else { + newImage = [imageContainer asdk_image]; + [strongSelf _locked__setImage:newImage]; + } + strongSelf->_networkImageNodeFlags.imageLoaded = YES; + } + + strongSelf->_downloadIdentifier = nil; + strongSelf->_cacheSentinel++; + + void (^calloutBlock)(ASNetworkImageNode *inst); + + if (newImage) { + if (strongSelf->_networkImageNodeFlags.delegateDidLoadImageWithInfo) { + calloutBlock = ^(ASNetworkImageNode *strongSelf) { + const auto info = [[ASNetworkImageLoadInfo alloc] initWithURL:URL sourceType:imageSource downloadIdentifier:downloadIdentifier userInfo:userInfo]; + [delegate imageNode:strongSelf didLoadImage:newImage info:info]; + }; + } else if (strongSelf->_networkImageNodeFlags.delegateDidLoadImage) { + calloutBlock = ^(ASNetworkImageNode *strongSelf) { + [delegate imageNode:strongSelf didLoadImage:newImage]; + }; + } + } else if (error && strongSelf->_networkImageNodeFlags.delegateDidFailWithError) { + calloutBlock = ^(ASNetworkImageNode *strongSelf) { + [delegate imageNode:strongSelf didFailWithError:error]; + }; + } + + if (calloutBlock) { + if (ASNetworkImageNode.useMainThreadDelegateCallbacks) { + ASPerformBlockOnMainThread(^{ + if (auto strongSelf = weakSelf) { + calloutBlock(strongSelf); + } + }); + } else { + calloutBlock(strongSelf); + } + } + }); + }; + + // As the _cache and _downloader is only set once in the intializer we don't have to use a + // lock in here + if (_cache != nil) { + NSInteger cacheSentinel = ASLockedSelf(++_cacheSentinel); + + as_log_verbose(ASImageLoadingLog(), "Decaching image for %@ url: %@", self, URL); + + ASImageCacherCompletion completion = ^(id imageContainer, ASImageCacheType cacheType) { + // If the cache sentinel changed, that means this request was cancelled. + if (ASLockedSelf(self->_cacheSentinel != cacheSentinel)) { + return; + } + + if ([imageContainer asdk_image] == nil && [imageContainer asdk_animatedImageData] == nil && self->_downloader != nil) { + if (delegateDidFailToLoadImageFromCache) { + [delegate imageNodeDidFailToLoadImageFromCache:self]; + } + if (delegateWillLoadImageFromNetwork) { + [delegate imageNodeWillLoadImageFromNetwork:self]; + } + [self _downloadImageWithCompletion:^(id imageContainer, NSError *error, id downloadIdentifier, id userInfo) { + finished(imageContainer, error, downloadIdentifier, ASNetworkImageSourceDownload, userInfo); + }]; + } else { + if (delegateDidLoadImageFromCache) { + [delegate imageNodeDidLoadImageFromCache:self]; + } + as_log_verbose(ASImageLoadingLog(), "Decached image for %@ img: %@ url: %@ cacheType: %@", self, [imageContainer asdk_image], URL, cacheType); + finished(imageContainer, nil, nil, cacheType == ASImageCacheTypeSynchronous ? ASNetworkImageSourceSynchronousCache : ASNetworkImageSourceAsynchronousCache, nil); + } + }; + + if (delegateWillLoadImageFromCache) { + [delegate imageNodeWillLoadImageFromCache:self]; + } + [_cache cachedImageWithURL:URL + callbackQueue:[self callbackQueue] + completion:completion]; + } else { + if (delegateWillLoadImageFromNetwork) { + [delegate imageNodeWillLoadImageFromNetwork:self]; + } + [self _downloadImageWithCompletion:^(id imageContainer, NSError *error, id downloadIdentifier, id userInfo) { + finished(imageContainer, error, downloadIdentifier, ASNetworkImageSourceDownload, userInfo); + }]; + } + } + } +} + +#pragma mark - ASDisplayNode+Subclasses + +- (void)displayDidFinish +{ + [super displayDidFinish]; + + id delegate = nil; + + { + ASLockScopeSelf(); + if (_networkImageNodeFlags.delegateDidFinishDecoding && self.layer.contents != nil) { + /* We store the image quality in _currentImageQuality whenever _image is set. On the following displayDidFinish, we'll know that + _currentImageQuality is the quality of the image that has just finished rendering. In order for this to be accurate, we + need to be sure we are on main thread when we set _currentImageQuality. Otherwise, it is possible for _currentImageQuality + to be modified at a point where it is too late to cancel the main thread's previous display (the final sentinel check has passed), + but before the displayDidFinish of the previous display pass is called. In this situation, displayDidFinish would be called and we + would set _renderedImageQuality to the new _currentImageQuality, but the actual quality of the rendered image should be the previous + value stored in _currentImageQuality. */ + + _renderedImageQuality = _currentImageQuality; + + // Assign the delegate to be used + delegate = _delegate; + } + } + + if (delegate != nil) { + [delegate imageNodeDidFinishDecoding:self]; + } +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASNodeController+Beta.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASNodeController+Beta.h new file mode 100644 index 0000000..6501a11 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASNodeController+Beta.h @@ -0,0 +1,62 @@ +// +// ASNodeController+Beta.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import // for ASInterfaceState protocol + +/* ASNodeController is currently beta and open to change in the future */ +@interface ASNodeController<__covariant DisplayNodeType : ASDisplayNode *> + : NSObject + +@property (nonatomic, strong /* may be weak! */) DisplayNodeType node; + +// Until an ASNodeController can be provided in place of an ASCellNode, some apps may prefer to have +// nodes keep their controllers alive (and a weak reference from controller to node) + +@property (nonatomic) BOOL shouldInvertStrongReference; + +- (void)loadNode; + +// for descriptions see definition +- (void)nodeDidLoad ASDISPLAYNODE_REQUIRES_SUPER; +- (void)nodeDidLayout ASDISPLAYNODE_REQUIRES_SUPER; + +// This is only called during Yoga-driven layouts. +- (void)nodeWillCalculateLayout:(ASSizeRange)constrainedSize ASDISPLAYNODE_REQUIRES_SUPER; + +- (void)didEnterVisibleState ASDISPLAYNODE_REQUIRES_SUPER; +- (void)didExitVisibleState ASDISPLAYNODE_REQUIRES_SUPER; + +- (void)didEnterDisplayState ASDISPLAYNODE_REQUIRES_SUPER; +- (void)didExitDisplayState ASDISPLAYNODE_REQUIRES_SUPER; + +- (void)didEnterPreloadState ASDISPLAYNODE_REQUIRES_SUPER; +- (void)didExitPreloadState ASDISPLAYNODE_REQUIRES_SUPER; + +- (void)interfaceStateDidChange:(ASInterfaceState)newState + fromState:(ASInterfaceState)oldState ASDISPLAYNODE_REQUIRES_SUPER; + +- (void)hierarchyDisplayDidFinish ASDISPLAYNODE_REQUIRES_SUPER; + +- (void)didEnterHierarchy ASDISPLAYNODE_REQUIRES_SUPER; +- (void)didExitHierarchy ASDISPLAYNODE_REQUIRES_SUPER; + +/** + * @discussion Attempts (via ASLockSequence, a backing-off spinlock similar to + * std::lock()) to lock both the node and its ASNodeController, if one exists. + */ +- (ASLockSet)lockPair; + +@end + +@interface ASDisplayNode (ASNodeController) + +@property(nonatomic, readonly) ASNodeController *nodeController; + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASNodeController+Beta.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASNodeController+Beta.mm new file mode 100644 index 0000000..0245a7d --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASNodeController+Beta.mm @@ -0,0 +1,136 @@ +// +// ASNodeController+Beta.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +#define _node (_shouldInvertStrongReference ? _weakNode : _strongNode) + +@implementation ASNodeController +{ + ASDisplayNode *_strongNode; + __weak ASDisplayNode *_weakNode; + AS::RecursiveMutex __instanceLock__; +} + +- (void)loadNode +{ + ASLockScopeSelf(); + self.node = [[ASDisplayNode alloc] init]; +} + +- (ASDisplayNode *)node +{ + ASLockScopeSelf(); + if (_node == nil) { + [self loadNode]; + } + return _node; +} + +- (void)setupReferencesWithNode:(ASDisplayNode *)node +{ + ASLockScopeSelf(); + if (_shouldInvertStrongReference) { + // The node should own the controller; weak reference from controller to node. + _weakNode = node; + _strongNode = nil; + } else { + // The controller should own the node; weak reference from node to controller. + _strongNode = node; + _weakNode = nil; + } + + [node __setNodeController:self]; +} + +- (void)setNode:(ASDisplayNode *)node +{ + ASLockScopeSelf(); + if (node == _node) { + return; + } + [self setupReferencesWithNode:node]; + [node addInterfaceStateDelegate:self]; +} + +- (void)setShouldInvertStrongReference:(BOOL)shouldInvertStrongReference +{ + ASLockScopeSelf(); + if (_shouldInvertStrongReference != shouldInvertStrongReference) { + // Because the BOOL controls which ivar we access, get the node before toggling. + ASDisplayNode *node = _node; + _shouldInvertStrongReference = shouldInvertStrongReference; + [self setupReferencesWithNode:node]; + } +} + +// subclass overrides +- (void)nodeDidLoad {} +- (void)nodeDidLayout {} +- (void)nodeWillCalculateLayout:(ASSizeRange)constrainedSize {} + +- (void)didEnterVisibleState {} +- (void)didExitVisibleState {} + +- (void)didEnterDisplayState {} +- (void)didExitDisplayState {} + +- (void)didEnterPreloadState {} +- (void)didExitPreloadState {} + +- (void)interfaceStateDidChange:(ASInterfaceState)newState + fromState:(ASInterfaceState)oldState {} + +- (void)hierarchyDisplayDidFinish {} + +- (void)didEnterHierarchy {} +- (void)didExitHierarchy {} + +- (ASLockSet)lockPair { + ASLockSet lockSet = ASLockSequence(^BOOL(ASAddLockBlock addLock) { + if (!addLock(_node)) { + return NO; + } + if (!addLock(self)) { + return NO; + } + return YES; + }); + + return lockSet; +} + +#pragma mark NSLocking + +- (void)lock +{ + __instanceLock__.lock(); +} + +- (void)unlock +{ + __instanceLock__.unlock(); +} + +- (BOOL)tryLock +{ + return __instanceLock__.try_lock(); +} + +@end + +@implementation ASDisplayNode (ASNodeController) + +- (ASNodeController *)nodeController +{ + return _weakNodeController ?: _strongNodeController; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASPagerFlowLayout.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASPagerFlowLayout.h new file mode 100644 index 0000000..17c61d6 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASPagerFlowLayout.h @@ -0,0 +1,18 @@ +// +// ASPagerFlowLayout.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface ASPagerFlowLayout : UICollectionViewFlowLayout + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASPagerFlowLayout.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASPagerFlowLayout.mm new file mode 100644 index 0000000..301cea2 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASPagerFlowLayout.mm @@ -0,0 +1,110 @@ +// +// ASPagerFlowLayout.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import + +@interface ASPagerFlowLayout () { + __weak ASCellNode *_currentCellNode; +} + +@end + +//TODO make this an ASCollectionViewLayout +@implementation ASPagerFlowLayout + +- (ASCollectionView *)asCollectionView +{ + // Dynamic cast is too slow and not worth it. + return (ASCollectionView *)self.collectionView; +} + +- (void)prepareLayout +{ + [super prepareLayout]; + if (_currentCellNode == nil) { + [self _updateCurrentNode]; + } +} + +- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset +{ + // Don't mess around if the user is interacting with the page node. Although if just a rotation happened we should + // try to use the current index path to not end up setting the target content offset to something in between pages + if (!self.collectionView.decelerating && !self.collectionView.tracking) { + NSIndexPath *indexPath = [self.asCollectionView indexPathForNode:_currentCellNode]; + if (indexPath) { + return [self _targetContentOffsetForItemAtIndexPath:indexPath proposedContentOffset:proposedContentOffset]; + } + } + + return [super targetContentOffsetForProposedContentOffset:proposedContentOffset]; +} + +- (CGPoint)_targetContentOffsetForItemAtIndexPath:(NSIndexPath *)indexPath proposedContentOffset:(CGPoint)proposedContentOffset +{ + if ([self _dataSourceIsEmpty]) { + return proposedContentOffset; + } + + UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForItemAtIndexPath:indexPath]; + if (attributes == nil) { + return proposedContentOffset; + } + + CGFloat xOffset = (CGRectGetWidth(self.collectionView.bounds) - CGRectGetWidth(attributes.frame)) / 2.0; + return CGPointMake(attributes.frame.origin.x - xOffset, proposedContentOffset.y); +} + +- (BOOL)_dataSourceIsEmpty +{ + return ([self.collectionView numberOfSections] == 0 || + [self.collectionView numberOfItemsInSection:0] == 0); +} + +- (void)_updateCurrentNode +{ + // Never change node during an animated bounds change (rotation) + // NOTE! Listening for -prepareForAnimatedBoundsChange and -finalizeAnimatedBoundsChange + // isn't sufficient here! It's broken! + NSArray *animKeys = self.collectionView.layer.animationKeys; + for (NSString *key in animKeys) { + if ([key hasPrefix:@"bounds"]) { + return; + } + } + + CGRect bounds = self.collectionView.bounds; + CGRect rect = CGRectMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds), 1, 1); + + NSIndexPath *indexPath = [self layoutAttributesForElementsInRect:rect].firstObject.indexPath; + if (indexPath) { + ASCellNode *node = [self.asCollectionView nodeForItemAtIndexPath:indexPath]; + if (node) { + _currentCellNode = node; + } + } +} + +- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds +{ + [self _updateCurrentNode]; + return [super shouldInvalidateLayoutForBoundsChange:newBounds]; +} + +- (UICollectionViewLayoutInvalidationContext *)invalidationContextForBoundsChange:(CGRect)newBounds +{ + UICollectionViewFlowLayoutInvalidationContext *ctx = (UICollectionViewFlowLayoutInvalidationContext *)[super invalidationContextForBoundsChange:newBounds]; + ctx.invalidateFlowLayoutDelegateMetrics = YES; + ctx.invalidateFlowLayoutAttributes = YES; + return ctx; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASPagerNode+Beta.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASPagerNode+Beta.h new file mode 100644 index 0000000..a768f6b --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASPagerNode+Beta.h @@ -0,0 +1,15 @@ +// +// ASPagerNode+Beta.h +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +@interface ASPagerNode (Beta) + +- (instancetype)initUsingAsyncCollectionLayout; + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASPagerNode.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASPagerNode.h new file mode 100644 index 0000000..9fb9977 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASPagerNode.h @@ -0,0 +1,133 @@ +// +// ASPagerNode.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +@class ASPagerNode; +@class ASPagerFlowLayout; + +NS_ASSUME_NONNULL_BEGIN + +#define ASPagerNodeDataSource ASPagerDataSource +@protocol ASPagerDataSource + +/** + * This method replaces -collectionView:numberOfItemsInSection: + * + * @param pagerNode The sender. + * @return The total number of pages that can display in the pagerNode. + */ +- (NSInteger)numberOfPagesInPagerNode:(ASPagerNode *)pagerNode; + +@optional + +/** + * This method replaces -collectionView:nodeForItemAtIndexPath: + * + * @param pagerNode The sender. + * @param index The index of the requested node. + * @return a node for display at this index. This will be called on the main thread and should + * not implement reuse (it will be called once per row). Unlike UICollectionView's version, + * this method is not called when the row is about to display. + */ +- (ASCellNode *)pagerNode:(ASPagerNode *)pagerNode nodeAtIndex:(NSInteger)index; + +/** + * This method replaces -collectionView:nodeBlockForItemAtIndexPath: + * This method takes precedence over pagerNode:nodeAtIndex: if implemented. + * + * @param pagerNode The sender. + * @param index The index of the requested node. + * @return a block that creates the node for display at this index. + * Must be thread-safe (can be called on the main thread or a background + * queue) and should not implement reuse (it will be called once per row). + */ +- (ASCellNodeBlock)pagerNode:(ASPagerNode *)pagerNode nodeBlockAtIndex:(NSInteger)index; + +@end + +@protocol ASPagerDelegate +@end + +/** + * A horizontal, paging collection node. + */ +@interface ASPagerNode : ASCollectionNode + +/** + * Configures a default horizontal, paging flow layout with 0 inter-item spacing. + */ +- (instancetype)init; + +/** + * Initializer with custom-configured flow layout properties. + * + * NOTE: The flow layout must have a horizontal scroll direction. + */ +- (instancetype)initWithCollectionViewLayout:(ASPagerFlowLayout *)flowLayout; + +/** + * Data Source is required, and uses a different protocol from ASCollectionNode. + */ +- (void)setDataSource:(nullable id )dataSource; +- (nullable id )dataSource AS_WARN_UNUSED_RESULT; + +/** + * Delegate is optional. + * This includes UIScrollViewDelegate as well as most methods from UICollectionViewDelegate, like willDisplay... + */ +- (void)setDelegate:(nullable id )delegate; +- (nullable id )delegate AS_WARN_UNUSED_RESULT; + +/** + * The underlying ASCollectionView object. + */ +@property (readonly) ASCollectionView *view; + +/** + * Returns the current page index. Main thread only. + */ +@property (nonatomic, readonly) NSInteger currentPageIndex; + +/** + * Scroll the contents of the receiver to ensure that the page is visible + */ +- (void)scrollToPageAtIndex:(NSInteger)index animated:(BOOL)animated; + +/** + * Returns the node for the passed page index + */ +- (ASCellNode *)nodeForPageAtIndex:(NSInteger)index AS_WARN_UNUSED_RESULT; + +/** + * Returns the index of the page for the cell passed or NSNotFound + */ +- (NSInteger)indexOfPageWithNode:(ASCellNode *)node; + +/** + * Tells the pager node to allow its view controller to automatically adjust its content insets. + * + * @see UIViewController.automaticallyAdjustsScrollViewInsets + * + * @discussion ASPagerNode should usually not have its content insets automatically adjusted + * because it scrolls horizontally, and flow layout will log errors because the pages + * do not fit between the top & bottom insets of the collection view. + * + * The default value is NO, which means that ASPagerNode expects that its view controller will + * have automaticallyAdjustsScrollViewInsets=NO. + * + * If this property is NO, but your view controller has automaticallyAdjustsScrollViewInsets=YES, + * the pager node will set the property on the view controller to NO and log a warning message. In the future, + * the pager node will just log the warning, and you'll need to configure your view controller on your own. + */ +@property (nonatomic) BOOL allowsAutomaticInsetsAdjustment; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASPagerNode.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASPagerNode.mm new file mode 100644 index 0000000..7ad8f1a --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASPagerNode.mm @@ -0,0 +1,230 @@ +// +// ASPagerNode.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import +#import +#import +#import +#import +#import +#import +#import +#import + +@interface ASPagerNode () +{ + __weak id _pagerDataSource; + ASPagerNodeProxy *_proxyDataSource; + struct { + unsigned nodeBlockAtIndex:1; + unsigned nodeAtIndex:1; + } _pagerDataSourceFlags; + BOOL _allowsAutomaticInsetsAdjustment; + + __weak id _pagerDelegate; + ASPagerNodeProxy *_proxyDelegate; +} + +@end + +@implementation ASPagerNode + +@dynamic view, delegate, dataSource; + +#pragma mark - Lifecycle + +- (instancetype)init +{ + ASPagerFlowLayout *flowLayout = [[ASPagerFlowLayout alloc] init]; + flowLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal; + flowLayout.minimumInteritemSpacing = 0; + flowLayout.minimumLineSpacing = 0; + + return [self initWithCollectionViewLayout:flowLayout]; +} + +- (instancetype)initWithCollectionViewLayout:(ASPagerFlowLayout *)flowLayout; +{ + ASDisplayNodeAssert([flowLayout isKindOfClass:[ASPagerFlowLayout class]], @"ASPagerNode requires a flow layout."); + ASDisplayNodeAssertTrue(flowLayout.scrollDirection == UICollectionViewScrollDirectionHorizontal); + self = [super initWithCollectionViewLayout:flowLayout]; + return self; +} + +- (instancetype)initUsingAsyncCollectionLayout +{ + ASCollectionGalleryLayoutDelegate *layoutDelegate = [[ASCollectionGalleryLayoutDelegate alloc] initWithScrollableDirections:ASScrollDirectionHorizontalDirections]; + self = [super initWithLayoutDelegate:layoutDelegate layoutFacilitator:nil]; + if (self) { + layoutDelegate.propertiesProvider = self; + } + return self; +} + +#pragma mark - ASDisplayNode + +- (void)didLoad +{ + [super didLoad]; + + ASCollectionView *cv = self.view; + cv.asyncDataSource = (id)_proxyDataSource ?: self; + cv.asyncDelegate = (id)_proxyDelegate ?: self; +#if TARGET_OS_IOS + cv.pagingEnabled = YES; + cv.scrollsToTop = NO; +#endif + cv.allowsSelection = NO; + cv.showsVerticalScrollIndicator = NO; + cv.showsHorizontalScrollIndicator = NO; + + ASRangeTuningParameters minimumRenderParams = { .leadingBufferScreenfuls = 0.0, .trailingBufferScreenfuls = 0.0 }; + ASRangeTuningParameters minimumPreloadParams = { .leadingBufferScreenfuls = 1.0, .trailingBufferScreenfuls = 1.0 }; + [self setTuningParameters:minimumRenderParams forRangeMode:ASLayoutRangeModeMinimum rangeType:ASLayoutRangeTypeDisplay]; + [self setTuningParameters:minimumPreloadParams forRangeMode:ASLayoutRangeModeMinimum rangeType:ASLayoutRangeTypePreload]; + + ASRangeTuningParameters fullRenderParams = { .leadingBufferScreenfuls = 1.0, .trailingBufferScreenfuls = 1.0 }; + ASRangeTuningParameters fullPreloadParams = { .leadingBufferScreenfuls = 2.0, .trailingBufferScreenfuls = 2.0 }; + [self setTuningParameters:fullRenderParams forRangeMode:ASLayoutRangeModeFull rangeType:ASLayoutRangeTypeDisplay]; + [self setTuningParameters:fullPreloadParams forRangeMode:ASLayoutRangeModeFull rangeType:ASLayoutRangeTypePreload]; +} + +#pragma mark - Getters / Setters + +- (NSInteger)currentPageIndex +{ + return (self.view.contentOffset.x / [self pageSize].width); +} + +- (CGSize)pageSize +{ + UIEdgeInsets contentInset = self.contentInset; + CGSize pageSize = self.bounds.size; + pageSize.height -= (contentInset.top + contentInset.bottom); + return pageSize; +} + +#pragma mark - Helpers + +- (void)scrollToPageAtIndex:(NSInteger)index animated:(BOOL)animated +{ + NSIndexPath *indexPath = [NSIndexPath indexPathForItem:index inSection:0]; + [self scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionLeft animated:animated]; +} + +- (ASCellNode *)nodeForPageAtIndex:(NSInteger)index +{ + return [self nodeForItemAtIndexPath:[NSIndexPath indexPathForItem:index inSection:0]]; +} + +- (NSInteger)indexOfPageWithNode:(ASCellNode *)node +{ + NSIndexPath *indexPath = [self indexPathForNode:node]; + if (!indexPath) { + return NSNotFound; + } + return indexPath.row; +} + +#pragma mark - ASCollectionGalleryLayoutPropertiesProviding + +- (CGSize)galleryLayoutDelegate:(nonnull ASCollectionGalleryLayoutDelegate *)delegate sizeForElements:(nonnull ASElementMap *)elements +{ + ASDisplayNodeAssertMainThread(); + return [self pageSize]; +} + +#pragma mark - ASCollectionDataSource + +- (ASCellNodeBlock)collectionNode:(ASCollectionNode *)collectionNode nodeBlockForItemAtIndexPath:(NSIndexPath *)indexPath +{ + if (_pagerDataSourceFlags.nodeBlockAtIndex) { + return [_pagerDataSource pagerNode:self nodeBlockAtIndex:indexPath.item]; + } else if (_pagerDataSourceFlags.nodeAtIndex) { + ASCellNode *node = [_pagerDataSource pagerNode:self nodeAtIndex:indexPath.item]; + return ^{ return node; }; + } else { + ASDisplayNodeFailAssert(@"Pager data source must implement either %@ or %@. Data source: %@", NSStringFromSelector(@selector(pagerNode:nodeBlockAtIndex:)), NSStringFromSelector(@selector(pagerNode:nodeAtIndex:)), _pagerDataSource); + return ^{ + return [[ASCellNode alloc] init]; + }; + } +} + +- (NSInteger)collectionNode:(ASCollectionNode *)collectionNode numberOfItemsInSection:(NSInteger)section +{ + ASDisplayNodeAssert(_pagerDataSource != nil, @"ASPagerNode must have a data source to load nodes to display"); + return [_pagerDataSource numberOfPagesInPagerNode:self]; +} + +#pragma mark - ASCollectionDelegate + +- (ASSizeRange)collectionNode:(ASCollectionNode *)collectionNode constrainedSizeForItemAtIndexPath:(NSIndexPath *)indexPath +{ + return ASSizeRangeMake([self pageSize]); +} + +#pragma mark - Data Source Proxy + +- (id )dataSource +{ + return _pagerDataSource; +} + +- (void)setDataSource:(id )dataSource +{ + if (dataSource != _pagerDataSource) { + _pagerDataSource = dataSource; + + if (dataSource == nil) { + memset(&_pagerDataSourceFlags, 0, sizeof(_pagerDataSourceFlags)); + } else { + _pagerDataSourceFlags.nodeBlockAtIndex = [_pagerDataSource respondsToSelector:@selector(pagerNode:nodeBlockAtIndex:)]; + _pagerDataSourceFlags.nodeAtIndex = [_pagerDataSource respondsToSelector:@selector(pagerNode:nodeAtIndex:)]; + } + + _proxyDataSource = dataSource ? [[ASPagerNodeProxy alloc] initWithTarget:dataSource interceptor:self] : nil; + + super.dataSource = (id )_proxyDataSource; + } +} + +- (void)setDelegate:(id)delegate +{ + if (delegate != _pagerDelegate) { + _pagerDelegate = delegate; + _proxyDelegate = delegate ? [[ASPagerNodeProxy alloc] initWithTarget:delegate interceptor:self] : nil; + super.delegate = (id )_proxyDelegate; + } +} + +- (void)proxyTargetHasDeallocated:(ASDelegateProxy *)proxy +{ + [self setDataSource:nil]; + [self setDelegate:nil]; +} + +- (void)didEnterHierarchy +{ + [super didEnterHierarchy]; + + // Check that our view controller does not automatically set our content insets + // In every use case I can imagine, the pager is not hosted inside a range-managed node. + if (_allowsAutomaticInsetsAdjustment == NO) { + UIViewController *vc = [self.view asdk_associatedViewController]; + if (vc.automaticallyAdjustsScrollViewInsets) { + NSLog(@"AsyncDisplayKit: ASPagerNode is setting automaticallyAdjustsScrollViewInsets=NO on its owning view controller %@. This automatic behavior will be disabled in the future. Set allowsAutomaticInsetsAdjustment=YES on the pager node to suppress this behavior.", vc); + vc.automaticallyAdjustsScrollViewInsets = NO; + } + } +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASRangeManagingNode.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASRangeManagingNode.h new file mode 100644 index 0000000..c331a77 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASRangeManagingNode.h @@ -0,0 +1,31 @@ +// +// ASRangeManagingNode.h +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +@class ASCellNode; + +NS_ASSUME_NONNULL_BEGIN + +/** + * Basically ASTableNode or ASCollectionNode. + */ +@protocol ASRangeManagingNode + +/** + * Retrieve the index path for the given node, if it's a member of this container. + * + * @param node The node. + * @return The index path, or nil if the node is not part of this container. + */ +- (nullable NSIndexPath *)indexPathForNode:(ASCellNode *)node; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASRunLoopQueue.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASRunLoopQueue.h new file mode 100644 index 0000000..298fe76 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASRunLoopQueue.h @@ -0,0 +1,91 @@ +// +// ASRunLoopQueue.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@protocol ASCATransactionQueueObserving +- (void)prepareForCATransactionCommit; +@end + +@interface ASAbstractRunLoopQueue : NSObject +@end + +AS_SUBCLASSING_RESTRICTED +@interface ASRunLoopQueue : ASAbstractRunLoopQueue + +/** + * Create a new queue with the given run loop and handler. + * + * @param runloop The run loop that will drive this queue. + * @param retainsObjects Whether the queue should retain its objects. + * @param handlerBlock An optional block to be run for each enqueued object. + * + * @discussion You may pass @c nil for the handler if you simply want the objects to + * be retained at enqueue time, and released during the run loop step. This is useful + * for creating a "main deallocation queue", as @c ASDeallocQueue creates its own + * worker thread with its own run loop. + */ +- (instancetype)initWithRunLoop:(CFRunLoopRef)runloop + retainObjects:(BOOL)retainsObjects + handler:(nullable void(^)(ObjectType dequeuedItem, BOOL isQueueDrained))handlerBlock; + +- (void)enqueue:(ObjectType)object; + +@property (readonly) BOOL isEmpty; + +@property (nonatomic) NSUInteger batchSize; // Default == 1. +@property (nonatomic) BOOL ensureExclusiveMembership; // Default == YES. Set-like behavior. + +@end + + + +/** + * The queue to run on main run loop before CATransaction commit. + * + * @discussion this queue will run after ASRunLoopQueue and before CATransaction commit + * to get last chance of updating/coalesce info like interface state. + * Each node will only be called once per transaction commit to reflect interface change. + */ +AS_SUBCLASSING_RESTRICTED +@interface ASCATransactionQueue : ASAbstractRunLoopQueue + +@property (readonly) BOOL isEmpty; + +@property (readonly, getter=isEnabled) BOOL enabled; + +- (void)enqueue:(id)object; + +@end + +extern ASCATransactionQueue *_ASSharedCATransactionQueue; +extern dispatch_once_t _ASSharedCATransactionQueueOnceToken; + +NS_INLINE ASCATransactionQueue *ASCATransactionQueueGet(void) { + dispatch_once(&_ASSharedCATransactionQueueOnceToken, ^{ + _ASSharedCATransactionQueue = [[ASCATransactionQueue alloc] init]; + }); + return _ASSharedCATransactionQueue; +} + +@interface ASDeallocQueue : NSObject + ++ (ASDeallocQueue *)sharedDeallocationQueue NS_RETURNS_RETAINED; + +- (void)drain; + +- (void)releaseObjectInBackground:(id __strong _Nullable * _Nonnull)objectPtr; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASRunLoopQueue.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASRunLoopQueue.mm new file mode 100644 index 0000000..fd896af --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASRunLoopQueue.mm @@ -0,0 +1,491 @@ +// +// ASRunLoopQueue.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import +#import +#import +#import +#import + +#define ASRunLoopQueueLoggingEnabled 0 +#define ASRunLoopQueueVerboseLoggingEnabled 0 + +using AS::MutexLocker; + +static void runLoopSourceCallback(void *info) { + // No-op +#if ASRunLoopQueueVerboseLoggingEnabled + NSLog(@"<%@> - Called runLoopSourceCallback", info); +#endif +} + +#pragma mark - ASDeallocQueue + +@implementation ASDeallocQueue { + std::vector _queue; + AS::Mutex _lock; +} + ++ (ASDeallocQueue *)sharedDeallocationQueue NS_RETURNS_RETAINED +{ + static ASDeallocQueue *deallocQueue = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + deallocQueue = [[ASDeallocQueue alloc] init]; + }); + return deallocQueue; +} + +- (void)dealloc +{ + ASDisplayNodeFailAssert(@"Singleton should not dealloc."); +} + +- (void)releaseObjectInBackground:(id _Nullable __strong *)objectPtr +{ + NSParameterAssert(objectPtr != NULL); + + // Cast to CFType so we can manipulate retain count manually. + const auto cfPtr = (CFTypeRef *)(void *)objectPtr; + if (!cfPtr || !*cfPtr) { + return; + } + + _lock.lock(); + const auto isFirstEntry = _queue.empty(); + // Push the pointer into our queue and clear their pointer. + // This "steals" the +1 from ARC and nils their pointer so they can't + // access or release the object. + _queue.push_back(*cfPtr); + *cfPtr = NULL; + _lock.unlock(); + + if (isFirstEntry) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.100 * NSEC_PER_SEC)), dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0), ^{ + [self drain]; + }); + } +} + +- (void)drain +{ + _lock.lock(); + const auto q = std::move(_queue); + _lock.unlock(); + for (CFTypeRef ref : q) { + // NOTE: Could check that retain count is 1 and retry later if not. + CFRelease(ref); + } +} + +@end + +@implementation ASAbstractRunLoopQueue + +- (instancetype)init +{ + self = [super init]; + if (self == nil) { + return nil; + } + ASDisplayNodeAssert(self.class != [ASAbstractRunLoopQueue class], @"Should never create instances of abstract class ASAbstractRunLoopQueue."); + return self; +} + +@end + +#pragma mark - ASRunLoopQueue + +@interface ASRunLoopQueue () { + CFRunLoopRef _runLoop; + CFRunLoopSourceRef _runLoopSource; + CFRunLoopObserverRef _runLoopObserver; + NSPointerArray *_internalQueue; // Use NSPointerArray so we can decide __strong or __weak per-instance. + AS::RecursiveMutex _internalQueueLock; + + // In order to not pollute the top-level activities, each queue has 1 root activity. + os_activity_t _rootActivity; + +#if ASRunLoopQueueLoggingEnabled + NSTimer *_runloopQueueLoggingTimer; +#endif +} + +@property (nonatomic) void (^queueConsumer)(id dequeuedItem, BOOL isQueueDrained); + +@end + +@implementation ASRunLoopQueue + +- (instancetype)initWithRunLoop:(CFRunLoopRef)runloop retainObjects:(BOOL)retainsObjects handler:(void (^)(id _Nullable, BOOL))handlerBlock +{ + if (self = [super init]) { + _runLoop = runloop; + NSPointerFunctionsOptions options = retainsObjects ? NSPointerFunctionsStrongMemory : NSPointerFunctionsWeakMemory; + _internalQueue = [[NSPointerArray alloc] initWithOptions:options]; + _queueConsumer = handlerBlock; + _batchSize = 1; + _ensureExclusiveMembership = YES; + + // We don't want to pollute the top-level app activities with run loop batches, so we create one top-level + // activity per queue, and each batch activity joins that one instead. + _rootActivity = as_activity_create("Process run loop queue items", OS_ACTIVITY_NONE, OS_ACTIVITY_FLAG_DEFAULT); + { + // Log a message identifying this queue into the queue's root activity. + as_activity_scope_verbose(_rootActivity); + as_log_verbose(ASDisplayLog(), "Created run loop queue: %@", self); + } + + // Self is guaranteed to outlive the observer. Without the high cost of a weak pointer, + // unowned(__unsafe_unretained) allows us to avoid flagging the memory cycle detector. + unowned __typeof__(self) weakSelf = self; + void (^handlerBlock) (CFRunLoopObserverRef observer, CFRunLoopActivity activity) = ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) { + [weakSelf processQueue]; + }; + _runLoopObserver = CFRunLoopObserverCreateWithHandler(NULL, kCFRunLoopBeforeWaiting, true, 0, handlerBlock); + CFRunLoopAddObserver(_runLoop, _runLoopObserver, kCFRunLoopCommonModes); + + // It is not guaranteed that the runloop will turn if it has no scheduled work, and this causes processing of + // the queue to stop. Attaching a custom loop source to the run loop and signal it if new work needs to be done + CFRunLoopSourceContext sourceContext = {}; + sourceContext.perform = runLoopSourceCallback; +#if ASRunLoopQueueLoggingEnabled + sourceContext.info = (__bridge void *)self; +#endif + _runLoopSource = CFRunLoopSourceCreate(NULL, 0, &sourceContext); + CFRunLoopAddSource(runloop, _runLoopSource, kCFRunLoopCommonModes); + +#if ASRunLoopQueueLoggingEnabled + _runloopQueueLoggingTimer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(checkRunLoop) userInfo:nil repeats:YES]; + [[NSRunLoop mainRunLoop] addTimer:_runloopQueueLoggingTimer forMode:NSRunLoopCommonModes]; +#endif + } + return self; +} + +- (void)dealloc +{ + if (CFRunLoopContainsSource(_runLoop, _runLoopSource, kCFRunLoopCommonModes)) { + CFRunLoopRemoveSource(_runLoop, _runLoopSource, kCFRunLoopCommonModes); + } + CFRelease(_runLoopSource); + _runLoopSource = nil; + + if (CFRunLoopObserverIsValid(_runLoopObserver)) { + CFRunLoopObserverInvalidate(_runLoopObserver); + } + CFRelease(_runLoopObserver); + _runLoopObserver = nil; +} + +#if ASRunLoopQueueLoggingEnabled +- (void)checkRunLoop +{ + NSLog(@"<%@> - Jobs: %ld", self, _internalQueue.count); +} +#endif + +- (void)processQueue +{ + BOOL hasExecutionBlock = (_queueConsumer != nil); + + // If we have an execution block, this vector will be populated, otherwise remains empty. + // This is to avoid needlessly retaining/releasing the objects if we don't have a block. + std::vector itemsToProcess; + + BOOL isQueueDrained = NO; + { + MutexLocker l(_internalQueueLock); + + NSInteger internalQueueCount = _internalQueue.count; + // Early-exit if the queue is empty. + if (internalQueueCount == 0) { + return; + } + + ASSignpostStart(RunLoopQueueBatch, self, "%s", object_getClassName(self)); + + // Snatch the next batch of items. + NSInteger maxCountToProcess = MIN(internalQueueCount, self.batchSize); + + /** + * For each item in the next batch, if it's non-nil then NULL it out + * and if we have an execution block then add it in. + * This could be written a bunch of different ways but + * this particular one nicely balances readability, safety, and efficiency. + */ + NSInteger foundItemCount = 0; + for (NSInteger i = 0; i < internalQueueCount && foundItemCount < maxCountToProcess; i++) { + /** + * It is safe to use unsafe_unretained here. If the queue is weak, the + * object will be added to the autorelease pool. If the queue is strong, + * it will retain the object until we transfer it (retain it) in itemsToProcess. + */ + unowned id ptr = (__bridge id)[_internalQueue pointerAtIndex:i]; + if (ptr != nil) { + foundItemCount++; + if (hasExecutionBlock) { + itemsToProcess.push_back(ptr); + } + [_internalQueue replacePointerAtIndex:i withPointer:NULL]; + } + } + + if (foundItemCount == 0) { + // If _internalQueue holds weak references, and all of them just become NULL, then the array + // is never marked as needsCompletion, and compact will return early, not removing the NULL's. + // Inserting a NULL here ensures the compaction will take place. + // See http://www.openradar.me/15396578 and https://stackoverflow.com/a/40274426/1136669 + [_internalQueue addPointer:NULL]; + } + + [_internalQueue compact]; + if (_internalQueue.count == 0) { + isQueueDrained = YES; + } + } + + // itemsToProcess will be empty if _queueConsumer == nil so no need to check again. + const auto count = itemsToProcess.size(); + if (count > 0) { + as_activity_scope_verbose(as_activity_create("Process run loop queue batch", _rootActivity, OS_ACTIVITY_FLAG_DEFAULT)); + const auto itemsEnd = itemsToProcess.cend(); + for (auto iterator = itemsToProcess.begin(); iterator < itemsEnd; iterator++) { + unowned id value = *iterator; + _queueConsumer(value, isQueueDrained && iterator == itemsEnd - 1); + as_log_verbose(ASDisplayLog(), "processed %@", value); + } + if (count > 1) { + as_log_verbose(ASDisplayLog(), "processed %lu items", (unsigned long)count); + } + } + + // If the queue is not fully drained yet force another run loop to process next batch of items + if (!isQueueDrained) { + CFRunLoopSourceSignal(_runLoopSource); + CFRunLoopWakeUp(_runLoop); + } + + ASSignpostEnd(RunLoopQueueBatch, self, "count: %d", (int)count); +} + +- (void)enqueue:(id)object +{ + if (!object) { + return; + } + + MutexLocker l(_internalQueueLock); + + // Check if the object exists. + BOOL foundObject = NO; + + if (_ensureExclusiveMembership) { + for (id currentObject in _internalQueue) { + if (currentObject == object) { + foundObject = YES; + break; + } + } + } + + if (!foundObject) { + [_internalQueue addPointer:(__bridge void *)object]; + if (_internalQueue.count == 1) { + CFRunLoopSourceSignal(_runLoopSource); + CFRunLoopWakeUp(_runLoop); + } + } +} + +- (BOOL)isEmpty +{ + MutexLocker l(_internalQueueLock); + return _internalQueue.count == 0; +} + +ASSynthesizeLockingMethodsWithMutex(_internalQueueLock) + +@end + +#pragma mark - ASCATransactionQueue + +@interface ASCATransactionQueue () { + CFRunLoopSourceRef _runLoopSource; + CFRunLoopObserverRef _preTransactionObserver; + + // Current buffer for new entries, only accessed from within its mutex. + std::vector> _internalQueue; + + // No retain, no release, pointer hash, pointer equality. + // Enforce uniqueness in our queue. std::unordered_set does a heap allocation for each entry – not good. + CFMutableSetRef _internalQueueHashSet; + + // Temporary buffer, only accessed from the main thread in -process. + std::vector> _batchBuffer; + + AS::Mutex _internalQueueLock; + + // In order to not pollute the top-level activities, each queue has 1 root activity. + os_activity_t _rootActivity; + +#if ASRunLoopQueueLoggingEnabled + NSTimer *_runloopQueueLoggingTimer; +#endif +} + +@end + +@implementation ASCATransactionQueue + +// CoreAnimation commit order is 2000000, the goal of this is to process shortly beforehand +// but after most other scheduled work on the runloop has processed. +static int const kASASCATransactionQueueOrder = 1000000; + +ASCATransactionQueue *_ASSharedCATransactionQueue; +dispatch_once_t _ASSharedCATransactionQueueOnceToken; + +- (instancetype)init +{ + if (self = [super init]) { + _internalQueueHashSet = CFSetCreateMutable(NULL, 0, NULL); + + // This is going to be a very busy queue – every node in the preload range will enter this queue. + // Save some time on first render by reserving space up front. + static constexpr int kInternalQueueInitialCapacity = 64; + _internalQueue.reserve(kInternalQueueInitialCapacity); + _batchBuffer.reserve(kInternalQueueInitialCapacity); + + // We don't want to pollute the top-level app activities with run loop batches, so we create one top-level + // activity per queue, and each batch activity joins that one instead. + _rootActivity = as_activity_create("Process run loop queue items", OS_ACTIVITY_NONE, OS_ACTIVITY_FLAG_DEFAULT); + { + // Log a message identifying this queue into the queue's root activity. + as_activity_scope_verbose(_rootActivity); + as_log_verbose(ASDisplayLog(), "Created run loop queue: %@", self); + } + + // Self is guaranteed to outlive the observer. Without the high cost of a weak pointer, + // unowned(__unsafe_unretained) allows us to avoid flagging the memory cycle detector. + unowned __typeof__(self) weakSelf = self; + _preTransactionObserver = CFRunLoopObserverCreateWithHandler(NULL, kCFRunLoopBeforeWaiting, true, kASASCATransactionQueueOrder, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) { + while (!weakSelf->_internalQueue.empty()) { + [weakSelf processQueue]; + } + }); + + CFRunLoopAddObserver(CFRunLoopGetMain(), _preTransactionObserver, kCFRunLoopCommonModes); + + // It is not guaranteed that the runloop will turn if it has no scheduled work, and this causes processing of + // the queue to stop. Attaching a custom loop source to the run loop and signal it if new work needs to be done + CFRunLoopSourceContext sourceContext = {}; + sourceContext.perform = runLoopSourceCallback; +#if ASRunLoopQueueLoggingEnabled + sourceContext.info = (__bridge void *)self; +#endif + _runLoopSource = CFRunLoopSourceCreate(NULL, 0, &sourceContext); + CFRunLoopAddSource(CFRunLoopGetMain(), _runLoopSource, kCFRunLoopCommonModes); + +#if ASRunLoopQueueLoggingEnabled + _runloopQueueLoggingTimer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(checkRunLoop) userInfo:nil repeats:YES]; + [[NSRunLoop mainRunLoop] addTimer:_runloopQueueLoggingTimer forMode:NSRunLoopCommonModes]; +#endif + } + return self; +} + +- (void)dealloc +{ + ASDisplayNodeAssertMainThread(); + + CFRelease(_internalQueueHashSet); + CFRunLoopRemoveSource(CFRunLoopGetMain(), _runLoopSource, kCFRunLoopCommonModes); + CFRelease(_runLoopSource); + _runLoopSource = nil; + + if (CFRunLoopObserverIsValid(_preTransactionObserver)) { + CFRunLoopObserverInvalidate(_preTransactionObserver); + } + CFRelease(_preTransactionObserver); + _preTransactionObserver = nil; +} + +#if ASRunLoopQueueLoggingEnabled +- (void)checkRunLoop +{ + NSLog(@"<%@> - Jobs: %ld", self, _internalQueue.count); +} +#endif + +- (void)processQueue +{ + ASDisplayNodeAssertMainThread(); + + AS::UniqueLock l(_internalQueueLock); + NSInteger count = _internalQueue.size(); + // Early-exit if the queue is empty. + if (count == 0) { + return; + } + as_activity_scope_verbose(as_activity_create("Process run loop queue batch", _rootActivity, OS_ACTIVITY_FLAG_DEFAULT)); + ASSignpostStart(RunLoopQueueBatch, self, "CATransactionQueue"); + + // Swap buffers, clear our hash table. + _internalQueue.swap(_batchBuffer); + CFSetRemoveAllValues(_internalQueueHashSet); + + // Unlock early. We are done with internal queue, and batch buffer is main-thread-only so no lock. + l.unlock(); + + for (const id &value : _batchBuffer) { + [value prepareForCATransactionCommit]; + as_log_verbose(ASDisplayLog(), "processed %@", value); + } + _batchBuffer.clear(); + as_log_verbose(ASDisplayLog(), "processed %lu items", (unsigned long)count); + ASSignpostEnd(RunLoopQueueBatch, self, "count: %d", (int)count); +} + +- (void)enqueue:(id)object +{ + if (!object) { + return; + } + + if (!self.enabled) { + [object prepareForCATransactionCommit]; + return; + } + + MutexLocker l(_internalQueueLock); + if (CFSetContainsValue(_internalQueueHashSet, (__bridge void *)object)) { + return; + } + CFSetAddValue(_internalQueueHashSet, (__bridge void *)object); + _internalQueue.emplace_back(object); + if (_internalQueue.size() == 1) { + CFRunLoopSourceSignal(_runLoopSource); + CFRunLoopWakeUp(CFRunLoopGetMain()); + } +} + +- (BOOL)isEmpty +{ + MutexLocker l(_internalQueueLock); + return _internalQueue.empty(); +} + +- (BOOL)isEnabled +{ + return ASActivateExperimentalFeature(ASExperimentalInterfaceStateCoalescing); +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASScrollNode.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASScrollNode.h new file mode 100644 index 0000000..1137e89 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASScrollNode.h @@ -0,0 +1,50 @@ +// +// ASScrollNode.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@class UIScrollView; + +/** + * Simple node that wraps UIScrollView. + */ +@interface ASScrollNode : ASDisplayNode + +/** + * @abstract The node's UIScrollView. + */ +@property (readonly) UIScrollView *view; + +/** + * @abstract When enabled, the size calculated by the node's layout spec is used as + * the .contentSize of the scroll view, instead of the bounds size. The bounds is instead + * allowed to match the parent's size (whenever it is finite - otherwise, the bounds size + * also grows to the full contentSize). It also works with .layoutSpecBlock(). + * NOTE: Most users of ASScrollNode will want to use this, and may be enabled by default later. + */ +@property BOOL automaticallyManagesContentSize; + +/** + * @abstract This property controls how the constrainedSize is interpreted when sizing the content. + * if you are using automaticallyManagesContentSize, it can be crucial to ensure that the sizing is + * done how you expect. + * Vertical: The constrainedSize is interpreted as having unbounded .height (CGFLOAT_MAX), allowing + * stacks and other content in the layout spec to expand and result in scrollable content. + * Horizontal: The constrainedSize is interpreted as having unbounded .width (CGFLOAT_MAX), ... + * Vertical & Horizontal: the constrainedSize is interpreted as unbounded in both directions. + * @default ASScrollDirectionVerticalDirections + */ +@property ASScrollDirection scrollableDirections; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASScrollNode.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASScrollNode.mm new file mode 100644 index 0000000..bb9c826 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASScrollNode.mm @@ -0,0 +1,187 @@ +// +// ASScrollNode.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import +#import +#import +#import +#import +#import +#import + +@interface ASScrollView : UIScrollView +@end + +@implementation ASScrollView + +// This special +layerClass allows ASScrollNode to get -layout calls from -layoutSublayers. ++ (Class)layerClass +{ + return [_ASDisplayLayer class]; +} + +- (ASScrollNode *)scrollNode +{ + return (ASScrollNode *)ASViewToDisplayNode(self); +} + +#pragma mark - _ASDisplayView behavior substitutions +// Need these to drive interfaceState so we know when we are visible, if not nested in another range-managing element. +// Because our superclass is a true UIKit class, we cannot also subclass _ASDisplayView. +- (void)willMoveToWindow:(UIWindow *)newWindow +{ + ASDisplayNode *node = self.scrollNode; // Create strong reference to weak ivar. + BOOL visible = (newWindow != nil); + if (visible && !node.inHierarchy) { + [node __enterHierarchy]; + } +} + +- (void)didMoveToWindow +{ + ASDisplayNode *node = self.scrollNode; // Create strong reference to weak ivar. + BOOL visible = (self.window != nil); + if (!visible && node.inHierarchy) { + [node __exitHierarchy]; + } +} + +- (NSArray *)accessibilityElements +{ + return [self.asyncdisplaykit_node accessibilityElements]; +} + +@end + +@implementation ASScrollNode +{ + ASScrollDirection _scrollableDirections; + BOOL _automaticallyManagesContentSize; + CGSize _contentCalculatedSizeFromLayout; +} +@dynamic view; + +- (instancetype)init +{ + if (self = [super init]) { + [self setViewBlock:^UIView *{ return [[ASScrollView alloc] init]; }]; + } + return self; +} + +- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize + restrictedToSize:(ASLayoutElementSize)size + relativeToParentSize:(CGSize)parentSize +{ + ASScopedLockSelfOrToRoot(); + + ASSizeRange contentConstrainedSize = constrainedSize; + if (ASScrollDirectionContainsVerticalDirection(_scrollableDirections)) { + contentConstrainedSize.max.height = CGFLOAT_MAX; + } + if (ASScrollDirectionContainsHorizontalDirection(_scrollableDirections)) { + contentConstrainedSize.max.width = CGFLOAT_MAX; + } + + ASLayout *layout = [super calculateLayoutThatFits:contentConstrainedSize + restrictedToSize:size + relativeToParentSize:parentSize]; + + if (_automaticallyManagesContentSize) { + // To understand this code, imagine we're containing a horizontal stack set within a vertical table node. + // Our parentSize is fixed ~375pt width, but 0 - INF height. Our stack measures 1000pt width, 50pt height. + // In this case, we want our scrollNode.bounds to be 375pt wide, and 50pt high. ContentSize 1000pt, 50pt. + // We can achieve this behavior by: + // 1. Always set contentSize to layout.size. + // 2. Set bounds to a size that is calculated by clamping parentSize against constrained size, + // unless one dimension is not defined, in which case adopt the contentSize for that dimension. + _contentCalculatedSizeFromLayout = layout.size; + CGSize selfSize = ASSizeRangeClamp(constrainedSize, parentSize); + if (ASPointsValidForLayout(selfSize.width) == NO) { + selfSize.width = _contentCalculatedSizeFromLayout.width; + } + if (ASPointsValidForLayout(selfSize.height) == NO) { + selfSize.height = _contentCalculatedSizeFromLayout.height; + } + + // The side effect for layout with CGFLOAT_MAX is that the min-height/width on the child of + // ScrollNode may not be applied correctly. Resulting in the contentSize less than the + // scrollNode's bounds which may not be what the child want (e.g. The child want to fill + // ScrollNode's bounds). In that case we need to give it a chance to layout with ScrollNode's + // bound in case children want to fill the ScrollNode's bound. + if ((ASScrollDirectionContainsVerticalDirection(_scrollableDirections) && + layout.size.height < selfSize.height) || + (ASScrollDirectionContainsHorizontalDirection(_scrollableDirections) && + layout.size.width < selfSize.width)) { + layout = [super calculateLayoutThatFits:constrainedSize + restrictedToSize:size + relativeToParentSize:parentSize]; + } + + // Don't provide a position, as that should be set by the parent. + layout = [ASLayout layoutWithLayoutElement:self + size:selfSize + sublayouts:layout.sublayouts]; + } + return layout; +} + +- (void)layout +{ + [super layout]; + + ASLockScopeSelf(); // Lock for using our two instance variables. + + if (_automaticallyManagesContentSize) { + CGSize contentSize = _contentCalculatedSizeFromLayout; + if (ASIsCGSizeValidForLayout(contentSize) == NO) { + NSLog(@"%@ calculated a size in its layout spec that can't be applied to .contentSize: %@. Applying parentSize (scrollNode's bounds) instead: %@.", self, NSStringFromCGSize(contentSize), NSStringFromCGSize(self.calculatedSize)); + contentSize = self.calculatedSize; + } + self.view.contentSize = contentSize; + } +} + +- (BOOL)automaticallyManagesContentSize +{ + ASLockScopeSelf(); + return _automaticallyManagesContentSize; +} + +- (void)setAutomaticallyManagesContentSize:(BOOL)automaticallyManagesContentSize +{ + ASLockScopeSelf(); + _automaticallyManagesContentSize = automaticallyManagesContentSize; + if (_automaticallyManagesContentSize == YES + && ASScrollDirectionContainsVerticalDirection(_scrollableDirections) == NO + && ASScrollDirectionContainsHorizontalDirection(_scrollableDirections) == NO) { + // Set the @default value, for more user-friendly behavior of the most + // common use cases of .automaticallyManagesContentSize. + _scrollableDirections = ASScrollDirectionVerticalDirections; + } +} + +- (ASScrollDirection)scrollableDirections +{ + ASLockScopeSelf(); + return _scrollableDirections; +} + +- (void)setScrollableDirections:(ASScrollDirection)scrollableDirections +{ + ASLockScopeSelf(); + if (_scrollableDirections != scrollableDirections) { + _scrollableDirections = scrollableDirections; + [self setNeedsLayout]; + } +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASSectionController.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASSectionController.h new file mode 100644 index 0000000..6807596 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASSectionController.h @@ -0,0 +1,86 @@ +// +// ASSectionController.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@class ASBatchContext; + +/** + * A protocol that your section controllers should conform to, in order to be used with Texture. + * + * @note Your supplementary view source should conform to @c ASSupplementaryNodeSource. + */ +@protocol ASSectionController + +@optional + +/** + * A method to provide the node block for the item at the given index. + * The node block you return will be run asynchronously off the main thread, + * so it's important to retrieve any objects from your section _outside_ the block + * because by the time the block is run, the array may have changed. + * + * @param index The index of the item. + * @return A block to be run concurrently to build the node for this item. + * @see collectionNode:nodeBlockForItemAtIndexPath: + */ +- (ASCellNodeBlock)nodeBlockForItemAtIndex:(NSInteger)index; + +/** + * Similar to -collectionView:cellForItemAtIndexPath:. + * + * Note: only called if nodeBlockForItemAtIndex: returns nil. + * + * @param index The index of the item. + * + * @return A node to display for the given item. This will be called on the main thread and should + * not implement reuse (it will be called once per item). Unlike UICollectionView's version, + * this method is not called when the item is about to display. + */ +- (ASCellNode *)nodeForItemAtIndex:(NSInteger)index; + +/** + * Asks the section controller whether it should batch fetch because the user is + * near the end of the current data set. + * + * @discussion Use this method to conditionally fetch batches. Example use cases are: limiting the total number of + * objects that can be fetched or no network connection. + * + * If not implemented, the assumed return value is @c YES. + */ +- (BOOL)shouldBatchFetch; + +/** + * Asks the section controller to begin fetching more content (tail loading) because + * the user is near the end of the current data set. + * + * @param context A context object that must be notified when the batch fetch is completed. + * + * @discussion You must eventually call -completeBatchFetching: with an argument of YES in order to receive future + * notifications to do batch fetches. This method is called on a background queue. + */ +- (void)beginBatchFetchWithContext:(ASBatchContext *)context; + +/** + * A method to provide the size range used for measuring the item + * at the given index. + * + * @param index The index of the item. + * @return A size range used for asynchronously measuring the node at this index. + * @see collectionNode:constrainedSizeForItemAtIndexPath: + */ +- (ASSizeRange)sizeRangeForItemAtIndex:(NSInteger)index; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASSupplementaryNodeSource.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASSupplementaryNodeSource.h new file mode 100644 index 0000000..20beddd --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASSupplementaryNodeSource.h @@ -0,0 +1,51 @@ +// +// ASSupplementaryNodeSource.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@protocol ASSupplementaryNodeSource + +@optional + +/** + * A method to provide the node-block for the supplementary element. + * + * @param elementKind The kind of supplementary element. + * @param index The index of the item. + * @return A node block for the supplementary element. + * @see collectionNode:nodeForSupplementaryElementOfKind:atIndexPath: + */ +- (ASCellNodeBlock)nodeBlockForSupplementaryElementOfKind:(NSString *)elementKind atIndex:(NSInteger)index; + +/** + * Asks the controller to provide a node to display for the given supplementary element. + * + * @param kind The kind of supplementary element. + * @param index The index of the item. + */ +- (ASCellNode *)nodeForSupplementaryElementOfKind:(NSString *)kind atIndex:(NSInteger)index; + +/** + * A method to provide the size range used for measuring the supplementary + * element of the given kind at the given index. + * + * @param elementKind The kind of supplementary element. + * @param index The index of the item. + * @return A size range used for asynchronously measuring the node. + * @see collectionNode:constrainedSizeForSupplementaryElementOfKind:atIndexPath: + */ +- (ASSizeRange)sizeRangeForSupplementaryElementOfKind:(NSString *)elementKind atIndex:(NSInteger)index; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASTabBarController.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASTabBarController.h new file mode 100644 index 0000000..faa67a0 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASTabBarController.h @@ -0,0 +1,28 @@ +// +// ASTabBarController.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * ASTabBarController + * + * @discussion ASTabBarController is a drop in replacement for UITabBarController + * which implements the memory efficiency improving @c ASManagesChildVisibilityDepth protocol. + * + * @see ASManagesChildVisibilityDepth + */ +@interface ASTabBarController : UITabBarController + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASTabBarController.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASTabBarController.mm new file mode 100644 index 0000000..63867f1 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASTabBarController.mm @@ -0,0 +1,84 @@ +// +// ASTabBarController.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +@implementation ASTabBarController +{ + BOOL _parentManagesVisibilityDepth; + NSInteger _visibilityDepth; +} + +ASVisibilityDidMoveToParentViewController; + +ASVisibilityViewWillAppear; + +ASVisibilityViewDidDisappearImplementation; + +ASVisibilitySetVisibilityDepth; + +ASVisibilityDepthImplementation; + +- (void)visibilityDepthDidChange +{ + for (UIViewController *viewController in self.viewControllers) { + if ([viewController conformsToProtocol:@protocol(ASVisibilityDepth)]) { + [(id )viewController visibilityDepthDidChange]; + } + } +} + +- (NSInteger)visibilityDepthOfChildViewController:(UIViewController *)childViewController +{ + NSUInteger viewControllerIndex = [self.viewControllers indexOfObjectIdenticalTo:childViewController]; + if (viewControllerIndex == NSNotFound) { + //If childViewController is not actually a child, return NSNotFound which is also a really large number. + return NSNotFound; + } + + if (self.selectedViewController == childViewController) { + return [self visibilityDepth]; + } + return [self visibilityDepth] + 1; +} + +#pragma mark - UIKit overrides + +- (void)setViewControllers:(NSArray<__kindof UIViewController *> *)viewControllers +{ + [super setViewControllers:viewControllers]; + [self visibilityDepthDidChange]; +} + +- (void)setViewControllers:(NSArray<__kindof UIViewController *> *)viewControllers animated:(BOOL)animated +{ + [super setViewControllers:viewControllers animated:animated]; + [self visibilityDepthDidChange]; +} + +- (void)setSelectedIndex:(NSUInteger)selectedIndex +{ + as_activity_create_for_scope("Set selected index of ASTabBarController"); + os_log_info(ASNodeLog(), "Selected tab %tu of %@", selectedIndex, self); + + [super setSelectedIndex:selectedIndex]; + [self visibilityDepthDidChange]; +} + +- (void)setSelectedViewController:(__kindof UIViewController *)selectedViewController +{ + as_activity_create_for_scope("Set selected view controller of ASTabBarController"); + os_log_info(ASNodeLog(), "Selected view controller %@ of %@", selectedViewController, self); + + [super setSelectedViewController:selectedViewController]; + [self visibilityDepthDidChange]; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASTableNode+Beta.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASTableNode+Beta.h new file mode 100644 index 0000000..6d580bd --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASTableNode+Beta.h @@ -0,0 +1,21 @@ +// +// ASTableNode+Beta.h +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +@protocol ASBatchFetchingDelegate; + +NS_ASSUME_NONNULL_BEGIN + +@interface ASTableNode (Beta) + +@property (nonatomic, weak) id batchFetchingDelegate; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASTableNode.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASTableNode.h new file mode 100644 index 0000000..772f3a7 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASTableNode.h @@ -0,0 +1,763 @@ +// +// ASTableNode.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@protocol ASTableDataSource; +@protocol ASTableDelegate; +@class ASTableView, ASBatchContext; + +/** + * ASTableNode is a node based class that wraps an ASTableView. It can be used + * as a subnode of another node, and provide room for many (great) features and improvements later on. + */ +@interface ASTableNode : ASDisplayNode + +- (instancetype)init; // UITableViewStylePlain +- (instancetype)initWithStyle:(UITableViewStyle)style NS_DESIGNATED_INITIALIZER; + +@property (readonly) ASTableView *view; + +// These properties can be set without triggering the view to be created, so it's fine to set them in -init. +@property (nonatomic, weak) id delegate; +@property (nonatomic, weak) id dataSource; + +/** + * The number of screens left to scroll before the delegate -tableNode:beginBatchFetchingWithContext: is called. + * + * Defaults to two screenfuls. + */ +@property (nonatomic) CGFloat leadingScreensForBatching; + +/* + * A Boolean value that determines whether the table will be flipped. + * If the value of this property is YES, the first cell node will be at the bottom of the table (as opposed to the top by default). This is useful for chat/messaging apps. The default value is NO. + */ +@property (nonatomic) BOOL inverted; + +/** + * The distance that the content view is inset from the table node edges. Defaults to UIEdgeInsetsZero. + */ +@property (nonatomic) UIEdgeInsets contentInset; + +/** + * The offset of the content view's origin from the table node's origin. Defaults to CGPointZero. + */ +@property (nonatomic) CGPoint contentOffset; + +/** + * Sets the offset from the content node’s origin to the table node’s origin. + * + * @param contentOffset The offset + * + * @param animated YES to animate to this new offset at a constant velocity, NO to not aniamte and immediately make the transition. + */ +- (void)setContentOffset:(CGPoint)contentOffset animated:(BOOL)animated; + +/** + * YES to automatically adjust the contentOffset when cells are inserted or deleted above + * visible cells, maintaining the users' visible scroll position. + * + * @note This is only applied to non-animated updates. For animated updates, there is no way to + * synchronize or "cancel out" the appearance of a scroll due to UITableView API limitations. + * + * default is NO. + */ +@property (nonatomic) BOOL automaticallyAdjustsContentOffset; + +/** + * A Boolean value that determines whether paging is enabled for the scroll view. + * The default value of this property is NO. + */ +@property (nonatomic, getter=isPagingEnabled) BOOL pagingEnabled __TVOS_PROHIBITED; + +/* + * A Boolean value that determines whether users can select a row. + * If the value of this property is YES (the default), users can select rows. If you set it to NO, they cannot select rows. Setting this property affects cell selection only when the table view is not in editing mode. If you want to restrict selection of cells in editing mode, use `allowsSelectionDuringEditing`. + */ +@property (nonatomic) BOOL allowsSelection; +/* + * A Boolean value that determines whether users can select cells while the table view is in editing mode. + * If the value of this property is YES, users can select rows during editing. The default value is NO. If you want to restrict selection of cells regardless of mode, use allowsSelection. + */ +@property (nonatomic) BOOL allowsSelectionDuringEditing; +/* + * A Boolean value that determines whether users can select more than one row outside of editing mode. + * This property controls whether multiple rows can be selected simultaneously outside of editing mode. When the value of this property is YES, each row that is tapped acquires a selected appearance. Tapping the row again removes the selected appearance. If you access indexPathsForSelectedRows, you can get the index paths that identify the selected rows. + */ +@property (nonatomic) BOOL allowsMultipleSelection; +/* + * A Boolean value that controls whether users can select more than one cell simultaneously in editing mode. + * The default value of this property is NO. If you set it to YES, check marks appear next to selected rows in editing mode. In addition, UITableView does not query for editing styles when it goes into editing mode. If you access indexPathsForSelectedRows, you can get the index paths that identify the selected rows. + */ +@property (nonatomic) BOOL allowsMultipleSelectionDuringEditing; + +/** + * Tuning parameters for a range type in full mode. + * + * @param rangeType The range type to get the tuning parameters for. + * + * @return A tuning parameter value for the given range type in full mode. + * + * @see ASLayoutRangeMode + * @see ASLayoutRangeType + */ +- (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType AS_WARN_UNUSED_RESULT; + +/** + * Set the tuning parameters for a range type in full mode. + * + * @param tuningParameters The tuning parameters to store for a range type. + * @param rangeType The range type to set the tuning parameters for. + * + * @see ASLayoutRangeMode + * @see ASLayoutRangeType + */ +- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType; + +/** + * Tuning parameters for a range type in the specified mode. + * + * @param rangeMode The range mode to get the running parameters for. + * @param rangeType The range type to get the tuning parameters for. + * + * @return A tuning parameter value for the given range type in the given mode. + * + * @see ASLayoutRangeMode + * @see ASLayoutRangeType + */ +- (ASRangeTuningParameters)tuningParametersForRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType AS_WARN_UNUSED_RESULT; + +/** + * Set the tuning parameters for a range type in the specified mode. + * + * @param tuningParameters The tuning parameters to store for a range type. + * @param rangeMode The range mode to set the running parameters for. + * @param rangeType The range type to set the tuning parameters for. + * + * @see ASLayoutRangeMode + * @see ASLayoutRangeType + */ +- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType; + +/** + * Scrolls the table to the given row. + * + * @param indexPath The index path of the row. + * @param scrollPosition Where the row should end up after the scroll. + * @param animated Whether the scroll should be animated or not. + * + * This method must be called on the main thread. + */ +- (void)scrollToRowAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UITableViewScrollPosition)scrollPosition animated:(BOOL)animated; + +/** + * Reload everything from scratch, destroying the working range and all cached nodes. + * + * @param completion block to run on completion of asynchronous loading or nil. If supplied, the block is run on + * the main thread. + * @warning This method is substantially more expensive than UITableView's version. + */ +- (void)reloadDataWithCompletion:(nullable void (^)(void))completion; + +/** + * Reload everything from scratch, destroying the working range and all cached nodes. + * + * @warning This method is substantially more expensive than UITableView's version. + */ +- (void)reloadData; + +/** + * Triggers a relayout of all nodes. + * + * @discussion This method invalidates and lays out every cell node in the table view. + */ +- (void)relayoutItems; + +/** + * Perform a batch of updates asynchronously, optionally disabling all animations in the batch. This method must be called from the main thread. + * The data source must be updated to reflect the changes before the update block completes. + * + * @param animated NO to disable animations for this batch + * @param updates The block that performs the relevant insert, delete, reload, or move operations. + * @param completion A completion handler block to execute when all of the operations are finished. This block takes a single + * Boolean parameter that contains the value YES if all of the related animations completed successfully or + * NO if they were interrupted. This parameter may be nil. If supplied, the block is run on the main thread. + */ +- (void)performBatchAnimated:(BOOL)animated updates:(nullable AS_NOESCAPE void (^)(void))updates completion:(nullable void (^)(BOOL finished))completion; + +/** + * Perform a batch of updates asynchronously with animations in the batch. This method must be called from the main thread. + * The data source must be updated to reflect the changes before the update block completes. + * + * @param updates The block that performs the relevant insert, delete, reload, or move operations. + * @param completion A completion handler block to execute when all of the operations are finished. This block takes a single + * Boolean parameter that contains the value YES if all of the related animations completed successfully or + * NO if they were interrupted. This parameter may be nil. If supplied, the block is run on the main thread. + */ +- (void)performBatchUpdates:(nullable AS_NOESCAPE void (^)(void))updates completion:(nullable void (^)(BOOL finished))completion; + +/** + * Returns YES if the ASCollectionNode is still processing changes from performBatchUpdates:. + * This is typically the concurrent allocation (calling nodeBlocks) and layout of newly inserted + * ASCellNodes. If YES is returned, then calling -waitUntilAllUpdatesAreProcessed may take tens of + * milliseconds to return as it blocks on these concurrent operations. + * + * Returns NO if ASCollectionNode is fully synchronized with the underlying UICollectionView. This + * means that until the next performBatchUpdates: is called, it is safe to compare UIKit values + * (such as from UICollectionViewLayout) with your app's data source. + * + * This method will always return NO if called immediately after -waitUntilAllUpdatesAreProcessed. + */ +@property (nonatomic, readonly) BOOL isProcessingUpdates; + +/** + * Schedules a block to be performed (on the main thread) after processing of performBatchUpdates: + * is finished (completely synchronized to UIKit). The blocks will be run at the moment that + * -isProcessingUpdates changes from YES to NO; + * + * When isProcessingUpdates == NO, the block is run block immediately (before the method returns). + * + * Blocks scheduled by this mechanism are NOT guaranteed to run in the order they are scheduled. + * They may also be delayed if performBatchUpdates continues to be called; the blocks will wait until + * all running updates are finished. + * + * Calling -waitUntilAllUpdatesAreProcessed is one way to flush any pending update completion blocks. + */ +- (void)onDidFinishProcessingUpdates:(void (^)(void))didFinishProcessingUpdates; + +/** + * Blocks execution of the main thread until all section and item updates are committed to the view. This method must be called from the main thread. + */ +- (void)waitUntilAllUpdatesAreProcessed; + +/** + * Inserts one or more sections, with an option to animate the insertion. + * + * @param sections An index set that specifies the sections to insert. + * + * @param animation A constant that indicates how the insertion is to be animated. See UITableViewRowAnimation. + * + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes + * before this method is called. + */ +- (void)insertSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation; + +/** + * Deletes one or more sections, with an option to animate the deletion. + * + * @param sections An index set that specifies the sections to delete. + * + * @param animation A constant that indicates how the deletion is to be animated. See UITableViewRowAnimation. + * + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes + * before this method is called. + */ +- (void)deleteSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation; + +/** + * Reloads the specified sections using a given animation effect. + * + * @param sections An index set that specifies the sections to reload. + * + * @param animation A constant that indicates how the reloading is to be animated. See UITableViewRowAnimation. + * + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes + * before this method is called. + */ +- (void)reloadSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation; + +/** + * Moves a section to a new location. + * + * @param section The index of the section to move. + * + * @param newSection The index that is the destination of the move for the section. + * + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes + * before this method is called. + */ +- (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection; + +/** + * Inserts rows at the locations identified by an array of index paths, with an option to animate the insertion. + * + * @param indexPaths An array of NSIndexPath objects, each representing a row index and section index that together identify a row. + * + * @param animation A constant that indicates how the insertion is to be animated. See UITableViewRowAnimation. + * + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes + * before this method is called. + */ +- (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation; + +/** + * Deletes the rows specified by an array of index paths, with an option to animate the deletion. + * + * @param indexPaths An array of NSIndexPath objects identifying the rows to delete. + * + * @param animation A constant that indicates how the deletion is to be animated. See UITableViewRowAnimation. + * + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes + * before this method is called. + */ +- (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation; + +/** + * Reloads the specified rows using a given animation effect. + * + * @param indexPaths An array of NSIndexPath objects identifying the rows to reload. + * + * @param animation A constant that indicates how the reloading is to be animated. See UITableViewRowAnimation. + * + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes + * before this method is called. + */ +- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation; + +/** + * Moves the row at a specified location to a destination location. + * + * @param indexPath The index path identifying the row to move. + * + * @param newIndexPath The index path that is the destination of the move for the row. + * + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes + * before this method is called. + */ +- (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath; + +#pragma mark - Selection + +/** + * Selects a row in the table view identified by index path, optionally scrolling the row to a location in the table view. + * This method does not cause any selection-related delegate methods to be called. + * + * @param indexPath An index path identifying a row in the table view. + * + * @param animated Specify YES to animate the change in the selection or NO to make the change without animating it. + * + * @param scrollPosition A constant that identifies a relative position in the table view (top, middle, bottom) for the row when scrolling concludes. See `UITableViewScrollPosition` for descriptions of valid constants. + * + * @discussion This method must be called from the main thread. + */ +- (void)selectRowAtIndexPath:(nullable NSIndexPath *)indexPath animated:(BOOL)animated scrollPosition:(UITableViewScrollPosition)scrollPosition; + +/* + * Deselects a given row identified by index path, with an option to animate the deselection. + * This method does not cause any selection-related delegate methods to be called. + * Calling this method does not cause any scrolling to the deselected row. + * + * @param indexPath An index path identifying a row in the table view. + * + * @param animated Specify YES to animate the change in the selection or NO to make the change without animating it. + * + * @discussion This method must be called from the main thread. + */ +- (void)deselectRowAtIndexPath:(NSIndexPath *)indexPath animated:(BOOL)animated; + +#pragma mark - Querying Data + +/** + * Retrieves the number of rows in the given section. + * + * @param section The section. + * + * @return The number of rows. + */ +- (NSInteger)numberOfRowsInSection:(NSInteger)section AS_WARN_UNUSED_RESULT; + +/** + * The number of sections in the table node. + */ +@property (nonatomic, readonly) NSInteger numberOfSections; + +/** + * Similar to -visibleCells. + * + * @return an array containing the nodes being displayed on screen. This must be called on the main thread. + */ +@property (nonatomic, readonly) NSArray<__kindof ASCellNode *> *visibleNodes; + +/** + * Retrieves the node for the row at the given index path. + */ +- (nullable __kindof ASCellNode *)nodeForRowAtIndexPath:(NSIndexPath *)indexPath AS_WARN_UNUSED_RESULT; + +/** + * Similar to -indexPathForCell:. + * + * @param cellNode a node for a row. + * + * @return The index path to this row, if it exists. + * + * @discussion This method will return @c nil for a node that is still being + * displayed in the table view, if the data source has deleted the row. + * That is, the node is visible but it no longer corresponds + * to any item in the data source and will be removed soon. + */ +- (nullable NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode AS_WARN_UNUSED_RESULT; + +/** + * Similar to -[UITableView rectForRowAtIndexPath:] + * + * @param indexPath An index path identifying a row in the table view. + * + * @return A rectangle defining the area in which the table view draws the row or CGRectZero if indexPath is invalid. + * + * @discussion This method must be called from the main thread. + */ +- (CGRect)rectForRowAtIndexPath:(NSIndexPath *)indexPath AS_WARN_UNUSED_RESULT; + +/** + * Similar to -[UITableView cellForRowAtIndexPath:] + * + * @param indexPath An index path identifying a row in the table view. + * + * @return An object representing a cell of the table, or nil if the cell is not visible or indexPath is out of range. + * + * @discussion This method must be called from the main thread. + */ +- (nullable __kindof UITableViewCell *)cellForRowAtIndexPath:(NSIndexPath *)indexPath AS_WARN_UNUSED_RESULT; + +/** + * Similar to UITableView.indexPathForSelectedRow + * + * @return The value of this property is an index path identifying the row and section + * indexes of the selected row, or nil if the index path is invalid. If there are multiple selections, + * this property contains the first index-path object in the array of row selections; + * this object has the lowest index values for section and row. + * + * @discussion This method must be called from the main thread. + */ +@property (nullable, nonatomic, copy, readonly) NSIndexPath *indexPathForSelectedRow; + +@property (nonatomic, readonly, nullable) NSArray *indexPathsForSelectedRows; + +/** + * Similar to -[UITableView indexPathForRowAtPoint:] + * + * @param point A point in the local coordinate system of the table view (the table view’€™s bounds). + * + * @return An index path representing the row and section associated with point, + * or nil if the point is out of the bounds of any row. + * + * @discussion This method must be called from the main thread. + */ +- (nullable NSIndexPath *)indexPathForRowAtPoint:(CGPoint)point AS_WARN_UNUSED_RESULT; + +/** + * Similar to -[UITableView indexPathsForRowsInRect:] + * + * @param rect A rectangle defining an area of the table view in local coordinates. + * + * @return An array of NSIndexPath objects each representing a row and section index identifying a row within rect. + * Returns an empty array if there aren’t any rows to return. + * + * @discussion This method must be called from the main thread. + */ +- (nullable NSArray *)indexPathsForRowsInRect:(CGRect)rect AS_WARN_UNUSED_RESULT; + +/** + * Similar to -[UITableView indexPathsForVisibleRows] + * + * @return The value of this property is an array of NSIndexPath objects each representing a row index and section index + * that together identify a visible row in the table view. If no rows are visible, the value is nil. + * + * @discussion This method must be called from the main thread. + */ +- (NSArray *)indexPathsForVisibleRows AS_WARN_UNUSED_RESULT; + +@end + +/** + * This is a node-based UITableViewDataSource. + */ +@protocol ASTableDataSource + +@optional + +/** + * Asks the data source for the number of sections in the table node. + * + * @see @c numberOfSectionsInTableView: + */ +- (NSInteger)numberOfSectionsInTableNode:(ASTableNode *)tableNode; + +/** + * Asks the data source for the number of rows in the given section of the table node. + * + * @see @c numberOfSectionsInTableView: + */ +- (NSInteger)tableNode:(ASTableNode *)tableNode numberOfRowsInSection:(NSInteger)section; + +/** + * Asks the data source for a block to create a node to represent the row at the given index path. + * The block will be run by the table node concurrently in the background before the row is inserted + * into the table view. + * + * @param tableNode The sender. + * @param indexPath The index path of the row. + * + * @return a block that creates the node for display at this indexpath. + * Must be thread-safe (can be called on the main thread or a background + * queue) and should not implement reuse (it will be called once per row). + * + * @note This method takes precedence over tableNode:nodeForRowAtIndexPath: if implemented. + */ +- (ASCellNodeBlock)tableNode:(ASTableNode *)tableNode nodeBlockForRowAtIndexPath:(NSIndexPath *)indexPath; + +/** + * Asks the data source for a node to represent the row at the given index path. + * + * @param tableNode The sender. + * @param indexPath The index path of the row. + * + * @return a node to display for this row. This will be called on the main thread and should not implement reuse (it will be called once per row). Unlike UITableView's version, this method + * is not called when the row is about to display. + */ +- (ASCellNode *)tableNode:(ASTableNode *)tableNode nodeForRowAtIndexPath:(NSIndexPath *)indexPath; + +/** + * Similar to -tableView:cellForRowAtIndexPath:. + * + * @param tableView The sender. + * + * @param indexPath The index path of the requested node. + * + * @return a node for display at this indexpath. This will be called on the main thread and should not implement reuse (it will be called once per row). Unlike UITableView's version, this method + * is not called when the row is about to display. + */ +- (ASCellNode *)tableView:(ASTableView *)tableView nodeForRowAtIndexPath:(NSIndexPath *)indexPath AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode's method instead."); + +/** + * Similar to -tableView:nodeForRowAtIndexPath: + * This method takes precedence over tableView:nodeForRowAtIndexPath: if implemented. + * @param tableView The sender. + * + * @param indexPath The index path of the requested node. + * + * @return a block that creates the node for display at this indexpath. + * Must be thread-safe (can be called on the main thread or a background + * queue) and should not implement reuse (it will be called once per row). + */ +- (ASCellNodeBlock)tableView:(ASTableView *)tableView nodeBlockForRowAtIndexPath:(NSIndexPath *)indexPath AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode's method instead."); + +/** + * Indicator to lock the data source for data fetching in async mode. + * We should not update the data source until the data source has been unlocked. Otherwise, it will incur data inconsistency or exception + * due to the data access in async mode. + * + * @param tableView The sender. + * @deprecated The data source is always accessed on the main thread, and this method will not be called. + */ +- (void)tableViewLockDataSource:(ASTableView *)tableView ASDISPLAYNODE_DEPRECATED_MSG("Data source accesses are on the main thread. Method will not be called."); + +/** + * Indicator to unlock the data source for data fetching in asyn mode. + * We should not update the data source until the data source has been unlocked. Otherwise, it will incur data inconsistency or exception + * due to the data access in async mode. + * + * @param tableView The sender. + * @deprecated The data source is always accessed on the main thread, and this method will not be called. + */ +- (void)tableViewUnlockDataSource:(ASTableView *)tableView ASDISPLAYNODE_DEPRECATED_MSG("Data source accesses are on the main thread. Method will not be called."); + +/** + * Generate a unique identifier for an element in a table. This helps state restoration persist the scroll position + * of a table view even when the data in that table changes. See the documentation for UIDataSourceModelAssociation for more information. + * + * @param indexPath The index path of the requested node. + * + * @param tableNode The sender. + * + * @return a unique identifier for the element at the given path. Return nil if the index path does not exist in the table. + */ +- (nullable NSString *)modelIdentifierForElementAtIndexPath:(NSIndexPath *)indexPath inNode:(ASTableNode *)tableNode; + +/** + * Similar to -tableView:cellForRowAtIndexPath:. See the documentation for UIDataSourceModelAssociation for more information. + * + * @param identifier The model identifier of the element, previously generated by a call to modelIdentifierForElementAtIndexPath. + * + * @param tableNode The sender. + * + * @return the index path to the current position of the matching element in the table. Return nil if the element is not found. + */ +- (nullable NSIndexPath *)indexPathForElementWithModelIdentifier:(NSString *)identifier inNode:(ASTableNode *)tableNode; + +@end + +/** + * This is a node-based UITableViewDelegate. + * + * Note that -tableView:heightForRowAtIndexPath: has been removed; instead, your custom ASCellNode subclasses are + * responsible for deciding their preferred onscreen height in -calculateSizeThatFits:. + */ +@protocol ASTableDelegate + +@optional + +- (void)tableNode:(ASTableNode *)tableNode willDisplayRowWithNode:(ASCellNode *)node; + +- (void)tableNode:(ASTableNode *)tableNode didEndDisplayingRowWithNode:(ASCellNode *)node; + +- (nullable NSIndexPath *)tableNode:(ASTableNode *)tableNode willSelectRowAtIndexPath:(NSIndexPath *)indexPath; + +- (void)tableNode:(ASTableNode *)tableNode didSelectRowAtIndexPath:(NSIndexPath *)indexPath; + +- (nullable NSIndexPath *)tableNode:(ASTableNode *)tableNode willDeselectRowAtIndexPath:(NSIndexPath *)indexPath; + +- (void)tableNode:(ASTableNode *)tableNode didDeselectRowAtIndexPath:(NSIndexPath *)indexPath; + +- (BOOL)tableNode:(ASTableNode *)tableNode shouldHighlightRowAtIndexPath:(NSIndexPath *)indexPath; +- (void)tableNode:(ASTableNode *)tableNode didHighlightRowAtIndexPath:(NSIndexPath *)indexPath; +- (void)tableNode:(ASTableNode *)tableNode didUnhighlightRowAtIndexPath:(NSIndexPath *)indexPath; + +- (BOOL)tableNode:(ASTableNode *)tableNode shouldShowMenuForRowAtIndexPath:(NSIndexPath *)indexPath; +- (BOOL)tableNode:(ASTableNode *)tableNode canPerformAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(nullable id)sender; +- (void)tableNode:(ASTableNode *)tableNode performAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(nullable id)sender; + +/** + * Provides the constrained size range for measuring the row at the index path. + * Note: the widths in the returned size range are ignored! + * + * @param tableNode The sender. + * + * @param indexPath The index path of the node. + * + * @return A constrained size range for layout the node at this index path. + */ +- (ASSizeRange)tableNode:(ASTableNode *)tableNode constrainedSizeForRowAtIndexPath:(NSIndexPath *)indexPath; + +/** + * Receive a message that the tableView is near the end of its data set and more data should be fetched if necessary. + * + * @param tableNode The sender. + * @param context A context object that must be notified when the batch fetch is completed. + * + * @discussion You must eventually call -completeBatchFetching: with an argument of YES in order to receive future + * notifications to do batch fetches. This method is called on a background queue. + * + * ASTableView currently only supports batch events for tail loads. If you require a head load, consider implementing a + * UIRefreshControl. + */ +- (void)tableNode:(ASTableNode *)tableNode willBeginBatchFetchWithContext:(ASBatchContext *)context; + +/** + * Tell the tableView if batch fetching should begin. + * + * @param tableNode The sender. + * + * @discussion Use this method to conditionally fetch batches. Example use cases are: limiting the total number of + * objects that can be fetched or no network connection. + * + * If not implemented, the tableView assumes that it should notify its asyncDelegate when batch fetching + * should occur. + */ +- (BOOL)shouldBatchFetchForTableNode:(ASTableNode *)tableNode; + +/** + * Informs the delegate that the table view will add the given node + * at the given index path to the view hierarchy. + * + * @param tableView The sender. + * @param node The node that will be displayed. + * @param indexPath The index path of the row that will be displayed. + * + * @warning AsyncDisplayKit processes table view edits asynchronously. The index path + * passed into this method may not correspond to the same item in your data source + * if your data source has been updated since the last edit was processed. + */ +- (void)tableView:(ASTableView *)tableView willDisplayNode:(ASCellNode *)node forRowAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode's method instead."); + +/** + * Informs the delegate that the table view did remove the provided node from the view hierarchy. + * This may be caused by the node scrolling out of view, or by deleting the row + * or its containing section with @c deleteRowsAtIndexPaths:withRowAnimation: or @c deleteSections:withRowAnimation: . + * + * @param tableView The sender. + * @param node The node which was removed from the view hierarchy. + * @param indexPath The index path at which the node was located before the removal. + * + * @warning AsyncDisplayKit processes table view edits asynchronously. The index path + * passed into this method may not correspond to the same item in your data source + * if your data source has been updated since the last edit was processed. + */ +- (void)tableView:(ASTableView *)tableView didEndDisplayingNode:(ASCellNode *)node forRowAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode's method instead."); + +/** + * Receive a message that the tableView is near the end of its data set and more data should be fetched if necessary. + * + * @param tableView The sender. + * @param context A context object that must be notified when the batch fetch is completed. + * + * @discussion You must eventually call -completeBatchFetching: with an argument of YES in order to receive future + * notifications to do batch fetches. This method is called on a background queue. + * + * ASTableView currently only supports batch events for tail loads. If you require a head load, consider implementing a + * UIRefreshControl. + */ +- (void)tableView:(ASTableView *)tableView willBeginBatchFetchWithContext:(ASBatchContext *)context ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode's method instead."); + +/** + * Tell the tableView if batch fetching should begin. + * + * @param tableView The sender. + * + * @discussion Use this method to conditionally fetch batches. Example use cases are: limiting the total number of + * objects that can be fetched or no network connection. + * + * If not implemented, the tableView assumes that it should notify its asyncDelegate when batch fetching + * should occur. + */ +- (BOOL)shouldBatchFetchForTableView:(ASTableView *)tableView AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode's method instead."); + +/** + * Provides the constrained size range for measuring the row at the index path. + * Note: the widths in the returned size range are ignored! + * + * @param tableView The sender. + * + * @param indexPath The index path of the node. + * + * @return A constrained size range for layout the node at this index path. + */ +- (ASSizeRange)tableView:(ASTableView *)tableView constrainedSizeForRowAtIndexPath:(NSIndexPath *)indexPath AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode's method instead."); + +/** + * Informs the delegate that the table view will add the node + * at the given index path to the view hierarchy. + * + * @param tableView The sender. + * @param indexPath The index path of the row that will be displayed. + * + * @warning AsyncDisplayKit processes table view edits asynchronously. The index path + * passed into this method may not correspond to the same item in your data source + * if your data source has been updated since the last edit was processed. + * + * This method is deprecated. Use @c tableView:willDisplayNode:forRowAtIndexPath: instead. + */ +- (void)tableView:(ASTableView *)tableView willDisplayNodeForRowAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode's method instead."); + +@end + +@interface ASTableNode (Deprecated) + +- (void)waitUntilAllUpdatesAreCommitted ASDISPLAYNODE_DEPRECATED_MSG("This method has been renamed to -waitUntilAllUpdatesAreProcessed."); + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASTableNode.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASTableNode.mm new file mode 100644 index 0000000..d8acc8a --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASTableNode.mm @@ -0,0 +1,894 @@ +// +// ASTableNode.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + +#pragma mark - _ASTablePendingState + +@interface _ASTablePendingState : NSObject { +@public + std::vector> _tuningParameters; +} +@property (nonatomic, weak) id delegate; +@property (nonatomic, weak) id dataSource; +@property (nonatomic) ASLayoutRangeMode rangeMode; +@property (nonatomic) BOOL allowsSelection; +@property (nonatomic) BOOL allowsSelectionDuringEditing; +@property (nonatomic) BOOL allowsMultipleSelection; +@property (nonatomic) BOOL allowsMultipleSelectionDuringEditing; +@property (nonatomic) BOOL inverted; +@property (nonatomic) CGFloat leadingScreensForBatching; +@property (nonatomic) UIEdgeInsets contentInset; +@property (nonatomic) CGPoint contentOffset; +@property (nonatomic) BOOL animatesContentOffset; +@property (nonatomic) BOOL automaticallyAdjustsContentOffset; +@property (nonatomic) BOOL pagingEnabled; +@end + +@implementation _ASTablePendingState + +#pragma mark - Lifecycle + +- (instancetype)init +{ + self = [super init]; + if (self) { + _rangeMode = ASLayoutRangeModeUnspecified; + _tuningParameters = [ASAbstractLayoutController defaultTuningParameters]; + _allowsSelection = YES; + _allowsSelectionDuringEditing = NO; + _allowsMultipleSelection = NO; + _allowsMultipleSelectionDuringEditing = NO; + _inverted = NO; + _leadingScreensForBatching = 2; + _contentInset = UIEdgeInsetsZero; + _contentOffset = CGPointZero; + _animatesContentOffset = NO; + _automaticallyAdjustsContentOffset = NO; + _pagingEnabled = NO; + } + return self; +} + +#pragma mark Tuning Parameters + +- (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType +{ + return [self tuningParametersForRangeMode:ASLayoutRangeModeFull rangeType:rangeType]; +} + +- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType +{ + return [self setTuningParameters:tuningParameters forRangeMode:ASLayoutRangeModeFull rangeType:rangeType]; +} + +- (ASRangeTuningParameters)tuningParametersForRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType +{ + ASDisplayNodeAssert(rangeMode < _tuningParameters.size() && rangeType < _tuningParameters[rangeMode].size(), @"Requesting a range that is OOB for the configured tuning parameters"); + return _tuningParameters[rangeMode][rangeType]; +} + +- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType +{ + ASDisplayNodeAssert(rangeMode < _tuningParameters.size() && rangeType < _tuningParameters[rangeMode].size(), @"Setting a range that is OOB for the configured tuning parameters"); + _tuningParameters[rangeMode][rangeType] = tuningParameters; +} + +@end + +#pragma mark - ASTableView + +@interface ASTableNode () +{ + AS::RecursiveMutex _environmentStateLock; + id _batchFetchingDelegate; +} + +@property (nonatomic) _ASTablePendingState *pendingState; +@property (nonatomic, weak) ASRangeController *rangeController; +@end + +@implementation ASTableNode + +#pragma mark Lifecycle + +- (instancetype)initWithStyle:(UITableViewStyle)style +{ + if (self = [super init]) { + __weak __typeof__(self) weakSelf = self; + [self setViewBlock:^{ + // Variable will be unused if event logging is off. + __unused __typeof__(self) strongSelf = weakSelf; + return [[ASTableView alloc] _initWithFrame:CGRectZero style:style dataControllerClass:nil owningNode:strongSelf]; + }]; + } + return self; +} + +- (instancetype)init +{ + return [self initWithStyle:UITableViewStylePlain]; +} + +#if ASDISPLAYNODE_ASSERTIONS_ENABLED +- (void)dealloc +{ + if (self.nodeLoaded) { + __weak UIView *view = self.view; + ASPerformBlockOnMainThread(^{ + ASDisplayNodeCAssertNil(view.superview, @"Node's view should be removed from hierarchy."); + }); + } +} +#endif + +#pragma mark ASDisplayNode + +- (void)didLoad +{ + [super didLoad]; + + ASTableView *view = self.view; + view.tableNode = self; + + _rangeController = view.rangeController; + + if (_pendingState) { + _ASTablePendingState *pendingState = _pendingState; + self.pendingState = nil; + view.asyncDelegate = pendingState.delegate; + view.asyncDataSource = pendingState.dataSource; + view.inverted = pendingState.inverted; + view.allowsSelection = pendingState.allowsSelection; + view.allowsSelectionDuringEditing = pendingState.allowsSelectionDuringEditing; + view.allowsMultipleSelection = pendingState.allowsMultipleSelection; + view.allowsMultipleSelectionDuringEditing = pendingState.allowsMultipleSelectionDuringEditing; + view.automaticallyAdjustsContentOffset = pendingState.automaticallyAdjustsContentOffset; +#if !TARGET_OS_TV + view.pagingEnabled = pendingState.pagingEnabled; +#endif + + UIEdgeInsets contentInset = pendingState.contentInset; + if (!UIEdgeInsetsEqualToEdgeInsets(contentInset, UIEdgeInsetsZero)) { + view.contentInset = contentInset; + } + + CGPoint contentOffset = pendingState.contentOffset; + if (!CGPointEqualToPoint(contentOffset, CGPointZero)) { + [view setContentOffset:contentOffset animated:pendingState.animatesContentOffset]; + } + + const auto tuningParametersVector = pendingState->_tuningParameters; + const auto tuningParametersVectorSize = tuningParametersVector.size(); + for (NSInteger rangeMode = 0; rangeMode < tuningParametersVectorSize; rangeMode++) { + const auto tuningparametersRangeModeVector = tuningParametersVector[rangeMode]; + const auto tuningParametersVectorRangeModeSize = tuningparametersRangeModeVector.size(); + for (NSInteger rangeType = 0; rangeType < tuningParametersVectorRangeModeSize; rangeType++) { + ASRangeTuningParameters tuningParameters = tuningparametersRangeModeVector[rangeType]; + [_rangeController setTuningParameters:tuningParameters + forRangeMode:(ASLayoutRangeMode)rangeMode + rangeType:(ASLayoutRangeType)rangeType]; + } + } + + if (pendingState.rangeMode != ASLayoutRangeModeUnspecified) { + [_rangeController updateCurrentRangeWithMode:pendingState.rangeMode]; + } + } +} + +- (ASTableView *)view +{ + return (ASTableView *)[super view]; +} + +- (void)clearContents +{ + [super clearContents]; + [self.rangeController clearContents]; +} + +- (void)interfaceStateDidChange:(ASInterfaceState)newState fromState:(ASInterfaceState)oldState +{ + [super interfaceStateDidChange:newState fromState:oldState]; + [ASRangeController layoutDebugOverlayIfNeeded]; +} + +- (void)didEnterPreloadState +{ + [super didEnterPreloadState]; + // Intentionally allocate the view here and trigger a layout pass on it, which in turn will trigger the intial data load. + // We can get rid of this call later when ASDataController, ASRangeController and ASCollectionLayout can operate without the view. + [self.view layoutIfNeeded]; +} + +#if ASRangeControllerLoggingEnabled +- (void)didEnterVisibleState +{ + [super didEnterVisibleState]; + NSLog(@"%@ - visible: YES", self); +} + +- (void)didExitVisibleState +{ + [super didExitVisibleState]; + NSLog(@"%@ - visible: NO", self); +} +#endif + +- (void)didExitPreloadState +{ + [super didExitPreloadState]; + [self.rangeController clearPreloadedData]; +} + +#pragma mark Setter / Getter + +// TODO: Implement this without the view. Then revisit ASLayoutElementCollectionTableSetTraitCollection +- (ASDataController *)dataController +{ + return self.view.dataController; +} + +- (_ASTablePendingState *)pendingState +{ + if (!_pendingState && ![self isNodeLoaded]) { + _pendingState = [[_ASTablePendingState alloc] init]; + } + ASDisplayNodeAssert(![self isNodeLoaded] || !_pendingState, @"ASTableNode should not have a pendingState once it is loaded"); + return _pendingState; +} + +- (void)setInverted:(BOOL)inverted +{ + self.transform = inverted ? CATransform3DMakeScale(1, -1, 1) : CATransform3DIdentity; + if ([self pendingState]) { + _pendingState.inverted = inverted; + } else { + ASDisplayNodeAssert([self isNodeLoaded], @"ASTableNode should be loaded if pendingState doesn't exist"); + self.view.inverted = inverted; + } +} + +- (BOOL)inverted +{ + if ([self pendingState]) { + return _pendingState.inverted; + } else { + return self.view.inverted; + } +} + +- (void)setLeadingScreensForBatching:(CGFloat)leadingScreensForBatching +{ + _ASTablePendingState *pendingState = self.pendingState; + if (pendingState) { + pendingState.leadingScreensForBatching = leadingScreensForBatching; + } else { + ASDisplayNodeAssert(self.nodeLoaded, @"ASTableNode should be loaded if pendingState doesn't exist"); + self.view.leadingScreensForBatching = leadingScreensForBatching; + } +} + +- (CGFloat)leadingScreensForBatching +{ + _ASTablePendingState *pendingState = self.pendingState; + if (pendingState) { + return pendingState.leadingScreensForBatching; + } else { + return self.view.leadingScreensForBatching; + } +} + +- (void)setContentInset:(UIEdgeInsets)contentInset +{ + _ASTablePendingState *pendingState = self.pendingState; + if (pendingState) { + pendingState.contentInset = contentInset; + } else { + ASDisplayNodeAssert(self.nodeLoaded, @"ASTableNode should be loaded if pendingState doesn't exist"); + self.view.contentInset = contentInset; + } +} + +- (UIEdgeInsets)contentInset +{ + _ASTablePendingState *pendingState = self.pendingState; + if (pendingState) { + return pendingState.contentInset; + } else { + return self.view.contentInset; + } +} + +- (void)setContentOffset:(CGPoint)contentOffset +{ + [self setContentOffset:contentOffset animated:NO]; +} + +- (void)setContentOffset:(CGPoint)contentOffset animated:(BOOL)animated +{ + _ASTablePendingState *pendingState = self.pendingState; + if (pendingState) { + pendingState.contentOffset = contentOffset; + pendingState.animatesContentOffset = animated; + } else { + ASDisplayNodeAssert(self.nodeLoaded, @"ASTableNode should be loaded if pendingState doesn't exist"); + [self.view setContentOffset:contentOffset animated:animated]; + } +} + +- (CGPoint)contentOffset +{ + _ASTablePendingState *pendingState = self.pendingState; + if (pendingState) { + return pendingState.contentOffset; + } else { + return self.view.contentOffset; + } +} + +- (void)setAutomaticallyAdjustsContentOffset:(BOOL)automaticallyAdjustsContentOffset +{ + _ASTablePendingState *pendingState = self.pendingState; + if (pendingState) { + pendingState.automaticallyAdjustsContentOffset = automaticallyAdjustsContentOffset; + } else { + ASDisplayNodeAssert(self.nodeLoaded, @"ASTableNode should be loaded if pendingState doesn't exist"); + self.view.automaticallyAdjustsContentOffset = automaticallyAdjustsContentOffset; + } +} + +- (BOOL)automaticallyAdjustsContentOffset +{ + _ASTablePendingState *pendingState = self.pendingState; + if (pendingState) { + return pendingState.automaticallyAdjustsContentOffset; + } else { + return self.view.automaticallyAdjustsContentOffset; + } +} + +#if !TARGET_OS_TV +- (void)setPagingEnabled:(BOOL)pagingEnabled +{ + _ASTablePendingState *pendingState = self.pendingState; + if (pendingState) { + pendingState.pagingEnabled = pagingEnabled; + } else { + ASDisplayNodeAssert([self isNodeLoaded], + @"ASCollectionNode should be loaded if pendingState doesn't exist"); + self.view.pagingEnabled = pagingEnabled; + } +} + +- (BOOL)isPagingEnabled +{ + _ASTablePendingState *pendingState = self.pendingState; + if (pendingState) { + return pendingState.pagingEnabled; + } else { + return self.view.isPagingEnabled; + } +} +#endif + +- (void)setDelegate:(id )delegate +{ + if ([self pendingState]) { + _pendingState.delegate = delegate; + } else { + ASDisplayNodeAssert([self isNodeLoaded], @"ASTableNode should be loaded if pendingState doesn't exist"); + + // Manually trampoline to the main thread. The view requires this be called on main + // and asserting here isn't an option – it is a common pattern for users to clear + // the delegate/dataSource in dealloc, which may be running on a background thread. + // It is important that we avoid retaining self in this block, so that this method is dealloc-safe. + ASTableView *view = self.view; + ASPerformBlockOnMainThread(^{ + view.asyncDelegate = delegate; + }); + } +} + +- (id )delegate +{ + if ([self pendingState]) { + return _pendingState.delegate; + } else { + return self.view.asyncDelegate; + } +} + +- (void)setDataSource:(id )dataSource +{ + if ([self pendingState]) { + _pendingState.dataSource = dataSource; + } else { + ASDisplayNodeAssert([self isNodeLoaded], @"ASTableNode should be loaded if pendingState doesn't exist"); + + // Manually trampoline to the main thread. The view requires this be called on main + // and asserting here isn't an option – it is a common pattern for users to clear + // the delegate/dataSource in dealloc, which may be running on a background thread. + // It is important that we avoid retaining self in this block, so that this method is dealloc-safe. + ASTableView *view = self.view; + ASPerformBlockOnMainThread(^{ + view.asyncDataSource = dataSource; + }); + } +} + +- (id )dataSource +{ + if ([self pendingState]) { + return _pendingState.dataSource; + } else { + ASDisplayNodeAssert([self isNodeLoaded], @"ASTableNode should be loaded if pendingState doesn't exist"); + return self.view.asyncDataSource; + } +} + +- (void)setAllowsSelection:(BOOL)allowsSelection +{ + if ([self pendingState]) { + _pendingState.allowsSelection = allowsSelection; + } else { + ASDisplayNodeAssert([self isNodeLoaded], @"ASTableNode should be loaded if pendingState doesn't exist"); + self.view.allowsSelection = allowsSelection; + } +} + +- (BOOL)allowsSelection +{ + if ([self pendingState]) { + return _pendingState.allowsSelection; + } else { + return self.view.allowsSelection; + } +} + +- (void)setAllowsSelectionDuringEditing:(BOOL)allowsSelectionDuringEditing +{ + if ([self pendingState]) { + _pendingState.allowsSelectionDuringEditing = allowsSelectionDuringEditing; + } else { + ASDisplayNodeAssert([self isNodeLoaded], @"ASTableNode should be loaded if pendingState doesn't exist"); + self.view.allowsSelectionDuringEditing = allowsSelectionDuringEditing; + } +} + +- (BOOL)allowsSelectionDuringEditing +{ + if ([self pendingState]) { + return _pendingState.allowsSelectionDuringEditing; + } else { + return self.view.allowsSelectionDuringEditing; + } +} + +- (void)setAllowsMultipleSelection:(BOOL)allowsMultipleSelection +{ + if ([self pendingState]) { + _pendingState.allowsMultipleSelection = allowsMultipleSelection; + } else { + ASDisplayNodeAssert([self isNodeLoaded], @"ASTableNode should be loaded if pendingState doesn't exist"); + self.view.allowsMultipleSelection = allowsMultipleSelection; + } +} + +- (BOOL)allowsMultipleSelection +{ + if ([self pendingState]) { + return _pendingState.allowsMultipleSelection; + } else { + return self.view.allowsMultipleSelection; + } +} + +- (void)setAllowsMultipleSelectionDuringEditing:(BOOL)allowsMultipleSelectionDuringEditing +{ + if ([self pendingState]) { + _pendingState.allowsMultipleSelectionDuringEditing = allowsMultipleSelectionDuringEditing; + } else { + ASDisplayNodeAssert([self isNodeLoaded], @"ASTableNode should be loaded if pendingState doesn't exist"); + self.view.allowsMultipleSelectionDuringEditing = allowsMultipleSelectionDuringEditing; + } +} + +- (BOOL)allowsMultipleSelectionDuringEditing +{ + if ([self pendingState]) { + return _pendingState.allowsMultipleSelectionDuringEditing; + } else { + return self.view.allowsMultipleSelectionDuringEditing; + } +} + +- (void)setBatchFetchingDelegate:(id)batchFetchingDelegate +{ + _batchFetchingDelegate = batchFetchingDelegate; +} + +- (id)batchFetchingDelegate +{ + return _batchFetchingDelegate; +} + +#pragma mark ASRangeControllerUpdateRangeProtocol + +- (void)updateCurrentRangeWithMode:(ASLayoutRangeMode)rangeMode +{ + if ([self pendingState]) { + _pendingState.rangeMode = rangeMode; + } else { + ASDisplayNodeAssert([self isNodeLoaded], @"ASTableNode should be loaded if pendingState doesn't exist"); + [self.rangeController updateCurrentRangeWithMode:rangeMode]; + } +} + +#pragma mark ASEnvironment + +ASLayoutElementCollectionTableSetTraitCollection(_environmentStateLock) + +#pragma mark - Range Tuning + +- (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType +{ + return [self tuningParametersForRangeMode:ASLayoutRangeModeFull rangeType:rangeType]; +} + +- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType +{ + [self setTuningParameters:tuningParameters forRangeMode:ASLayoutRangeModeFull rangeType:rangeType]; +} + +- (ASRangeTuningParameters)tuningParametersForRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType +{ + if ([self pendingState]) { + return [_pendingState tuningParametersForRangeMode:rangeMode rangeType:rangeType]; + } else { + return [self.rangeController tuningParametersForRangeMode:rangeMode rangeType:rangeType]; + } +} + +- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType +{ + if ([self pendingState]) { + [_pendingState setTuningParameters:tuningParameters forRangeMode:rangeMode rangeType:rangeType]; + } else { + return [self.rangeController setTuningParameters:tuningParameters forRangeMode:rangeMode rangeType:rangeType]; + } +} + +#pragma mark - Selection + +- (void)selectRowAtIndexPath:(nullable NSIndexPath *)indexPath animated:(BOOL)animated scrollPosition:(UITableViewScrollPosition)scrollPosition +{ + ASDisplayNodeAssertMainThread(); + ASTableView *tableView = self.view; + + indexPath = [tableView convertIndexPathFromTableNode:indexPath waitingIfNeeded:YES]; + if (indexPath != nil) { + [tableView selectRowAtIndexPath:indexPath animated:animated scrollPosition:scrollPosition]; + } else { + NSLog(@"Failed to select row at index path %@ because the row never reached the view.", indexPath); + } + +} + +- (void)deselectRowAtIndexPath:(NSIndexPath *)indexPath animated:(BOOL)animated +{ + ASDisplayNodeAssertMainThread(); + ASTableView *tableView = self.view; + + indexPath = [tableView convertIndexPathFromTableNode:indexPath waitingIfNeeded:YES]; + if (indexPath != nil) { + [tableView deselectRowAtIndexPath:indexPath animated:animated]; + } else { + NSLog(@"Failed to deselect row at index path %@ because the row never reached the view.", indexPath); + } +} + +- (void)scrollToRowAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UITableViewScrollPosition)scrollPosition animated:(BOOL)animated +{ + ASDisplayNodeAssertMainThread(); + ASTableView *tableView = self.view; + + indexPath = [tableView convertIndexPathFromTableNode:indexPath waitingIfNeeded:YES]; + + if (indexPath != nil) { + [tableView scrollToRowAtIndexPath:indexPath atScrollPosition:scrollPosition animated:animated]; + } else { + NSLog(@"Failed to scroll to row at index path %@ because the row never reached the view.", indexPath); + } +} + +#pragma mark - Querying Data + +- (void)reloadDataInitiallyIfNeeded +{ + ASDisplayNodeAssertMainThread(); + if (!self.dataController.initialReloadDataHasBeenCalled) { + // Note: Just calling reloadData isn't enough here – we need to + // ensure that _nodesConstrainedWidth is updated first. + [self.view layoutIfNeeded]; + } +} + +- (NSInteger)numberOfRowsInSection:(NSInteger)section +{ + ASDisplayNodeAssertMainThread(); + [self reloadDataInitiallyIfNeeded]; + return [self.dataController.pendingMap numberOfItemsInSection:section]; +} + +- (NSInteger)numberOfSections +{ + ASDisplayNodeAssertMainThread(); + [self reloadDataInitiallyIfNeeded]; + return [self.dataController.pendingMap numberOfSections]; +} + +- (NSArray<__kindof ASCellNode *> *)visibleNodes +{ + ASDisplayNodeAssertMainThread(); + return self.isNodeLoaded ? [self.view visibleNodes] : @[]; +} + +- (NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode +{ + return [self.dataController.pendingMap indexPathForElement:cellNode.collectionElement]; +} + +- (ASCellNode *)nodeForRowAtIndexPath:(NSIndexPath *)indexPath +{ + [self reloadDataInitiallyIfNeeded]; + return [self.dataController.pendingMap elementForItemAtIndexPath:indexPath].node; +} + +- (CGRect)rectForRowAtIndexPath:(NSIndexPath *)indexPath +{ + ASDisplayNodeAssertMainThread(); + ASTableView *tableView = self.view; + + indexPath = [tableView convertIndexPathFromTableNode:indexPath waitingIfNeeded:YES]; + return [tableView rectForRowAtIndexPath:indexPath]; +} + +- (nullable __kindof UITableViewCell *)cellForRowAtIndexPath:(NSIndexPath *)indexPath +{ + ASDisplayNodeAssertMainThread(); + ASTableView *tableView = self.view; + + indexPath = [tableView convertIndexPathFromTableNode:indexPath waitingIfNeeded:YES]; + if (indexPath == nil) { + return nil; + } + return [tableView cellForRowAtIndexPath:indexPath]; +} + +- (nullable NSIndexPath *)indexPathForSelectedRow +{ + ASDisplayNodeAssertMainThread(); + ASTableView *tableView = self.view; + + NSIndexPath *indexPath = tableView.indexPathForSelectedRow; + if (indexPath != nil) { + return [tableView convertIndexPathToTableNode:indexPath]; + } + return indexPath; +} + +- (NSArray *)indexPathsForSelectedRows +{ + ASDisplayNodeAssertMainThread(); + ASTableView *tableView = self.view; + + return [tableView convertIndexPathsToTableNode:tableView.indexPathsForSelectedRows]; +} + +- (nullable NSIndexPath *)indexPathForRowAtPoint:(CGPoint)point +{ + ASDisplayNodeAssertMainThread(); + ASTableView *tableView = self.view; + + NSIndexPath *indexPath = [tableView indexPathForRowAtPoint:point]; + if (indexPath != nil) { + return [tableView convertIndexPathToTableNode:indexPath]; + } + return indexPath; +} + +- (nullable NSArray *)indexPathsForRowsInRect:(CGRect)rect +{ + ASDisplayNodeAssertMainThread(); + ASTableView *tableView = self.view; + return [tableView convertIndexPathsToTableNode:[tableView indexPathsForRowsInRect:rect]]; +} + +- (NSArray *)indexPathsForVisibleRows +{ + ASDisplayNodeAssertMainThread(); + NSMutableArray *indexPathsArray = [NSMutableArray new]; + for (ASCellNode *cell in [self visibleNodes]) { + NSIndexPath *indexPath = [self indexPathForNode:cell]; + if (indexPath) { + [indexPathsArray addObject:indexPath]; + } + } + return indexPathsArray; +} + +#pragma mark - Editing + +- (void)reloadDataWithCompletion:(void (^)())completion +{ + ASDisplayNodeAssertMainThread(); + if (self.nodeLoaded) { + [self.view reloadDataWithCompletion:completion]; + } else { + if (completion) { + completion(); + } + } +} + +- (void)reloadData +{ + [self reloadDataWithCompletion:nil]; +} + +- (void)relayoutItems +{ + [self.view relayoutItems]; +} + +- (void)performBatchAnimated:(BOOL)animated updates:(NS_NOESCAPE void (^)())updates completion:(void (^)(BOOL))completion +{ + ASDisplayNodeAssertMainThread(); + if (self.nodeLoaded) { + ASTableView *tableView = self.view; + [tableView beginUpdates]; + if (updates) { + updates(); + } + [tableView endUpdatesAnimated:animated completion:completion]; + } else { + if (updates) { + updates(); + } + } +} + +- (void)performBatchUpdates:(NS_NOESCAPE void (^)())updates completion:(void (^)(BOOL))completion +{ + [self performBatchAnimated:YES updates:updates completion:completion]; +} + +- (void)insertSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation +{ + ASDisplayNodeAssertMainThread(); + if (self.nodeLoaded) { + [self.view insertSections:sections withRowAnimation:animation]; + } +} + +- (void)deleteSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation +{ + ASDisplayNodeAssertMainThread(); + if (self.nodeLoaded) { + [self.view deleteSections:sections withRowAnimation:animation]; + } +} + +- (void)reloadSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation +{ + ASDisplayNodeAssertMainThread(); + if (self.nodeLoaded) { + [self.view reloadSections:sections withRowAnimation:animation]; + } +} + +- (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection +{ + ASDisplayNodeAssertMainThread(); + if (self.nodeLoaded) { + [self.view moveSection:section toSection:newSection]; + } +} + +- (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation +{ + ASDisplayNodeAssertMainThread(); + if (self.nodeLoaded) { + [self.view insertRowsAtIndexPaths:indexPaths withRowAnimation:animation]; + } +} + +- (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation +{ + ASDisplayNodeAssertMainThread(); + if (self.nodeLoaded) { + [self.view deleteRowsAtIndexPaths:indexPaths withRowAnimation:animation]; + } +} + +- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation +{ + ASDisplayNodeAssertMainThread(); + if (self.nodeLoaded) { + [self.view reloadRowsAtIndexPaths:indexPaths withRowAnimation:animation]; + } +} + +- (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath +{ + ASDisplayNodeAssertMainThread(); + if (self.nodeLoaded) { + [self.view moveRowAtIndexPath:indexPath toIndexPath:newIndexPath]; + } +} + +- (BOOL)isProcessingUpdates +{ + return (self.nodeLoaded ? [self.view isProcessingUpdates] : NO); +} + +- (void)onDidFinishProcessingUpdates:(void (^)())completion +{ + if (!completion) { + return; + } + if (!self.nodeLoaded) { + completion(); + } else { + [self.view onDidFinishProcessingUpdates:completion]; + } +} + +- (void)waitUntilAllUpdatesAreProcessed +{ + ASDisplayNodeAssertMainThread(); + if (self.nodeLoaded) { + [self.view waitUntilAllUpdatesAreCommitted]; + } +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-implementations" +- (void)waitUntilAllUpdatesAreCommitted +{ + [self waitUntilAllUpdatesAreProcessed]; +} +#pragma clang diagnostic pop + +#pragma mark - Debugging (Private) + +- (NSMutableArray *)propertiesForDebugDescription +{ + NSMutableArray *result = [super propertiesForDebugDescription]; + [result addObject:@{ @"dataSource" : ASObjectDescriptionMakeTiny(self.dataSource) }]; + [result addObject:@{ @"delegate" : ASObjectDescriptionMakeTiny(self.delegate) }]; + return result; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASTableView.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASTableView.h new file mode 100644 index 0000000..a2258b3 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASTableView.h @@ -0,0 +1,245 @@ +// +// ASTableView.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@class ASCellNode; +@protocol ASTableDataSource; +@protocol ASTableDelegate; +@class ASTableNode; + +/** + * Asynchronous UITableView with Intelligent Preloading capabilities. + * + * @note ASTableNode is strongly recommended over ASTableView. This class is provided for adoption convenience. + */ +@interface ASTableView : UITableView + +/// The corresponding table node, or nil if one does not exist. +@property (nonatomic, weak, readonly) ASTableNode *tableNode; + +/** + * Retrieves the node for the row at the given index path. + */ +- (nullable ASCellNode *)nodeForRowAtIndexPath:(NSIndexPath *)indexPath AS_WARN_UNUSED_RESULT; + +@end + +@interface ASTableView (Deprecated) + +@property (nonatomic, weak) id asyncDelegate ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode's .delegate property instead."); +@property (nonatomic, weak) id asyncDataSource ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode .dataSource property instead."); + +/** + * Initializer. + * + * @param frame A rectangle specifying the initial location and size of the table view in its superview’€™s coordinates. + * The frame of the table view changes as table cells are added and deleted. + * + * @param style A constant that specifies the style of the table view. See UITableViewStyle for descriptions of valid constants. + */ +- (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style ASDISPLAYNODE_DEPRECATED_MSG("Please use ASTableNode instead of ASTableView."); + +/** + * The number of screens left to scroll before the delegate -tableView:beginBatchFetchingWithContext: is called. + * + * Defaults to two screenfuls. + */ +@property (nonatomic) CGFloat leadingScreensForBatching ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode property instead."); + +/** + * The distance that the content view is inset from the table view edges. Defaults to UIEdgeInsetsZero. + */ +@property (nonatomic) UIEdgeInsets contentInset ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode property instead"); + +/** + * The offset of the content view's origin from the table node's origin. Defaults to CGPointZero. + */ +@property (nonatomic) CGPoint contentOffset ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode property instead."); + +/** + * YES to automatically adjust the contentOffset when cells are inserted or deleted above + * visible cells, maintaining the users' visible scroll position. + * + * @note This is only applied to non-animated updates. For animated updates, there is no way to + * synchronize or "cancel out" the appearance of a scroll due to UITableView API limitations. + * + * default is NO. + */ +@property (nonatomic) BOOL automaticallyAdjustsContentOffset ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode property instead."); + +/* + * A Boolean value that determines whether the nodes that the data source renders will be flipped. + */ +@property (nonatomic) BOOL inverted ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode property instead."); + +@property (nonatomic, readonly, nullable) NSIndexPath *indexPathForSelectedRow ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode property instead."); + +@property (nonatomic, readonly, nullable) NSArray *indexPathsForSelectedRows ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode property instead."); + +@property (nonatomic, readonly, nullable) NSArray *indexPathsForVisibleRows ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode property instead."); + +/** + * Tuning parameters for a range type in full mode. + * + * @param rangeType The range type to get the tuning parameters for. + * + * @return A tuning parameter value for the given range type in full mode. + * + * @see ASLayoutRangeMode + * @see ASLayoutRangeType + */ +- (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode method instead."); + +/** + * Set the tuning parameters for a range type in full mode. + * + * @param tuningParameters The tuning parameters to store for a range type. + * @param rangeType The range type to set the tuning parameters for. + * + * @see ASLayoutRangeMode + * @see ASLayoutRangeType + */ +- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode method instead."); + +/** + * Tuning parameters for a range type in the specified mode. + * + * @param rangeMode The range mode to get the running parameters for. + * @param rangeType The range type to get the tuning parameters for. + * + * @return A tuning parameter value for the given range type in the given mode. + * + * @see ASLayoutRangeMode + * @see ASLayoutRangeType + */ +- (ASRangeTuningParameters)tuningParametersForRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode method instead."); + +/** + * Set the tuning parameters for a range type in the specified mode. + * + * @param tuningParameters The tuning parameters to store for a range type. + * @param rangeMode The range mode to set the running parameters for. + * @param rangeType The range type to set the tuning parameters for. + * + * @see ASLayoutRangeMode + * @see ASLayoutRangeType + */ +- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode method instead."); + +- (nullable __kindof UITableViewCell *)cellForRowAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode method instead."); + +- (void)scrollToRowAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UITableViewScrollPosition)scrollPosition animated:(BOOL)animated ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode method instead."); + +- (void)selectRowAtIndexPath:(NSIndexPath *)indexPath animated:(BOOL)animated scrollPosition:(UITableViewScrollPosition)scrollPosition ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode method instead."); + +- (nullable NSIndexPath *)indexPathForRowAtPoint:(CGPoint)point ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode method instead."); + +- (nullable NSArray *)indexPathsForRowsInRect:(CGRect)rect ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode method instead."); + +/** + * Similar to -visibleCells. + * + * @return an array containing the cell nodes being displayed on screen. + */ +- (NSArray *)visibleNodes AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode method instead."); + +/** + * Similar to -indexPathForCell:. + * + * @param cellNode a cellNode part of the table view + * + * @return an indexPath for this cellNode + */ +- (nullable NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode method instead."); + +/** + * Reload everything from scratch, destroying the working range and all cached nodes. + * + * @param completion block to run on completion of asynchronous loading or nil. If supplied, the block is run on + * the main thread. + * @warning This method is substantially more expensive than UITableView's version. + */ +-(void)reloadDataWithCompletion:(void (^ _Nullable)(void))completion ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode method instead."); + +/** + * Reload everything from scratch, destroying the working range and all cached nodes. + * + * @warning This method is substantially more expensive than UITableView's version. + */ +- (void)reloadData ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode method instead."); + +/** + * Triggers a relayout of all nodes. + * + * @discussion This method invalidates and lays out every cell node in the table view. + */ +- (void)relayoutItems ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode method instead."); + +- (void)beginUpdates ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode's -performBatchUpdates:completion: instead."); + +- (void)endUpdates ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode's -performBatchUpdates:completion: instead."); + +/** + * Concludes a series of method calls that insert, delete, select, or reload rows and sections of the table view. + * You call this method to bracket a series of method calls that begins with beginUpdates and that consists of operations + * to insert, delete, select, and reload rows and sections of the table view. When you call endUpdates, ASTableView begins animating + * the operations simultaneously. This method is must be called from the main thread. It's important to remember that the ASTableView will + * be processing the updates asynchronously after this call and are not guaranteed to be reflected in the ASTableView until + * the completion block is executed. + * + * @param animated NO to disable all animations. + * @param completion A completion handler block to execute when all of the operations are finished. This block takes a single + * Boolean parameter that contains the value YES if all of the related animations completed successfully or + * NO if they were interrupted. This parameter may be nil. If supplied, the block is run on the main thread. + */ +- (void)endUpdatesAnimated:(BOOL)animated completion:(void (^ _Nullable)(BOOL completed))completion ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode's -performBatchUpdates:completion: instead."); + +/** + * See ASTableNode.h for full documentation of these methods. + */ +@property (nonatomic, readonly) BOOL isProcessingUpdates; +- (void)onDidFinishProcessingUpdates:(void (^)(void))completion; +- (void)waitUntilAllUpdatesAreCommitted ASDISPLAYNODE_DEPRECATED_MSG("Use -[ASTableNode waitUntilAllUpdatesAreProcessed] instead."); + +- (void)insertSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode method instead."); + +- (void)deleteSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode method instead."); + +- (void)reloadSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode method instead."); + +- (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode method instead."); + +- (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode method instead."); + +- (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode method instead."); + +- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode method instead."); + +- (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode method instead."); + +- (void)setContentOffset:(CGPoint)contentOffset animated:(BOOL)animated ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode method instead."); + +@end + +ASDISPLAYNODE_DEPRECATED_MSG("Renamed to ASTableDataSource.") +@protocol ASTableViewDataSource +@end + +ASDISPLAYNODE_DEPRECATED_MSG("Renamed to ASTableDelegate.") +@protocol ASTableViewDelegate +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASTableView.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASTableView.mm new file mode 100644 index 0000000..97eb09f --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASTableView.mm @@ -0,0 +1,2039 @@ +// +// ASTableView.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + + +static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; + +//#define LOG(...) NSLog(__VA_ARGS__) +#define LOG(...) + +/** + * See note at the top of ASCollectionView.mm near declaration of macro GET_COLLECTIONNODE_OR_RETURN + */ +#define GET_TABLENODE_OR_RETURN(__var, __val) \ + ASTableNode *__var = self.tableNode; \ + if (__var == nil) { \ + return __val; \ + } + +#define UITABLEVIEW_RESPONDS_TO_SELECTOR() \ + ({ \ + static BOOL superResponds; \ + static dispatch_once_t onceToken; \ + dispatch_once(&onceToken, ^{ \ + superResponds = [UITableView instancesRespondToSelector:_cmd]; \ + }); \ + superResponds; \ + }) + +@interface UITableView (ScrollViewDelegate) + +- (void)scrollViewDidScroll:(UIScrollView *)scrollView; +- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView; +- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView; +- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset; +- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate; + +@end + +#pragma mark - +#pragma mark ASCellNode<->UITableViewCell bridging. + +@class _ASTableViewCell; + +@protocol _ASTableViewCellDelegate +- (void)didLayoutSubviewsOfTableViewCell:(_ASTableViewCell *)tableViewCell; +@end + +@interface _ASTableViewCell : UITableViewCell +@property (nonatomic, weak) id<_ASTableViewCellDelegate> delegate; +@property (nonatomic, readonly) ASCellNode *node; +@property (nonatomic) ASCollectionElement *element; +@end + +@implementation _ASTableViewCell +// TODO add assertions to prevent use of view-backed UITableViewCell properties (eg .textLabel) + +- (void)layoutSubviews +{ + [super layoutSubviews]; + [_delegate didLayoutSubviewsOfTableViewCell:self]; +} + +- (void)didTransitionToState:(UITableViewCellStateMask)state +{ + [self setNeedsLayout]; + [self layoutIfNeeded]; + [super didTransitionToState:state]; +} + +- (ASCellNode *)node +{ + return self.element.node; +} + +- (void)setElement:(ASCollectionElement *)element +{ + _element = element; + ASCellNode *node = element.node; + + if (node) { + self.backgroundColor = node.backgroundColor; + self.selectedBackgroundView = node.selectedBackgroundView; + self.backgroundView = node.backgroundView; +#if TARGET_OS_IOS + self.separatorInset = node.separatorInset; +#endif + self.selectionStyle = node.selectionStyle; + self.focusStyle = node.focusStyle; + self.accessoryType = node.accessoryType; + + // the following ensures that we clip the entire cell to it's bounds if node.clipsToBounds is set (the default) + // This is actually a workaround for a bug we are seeing in some rare cases (selected background view + // overlaps other cells if size of ASCellNode has changed.) + self.clipsToBounds = node.clipsToBounds; + + // If the cell node has been explicitly configured with a tint color, we can apply that directly to the cell view to preserve the previous behavior + self.tintColor = node->_tintColor; + } + + [node __setSelectedFromUIKit:self.selected]; + [node __setHighlightedFromUIKit:self.highlighted]; +} + +- (BOOL)consumesCellNodeVisibilityEvents +{ + ASCellNode *node = self.node; + if (node == nil) { + return NO; + } + return ASSubclassOverridesSelector([ASCellNode class], [node class], @selector(cellNodeVisibilityEvent:inScrollView:withCellFrame:)); +} + +- (void)setSelected:(BOOL)selected animated:(BOOL)animated +{ + [super setSelected:selected animated:animated]; + [self.node __setSelectedFromUIKit:selected]; +} + +- (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated +{ + [super setHighlighted:highlighted animated:animated]; + [self.node __setHighlightedFromUIKit:highlighted]; +} + +- (void)prepareForReuse +{ + // Need to clear element before UIKit calls setSelected:NO / setHighlighted:NO on its cells + self.element = nil; + [super prepareForReuse]; +} + +@end + +#pragma mark - +#pragma mark ASTableView + +@interface ASTableView () +{ + ASTableViewProxy *_proxyDataSource; + ASTableViewProxy *_proxyDelegate; + + ASTableLayoutController *_layoutController; + + ASRangeController *_rangeController; + + ASBatchContext *_batchContext; + + // When we update our data controller in response to an interactive move, + // we don't want to tell the table view about the change (it knows!) + BOOL _updatingInResponseToInteractiveMove; + BOOL _inverted; + + // The top cell node that was visible before the update. + __weak ASCellNode *_contentOffsetAdjustmentTopVisibleNode; + // The y-offset of the top visible row's origin before the update. + CGFloat _contentOffsetAdjustmentTopVisibleNodeOffset; + CGFloat _leadingScreensForBatching; + BOOL _automaticallyAdjustsContentOffset; + + CGPoint _deceleratingVelocity; + + CGFloat _nodesConstrainedWidth; + BOOL _queuedNodeHeightUpdate; + BOOL _isDeallocating; + NSHashTable<_ASTableViewCell *> *_cellsForVisibilityUpdates; + + // CountedSet because UIKit may display the same element in multiple cells e.g. during animations. + NSCountedSet *_visibleElements; + + NSHashTable *_cellsForLayoutUpdates; + + // See documentation on same property in ASCollectionView + BOOL _hasEverCheckedForBatchFetchingDueToUpdate; + + // The section index overlay view, if there is one present. + // This is useful because we need to measure our row nodes against (width - indexView.width). + __weak UIView *_sectionIndexView; + + /** + * The change set that we're currently building, if any. + */ + _ASHierarchyChangeSet *_changeSet; + + /** + * Counter used to keep track of nested batch updates. + */ + NSInteger _batchUpdateCount; + + /** + * Keep a strong reference to node till view is ready to release. + */ + ASTableNode *_keepalive_node; + + struct { + unsigned int scrollViewDidScroll:1; + unsigned int scrollViewWillBeginDragging:1; + unsigned int scrollViewDidEndDragging:1; + unsigned int scrollViewWillEndDragging:1; + unsigned int scrollViewDidEndDecelerating:1; + unsigned int tableNodeWillDisplayNodeForRow:1; + unsigned int tableViewWillDisplayNodeForRow:1; + unsigned int tableViewWillDisplayNodeForRowDeprecated:1; + unsigned int tableNodeDidEndDisplayingNodeForRow:1; + unsigned int tableViewDidEndDisplayingNodeForRow:1; + unsigned int tableNodeWillBeginBatchFetch:1; + unsigned int tableViewWillBeginBatchFetch:1; + unsigned int shouldBatchFetchForTableView:1; + unsigned int shouldBatchFetchForTableNode:1; + unsigned int tableViewConstrainedSizeForRow:1; + unsigned int tableNodeConstrainedSizeForRow:1; + unsigned int tableViewWillSelectRow:1; + unsigned int tableNodeWillSelectRow:1; + unsigned int tableViewDidSelectRow:1; + unsigned int tableNodeDidSelectRow:1; + unsigned int tableViewWillDeselectRow:1; + unsigned int tableNodeWillDeselectRow:1; + unsigned int tableViewDidDeselectRow:1; + unsigned int tableNodeDidDeselectRow:1; + unsigned int tableViewShouldHighlightRow:1; + unsigned int tableNodeShouldHighlightRow:1; + unsigned int tableViewDidHighlightRow:1; + unsigned int tableNodeDidHighlightRow:1; + unsigned int tableViewDidUnhighlightRow:1; + unsigned int tableNodeDidUnhighlightRow:1; + unsigned int tableViewShouldShowMenuForRow:1; + unsigned int tableNodeShouldShowMenuForRow:1; + unsigned int tableViewCanPerformActionForRow:1; + unsigned int tableNodeCanPerformActionForRow:1; + unsigned int tableViewPerformActionForRow:1; + unsigned int tableNodePerformActionForRow:1; + } _asyncDelegateFlags; + + struct { + unsigned int numberOfSectionsInTableView:1; + unsigned int numberOfSectionsInTableNode:1; + unsigned int tableNodeNumberOfRowsInSection:1; + unsigned int tableViewNumberOfRowsInSection:1; + unsigned int tableViewNodeBlockForRow:1; + unsigned int tableNodeNodeBlockForRow:1; + unsigned int tableViewNodeForRow:1; + unsigned int tableNodeNodeForRow:1; + unsigned int tableViewCanMoveRow:1; + unsigned int tableNodeCanMoveRow:1; + unsigned int tableViewMoveRow:1; + unsigned int tableNodeMoveRow:1; + unsigned int sectionIndexMethods:1; // if both section index methods are implemented + unsigned int modelIdentifierMethods:1; // if both modelIdentifierForElementAtIndexPath and indexPathForElementWithModelIdentifier are implemented + } _asyncDataSourceFlags; +} + +@property (nonatomic) ASDataController *dataController; + +@property (nonatomic, weak) ASTableNode *tableNode; + +@property (nonatomic) BOOL test_enableSuperUpdateCallLogging; +@end + +@implementation ASTableView +{ + __weak id _asyncDelegate; + __weak id _asyncDataSource; +} + +// Using _ASDisplayLayer ensures things like -layout are properly forwarded to ASTableNode. ++ (Class)layerClass +{ + return [_ASDisplayLayer class]; +} + ++ (Class)dataControllerClass +{ + return [ASDataController class]; +} + +#pragma mark - +#pragma mark Lifecycle + +- (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style +{ + return [self _initWithFrame:frame style:style dataControllerClass:nil owningNode:nil]; +} + +- (instancetype)_initWithFrame:(CGRect)frame style:(UITableViewStyle)style dataControllerClass:(Class)dataControllerClass owningNode:(ASTableNode *)tableNode +{ + if (!(self = [super initWithFrame:frame style:style])) { + return nil; + } + _cellsForVisibilityUpdates = [NSHashTable hashTableWithOptions:NSHashTableObjectPointerPersonality]; + _cellsForLayoutUpdates = [NSHashTable hashTableWithOptions:NSHashTableObjectPointerPersonality]; + if (!dataControllerClass) { + dataControllerClass = [[self class] dataControllerClass]; + } + + _layoutController = [[ASTableLayoutController alloc] initWithTableView:self]; + + _rangeController = [[ASRangeController alloc] init]; + _rangeController.layoutController = _layoutController; + _rangeController.dataSource = self; + _rangeController.delegate = self; + + _dataController = [[dataControllerClass alloc] initWithDataSource:self node:tableNode]; + _dataController.delegate = _rangeController; + + _leadingScreensForBatching = 2.0; + _batchContext = [[ASBatchContext alloc] init]; + _visibleElements = [[NSCountedSet alloc] init]; + + _automaticallyAdjustsContentOffset = NO; + + _nodesConstrainedWidth = self.bounds.size.width; + + _proxyDelegate = [[ASTableViewProxy alloc] initWithTarget:nil interceptor:self]; + super.delegate = (id)_proxyDelegate; + + _proxyDataSource = [[ASTableViewProxy alloc] initWithTarget:nil interceptor:self]; + super.dataSource = (id)_proxyDataSource; + + [self registerClass:_ASTableViewCell.class forCellReuseIdentifier:kCellReuseIdentifier]; + + // iOS 11 automatically uses estimated heights, so disable those (see PR #485) + if (AS_AT_LEAST_IOS11) { + super.estimatedRowHeight = 0.0; + super.estimatedSectionHeaderHeight = 0.0; + super.estimatedSectionFooterHeight = 0.0; + } + + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + NSLog(@"Warning: AsyncDisplayKit is not designed to be used with Interface Builder. Table properties set in IB will be lost."); + return [self initWithFrame:CGRectZero style:UITableViewStylePlain]; +} + +- (void)dealloc +{ + ASDisplayNodeAssertMainThread(); + ASDisplayNodeCAssert(_batchUpdateCount == 0, @"ASTableView deallocated in the middle of a batch update."); + + // Sometimes the UIKit classes can call back to their delegate even during deallocation. + _isDeallocating = YES; + if (!ASActivateExperimentalFeature(ASExperimentalCollectionTeardown)) { + [self setAsyncDelegate:nil]; + [self setAsyncDataSource:nil]; + } +} + +#pragma mark - +#pragma mark Overrides + +- (void)setDataSource:(id)dataSource +{ + // UIKit can internally generate a call to this method upon changing the asyncDataSource; only assert for non-nil. + ASDisplayNodeAssert(dataSource == nil, @"ASTableView uses asyncDataSource, not UITableView's dataSource property."); +} + +- (void)setDelegate:(id)delegate +{ + // Our UIScrollView superclass sets its delegate to nil on dealloc. Only assert if we get a non-nil value here. + ASDisplayNodeAssert(delegate == nil, @"ASTableView uses asyncDelegate, not UITableView's delegate property."); +} + +- (id)asyncDataSource +{ + return _asyncDataSource; +} + +- (void)setAsyncDataSource:(id)asyncDataSource +{ + // Changing super.dataSource will trigger a setNeedsLayout, so this must happen on the main thread. + ASDisplayNodeAssertMainThread(); + + // Note: It's common to check if the value hasn't changed and short-circuit but we aren't doing that here to handle + // the (common) case of nilling the asyncDataSource in the ViewController's dealloc. In this case our _asyncDataSource + // will return as nil (ARC magic) even though the _proxyDataSource still exists. It's really important to hold a strong + // reference to the old dataSource in this case because calls to ASTableViewProxy will start failing and cause crashes. + NS_VALID_UNTIL_END_OF_SCOPE id oldDataSource = self.dataSource; + + if (asyncDataSource == nil) { + _asyncDataSource = nil; + _proxyDataSource = _isDeallocating ? nil : [[ASTableViewProxy alloc] initWithTarget:nil interceptor:self]; + + memset(&_asyncDataSourceFlags, 0, sizeof(_asyncDataSourceFlags)); + } else { + _asyncDataSource = asyncDataSource; + _proxyDataSource = [[ASTableViewProxy alloc] initWithTarget:_asyncDataSource interceptor:self]; + + _asyncDataSourceFlags.numberOfSectionsInTableView = [_asyncDataSource respondsToSelector:@selector(numberOfSectionsInTableView:)]; + _asyncDataSourceFlags.numberOfSectionsInTableNode = [_asyncDataSource respondsToSelector:@selector(numberOfSectionsInTableNode:)]; + _asyncDataSourceFlags.tableViewNumberOfRowsInSection = [_asyncDataSource respondsToSelector:@selector(tableView:numberOfRowsInSection:)]; + _asyncDataSourceFlags.tableNodeNumberOfRowsInSection = [_asyncDataSource respondsToSelector:@selector(tableNode:numberOfRowsInSection:)]; + _asyncDataSourceFlags.tableViewNodeForRow = [_asyncDataSource respondsToSelector:@selector(tableView:nodeForRowAtIndexPath:)]; + _asyncDataSourceFlags.tableNodeNodeForRow = [_asyncDataSource respondsToSelector:@selector(tableNode:nodeForRowAtIndexPath:)]; + _asyncDataSourceFlags.tableViewNodeBlockForRow = [_asyncDataSource respondsToSelector:@selector(tableView:nodeBlockForRowAtIndexPath:)]; + _asyncDataSourceFlags.tableNodeNodeBlockForRow = [_asyncDataSource respondsToSelector:@selector(tableNode:nodeBlockForRowAtIndexPath:)]; + _asyncDataSourceFlags.tableViewCanMoveRow = [_asyncDataSource respondsToSelector:@selector(tableView:canMoveRowAtIndexPath:)]; + _asyncDataSourceFlags.tableViewMoveRow = [_asyncDataSource respondsToSelector:@selector(tableView:moveRowAtIndexPath:toIndexPath:)]; + _asyncDataSourceFlags.sectionIndexMethods = [_asyncDataSource respondsToSelector:@selector(sectionIndexTitlesForTableView:)] && [_asyncDataSource respondsToSelector:@selector(tableView:sectionForSectionIndexTitle:atIndex:)]; + _asyncDataSourceFlags.modelIdentifierMethods = [_asyncDataSource respondsToSelector:@selector(modelIdentifierForElementAtIndexPath:inNode:)] && [_asyncDataSource respondsToSelector:@selector(indexPathForElementWithModelIdentifier:inNode:)]; + + ASDisplayNodeAssert(_asyncDataSourceFlags.tableViewNodeBlockForRow + || _asyncDataSourceFlags.tableViewNodeForRow + || _asyncDataSourceFlags.tableNodeNodeBlockForRow + || _asyncDataSourceFlags.tableNodeNodeForRow, @"Data source must implement tableNode:nodeBlockForRowAtIndexPath: or tableNode:nodeForRowAtIndexPath:"); + ASDisplayNodeAssert(_asyncDataSourceFlags.tableNodeNumberOfRowsInSection || _asyncDataSourceFlags.tableViewNumberOfRowsInSection, @"Data source must implement tableNode:numberOfRowsInSection:"); + } + + _dataController.validationErrorSource = asyncDataSource; + super.dataSource = (id)_proxyDataSource; + [self _asyncDelegateOrDataSourceDidChange]; +} + +- (id)asyncDelegate +{ + return _asyncDelegate; +} + +- (void)setAsyncDelegate:(id)asyncDelegate +{ + // Changing super.delegate will trigger a setNeedsLayout, so this must happen on the main thread. + ASDisplayNodeAssertMainThread(); + + // Note: It's common to check if the value hasn't changed and short-circuit but we aren't doing that here to handle + // the (common) case of nilling the asyncDelegate in the ViewController's dealloc. In this case our _asyncDelegate + // will return as nil (ARC magic) even though the _proxyDataSource still exists. It's really important to hold a strong + // reference to the old delegate in this case because calls to ASTableViewProxy will start failing and cause crashes. + NS_VALID_UNTIL_END_OF_SCOPE id oldDelegate = super.delegate; + + if (asyncDelegate == nil) { + _asyncDelegate = nil; + _proxyDelegate = _isDeallocating ? nil : [[ASTableViewProxy alloc] initWithTarget:nil interceptor:self]; + + memset(&_asyncDelegateFlags, 0, sizeof(_asyncDelegateFlags)); + } else { + _asyncDelegate = asyncDelegate; + _proxyDelegate = [[ASTableViewProxy alloc] initWithTarget:_asyncDelegate interceptor:self]; + + _asyncDelegateFlags.scrollViewDidScroll = [_asyncDelegate respondsToSelector:@selector(scrollViewDidScroll:)]; + + _asyncDelegateFlags.tableViewWillDisplayNodeForRow = [_asyncDelegate respondsToSelector:@selector(tableView:willDisplayNode:forRowAtIndexPath:)]; + _asyncDelegateFlags.tableNodeWillDisplayNodeForRow = [_asyncDelegate respondsToSelector:@selector(tableNode:willDisplayRowWithNode:)]; + if (_asyncDelegateFlags.tableViewWillDisplayNodeForRow == NO) { + _asyncDelegateFlags.tableViewWillDisplayNodeForRowDeprecated = [_asyncDelegate respondsToSelector:@selector(tableView:willDisplayNodeForRowAtIndexPath:)]; + } + _asyncDelegateFlags.tableViewDidEndDisplayingNodeForRow = [_asyncDelegate respondsToSelector:@selector(tableView:didEndDisplayingNode:forRowAtIndexPath:)]; + _asyncDelegateFlags.tableNodeDidEndDisplayingNodeForRow = [_asyncDelegate respondsToSelector:@selector(tableNode:didEndDisplayingRowWithNode:)]; + _asyncDelegateFlags.scrollViewWillEndDragging = [_asyncDelegate respondsToSelector:@selector(scrollViewWillEndDragging:withVelocity:targetContentOffset:)]; + _asyncDelegateFlags.scrollViewDidEndDecelerating = [_asyncDelegate respondsToSelector:@selector(scrollViewDidEndDecelerating:)]; + _asyncDelegateFlags.tableViewWillBeginBatchFetch = [_asyncDelegate respondsToSelector:@selector(tableView:willBeginBatchFetchWithContext:)]; + _asyncDelegateFlags.tableNodeWillBeginBatchFetch = [_asyncDelegate respondsToSelector:@selector(tableNode:willBeginBatchFetchWithContext:)]; + _asyncDelegateFlags.shouldBatchFetchForTableView = [_asyncDelegate respondsToSelector:@selector(shouldBatchFetchForTableView:)]; + _asyncDelegateFlags.shouldBatchFetchForTableNode = [_asyncDelegate respondsToSelector:@selector(shouldBatchFetchForTableNode:)]; + _asyncDelegateFlags.scrollViewWillBeginDragging = [_asyncDelegate respondsToSelector:@selector(scrollViewWillBeginDragging:)]; + _asyncDelegateFlags.scrollViewDidEndDragging = [_asyncDelegate respondsToSelector:@selector(scrollViewDidEndDragging:willDecelerate:)]; + _asyncDelegateFlags.tableViewConstrainedSizeForRow = [_asyncDelegate respondsToSelector:@selector(tableView:constrainedSizeForRowAtIndexPath:)]; + _asyncDelegateFlags.tableNodeConstrainedSizeForRow = [_asyncDelegate respondsToSelector:@selector(tableNode:constrainedSizeForRowAtIndexPath:)]; + + _asyncDelegateFlags.tableViewWillSelectRow = [_asyncDelegate respondsToSelector:@selector(tableView:willSelectRowAtIndexPath:)]; + _asyncDelegateFlags.tableNodeWillSelectRow = [_asyncDelegate respondsToSelector:@selector(tableNode:willSelectRowAtIndexPath:)]; + _asyncDelegateFlags.tableViewDidSelectRow = [_asyncDelegate respondsToSelector:@selector(tableView:didSelectRowAtIndexPath:)]; + _asyncDelegateFlags.tableNodeDidSelectRow = [_asyncDelegate respondsToSelector:@selector(tableNode:didSelectRowAtIndexPath:)]; + _asyncDelegateFlags.tableViewWillDeselectRow = [_asyncDelegate respondsToSelector:@selector(tableView:willDeselectRowAtIndexPath:)]; + _asyncDelegateFlags.tableNodeWillDeselectRow = [_asyncDelegate respondsToSelector:@selector(tableNode:willDeselectRowAtIndexPath:)]; + _asyncDelegateFlags.tableViewDidDeselectRow = [_asyncDelegate respondsToSelector:@selector(tableView:didDeselectRowAtIndexPath:)]; + _asyncDelegateFlags.tableNodeDidDeselectRow = [_asyncDelegate respondsToSelector:@selector(tableNode:didDeselectRowAtIndexPath:)]; + _asyncDelegateFlags.tableViewShouldHighlightRow = [_asyncDelegate respondsToSelector:@selector(tableView:shouldHighlightRowAtIndexPath:)]; + _asyncDelegateFlags.tableNodeShouldHighlightRow = [_asyncDelegate respondsToSelector:@selector(tableNode:shouldHighlightRowAtIndexPath:)]; + _asyncDelegateFlags.tableViewDidHighlightRow = [_asyncDelegate respondsToSelector:@selector(tableView:didHighlightRowAtIndexPath:)]; + _asyncDelegateFlags.tableNodeDidHighlightRow = [_asyncDelegate respondsToSelector:@selector(tableNode:didHighlightRowAtIndexPath:)]; + _asyncDelegateFlags.tableViewDidUnhighlightRow = [_asyncDelegate respondsToSelector:@selector(tableView:didUnhighlightRowAtIndexPath:)]; + _asyncDelegateFlags.tableNodeDidUnhighlightRow = [_asyncDelegate respondsToSelector:@selector(tableNode:didUnhighlightRowAtIndexPath:)]; + _asyncDelegateFlags.tableViewShouldShowMenuForRow = [_asyncDelegate respondsToSelector:@selector(tableView:shouldShowMenuForRowAtIndexPath:)]; + _asyncDelegateFlags.tableNodeShouldShowMenuForRow = [_asyncDelegate respondsToSelector:@selector(tableNode:shouldShowMenuForRowAtIndexPath:)]; + _asyncDelegateFlags.tableViewCanPerformActionForRow = [_asyncDelegate respondsToSelector:@selector(tableView:canPerformAction:forRowAtIndexPath:withSender:)]; + _asyncDelegateFlags.tableNodeCanPerformActionForRow = [_asyncDelegate respondsToSelector:@selector(tableNode:canPerformAction:forRowAtIndexPath:withSender:)]; + _asyncDelegateFlags.tableViewPerformActionForRow = [_asyncDelegate respondsToSelector:@selector(tableView:performAction:forRowAtIndexPath:withSender:)]; + _asyncDelegateFlags.tableNodePerformActionForRow = [_asyncDelegate respondsToSelector:@selector(tableNode:performAction:forRowAtIndexPath:withSender:)]; + } + + super.delegate = (id)_proxyDelegate; + [self _asyncDelegateOrDataSourceDidChange]; +} + +- (void)_asyncDelegateOrDataSourceDidChange +{ + ASDisplayNodeAssertMainThread(); + + if (_asyncDataSource == nil && _asyncDelegate == nil && !ASActivateExperimentalFeature(ASExperimentalSkipClearData)) { + [_dataController clearData]; + } +} + +- (void)proxyTargetHasDeallocated:(ASDelegateProxy *)proxy +{ + if (proxy == _proxyDelegate) { + [self setAsyncDelegate:nil]; + } else if (proxy == _proxyDataSource) { + [self setAsyncDataSource:nil]; + } +} + +- (void)reloadDataWithCompletion:(void (^)())completion +{ + ASDisplayNodeAssertMainThread(); + + if (! _dataController.initialReloadDataHasBeenCalled) { + // If this is the first reload, forward to super immediately to prevent it from triggering more "initial" loads while our data controller is working. + [super reloadData]; + } + + void (^batchUpdatesCompletion)(BOOL); + if (completion) { + batchUpdatesCompletion = ^(BOOL) { + completion(); + }; + } + + [self beginUpdates]; + [_changeSet reloadData]; + [self endUpdatesWithCompletion:batchUpdatesCompletion]; +} + +- (void)reloadData +{ + [self reloadDataWithCompletion:nil]; +} + +- (void)scrollToRowAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UITableViewScrollPosition)scrollPosition animated:(BOOL)animated +{ + if ([self validateIndexPath:indexPath]) { + [super scrollToRowAtIndexPath:indexPath atScrollPosition:scrollPosition animated:animated]; + } +} + +- (void)relayoutItems +{ + [_dataController relayoutAllNodesWithInvalidationBlock:nil]; +} + +- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType +{ + [_rangeController setTuningParameters:tuningParameters forRangeMode:ASLayoutRangeModeFull rangeType:rangeType]; +} + +- (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType +{ + return [_rangeController tuningParametersForRangeMode:ASLayoutRangeModeFull rangeType:rangeType]; +} + +- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType +{ + [_rangeController setTuningParameters:tuningParameters forRangeMode:rangeMode rangeType:rangeType]; +} + +- (ASRangeTuningParameters)tuningParametersForRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType +{ + return [_rangeController tuningParametersForRangeMode:rangeMode rangeType:rangeType]; +} + +- (ASElementMap *)elementMapForRangeController:(ASRangeController *)rangeController +{ + return _dataController.visibleMap; +} + +- (ASCellNode *)nodeForRowAtIndexPath:(NSIndexPath *)indexPath +{ + return [_dataController.visibleMap elementForItemAtIndexPath:indexPath].node; +} + +- (NSIndexPath *)convertIndexPathFromTableNode:(NSIndexPath *)indexPath waitingIfNeeded:(BOOL)wait +{ + NSIndexPath *viewIndexPath = [_dataController.visibleMap convertIndexPath:indexPath fromMap:_dataController.pendingMap]; + if (viewIndexPath == nil && wait) { + [self waitUntilAllUpdatesAreCommitted]; + return [self convertIndexPathFromTableNode:indexPath waitingIfNeeded:NO]; + } + return viewIndexPath; +} + +- (NSIndexPath *)convertIndexPathToTableNode:(NSIndexPath *)indexPath +{ + if ([self validateIndexPath:indexPath] == nil) { + return nil; + } + + return [_dataController.pendingMap convertIndexPath:indexPath fromMap:_dataController.visibleMap]; +} + +- (NSArray *)convertIndexPathsToTableNode:(NSArray *)indexPaths +{ + if (indexPaths == nil) { + return nil; + } + + NSMutableArray *indexPathsArray = [NSMutableArray new]; + + for (NSIndexPath *indexPathInView in indexPaths) { + NSIndexPath *indexPath = [self convertIndexPathToTableNode:indexPathInView]; + if (indexPath != nil) { + [indexPathsArray addObject:indexPath]; + } + } + return indexPathsArray; +} + +- (NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode +{ + return [self indexPathForNode:cellNode waitingIfNeeded:NO]; +} + +/** + * Asserts that the index path is a valid view-index-path, and returns it if so, nil otherwise. + */ +- (nullable NSIndexPath *)validateIndexPath:(nullable NSIndexPath *)indexPath +{ + if (indexPath == nil) { + return nil; + } + + NSInteger section = indexPath.section; + if (section >= self.numberOfSections) { + ASDisplayNodeFailAssert(@"Table view index path has invalid section %lu, section count = %lu", (unsigned long)section, (unsigned long)self.numberOfSections); + return nil; + } + + NSInteger item = indexPath.item; + // item == NSNotFound means e.g. "scroll to this section" and is acceptable + if (item != NSNotFound && item >= [self numberOfRowsInSection:section]) { + ASDisplayNodeFailAssert(@"Table view index path has invalid item %lu in section %lu, item count = %lu", (unsigned long)indexPath.item, (unsigned long)section, (unsigned long)[self numberOfRowsInSection:section]); + return nil; + } + + return indexPath; +} + +- (nullable NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode waitingIfNeeded:(BOOL)wait +{ + if (cellNode == nil) { + return nil; + } + + NSIndexPath *indexPath = [_dataController.visibleMap indexPathForElement:cellNode.collectionElement]; + indexPath = [self validateIndexPath:indexPath]; + if (indexPath == nil && wait) { + [self waitUntilAllUpdatesAreCommitted]; + return [self indexPathForNode:cellNode waitingIfNeeded:NO]; + } + return indexPath; +} + +- (NSArray *)visibleNodes +{ + const auto elements = [self visibleElementsForRangeController:_rangeController]; + return ASArrayByFlatMapping(elements, ASCollectionElement *e, e.node); +} + +- (void)beginUpdates +{ + ASDisplayNodeAssertMainThread(); + // _changeSet must be available during batch update + ASDisplayNodeAssertTrue((_batchUpdateCount > 0) == (_changeSet != nil)); + + if (_batchUpdateCount == 0) { + _changeSet = [[_ASHierarchyChangeSet alloc] initWithOldData:[_dataController itemCountsFromDataSource]]; + } + _batchUpdateCount++; +} + +- (void)endUpdates +{ + [self endUpdatesWithCompletion:nil]; +} + +- (void)endUpdatesWithCompletion:(void (^)(BOOL completed))completion +{ + // We capture the current state of whether animations are enabled if they don't provide us with one. + [self endUpdatesAnimated:[UIView areAnimationsEnabled] completion:completion]; +} + +- (void)endUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL completed))completion +{ + ASDisplayNodeAssertMainThread(); + ASDisplayNodeAssertNotNil(_changeSet, @"_changeSet must be available when batch update ends"); + + _batchUpdateCount--; + // Prevent calling endUpdatesAnimated:completion: in an unbalanced way + NSAssert(_batchUpdateCount >= 0, @"endUpdatesAnimated:completion: called without having a balanced beginUpdates call"); + + [_changeSet addCompletionHandler:completion]; + + if (_batchUpdateCount == 0) { + _ASHierarchyChangeSet *changeSet = _changeSet; + // Nil out _changeSet before forwarding to _dataController to allow the change set to cause subsequent batch updates on the same run loop + _changeSet = nil; + changeSet.animated = animated; + [_dataController updateWithChangeSet:changeSet]; + } +} + +- (BOOL)isProcessingUpdates +{ + return [_dataController isProcessingUpdates]; +} + +- (void)onDidFinishProcessingUpdates:(void (^)())completion +{ + [_dataController onDidFinishProcessingUpdates:completion]; +} + +- (void)waitUntilAllUpdatesAreCommitted +{ + ASDisplayNodeAssertMainThread(); + if (_batchUpdateCount > 0) { + // This assertion will be enabled soon. + // ASDisplayNodeFailAssert(@"Should not call %@ during batch update", NSStringFromSelector(_cmd)); + return; + } + + [_dataController waitUntilAllUpdatesAreProcessed]; +} + +- (void)layoutSubviews +{ + // Remeasure all rows if our row width has changed. + UIEdgeInsets contentInset = self.contentInset; + CGFloat constrainedWidth = self.bounds.size.width - [self sectionIndexWidth] - contentInset.left - contentInset.right; + if (constrainedWidth > 0 && _nodesConstrainedWidth != constrainedWidth) { + _nodesConstrainedWidth = constrainedWidth; + [_cellsForLayoutUpdates removeAllObjects]; + + [self beginUpdates]; + [_dataController relayoutAllNodesWithInvalidationBlock:nil]; + [self endUpdatesAnimated:(ASDisplayNodeLayerHasAnimations(self.layer) == NO) completion:nil]; + } else { + if (_cellsForLayoutUpdates.count > 0) { + NSArray *nodes = [_cellsForLayoutUpdates allObjects]; + [_cellsForLayoutUpdates removeAllObjects]; + + const auto nodesSizeChanged = [[NSMutableArray alloc] init]; + [_dataController relayoutNodes:nodes nodesSizeChanged:nodesSizeChanged]; + if (nodesSizeChanged.count > 0) { + [self requeryNodeHeights]; + } + } + } + + // To ensure _nodesConstrainedWidth is up-to-date for every usage, this call to super must be done last + [super layoutSubviews]; + [_rangeController updateIfNeeded]; +} + +#pragma mark - +#pragma mark Editing + +- (void)insertSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation +{ + ASDisplayNodeAssertMainThread(); + if (sections.count == 0) { return; } + [self beginUpdates]; + [_changeSet insertSections:sections animationOptions:animation]; + [self endUpdates]; +} + +- (void)deleteSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation +{ + ASDisplayNodeAssertMainThread(); + if (sections.count == 0) { return; } + [self beginUpdates]; + [_changeSet deleteSections:sections animationOptions:animation]; + [self endUpdates]; +} + +- (void)reloadSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation +{ + ASDisplayNodeAssertMainThread(); + if (sections.count == 0) { return; } + [self beginUpdates]; + [_changeSet reloadSections:sections animationOptions:animation]; + [self endUpdates]; +} + +- (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection +{ + ASDisplayNodeAssertMainThread(); + [self beginUpdates]; + [_changeSet moveSection:section toSection:newSection animationOptions:UITableViewRowAnimationNone]; + [self endUpdates]; +} + +- (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation +{ + ASDisplayNodeAssertMainThread(); + if (indexPaths.count == 0) { return; } + [self beginUpdates]; + [_changeSet insertItems:indexPaths animationOptions:animation]; + [self endUpdates]; +} + +- (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation +{ + ASDisplayNodeAssertMainThread(); + if (indexPaths.count == 0) { return; } + [self beginUpdates]; + [_changeSet deleteItems:indexPaths animationOptions:animation]; + [self endUpdates]; +} + +- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation +{ + ASDisplayNodeAssertMainThread(); + if (indexPaths.count == 0) { return; } + [self beginUpdates]; + [_changeSet reloadItems:indexPaths animationOptions:animation]; + [self endUpdates]; +} + +- (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath +{ + ASDisplayNodeAssertMainThread(); + [self beginUpdates]; + [_changeSet moveItemAtIndexPath:indexPath toIndexPath:newIndexPath animationOptions:UITableViewRowAnimationNone]; + [self endUpdates]; +} + +#pragma mark - +#pragma mark adjust content offset + +- (void)beginAdjustingContentOffset +{ + NSIndexPath *firstVisibleIndexPath = [self.indexPathsForVisibleRows sortedArrayUsingSelector:@selector(compare:)].firstObject; + if (firstVisibleIndexPath) { + ASCellNode *node = [self nodeForRowAtIndexPath:firstVisibleIndexPath]; + if (node) { + _contentOffsetAdjustmentTopVisibleNode = node; + _contentOffsetAdjustmentTopVisibleNodeOffset = [self rectForRowAtIndexPath:firstVisibleIndexPath].origin.y - self.bounds.origin.y; + } + } +} + +- (void)endAdjustingContentOffsetAnimated:(BOOL)animated +{ + // We can't do this for animated updates. + if (animated) { + return; + } + + // We can't do this if we didn't have a top visible row before. + if (_contentOffsetAdjustmentTopVisibleNode == nil) { + return; + } + + NSIndexPath *newIndexPathForTopVisibleRow = [self indexPathForNode:_contentOffsetAdjustmentTopVisibleNode]; + // We can't do this if our top visible row was deleted + if (newIndexPathForTopVisibleRow == nil) { + return; + } + + CGFloat newRowOriginYInSelf = [self rectForRowAtIndexPath:newIndexPathForTopVisibleRow].origin.y - self.bounds.origin.y; + CGPoint newContentOffset = self.contentOffset; + newContentOffset.y += (newRowOriginYInSelf - _contentOffsetAdjustmentTopVisibleNodeOffset); + self.contentOffset = newContentOffset; + _contentOffsetAdjustmentTopVisibleNode = nil; +} + +#pragma mark - Intercepted selectors + +- (void)setTableHeaderView:(UIView *)tableHeaderView +{ + // Typically the view will be nil before setting it, but reset state if it is being re-hosted. + [self.tableHeaderView.asyncdisplaykit_node exitHierarchyState:ASHierarchyStateRangeManaged]; + [super setTableHeaderView:tableHeaderView]; + [self.tableHeaderView.asyncdisplaykit_node enterHierarchyState:ASHierarchyStateRangeManaged]; +} + +- (void)setTableFooterView:(UIView *)tableFooterView +{ + // Typically the view will be nil before setting it, but reset state if it is being re-hosted. + [self.tableFooterView.asyncdisplaykit_node exitHierarchyState:ASHierarchyStateRangeManaged]; + [super setTableFooterView:tableFooterView]; + [self.tableFooterView.asyncdisplaykit_node enterHierarchyState:ASHierarchyStateRangeManaged]; +} + +- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath +{ + _ASTableViewCell *cell = [self dequeueReusableCellWithIdentifier:kCellReuseIdentifier forIndexPath:indexPath]; + cell.delegate = self; + + ASCollectionElement *element = [_dataController.visibleMap elementForItemAtIndexPath:indexPath]; + cell.element = element; + ASCellNode *node = element.node; + if (node) { + [_rangeController configureContentView:cell.contentView forCellNode:node]; + } + + return cell; +} + +- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath +{ + CGFloat height = 0.0; + + ASCollectionElement *element = [_dataController.visibleMap elementForItemAtIndexPath:indexPath]; + if (element != nil) { + ASCellNode *node = element.node; + ASDisplayNodeAssertNotNil(node, @"Node must not be nil!"); + height = [node layoutThatFits:element.constrainedSize].size.height; + } + +#if TARGET_OS_IOS + /** + * Weirdly enough, Apple expects the return value here to _include_ the height + * of the separator, if there is one! So if our node wants to be 43.5, we need + * to return 44. UITableView will make a cell of height 44 with a content view + * of height 43.5. + */ + if (tableView.separatorStyle != UITableViewCellSeparatorStyleNone) { + height += 1.0 / ASScreenScale(); + } +#endif + + return height; +} + +- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView +{ + return _dataController.visibleMap.numberOfSections; +} + +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section +{ + return [_dataController.visibleMap numberOfItemsInSection:section]; +} + +- (nullable NSString *)modelIdentifierForElementAtIndexPath:(NSIndexPath *)indexPath inView:(UIView *)view { + if (_asyncDataSourceFlags.modelIdentifierMethods) { + GET_TABLENODE_OR_RETURN(tableNode, nil); + NSIndexPath *convertedPath = [self convertIndexPathToTableNode:indexPath]; + if (convertedPath == nil) { + return nil; + } else { + return [_asyncDataSource modelIdentifierForElementAtIndexPath:convertedPath inNode:tableNode]; + } + } else { + return nil; + } +} + +- (nullable NSIndexPath *)indexPathForElementWithModelIdentifier:(NSString *)identifier inView:(UIView *)view { + if (_asyncDataSourceFlags.modelIdentifierMethods) { + GET_TABLENODE_OR_RETURN(tableNode, nil); + return [_asyncDataSource indexPathForElementWithModelIdentifier:identifier inNode:tableNode]; + } else { + return nil; + } +} + +- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath +{ + if (_asyncDataSourceFlags.tableViewCanMoveRow) { + return [_asyncDataSource tableView:self canMoveRowAtIndexPath:indexPath]; + } else { + return NO; + } +} + +- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath +{ + if (_asyncDataSourceFlags.tableViewMoveRow) { + [_asyncDataSource tableView:self moveRowAtIndexPath:sourceIndexPath toIndexPath:destinationIndexPath]; + } + + // Move node after informing data source in case they call nodeAtIndexPath: + // Get up to date + [self waitUntilAllUpdatesAreCommitted]; + // Set our flag to suppress informing super about the change. + _updatingInResponseToInteractiveMove = YES; + // Submit the move + [self moveRowAtIndexPath:sourceIndexPath toIndexPath:destinationIndexPath]; + // Wait for it to finish – should be fast! + [self waitUntilAllUpdatesAreCommitted]; + // Clear the flag + _updatingInResponseToInteractiveMove = NO; +} + +- (void)tableView:(UITableView *)tableView willDisplayCell:(_ASTableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath +{ + ASCollectionElement *element = cell.element; + if (element) { + ASDisplayNodeAssertTrue([_dataController.visibleMap elementForItemAtIndexPath:indexPath] == element); + [_visibleElements addObject:element]; + } else { + ASDisplayNodeAssert(NO, @"Unexpected nil element for willDisplayCell: %@, %@, %@", cell, self, indexPath); + return; + } + + ASCellNode *cellNode = element.node; + cellNode.scrollView = tableView; + + ASDisplayNodeAssertNotNil(cellNode, @"Expected node associated with cell that will be displayed not to be nil. indexPath: %@", indexPath); + + if (_asyncDelegateFlags.tableNodeWillDisplayNodeForRow) { + GET_TABLENODE_OR_RETURN(tableNode, (void)0); + [_asyncDelegate tableNode:tableNode willDisplayRowWithNode:cellNode]; + } else if (_asyncDelegateFlags.tableViewWillDisplayNodeForRow) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + [_asyncDelegate tableView:self willDisplayNode:cellNode forRowAtIndexPath:indexPath]; + } else if (_asyncDelegateFlags.tableViewWillDisplayNodeForRowDeprecated) { + [_asyncDelegate tableView:self willDisplayNodeForRowAtIndexPath:indexPath]; + } +#pragma clang diagnostic pop + + [_rangeController setNeedsUpdate]; + + if ([cell consumesCellNodeVisibilityEvents]) { + [_cellsForVisibilityUpdates addObject:cell]; + } +} + +- (void)tableView:(UITableView *)tableView didEndDisplayingCell:(_ASTableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath +{ + // Retrieve the element from cell instead of visible map because at this point visible map could have been updated and no longer holds the element. + ASCollectionElement *element = cell.element; + if (element) { + [_visibleElements removeObject:element]; + } else { + ASDisplayNodeAssert(NO, @"Unexpected nil element for didEndDisplayingCell: %@, %@, %@", cell, self, indexPath); + return; + } + + ASCellNode *cellNode = element.node; + + [_rangeController setNeedsUpdate]; + + ASDisplayNodeAssertNotNil(cellNode, @"Expected node associated with removed cell not to be nil."); + if (_asyncDelegateFlags.tableNodeDidEndDisplayingNodeForRow) { + if (ASTableNode *tableNode = self.tableNode) { + [_asyncDelegate tableNode:tableNode didEndDisplayingRowWithNode:cellNode]; + } + } else if (_asyncDelegateFlags.tableViewDidEndDisplayingNodeForRow) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + [_asyncDelegate tableView:self didEndDisplayingNode:cellNode forRowAtIndexPath:indexPath]; +#pragma clang diagnostic pop + } + + [_cellsForVisibilityUpdates removeObject:cell]; + + cellNode.scrollView = nil; +} + +- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(nonnull NSIndexPath *)indexPath +{ + if (_asyncDelegateFlags.tableNodeWillSelectRow) { + GET_TABLENODE_OR_RETURN(tableNode, indexPath); + NSIndexPath *result = [self convertIndexPathToTableNode:indexPath]; + // If this item was is gone, just let the table view do its default behavior and select. + if (result == nil) { + return indexPath; + } else { + result = [_asyncDelegate tableNode:tableNode willSelectRowAtIndexPath:result]; + result = [self convertIndexPathFromTableNode:result waitingIfNeeded:YES]; + return result; + } + } else if (_asyncDelegateFlags.tableViewWillSelectRow) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + return [_asyncDelegate tableView:self willSelectRowAtIndexPath:indexPath]; +#pragma clang diagnostic pop + } else { + return indexPath; + } +} + +- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(nonnull NSIndexPath *)indexPath +{ + if (_asyncDelegateFlags.tableNodeDidSelectRow) { + GET_TABLENODE_OR_RETURN(tableNode, (void)0); + indexPath = [self convertIndexPathToTableNode:indexPath]; + if (indexPath != nil) { + [_asyncDelegate tableNode:tableNode didSelectRowAtIndexPath:indexPath]; + } + } else if (_asyncDelegateFlags.tableViewDidSelectRow) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + [_asyncDelegate tableView:self didSelectRowAtIndexPath:indexPath]; +#pragma clang diagnostic pop + } +} + +- (NSIndexPath *)tableView:(UITableView *)tableView willDeselectRowAtIndexPath:(nonnull NSIndexPath *)indexPath +{ + if (_asyncDelegateFlags.tableNodeWillDeselectRow) { + GET_TABLENODE_OR_RETURN(tableNode, indexPath); + NSIndexPath *result = [self convertIndexPathToTableNode:indexPath]; + // If this item was is gone, just let the table view do its default behavior and deselect. + if (result == nil) { + return indexPath; + } else { + result = [_asyncDelegate tableNode:tableNode willDeselectRowAtIndexPath:result]; + result = [self convertIndexPathFromTableNode:result waitingIfNeeded:YES]; + return result; + } + } else if (_asyncDelegateFlags.tableViewWillDeselectRow) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + return [_asyncDelegate tableView:self willDeselectRowAtIndexPath:indexPath]; +#pragma clang diagnostic pop + } + return indexPath; +} + +- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(nonnull NSIndexPath *)indexPath +{ + if (_asyncDelegateFlags.tableNodeDidDeselectRow) { + GET_TABLENODE_OR_RETURN(tableNode, (void)0); + indexPath = [self convertIndexPathToTableNode:indexPath]; + if (indexPath != nil) { + [_asyncDelegate tableNode:tableNode didDeselectRowAtIndexPath:indexPath]; + } + } else if (_asyncDelegateFlags.tableViewDidDeselectRow) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + [_asyncDelegate tableView:self didDeselectRowAtIndexPath:indexPath]; +#pragma clang diagnostic pop + } +} + +- (BOOL)tableView:(UITableView *)tableView shouldHighlightRowAtIndexPath:(nonnull NSIndexPath *)indexPath +{ + if (_asyncDelegateFlags.tableNodeShouldHighlightRow) { + GET_TABLENODE_OR_RETURN(tableNode, NO); + indexPath = [self convertIndexPathToTableNode:indexPath]; + if (indexPath != nil) { + return [_asyncDelegate tableNode:tableNode shouldHighlightRowAtIndexPath:indexPath]; + } + } else if (_asyncDelegateFlags.tableViewShouldHighlightRow) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + return [_asyncDelegate tableView:self shouldHighlightRowAtIndexPath:indexPath]; +#pragma clang diagnostic pop + } + return YES; +} + +- (void)tableView:(UITableView *)tableView didHighlightRowAtIndexPath:(nonnull NSIndexPath *)indexPath +{ + if (_asyncDelegateFlags.tableNodeDidHighlightRow) { + GET_TABLENODE_OR_RETURN(tableNode, (void)0); + indexPath = [self convertIndexPathToTableNode:indexPath]; + if (indexPath != nil) { + return [_asyncDelegate tableNode:tableNode didHighlightRowAtIndexPath:indexPath]; + } + } else if (_asyncDelegateFlags.tableViewDidHighlightRow) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + [_asyncDelegate tableView:self didHighlightRowAtIndexPath:indexPath]; +#pragma clang diagnostic pop + } +} + +- (void)tableView:(UITableView *)tableView didUnhighlightRowAtIndexPath:(nonnull NSIndexPath *)indexPath +{ + if (_asyncDelegateFlags.tableNodeDidHighlightRow) { + GET_TABLENODE_OR_RETURN(tableNode, (void)0); + indexPath = [self convertIndexPathToTableNode:indexPath]; + if (indexPath != nil) { + return [_asyncDelegate tableNode:tableNode didUnhighlightRowAtIndexPath:indexPath]; + } + } else if (_asyncDelegateFlags.tableViewDidUnhighlightRow) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + [_asyncDelegate tableView:self didUnhighlightRowAtIndexPath:indexPath]; +#pragma clang diagnostic pop + } +} + +- (BOOL)tableView:(UITableView *)tableView shouldShowMenuForRowAtIndexPath:(nonnull NSIndexPath *)indexPath +{ + if (_asyncDelegateFlags.tableNodeShouldShowMenuForRow) { + GET_TABLENODE_OR_RETURN(tableNode, NO); + indexPath = [self convertIndexPathToTableNode:indexPath]; + if (indexPath != nil) { + return [_asyncDelegate tableNode:tableNode shouldShowMenuForRowAtIndexPath:indexPath]; + } + } else if (_asyncDelegateFlags.tableViewShouldShowMenuForRow) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + return [_asyncDelegate tableView:self shouldShowMenuForRowAtIndexPath:indexPath]; +#pragma clang diagnostic pop + } + return NO; +} + +- (BOOL)tableView:(UITableView *)tableView canPerformAction:(nonnull SEL)action forRowAtIndexPath:(nonnull NSIndexPath *)indexPath withSender:(nullable id)sender +{ + if (_asyncDelegateFlags.tableNodeCanPerformActionForRow) { + GET_TABLENODE_OR_RETURN(tableNode, NO); + indexPath = [self convertIndexPathToTableNode:indexPath]; + if (indexPath != nil) { + return [_asyncDelegate tableNode:tableNode canPerformAction:action forRowAtIndexPath:indexPath withSender:sender]; + } + } else if (_asyncDelegateFlags.tableViewCanPerformActionForRow) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + return [_asyncDelegate tableView:self canPerformAction:action forRowAtIndexPath:indexPath withSender:sender]; +#pragma clang diagnostic pop + } + return NO; +} + +- (void)tableView:(UITableView *)tableView performAction:(nonnull SEL)action forRowAtIndexPath:(nonnull NSIndexPath *)indexPath withSender:(nullable id)sender +{ + if (_asyncDelegateFlags.tableNodePerformActionForRow) { + GET_TABLENODE_OR_RETURN(tableNode, (void)0); + indexPath = [self convertIndexPathToTableNode:indexPath]; + if (indexPath != nil) { + [_asyncDelegate tableNode:tableNode performAction:action forRowAtIndexPath:indexPath withSender:sender]; + } + } else if (_asyncDelegateFlags.tableViewPerformActionForRow) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + [_asyncDelegate tableView:self performAction:action forRowAtIndexPath:indexPath withSender:sender]; +#pragma clang diagnostic pop + } +} + +- (void)scrollViewDidScroll:(UIScrollView *)scrollView +{ + if (scrollView != self && UITABLEVIEW_RESPONDS_TO_SELECTOR()) { + [super scrollViewDidScroll:scrollView]; + return; + } + ASInterfaceState interfaceState = [self interfaceStateForRangeController:_rangeController]; + if (ASInterfaceStateIncludesVisible(interfaceState)) { + [self _checkForBatchFetching]; + } + for (_ASTableViewCell *tableCell in _cellsForVisibilityUpdates) { + [[tableCell node] cellNodeVisibilityEvent:ASCellNodeVisibilityEventVisibleRectChanged + inScrollView:scrollView + withCellFrame:tableCell.frame]; + } + if (_asyncDelegateFlags.scrollViewDidScroll) { + [_asyncDelegate scrollViewDidScroll:scrollView]; + } +} + +- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset +{ + if (scrollView != self && UITABLEVIEW_RESPONDS_TO_SELECTOR()) { + [super scrollViewWillEndDragging:scrollView withVelocity:velocity targetContentOffset:targetContentOffset]; + return; + } + CGPoint contentOffset = scrollView.contentOffset; + _deceleratingVelocity = CGPointMake( + contentOffset.x - ((targetContentOffset != NULL) ? targetContentOffset->x : 0), + contentOffset.y - ((targetContentOffset != NULL) ? targetContentOffset->y : 0) + ); + + if (targetContentOffset != NULL) { + ASDisplayNodeAssert(_batchContext != nil, @"Batch context should exist"); + [self _beginBatchFetchingIfNeededWithContentOffset:*targetContentOffset velocity:velocity]; + } + + if (_asyncDelegateFlags.scrollViewWillEndDragging) { + [_asyncDelegate scrollViewWillEndDragging:scrollView withVelocity:velocity targetContentOffset:(targetContentOffset ? : &contentOffset)]; + } +} + +- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView +{ + if (scrollView != self && UITABLEVIEW_RESPONDS_TO_SELECTOR()) { + [super scrollViewDidEndDecelerating:scrollView]; + return; + } + _deceleratingVelocity = CGPointZero; + + if (_asyncDelegateFlags.scrollViewDidEndDecelerating) { + [_asyncDelegate scrollViewDidEndDecelerating:scrollView]; + } +} + +- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView +{ + if (scrollView != self && UITABLEVIEW_RESPONDS_TO_SELECTOR()) { + [super scrollViewWillBeginDragging:scrollView]; + return; + } + // If a scroll happens the current range mode needs to go to full + _rangeController.contentHasBeenScrolled = YES; + [_rangeController updateCurrentRangeWithMode:ASLayoutRangeModeFull]; + + for (_ASTableViewCell *tableViewCell in _cellsForVisibilityUpdates) { + [[tableViewCell node] cellNodeVisibilityEvent:ASCellNodeVisibilityEventWillBeginDragging + inScrollView:scrollView + withCellFrame:tableViewCell.frame]; + } + if (_asyncDelegateFlags.scrollViewWillBeginDragging) { + [_asyncDelegate scrollViewWillBeginDragging:scrollView]; + } +} + +- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate +{ + if (scrollView != self && UITABLEVIEW_RESPONDS_TO_SELECTOR()) { + [super scrollViewDidEndDragging:scrollView willDecelerate:decelerate]; + return; + } + for (_ASTableViewCell *tableViewCell in _cellsForVisibilityUpdates) { + [[tableViewCell node] cellNodeVisibilityEvent:ASCellNodeVisibilityEventDidEndDragging + inScrollView:scrollView + withCellFrame:tableViewCell.frame]; + } + if (_asyncDelegateFlags.scrollViewDidEndDragging) { + [_asyncDelegate scrollViewDidEndDragging:scrollView willDecelerate:decelerate]; + } +} + +#pragma mark - Misc + +- (BOOL)inverted +{ + return _inverted; +} + +- (void)setInverted:(BOOL)inverted +{ + _inverted = inverted; +} + +- (CGFloat)leadingScreensForBatching +{ + return _leadingScreensForBatching; +} + +- (void)setLeadingScreensForBatching:(CGFloat)leadingScreensForBatching +{ + if (_leadingScreensForBatching != leadingScreensForBatching) { + _leadingScreensForBatching = leadingScreensForBatching; + ASPerformBlockOnMainThread(^{ + [self _checkForBatchFetching]; + }); + } +} + +- (BOOL)automaticallyAdjustsContentOffset +{ + return _automaticallyAdjustsContentOffset; +} + +- (void)setAutomaticallyAdjustsContentOffset:(BOOL)automaticallyAdjustsContentOffset +{ + _automaticallyAdjustsContentOffset = automaticallyAdjustsContentOffset; +} + +#pragma mark - Scroll Direction + +- (ASScrollDirection)scrollDirection +{ + CGPoint scrollVelocity; + if (self.isTracking) { + scrollVelocity = [self.panGestureRecognizer velocityInView:self.superview]; + } else { + scrollVelocity = _deceleratingVelocity; + } + + ASScrollDirection scrollDirection = [self _scrollDirectionForVelocity:scrollVelocity]; + return ASScrollDirectionApplyTransform(scrollDirection, self.transform); +} + +- (ASScrollDirection)_scrollDirectionForVelocity:(CGPoint)scrollVelocity +{ + ASScrollDirection direction = ASScrollDirectionNone; + ASScrollDirection scrollableDirections = [self scrollableDirections]; + + if (ASScrollDirectionContainsVerticalDirection(scrollableDirections)) { // Can scroll vertically. + if (scrollVelocity.y < 0.0) { + direction |= ASScrollDirectionDown; + } else if (scrollVelocity.y > 0.0) { + direction |= ASScrollDirectionUp; + } + } + + return direction; +} + +- (ASScrollDirection)scrollableDirections +{ + ASScrollDirection scrollableDirection = ASScrollDirectionNone; + CGFloat totalContentWidth = self.contentSize.width + self.contentInset.left + self.contentInset.right; + CGFloat totalContentHeight = self.contentSize.height + self.contentInset.top + self.contentInset.bottom; + + if (self.alwaysBounceHorizontal || totalContentWidth > self.bounds.size.width) { // Can scroll horizontally. + scrollableDirection |= ASScrollDirectionHorizontalDirections; + } + if (self.alwaysBounceVertical || totalContentHeight > self.bounds.size.height) { // Can scroll vertically. + scrollableDirection |= ASScrollDirectionVerticalDirections; + } + return scrollableDirection; +} + + +#pragma mark - Batch Fetching + +- (ASBatchContext *)batchContext +{ + return _batchContext; +} + +- (BOOL)canBatchFetch +{ + // if the delegate does not respond to this method, there is no point in starting to fetch + BOOL canFetch = _asyncDelegateFlags.tableNodeWillBeginBatchFetch || _asyncDelegateFlags.tableViewWillBeginBatchFetch; + if (canFetch && _asyncDelegateFlags.shouldBatchFetchForTableNode) { + GET_TABLENODE_OR_RETURN(tableNode, NO); + return [_asyncDelegate shouldBatchFetchForTableNode:tableNode]; + } else if (canFetch && _asyncDelegateFlags.shouldBatchFetchForTableView) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + return [_asyncDelegate shouldBatchFetchForTableView:self]; +#pragma clang diagnostic pop + } else { + return canFetch; + } +} + +- (id)batchFetchingDelegate +{ + return self.tableNode.batchFetchingDelegate; +} + +- (void)_scheduleCheckForBatchFetchingForNumberOfChanges:(NSUInteger)changes +{ + // Prevent fetching will continually trigger in a loop after reaching end of content and no new content was provided + if (changes == 0 && _hasEverCheckedForBatchFetchingDueToUpdate) { + return; + } + _hasEverCheckedForBatchFetchingDueToUpdate = YES; + + // Push this to the next runloop to be sure the scroll view has the right content size + dispatch_async(dispatch_get_main_queue(), ^{ + [self _checkForBatchFetching]; + }); +} + +- (void)_checkForBatchFetching +{ + // Dragging will be handled in scrollViewWillEndDragging:withVelocity:targetContentOffset: + if (self.isDragging || self.isTracking) { + return; + } + + [self _beginBatchFetchingIfNeededWithContentOffset:self.contentOffset velocity:CGPointZero]; +} + +- (void)_beginBatchFetchingIfNeededWithContentOffset:(CGPoint)contentOffset velocity:(CGPoint)velocity +{ + if (ASDisplayShouldFetchBatchForScrollView(self, self.scrollDirection, ASScrollDirectionVerticalDirections, contentOffset, velocity)) { + [self _beginBatchFetching]; + } +} + +- (void)_beginBatchFetching +{ + [_batchContext beginBatchFetching]; + if (_asyncDelegateFlags.tableNodeWillBeginBatchFetch) { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + GET_TABLENODE_OR_RETURN(tableNode, (void)0); + [self->_asyncDelegate tableNode:tableNode willBeginBatchFetchWithContext:self->_batchContext]; + }); + } else if (_asyncDelegateFlags.tableViewWillBeginBatchFetch) { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + [self->_asyncDelegate tableView:self willBeginBatchFetchWithContext:self->_batchContext]; +#pragma clang diagnostic pop + }); + } +} + +#pragma mark - ASRangeControllerDataSource + +- (ASRangeController *)rangeController +{ + return _rangeController; +} + +- (NSHashTable *)visibleElementsForRangeController:(ASRangeController *)rangeController +{ + return ASPointerTableByFlatMapping(_visibleElements, id element, element); +} + +- (ASScrollDirection)scrollDirectionForRangeController:(ASRangeController *)rangeController +{ + return self.scrollDirection; +} + +- (ASInterfaceState)interfaceStateForRangeController:(ASRangeController *)rangeController +{ + return ASInterfaceStateForDisplayNode(self.tableNode, self.window); +} + +- (NSString *)nameForRangeControllerDataSource +{ + return self.asyncDataSource ? NSStringFromClass([self.asyncDataSource class]) : NSStringFromClass([self class]); +} + +#pragma mark - ASRangeControllerDelegate + +- (BOOL)rangeControllerShouldUpdateRanges:(ASRangeController *)rangeController +{ + return YES; +} + +- (void)rangeController:(ASRangeController *)rangeController updateWithChangeSet:(_ASHierarchyChangeSet *)changeSet updates:(dispatch_block_t)updates +{ + ASDisplayNodeAssertMainThread(); + if (!self.asyncDataSource || _updatingInResponseToInteractiveMove) { + updates(); + [changeSet executeCompletionHandlerWithFinished:NO]; + return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes + } + + if (changeSet.includesReloadData) { + LOG(@"UITableView reloadData"); + ASPerformBlockWithoutAnimation(!changeSet.animated, ^{ + if (self.test_enableSuperUpdateCallLogging) { + NSLog(@"-[super reloadData]"); + } + updates(); + [super reloadData]; + // Flush any range changes that happened as part of submitting the reload. + [self->_rangeController updateIfNeeded]; + [self _scheduleCheckForBatchFetchingForNumberOfChanges:1]; + [changeSet executeCompletionHandlerWithFinished:YES]; + }); + return; + } + + BOOL shouldAdjustContentOffset = (_automaticallyAdjustsContentOffset && !changeSet.includesReloadData); + if (shouldAdjustContentOffset) { + [self beginAdjustingContentOffset]; + } + + NSUInteger numberOfUpdates = 0; + + LOG(@"--- UITableView beginUpdates"); + [super beginUpdates]; + + updates(); + + for (_ASHierarchyItemChange *change in [changeSet itemChangesOfType:_ASHierarchyChangeTypeReload]) { + NSArray *indexPaths = change.indexPaths; + UITableViewRowAnimation animationOptions = (UITableViewRowAnimation)change.animationOptions; + + LOG(@"UITableView reloadRows:%ld rows", indexPaths.count); + BOOL preventAnimation = animationOptions == UITableViewRowAnimationNone; + ASPerformBlockWithoutAnimation(preventAnimation, ^{ + if (self.test_enableSuperUpdateCallLogging) { + NSLog(@"-[super reloadRowsAtIndexPaths]: %@", indexPaths); + } + [super reloadRowsAtIndexPaths:indexPaths withRowAnimation:animationOptions]; + }); + + numberOfUpdates++; + } + + for (_ASHierarchySectionChange *change in [changeSet sectionChangesOfType:_ASHierarchyChangeTypeReload]) { + NSIndexSet *sectionIndexes = change.indexSet; + UITableViewRowAnimation animationOptions = (UITableViewRowAnimation)change.animationOptions; + + LOG(@"UITableView reloadSections:%@", sectionIndexes); + BOOL preventAnimation = (animationOptions == UITableViewRowAnimationNone); + ASPerformBlockWithoutAnimation(preventAnimation, ^{ + if (self.test_enableSuperUpdateCallLogging) { + NSLog(@"-[super reloadSections]: %@", sectionIndexes); + } + [super reloadSections:sectionIndexes withRowAnimation:animationOptions]; + }); + + numberOfUpdates++; + } + + for (_ASHierarchyItemChange *change in [changeSet itemChangesOfType:_ASHierarchyChangeTypeOriginalDelete]) { + NSArray *indexPaths = change.indexPaths; + UITableViewRowAnimation animationOptions = (UITableViewRowAnimation)change.animationOptions; + + LOG(@"UITableView deleteRows:%ld rows", indexPaths.count); + BOOL preventAnimation = animationOptions == UITableViewRowAnimationNone; + ASPerformBlockWithoutAnimation(preventAnimation, ^{ + if (self.test_enableSuperUpdateCallLogging) { + NSLog(@"-[super deleteRowsAtIndexPaths]: %@", indexPaths); + } + [super deleteRowsAtIndexPaths:indexPaths withRowAnimation:animationOptions]; + }); + + numberOfUpdates++; + } + + for (_ASHierarchySectionChange *change in [changeSet sectionChangesOfType:_ASHierarchyChangeTypeOriginalDelete]) { + NSIndexSet *sectionIndexes = change.indexSet; + UITableViewRowAnimation animationOptions = (UITableViewRowAnimation)change.animationOptions; + + LOG(@"UITableView deleteSections:%@", sectionIndexes); + BOOL preventAnimation = (animationOptions == UITableViewRowAnimationNone); + ASPerformBlockWithoutAnimation(preventAnimation, ^{ + if (self.test_enableSuperUpdateCallLogging) { + NSLog(@"-[super deleteSections]: %@", sectionIndexes); + } + [super deleteSections:sectionIndexes withRowAnimation:animationOptions]; + }); + + numberOfUpdates++; + } + + for (_ASHierarchySectionChange *change in [changeSet sectionChangesOfType:_ASHierarchyChangeTypeOriginalInsert]) { + NSIndexSet *sectionIndexes = change.indexSet; + UITableViewRowAnimation animationOptions = (UITableViewRowAnimation)change.animationOptions; + + LOG(@"UITableView insertSections:%@", sectionIndexes); + BOOL preventAnimation = (animationOptions == UITableViewRowAnimationNone); + ASPerformBlockWithoutAnimation(preventAnimation, ^{ + if (self.test_enableSuperUpdateCallLogging) { + NSLog(@"-[super insertSections]: %@", sectionIndexes); + } + [super insertSections:sectionIndexes withRowAnimation:animationOptions]; + }); + + numberOfUpdates++; + } + + for (_ASHierarchyItemChange *change in [changeSet itemChangesOfType:_ASHierarchyChangeTypeOriginalInsert]) { + NSArray *indexPaths = change.indexPaths; + UITableViewRowAnimation animationOptions = (UITableViewRowAnimation)change.animationOptions; + + LOG(@"UITableView insertRows:%ld rows", indexPaths.count); + BOOL preventAnimation = (animationOptions == UITableViewRowAnimationNone); + ASPerformBlockWithoutAnimation(preventAnimation, ^{ + if (self.test_enableSuperUpdateCallLogging) { + NSLog(@"-[super insertRowsAtIndexPaths]: %@", indexPaths); + } + [super insertRowsAtIndexPaths:indexPaths withRowAnimation:animationOptions]; + }); + + numberOfUpdates++; + } + + LOG(@"--- UITableView endUpdates"); + ASPerformBlockWithoutAnimation(!changeSet.animated, ^{ + [super endUpdates]; + [self->_rangeController updateIfNeeded]; + [self _scheduleCheckForBatchFetchingForNumberOfChanges:numberOfUpdates]; + }); + if (shouldAdjustContentOffset) { + [self endAdjustingContentOffsetAnimated:changeSet.animated]; + } + [changeSet executeCompletionHandlerWithFinished:YES]; +} + +#pragma mark - ASDataControllerSource + +- (BOOL)dataController:(ASDataController *)dataController shouldEagerlyLayoutNode:(ASCellNode *)node +{ + return YES; +} + +- (BOOL)dataControllerShouldSerializeNodeCreation:(ASDataController *)dataController +{ + return NO; +} + +- (BOOL)dataController:(ASDataController *)dataController shouldSynchronouslyProcessChangeSet:(_ASHierarchyChangeSet *)changeSet +{ + // Reload data is expensive, don't block main while doing so. + if (changeSet.includesReloadData) { + return NO; + } + // For more details on this method, see the comment in the ASCollectionView implementation. + if (changeSet.countForAsyncLayout < 2) { + return YES; + } + CGSize contentSize = self.contentSize; + CGSize boundsSize = self.bounds.size; + if (contentSize.height <= boundsSize.height && contentSize.width <= boundsSize.width) { + return YES; + } + return NO; +} + +- (void)dataControllerDidFinishWaiting:(ASDataController *)dataController +{ + // ASCellLayoutMode is not currently supported on ASTableView (see ASCollectionView for details). +} + +- (id)dataController:(ASDataController *)dataController nodeModelForItemAtIndexPath:(NSIndexPath *)indexPath +{ + // Not currently supported for tables. Will be added when the collection API stabilizes. + return nil; +} + +- (ASCellNodeBlock)dataController:(ASDataController *)dataController nodeBlockAtIndexPath:(NSIndexPath *)indexPath shouldAsyncLayout:(BOOL *)shouldAsyncLayout +{ + ASCellNodeBlock block = nil; + + if (_asyncDataSourceFlags.tableNodeNodeBlockForRow) { + if (ASTableNode *tableNode = self.tableNode) { + block = [_asyncDataSource tableNode:tableNode nodeBlockForRowAtIndexPath:indexPath]; + } + } else if (_asyncDataSourceFlags.tableNodeNodeForRow) { + ASCellNode *node = nil; + if (ASTableNode *tableNode = self.tableNode) { + node = [_asyncDataSource tableNode:tableNode nodeForRowAtIndexPath:indexPath]; + } + if ([node isKindOfClass:[ASCellNode class]]) { + block = ^{ + return node; + }; + } else { + ASDisplayNodeFailAssert(@"Data source returned invalid node from tableNode:nodeForRowAtIndexPath:. Node: %@", node); + } + } else if (_asyncDataSourceFlags.tableViewNodeBlockForRow) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + block = [_asyncDataSource tableView:self nodeBlockForRowAtIndexPath:indexPath]; + } else if (_asyncDataSourceFlags.tableViewNodeForRow) { + ASCellNode *node = [_asyncDataSource tableView:self nodeForRowAtIndexPath:indexPath]; +#pragma clang diagnostic pop + if ([node isKindOfClass:[ASCellNode class]]) { + block = ^{ + return node; + }; + } else { + ASDisplayNodeFailAssert(@"Data source returned invalid node from tableView:nodeForRowAtIndexPath:. Node: %@", node); + } + } + + // Handle nil node block + if (block == nil) { + ASDisplayNodeFailAssert(@"ASTableNode could not get a node block for row at index path %@", indexPath); + block = ^{ + return [[ASCellNode alloc] init]; + }; + } + + // Wrap the node block + __weak __typeof__(self) weakSelf = self; + return ^{ + __typeof__(self) strongSelf = weakSelf; + ASCellNode *node = (block != nil ? block() : [[ASCellNode alloc] init]); + ASDisplayNodeAssert([node isKindOfClass:[ASCellNode class]], @"ASTableNode provided a non-ASCellNode! %@, %@", node, strongSelf); + + [node enterHierarchyState:ASHierarchyStateRangeManaged]; + if (node.interactionDelegate == nil) { + node.interactionDelegate = strongSelf; + } + if (self->_inverted) { + node.transform = CATransform3DMakeScale(1, -1, 1) ; + } + return node; + }; + return block; +} + +- (ASSizeRange)dataController:(ASDataController *)dataController constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath +{ + ASSizeRange constrainedSize = ASSizeRangeZero; + if (_asyncDelegateFlags.tableNodeConstrainedSizeForRow) { + GET_TABLENODE_OR_RETURN(tableNode, constrainedSize); + ASSizeRange delegateConstrainedSize = [_asyncDelegate tableNode:tableNode constrainedSizeForRowAtIndexPath:indexPath]; + // ignore widths in the returned size range (for TableView) + constrainedSize = ASSizeRangeMake(CGSizeMake(_nodesConstrainedWidth, delegateConstrainedSize.min.height), + CGSizeMake(_nodesConstrainedWidth, delegateConstrainedSize.max.height)); + } else if (_asyncDelegateFlags.tableViewConstrainedSizeForRow) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + ASSizeRange delegateConstrainedSize = [_asyncDelegate tableView:self constrainedSizeForRowAtIndexPath:indexPath]; +#pragma clang diagnostic pop + // ignore widths in the returned size range (for TableView) + constrainedSize = ASSizeRangeMake(CGSizeMake(_nodesConstrainedWidth, delegateConstrainedSize.min.height), + CGSizeMake(_nodesConstrainedWidth, delegateConstrainedSize.max.height)); + } else { + constrainedSize = ASSizeRangeMake(CGSizeMake(_nodesConstrainedWidth, 0), + CGSizeMake(_nodesConstrainedWidth, CGFLOAT_MAX)); + } + return constrainedSize; +} + +- (NSUInteger)dataController:(ASDataController *)dataController rowsInSection:(NSUInteger)section +{ + if (_asyncDataSourceFlags.tableNodeNumberOfRowsInSection) { + GET_TABLENODE_OR_RETURN(tableNode, 0); + return [_asyncDataSource tableNode:tableNode numberOfRowsInSection:section]; + } else if (_asyncDataSourceFlags.tableViewNumberOfRowsInSection) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + return [_asyncDataSource tableView:self numberOfRowsInSection:section]; +#pragma clang diagnostic pop + } else { + return 0; + } +} + +- (NSUInteger)numberOfSectionsInDataController:(ASDataController *)dataController +{ + if (_asyncDataSourceFlags.numberOfSectionsInTableNode) { + GET_TABLENODE_OR_RETURN(tableNode, 0); + return [_asyncDataSource numberOfSectionsInTableNode:tableNode]; + } else if (_asyncDataSourceFlags.numberOfSectionsInTableView) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + return [_asyncDataSource numberOfSectionsInTableView:self]; +#pragma clang diagnostic pop + } else { + return 1; // default section number + } +} + +- (BOOL)dataController:(ASDataController *)dataController presentedSizeForElement:(ASCollectionElement *)element matchesSize:(CGSize)size +{ + NSIndexPath *indexPath = [self indexPathForNode:element.node]; + if (indexPath == nil) { + ASDisplayNodeFailAssert(@"Data controller should not ask for presented size for element that is not presented."); + return YES; + } + CGRect rect = [self rectForRowAtIndexPath:indexPath]; + +#if TARGET_OS_IOS + /** + * Weirdly enough, Apple expects the return value in tableView:heightForRowAtIndexPath: to _include_ the height + * of the separator, if there is one! So if rectForRow would return 44.0 we need to use 43.5. + */ + if (self.separatorStyle != UITableViewCellSeparatorStyleNone) { + rect.size.height -= 1.0 / ASScreenScale(); + } +#endif + + return (fabs(rect.size.height - size.height) < FLT_EPSILON); +} + +#pragma mark - _ASTableViewCellDelegate + +- (void)didLayoutSubviewsOfTableViewCell:(_ASTableViewCell *)tableViewCell +{ + ASCellNode *node = tableViewCell.node; + if (node == nil || _asyncDataSource == nil) { + return; + } + + CGFloat contentViewWidth = tableViewCell.contentView.bounds.size.width; + ASSizeRange constrainedSize = node.constrainedSizeForCalculatedLayout; + + // Table view cells should always fill its content view width. + // Normally the content view width equals to the constrained size width (which equals to the table view width). + // If there is a mismatch between these values, for example after the table view entered or left editing mode, + // content view width is preferred and used to re-measure the cell node. + if (CGSizeEqualToSize(node.calculatedSize, CGSizeZero) == NO && contentViewWidth != constrainedSize.max.width) { + constrainedSize.min.width = contentViewWidth; + constrainedSize.max.width = contentViewWidth; + + // Re-measurement is done on main to ensure thread affinity. In the worst case, this is as fast as UIKit's implementation. + // + // Unloaded nodes *could* be re-measured off the main thread, but only with the assumption that content view width + // is the same for all cells (because there is no easy way to get that individual value before the node being assigned to a _ASTableViewCell). + // Also, in many cases, some nodes may not need to be re-measured at all, such as when user enters and then immediately leaves editing mode. + // To avoid premature optimization and making such assumption, as well as to keep ASTableView simple, re-measurement is strictly done on main. + CGSize oldSize = node.bounds.size; + const CGSize calculatedSize = [node layoutThatFits:constrainedSize].size; + node.frame = { .size = calculatedSize }; + + // After the re-measurement, set the new constrained size to the node's backing colleciton element. + node.collectionElement.constrainedSize = constrainedSize; + + // If the node height changed, trigger a height requery. + if (oldSize.height != calculatedSize.height) { + [self beginUpdates]; + [self endUpdatesAnimated:(ASDisplayNodeLayerHasAnimations(self.layer) == NO) completion:nil]; + } + } +} + +#pragma mark - ASCellNodeDelegate + +- (void)nodeSelectedStateDidChange:(ASCellNode *)node +{ + NSIndexPath *indexPath = [self indexPathForNode:node]; + if (indexPath) { + if (node.isSelected) { + [self selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone]; + } else { + [self deselectRowAtIndexPath:indexPath animated:NO]; + } + } +} + +- (void)nodeHighlightedStateDidChange:(ASCellNode *)node +{ + NSIndexPath *indexPath = [self indexPathForNode:node]; + if (indexPath) { + [self cellForRowAtIndexPath:indexPath].highlighted = node.isHighlighted; + } +} + +- (void)nodeDidInvalidateSize:(ASCellNode *)node +{ + [_cellsForLayoutUpdates addObject:node]; + [self setNeedsLayout]; +} + +// Cause UITableView to requery for the new height of this node +- (void)requeryNodeHeights +{ + _queuedNodeHeightUpdate = NO; + + [super beginUpdates]; + [super endUpdates]; +} + +#pragma mark - Helper Methods + +// Note: This is called every layout, and so it is very performance sensitive. +- (CGFloat)sectionIndexWidth +{ + // If they don't implement the methods, then there's no section index. + if (_asyncDataSourceFlags.sectionIndexMethods == NO) { + return 0; + } + + UIView *indexView = _sectionIndexView; + if (indexView.superview == self) { + return indexView.frame.size.width; + } + + CGRect bounds = self.bounds; + for (UIView *view in self.subviews) { + CGRect frame = view.frame; + // Section index is right-aligned and less than half-width. + if (CGRectGetMaxX(frame) == CGRectGetMaxX(bounds) && frame.size.width * 2 < bounds.size.width) { + _sectionIndexView = view; + return frame.size.width; + } + } + return 0; +} + +#pragma mark - _ASDisplayView behavior substitutions +// Need these to drive interfaceState so we know when we are visible, if not nested in another range-managing element. +// Because our superclass is a true UIKit class, we cannot also subclass _ASDisplayView. +- (void)willMoveToWindow:(UIWindow *)newWindow +{ + BOOL visible = (newWindow != nil); + ASDisplayNode *node = self.tableNode; + if (visible && !node.inHierarchy) { + [node __enterHierarchy]; + } +} + +- (void)didMoveToWindow +{ + BOOL visible = (self.window != nil); + ASDisplayNode *node = self.tableNode; + BOOL rangeControllerNeedsUpdate = ![node supportsRangeManagedInterfaceState];; + + if (!visible && node.inHierarchy) { + if (rangeControllerNeedsUpdate) { + rangeControllerNeedsUpdate = NO; + // Exit CellNodes first before Table to match UIKit behaviors (tear down bottom up). + // Although we have not yet cleared the interfaceState's Visible bit (this happens in __exitHierarchy), + // the ASRangeController will get the correct value from -interfaceStateForRangeController:. + [_rangeController updateRanges]; + } + [node __exitHierarchy]; + } + + // Updating the visible node index paths only for not range managed nodes. Range managed nodes will get their + // their update in the layout pass + if (rangeControllerNeedsUpdate) { + [_rangeController updateRanges]; + } + + // When we aren't visible, we will only fetch up to the visible area. Now that we are visible, + // we will fetch visible area + leading screens, so we need to check. + if (visible) { + [self _checkForBatchFetching]; + } +} + +- (void)willMoveToSuperview:(UIView *)newSuperview +{ + if (self.superview == nil && newSuperview != nil) { + _keepalive_node = self.tableNode; + } +} + +- (void)didMoveToSuperview +{ + if (self.superview == nil) { + _keepalive_node = nil; + } +} + +#pragma mark - Accessibility overrides + +- (NSArray *)accessibilityElements +{ + [self waitUntilAllUpdatesAreCommitted]; + return [super accessibilityElements]; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASTableViewInternal.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASTableViewInternal.h new file mode 100644 index 0000000..35a55d4 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASTableViewInternal.h @@ -0,0 +1,62 @@ +// +// ASTableViewInternal.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +@class ASDataController; +@class ASTableNode; +@class ASRangeController; + +@interface ASTableView (Internal) + +@property (nonatomic, readonly) ASDataController *dataController; +@property (nonatomic, weak) ASTableNode *tableNode; +@property (nonatomic, readonly) ASRangeController *rangeController; + +/** + * Initializer. + * + * @param frame A rectangle specifying the initial location and size of the table view in its superview’€™s coordinates. + * The frame of the table view changes as table cells are added and deleted. + * + * @param style A constant that specifies the style of the table view. See UITableViewStyle for descriptions of valid constants. + * + * @param dataControllerClass A controller class injected to and used to create a data controller for the table view. + */ +- (instancetype)_initWithFrame:(CGRect)frame style:(UITableViewStyle)style dataControllerClass:(Class)dataControllerClass owningNode:(ASTableNode *)tableNode; + +/// Set YES and we'll log every time we call [super insertRows…] etc +@property (nonatomic) BOOL test_enableSuperUpdateCallLogging; + +/** + * Attempt to get the view-layer index path for the row with the given index path. + * + * @param indexPath The index path of the row. + * @param wait If the item hasn't reached the view yet, this attempts to wait for updates to commit. + */ +- (NSIndexPath *)convertIndexPathFromTableNode:(NSIndexPath *)indexPath waitingIfNeeded:(BOOL)wait; + +/** + * Attempt to get the node index path given the view-layer index path. + * + * @param indexPath The index path of the row. + */ +- (NSIndexPath *)convertIndexPathToTableNode:(NSIndexPath *)indexPath; + +/** + * Attempt to get the node index paths given the view-layer index paths. + * + * @param indexPaths An array of index paths in the view space + */ +- (NSArray *)convertIndexPathsToTableNode:(NSArray *)indexPaths; + +/// Returns the width of the section index view on the right-hand side of the table, if one is present. +- (CGFloat)sectionIndexWidth; + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASTableViewProtocols.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASTableViewProtocols.h new file mode 100644 index 0000000..ea392e2 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASTableViewProtocols.h @@ -0,0 +1,101 @@ +// +// ASTableViewProtocols.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * This is a subset of UITableViewDataSource. + * + * @see ASTableDataSource + */ +@protocol ASCommonTableDataSource + +@optional + +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section ASDISPLAYNODE_DEPRECATED_MSG("Implement -tableNode:numberOfRowsInSection: instead."); + +- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView ASDISPLAYNODE_DEPRECATED_MSG("Implement numberOfSectionsInTableNode: instead."); + +- (nullable NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section; +- (nullable NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section; + +- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath; + +- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath; + +- (nullable NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView; +- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index; + +- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath; + +- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath; + +@end + + +/** + * This is a subset of UITableViewDelegate. + * + * @see ASTableDelegate + */ +@protocol ASCommonTableViewDelegate + +@optional + +- (void)tableView:(UITableView *)tableView willDisplayHeaderView:(UIView *)view forSection:(NSInteger)section; +- (void)tableView:(UITableView *)tableView willDisplayFooterView:(UIView *)view forSection:(NSInteger)section; +- (void)tableView:(UITableView *)tableView didEndDisplayingHeaderView:(UIView *)view forSection:(NSInteger)section; +- (void)tableView:(UITableView *)tableView didEndDisplayingFooterView:(UIView *)view forSection:(NSInteger)section; + +- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section; +- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section; + +- (nullable UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section; +- (nullable UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section; + +- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath; + +- (BOOL)tableView:(UITableView *)tableView shouldHighlightRowAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED_MSG("Implement -tableNode:shouldHighlightRowAtIndexPath: instead."); +- (void)tableView:(UITableView *)tableView didHighlightRowAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED_MSG("Implement -tableNode:didHighlightRowAtIndexPath: instead."); +- (void)tableView:(UITableView *)tableView didUnhighlightRowAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED_MSG("Implement -tableNode:didUnhighlightRowAtIndexPath: instead."); + +- (nullable NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED_MSG("Implement -tableNode:willSelectRowAtIndexPath: instead."); +- (nullable NSIndexPath *)tableView:(UITableView *)tableView willDeselectRowAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED_MSG("Implement -tableNode:willDeselectRowAtIndexPath: instead."); +- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED_MSG("Implement -tableNode:didSelectRowAtIndexPath: instead."); +- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED_MSG("Implement -tableNode:didDeselectRowAtIndexPath: instead."); + +- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath; +- (nullable NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath; +#if TARGET_OS_IOS +- (nullable NSArray *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath API_DEPRECATED_WITH_REPLACEMENT("tableView:trailingSwipeActionsConfigurationForRowAtIndexPath:", ios(8.0, 13.0)); +- (nullable UISwipeActionsConfiguration *)tableView:(UITableView *)tableView leadingSwipeActionsConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath API_AVAILABLE(ios(11.0)); +- (nullable UISwipeActionsConfiguration *)tableView:(UITableView *)tableView trailingSwipeActionsConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath API_AVAILABLE(ios(11.0)); +- (nullable UIContextMenuConfiguration *)tableView:(UITableView *)tableView contextMenuConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath point:(CGPoint)point API_AVAILABLE(ios(13.0)); +- (nullable UITargetedPreview *)tableView:(UITableView *)tableView previewForHighlightingContextMenuWithConfiguration:(UIContextMenuConfiguration *)configuration API_AVAILABLE(ios(13.0)); +- (nullable UITargetedPreview *)tableView:(UITableView *)tableView previewForDismissingContextMenuWithConfiguration:(UIContextMenuConfiguration *)configuration API_AVAILABLE(ios(13.0)); +- (void)tableView:(UITableView *)tableView willPerformPreviewActionForMenuWithConfiguration:(UIContextMenuConfiguration *)configuration animator:(id)animator API_AVAILABLE(ios(13.0)); +#endif +- (BOOL)tableView:(UITableView *)tableView shouldIndentWhileEditingRowAtIndexPath:(NSIndexPath *)indexPath; + +- (void)tableView:(UITableView*)tableView willBeginEditingRowAtIndexPath:(NSIndexPath *)indexPath; +- (void)tableView:(UITableView*)tableView didEndEditingRowAtIndexPath:(NSIndexPath *)indexPath; + +- (NSIndexPath *)tableView:(UITableView *)tableView targetIndexPathForMoveFromRowAtIndexPath:(NSIndexPath *)sourceIndexPath toProposedIndexPath:(NSIndexPath *)proposedDestinationIndexPath; + +- (NSInteger)tableView:(UITableView *)tableView indentationLevelForRowAtIndexPath:(NSIndexPath *)indexPath; + +- (BOOL)tableView:(UITableView *)tableView shouldShowMenuForRowAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED_MSG("Implement -tableNode:shouldShowMenuForRowAtIndexPath: instead."); +- (BOOL)tableView:(UITableView *)tableView canPerformAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(nullable id)sender ASDISPLAYNODE_DEPRECATED_MSG("Implement -tableNode:canPerformAction:forRowAtIndexPath:withSender: instead."); +- (void)tableView:(UITableView *)tableView performAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(nullable id)sender ASDISPLAYNODE_DEPRECATED_MSG("Implement -tableNode:performAction:forRowAtIndexPath:withSender: instead."); + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASTextNode+Beta.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASTextNode+Beta.h new file mode 100644 index 0000000..ad897c5 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASTextNode+Beta.h @@ -0,0 +1,45 @@ +// +// ASTextNode+Beta.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface ASTextNode () + +/** + @abstract An array of descending scale factors that will be applied to this text node to try to make it fit within its constrained size + @discussion This array should be in descending order and NOT contain the scale factor 1.0. For example, it could return @[@(.9), @(.85), @(.8)]; + @default nil (no scaling) + */ +@property (nullable, nonatomic, copy) NSArray *pointSizeScaleFactors; + +/** + @abstract Text margins for text laid out in the text node. + @discussion defaults to UIEdgeInsetsZero. + This property can be useful for handling text which does not fit within the view by default. An example: like UILabel, + ASTextNode will clip the left and right of the string "judar" if it's rendered in an italicised font. + */ +@property (nonatomic) UIEdgeInsets textContainerInset; + +/** + * Returns YES if this node is using the experimental implementation. NO otherwise. Will not change. + */ +@property (readonly) BOOL usingExperiment; + +/** + * Returns a Boolean indicating if the text node will truncate for the given constrained size + */ +- (BOOL)shouldTruncateForConstrainedSize:(ASSizeRange)constrainedSize; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASTextNode.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASTextNode.h new file mode 100644 index 0000000..b05a1d5 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASTextNode.h @@ -0,0 +1,278 @@ +// +// ASTextNode.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import +#import +#import + +#if (!AS_ENABLE_TEXTNODE) + +// Pull in ASTextNode2 to replace ASTextNode with ASTextNode2 +#import + +#else + +NS_ASSUME_NONNULL_BEGIN + +/** + @abstract Draws interactive rich text. + @discussion Backed by TextKit. + */ +@interface ASTextNode : ASControlNode + +/** + @abstract The styled text displayed by the node. + @discussion Defaults to nil, no text is shown. + For inline image attachments, add an attribute of key NSAttachmentAttributeName, with a value of an NSTextAttachment. + */ +@property (nullable, copy) NSAttributedString *attributedText; + +#pragma mark - Truncation + +/** + @abstract The attributedText to use when the text must be truncated. + @discussion Defaults to a localized ellipsis character. + */ +@property (nullable, copy) NSAttributedString *truncationAttributedText; + +/** + @summary The second attributed string appended for truncation. + @discussion This string will be highlighted on touches. + @default nil + */ +@property (nullable, copy) NSAttributedString *additionalTruncationMessage; + +/** + @abstract Determines how the text is truncated to fit within the receiver's maximum size. + @discussion Defaults to NSLineBreakByWordWrapping. + @note Setting a truncationMode in attributedString will override the truncation mode set here. + */ +@property NSLineBreakMode truncationMode; + +/** + @abstract If the text node is truncated. Text must have been sized first. + */ +@property (readonly, getter=isTruncated) BOOL truncated; + +/** + @abstract The maximum number of lines to render of the text before truncation. + @default 0 (No limit) + */ +@property NSUInteger maximumNumberOfLines; + +/** + @abstract The number of lines in the text. Text must have been sized first. + */ +@property (readonly) NSUInteger lineCount; + +/** + * An array of path objects representing the regions where text should not be displayed. + * + * @discussion The default value of this property is an empty array. You can + * assign an array of UIBezierPath objects to exclude text from one or more regions in + * the text node's bounds. You can use this property to have text wrap around images, + * shapes or other text like a fancy magazine. + */ +@property (nullable, copy) NSArray *exclusionPaths; + +#pragma mark - Placeholders + +/** + * @abstract ASTextNode has a special placeholder behavior when placeholderEnabled is YES. + * + * @discussion Defaults to NO. When YES, it draws rectangles for each line of text, + * following the true shape of the text's wrapping. This visually mirrors the overall + * shape and weight of paragraphs, making the appearance of the finished text less jarring. + */ +@property BOOL placeholderEnabled; + +/** + @abstract The placeholder color. + */ +@property (nullable, copy) UIColor *placeholderColor; + +/** + @abstract Inset each line of the placeholder. + */ +@property UIEdgeInsets placeholderInsets; + +#pragma mark - Shadow + +/** + @abstract When you set these ASDisplayNode properties, they are composited into the bitmap instead of being applied by CA. + + @property (nonatomic) CGColorRef shadowColor; + @property (nonatomic) CGFloat shadowOpacity; + @property (nonatomic) CGSize shadowOffset; + @property (nonatomic) CGFloat shadowRadius; + */ + +/** + @abstract The number of pixels used for shadow padding on each side of the receiver. + @discussion Each inset will be less than or equal to zero, so that applying + UIEdgeInsetsRect(boundingRectForText, shadowPadding) + will return a CGRect large enough to fit both the text and the appropriate shadow padding. + */ +@property (readonly) UIEdgeInsets shadowPadding; + +#pragma mark - Positioning + +/** + @abstract Returns an array of rects bounding the characters in a given text range. + @param textRange A range of text. Must be valid for the receiver's string. + @discussion Use this method to detect all the different rectangles a given range of text occupies. + The rects returned are not guaranteed to be contiguous (for example, if the given text range spans + a line break, the rects returned will be on opposite sides and different lines). The rects returned + are in the coordinate system of the receiver. + */ +- (NSArray *)rectsForTextRange:(NSRange)textRange AS_WARN_UNUSED_RESULT; + +/** + @abstract Returns an array of rects used for highlighting the characters in a given text range. + @param textRange A range of text. Must be valid for the receiver's string. + @discussion Use this method to detect all the different rectangles the highlights of a given range of text occupies. + The rects returned are not guaranteed to be contiguous (for example, if the given text range spans + a line break, the rects returned will be on opposite sides and different lines). The rects returned + are in the coordinate system of the receiver. This method is useful for visual coordination with a + highlighted range of text. + */ +- (NSArray *)highlightRectsForTextRange:(NSRange)textRange AS_WARN_UNUSED_RESULT; + +/** + @abstract Returns a bounding rect for the given text range. + @param textRange A range of text. Must be valid for the receiver's string. + @discussion The height of the frame returned is that of the receiver's line-height; adjustment for + cap-height and descenders is not performed. This method raises an exception if textRange is not + a valid substring range of the receiver's string. + */ +- (CGRect)frameForTextRange:(NSRange)textRange AS_WARN_UNUSED_RESULT; + +/** + @abstract Returns the trailing rectangle of space in the receiver, after the final character. + @discussion Use this method to detect which portion of the receiver is not occupied by characters. + The rect returned is in the coordinate system of the receiver. + */ +- (CGRect)trailingRect AS_WARN_UNUSED_RESULT; + + +#pragma mark - Actions + +/** + @abstract The set of attribute names to consider links. Defaults to NSLinkAttributeName. + */ +@property (copy) NSArray *linkAttributeNames; + +/** + @abstract Indicates whether the receiver has an entity at a given point. + @param point The point, in the receiver's coordinate system. + @param attributeNameOut The name of the attribute at the point. Can be NULL. + @param rangeOut The ultimate range of the found text. Can be NULL. + @result YES if an entity exists at `point`; NO otherwise. + */ +- (nullable id)linkAttributeValueAtPoint:(CGPoint)point attributeName:(out NSString * _Nullable * _Nullable)attributeNameOut range:(out NSRange * _Nullable)rangeOut AS_WARN_UNUSED_RESULT; + +/** + @abstract The style to use when highlighting text. + */ +@property ASTextNodeHighlightStyle highlightStyle; + +/** + @abstract The range of text highlighted by the receiver. Changes to this property are not animated by default. + */ +@property NSRange highlightRange; + +/** + @abstract Set the range of text to highlight, with optional animation. + + @param highlightRange The range of text to highlight. + + @param animated Whether the text should be highlighted with an animation. + */ +- (void)setHighlightRange:(NSRange)highlightRange animated:(BOOL)animated; + +/** + @abstract Responds to actions from links in the text node. + @discussion The delegate must be set before the node is loaded, and implement + textNode:longPressedLinkAttribute:value:atPoint:textRange: in order for + the long press gesture recognizer to be installed. + */ +@property (nullable, weak) id delegate; + +/** + @abstract If YES and a long press is recognized, touches are cancelled. Default is NO + */ +@property (nonatomic) BOOL longPressCancelsTouches; + +/** + @abstract if YES will not intercept touches for non-link areas of the text. Default is NO. + @discussion If you still want to handle tap truncation action when passthroughNonlinkTouches is YES, + you should set the alwaysHandleTruncationTokenTap to YES. + */ +@property (nonatomic) BOOL passthroughNonlinkTouches; + +/** + @abstract Always handle tap truncationAction, even the passthroughNonlinkTouches is YES. Default is NO. + @discussion if this is set to YES, the [ASTextNodeDelegate textNodeTappedTruncationToken:] callback will be called. + */ +@property (nonatomic) BOOL alwaysHandleTruncationTokenTap; + +/** + @abstract if YES will use the value of `self.tintColor` if the foreground color of text is not defined. + @discussion This is mainly used from ASButtonNode since by default text nodes do not respect tintColor settings unless contained within a interactive control + */ +@property (nonatomic) BOOL textColorFollowsTintColor; + +@end + +@interface ASTextNode (Unavailable) + +- (instancetype)initWithLayerBlock:(ASDisplayNodeLayerBlock)viewBlock didLoadBlock:(nullable ASDisplayNodeDidLoadBlock)didLoadBlock NS_UNAVAILABLE; + +- (instancetype)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock didLoadBlock:(nullable ASDisplayNodeDidLoadBlock)didLoadBlock NS_UNAVAILABLE; + +@end + +/** + * @abstract Text node unsupported properties + */ +@interface ASTextNode (Unsupported) + +@property (nullable, nonatomic) id textContainerLinePositionModifier; + +@end + +/** + * @abstract Text node deprecated properties + */ +@interface ASTextNode (Deprecated) + +/** + The attributedString and attributedText properties are equivalent, but attributedText is now the standard API + name in order to match UILabel and ASEditableTextNode. + + @see attributedText + */ +@property (nullable, copy) NSAttributedString *attributedString ASDISPLAYNODE_DEPRECATED_MSG("Use .attributedText instead."); + + +/** + The truncationAttributedString and truncationAttributedText properties are equivalent, but truncationAttributedText is now the + standard API name in order to match UILabel and ASEditableTextNode. + + @see truncationAttributedText + */ +@property (nullable, copy) NSAttributedString *truncationAttributedString ASDISPLAYNODE_DEPRECATED_MSG("Use .truncationAttributedText instead."); + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASTextNode.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASTextNode.mm new file mode 100644 index 0000000..afffba5 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASTextNode.mm @@ -0,0 +1,1541 @@ +// +// ASTextNode.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#if AS_ENABLE_TEXTNODE + +#import + +#import + +#import +#import + +#import +#import +#import +#import +#import +#import +#import + +#import +#import +#import + +#import +#import + +/** + * If set, we will record all values set to attributedText into an array + * and once we get 2000, we'll write them all out into a plist file. + * + * This is useful for gathering realistic text data sets from apps for performance + * testing. + */ +#define AS_TEXTNODE_RECORD_ATTRIBUTED_STRINGS 0 + +static const NSTimeInterval ASTextNodeHighlightFadeOutDuration = 0.15; +static const NSTimeInterval ASTextNodeHighlightFadeInDuration = 0.1; +static const CGFloat ASTextNodeHighlightLightOpacity = 0.11; +static const CGFloat ASTextNodeHighlightDarkOpacity = 0.22; +static NSString *ASTextNodeTruncationTokenAttributeName = @"ASTextNodeTruncationAttribute"; + +#pragma mark - ASTextKitRenderer + +@interface ASTextNodeRendererKey : NSObject +- (instancetype)initWithTextKitAttributes:(const ASTextKitAttributes &)attributes constrainedSize:(const CGSize)constrainedSize; +@end + +@implementation ASTextNodeRendererKey { + ASTextKitAttributes _attributes; + CGSize _constrainedSize; +} + +- (instancetype)initWithTextKitAttributes:(const ASTextKitAttributes &)attributes constrainedSize:(const CGSize)constrainedSize +{ + if (self = [super init]) { + _attributes = attributes; + _constrainedSize = constrainedSize; + } + return self; +} + +- (NSUInteger)hash +{ +#pragma clang diagnostic push +#pragma clang diagnostic warning "-Wpadded" + struct { + size_t attributesHash; + CGSize constrainedSize; +#pragma clang diagnostic pop + } data = { + _attributes.hash(), + _constrainedSize + }; + return ASHashBytes(&data, sizeof(data)); +} + +- (BOOL)isEqual:(ASTextNodeRendererKey *)object +{ + if (self == object) { + return YES; + } + if (!object) { + return NO; + } + // NOTE: Skip the class check for this specialized, internal Key object. + + return _attributes == object->_attributes && CGSizeEqualToSize(_constrainedSize, object->_constrainedSize); +} + +@end + +static NSCache *sharedRendererCache() +{ + static dispatch_once_t onceToken; + static NSCache *__rendererCache = nil; + dispatch_once(&onceToken, ^{ + __rendererCache = [[NSCache alloc] init]; + __rendererCache.countLimit = 500; // 500 renders cache + }); + return __rendererCache; +} + +/** + The concept here is that neither the node nor layout should ever have a strong reference to the renderer object. + This is to reduce memory load when loading thousands and thousands of text nodes into memory at once. Instead + we maintain a LRU renderer cache that is queried via a unique key based on text kit attributes and constrained size. + */ + +static ASTextKitRenderer *rendererForAttributes(ASTextKitAttributes attributes, CGSize constrainedSize) +{ + NSCache *cache = sharedRendererCache(); + + ASTextNodeRendererKey *key = [[ASTextNodeRendererKey alloc] initWithTextKitAttributes:attributes constrainedSize:constrainedSize]; + + ASTextKitRenderer *renderer = [cache objectForKey:key]; + if (renderer == nil) { + renderer = [[ASTextKitRenderer alloc] initWithTextKitAttributes:attributes constrainedSize:constrainedSize]; + [cache setObject:renderer forKey:key]; + } + + return renderer; +} + +#pragma mark - ASTextNodeDrawParameter + +@interface ASTextNodeDrawParameter : NSObject { +@package + ASTextKitAttributes _rendererAttributes; + UIColor *_backgroundColor; + UIEdgeInsets _textContainerInsets; + CGFloat _contentScale; + BOOL _opaque; + CGRect _bounds; + ASPrimitiveTraitCollection _traitCollection; +} +@end + +@implementation ASTextNodeDrawParameter + +- (instancetype)initWithRendererAttributes:(ASTextKitAttributes)rendererAttributes + backgroundColor:(/*nullable*/ UIColor *)backgroundColor + textContainerInsets:(UIEdgeInsets)textContainerInsets + contentScale:(CGFloat)contentScale + opaque:(BOOL)opaque + bounds:(CGRect)bounds + traitCollection: (ASPrimitiveTraitCollection)traitCollection +{ + self = [super init]; + if (self != nil) { + _rendererAttributes = rendererAttributes; + _backgroundColor = backgroundColor; + _textContainerInsets = textContainerInsets; + _contentScale = contentScale; + _opaque = opaque; + _bounds = bounds; + _traitCollection = traitCollection; + } + return self; +} + +- (ASTextKitRenderer *)rendererForBounds:(CGRect)bounds +{ + CGRect rect = UIEdgeInsetsInsetRect(bounds, _textContainerInsets); + return rendererForAttributes(_rendererAttributes, rect.size); +} + +@end + + +#pragma mark - ASTextNode + +@interface ASTextNode () + +@end + +@implementation ASTextNode { + CGSize _shadowOffset; + CGColorRef _shadowColor; + UIColor *_cachedShadowUIColor; + UIColor *_cachedTintColor; + UIColor *_placeholderColor; + CGFloat _shadowOpacity; + CGFloat _shadowRadius; + + UIEdgeInsets _textContainerInset; + + NSArray *_exclusionPaths; + + NSAttributedString *_attributedText; + NSAttributedString *_truncationAttributedText; + NSAttributedString *_additionalTruncationMessage; + NSAttributedString *_composedTruncationText; + NSArray *_pointSizeScaleFactors; + NSLineBreakMode _truncationMode; + + NSUInteger _maximumNumberOfLines; + + NSString *_highlightedLinkAttributeName; + id _highlightedLinkAttributeValue; + NSRange _highlightRange; + ASHighlightOverlayLayer *_activeHighlightLayer; + + UILongPressGestureRecognizer *_longPressGestureRecognizer; + ASTextNodeHighlightStyle _highlightStyle; + BOOL _longPressCancelsTouches; + BOOL _passthroughNonlinkTouches; + BOOL _alwaysHandleTruncationTokenTap; +} +@dynamic placeholderEnabled; + +static NSArray *DefaultLinkAttributeNames() { + static NSArray *names; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + names = @[ NSLinkAttributeName ]; + }); + return names; +} + +- (instancetype)init +{ + if (self = [super init]) { + // Load default values from superclass. + _shadowOffset = [super shadowOffset]; + _shadowColor = CGColorRetain([super shadowColor]); + _shadowOpacity = [super shadowOpacity]; + _shadowRadius = [super shadowRadius]; + + // Disable user interaction for text node by default. + self.userInteractionEnabled = NO; + self.needsDisplayOnBoundsChange = YES; + + _truncationMode = NSLineBreakByWordWrapping; + + // The common case is for a text node to be non-opaque and blended over some background. + self.opaque = NO; + self.backgroundColor = [UIColor clearColor]; + + self.linkAttributeNames = DefaultLinkAttributeNames(); + + // Accessibility + self.isAccessibilityElement = YES; + self.accessibilityTraits = self.defaultAccessibilityTraits; + + // Placeholders + // Disabled by default in ASDisplayNode, but add a few options for those who toggle + // on the special placeholder behavior of ASTextNode. + _placeholderColor = ASDisplayNodeDefaultPlaceholderColor(); + _placeholderInsets = UIEdgeInsetsMake(1.0, 0.0, 1.0, 0.0); + + // Tint color is applied when text nodes are within controls and indicate user action + // Most text nodes do not require interaction and this matches the default value of UILabel + _textColorFollowsTintColor = NO; + } + + return self; +} + +- (void)dealloc +{ + CGColorRelease(_shadowColor); +} + +- (BOOL)usingExperiment +{ + return NO; +} + +#pragma mark - Description + +- (NSString *)_plainStringForDescription +{ + NSString *plainString = [[self.attributedText string] stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]]; + if (plainString.length > 50) { + plainString = [[plainString substringToIndex:50] stringByAppendingString:@"\u2026"]; + } + return plainString; +} + +- (NSMutableArray *)propertiesForDescription +{ + NSMutableArray *result = [super propertiesForDescription]; + NSString *plainString = [self _plainStringForDescription]; + if (plainString.length > 0) { + [result addObject:@{ (id)kCFNull : ASStringWithQuotesIfMultiword(plainString) }]; + } + return result; +} + +- (NSMutableArray *)propertiesForDebugDescription +{ + NSMutableArray *result = [super propertiesForDebugDescription]; + NSString *plainString = [self _plainStringForDescription]; + if (plainString.length > 0) { + [result insertObject:@{ @"text" : ASStringWithQuotesIfMultiword(plainString) } atIndex:0]; + } + return result; +} + +#pragma mark - ASDisplayNode + +- (void)didLoad +{ + [super didLoad]; + + // If we are view-backed and the delegate cares, support the long-press callback. + SEL longPressCallback = @selector(textNode:longPressedLinkAttribute:value:atPoint:textRange:); + if (!self.isLayerBacked && [_delegate respondsToSelector:longPressCallback]) { + _longPressGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(_handleLongPress:)]; + _longPressGestureRecognizer.cancelsTouchesInView = self.longPressCancelsTouches; + _longPressGestureRecognizer.delegate = self; + [self.view addGestureRecognizer:_longPressGestureRecognizer]; + } +} + +- (BOOL)supportsLayerBacking +{ + if (!super.supportsLayerBacking) { + return NO; + } + + // If the text contains any links, return NO. + NSAttributedString *attributedText = self.attributedText; + NSRange range = NSMakeRange(0, attributedText.length); + for (NSString *linkAttributeName in _linkAttributeNames) { + __block BOOL hasLink = NO; + [attributedText enumerateAttribute:linkAttributeName inRange:range options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired usingBlock:^(id _Nullable value, NSRange range, BOOL * _Nonnull stop) { + hasLink = (value != nil); + *stop = YES; + }]; + if (hasLink) { + return NO; + } + } + return YES; +} + +#pragma mark - Renderer Management + +- (ASTextKitRenderer *)_renderer +{ + ASLockScopeSelf(); + return [self _locked_renderer]; +} + +- (ASTextKitRenderer *)_rendererWithBounds:(CGRect)bounds +{ + ASLockScopeSelf(); + return [self _locked_rendererWithBounds:bounds]; +} + +- (ASTextKitRenderer *)_locked_renderer +{ + DISABLED_ASAssertLocked(__instanceLock__); + return [self _locked_rendererWithBounds:[self _locked_threadSafeBounds]]; +} + +- (ASTextKitRenderer *)_locked_rendererWithBounds:(CGRect)bounds +{ + DISABLED_ASAssertLocked(__instanceLock__); + bounds = UIEdgeInsetsInsetRect(bounds, _textContainerInset); + return rendererForAttributes([self _locked_rendererAttributes], bounds.size); +} + +- (ASTextKitAttributes)_locked_rendererAttributes +{ + DISABLED_ASAssertLocked(__instanceLock__); + return { + .attributedString = _attributedText, + .truncationAttributedString = [self _locked_composedTruncationText], + .lineBreakMode = _truncationMode, + .maximumNumberOfLines = _maximumNumberOfLines, + .exclusionPaths = _exclusionPaths, + // use the property getter so a subclass can provide these scale factors on demand if desired + .pointSizeScaleFactors = self.pointSizeScaleFactors, + .shadowOffset = _shadowOffset, + .shadowColor = _cachedShadowUIColor, + .shadowOpacity = _shadowOpacity, + .shadowRadius = _shadowRadius, + .tintColor = _cachedTintColor + }; +} + +- (NSString *)defaultAccessibilityLabel +{ + ASLockScopeSelf(); + return _attributedText.string; +} + +- (UIAccessibilityTraits)defaultAccessibilityTraits +{ + return UIAccessibilityTraitStaticText; +} + +#pragma mark - Layout and Sizing + +- (void)setTextContainerInset:(UIEdgeInsets)textContainerInset +{ + if (ASLockedSelfCompareAssignCustom(_textContainerInset, textContainerInset, UIEdgeInsetsEqualToEdgeInsets)) { + [self setNeedsLayout]; + } +} + +- (UIEdgeInsets)textContainerInset +{ + return ASLockedSelf(_textContainerInset); +} + +- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize +{ + ASLockScopeSelf(); + + ASDisplayNodeAssert(constrainedSize.width >= 0, @"Constrained width for text (%f) is too narrow", constrainedSize.width); + ASDisplayNodeAssert(constrainedSize.height >= 0, @"Constrained height for text (%f) is too short", constrainedSize.height); + + // Cache the original constrained size for final size calculateion + CGSize originalConstrainedSize = constrainedSize; + + [self setNeedsDisplay]; + + ASTextKitRenderer *renderer = [self _locked_rendererWithBounds:{.size = constrainedSize}]; + CGSize size = renderer.size; + if (_attributedText.length > 0) { + self.style.ascender = [[self class] ascenderWithAttributedString:_attributedText]; + self.style.descender = [[_attributedText attribute:NSFontAttributeName atIndex:_attributedText.length - 1 effectiveRange:NULL] descender]; + if (renderer.currentScaleFactor > 0 && renderer.currentScaleFactor < 1.0) { + // while not perfect, this is a good estimate of what the ascender of the scaled font will be. + self.style.ascender *= renderer.currentScaleFactor; + self.style.descender *= renderer.currentScaleFactor; + } + } + + // Add the constrained size back textContainerInset + size.width += (_textContainerInset.left + _textContainerInset.right); + size.height += (_textContainerInset.top + _textContainerInset.bottom); + + return CGSizeMake(std::fmin(size.width, originalConstrainedSize.width), + std::fmin(size.height, originalConstrainedSize.height)); +} + +#pragma mark - Modifying User Text + +// Returns the ascender of the first character in attributedString by also including the line height if specified in paragraph style. ++ (CGFloat)ascenderWithAttributedString:(NSAttributedString *)attributedString +{ + UIFont *font = [attributedString attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL]; + NSParagraphStyle *paragraphStyle = [attributedString attribute:NSParagraphStyleAttributeName atIndex:0 effectiveRange:NULL]; + if (!paragraphStyle) { + return font.ascender; + } + CGFloat lineHeight = MAX(font.lineHeight, paragraphStyle.minimumLineHeight); + if (paragraphStyle.maximumLineHeight > 0) { + lineHeight = MIN(lineHeight, paragraphStyle.maximumLineHeight); + } + return lineHeight + font.descender; +} + +- (NSAttributedString *)attributedText +{ + ASLockScopeSelf(); + return _attributedText; +} + +- (void)setAttributedText:(NSAttributedString *)attributedText +{ + + if (attributedText == nil) { + attributedText = [[NSAttributedString alloc] initWithString:@"" attributes:nil]; + } + + NSAttributedString *oldAttributedText = nil; + + { + ASLockScopeSelf(); + if (ASObjectIsEqual(attributedText, _attributedText)) { + return; + } + + oldAttributedText = _attributedText; + + NSAttributedString *cleanedAttributedString = ASCleanseAttributedStringOfCoreTextAttributes(attributedText); + + // Invalidating the truncation text must be done while we still hold the lock. Because after we release it, + // another thread may set a new truncation text that will then be cleared by this thread, other may draw + // this soon-to-be-invalidated text. + [self _locked_invalidateTruncationText]; + + NSUInteger length = cleanedAttributedString.length; + if (length > 0) { + // Updating ascender and descender in one transaction while holding the lock. + ASLayoutElementStyle *style = [self _locked_style]; + style.ascender = [[self class] ascenderWithAttributedString:cleanedAttributedString]; + style.descender = [[attributedText attribute:NSFontAttributeName atIndex:cleanedAttributedString.length - 1 effectiveRange:NULL] descender]; + } + + // Update attributed text with cleaned attributed string + _attributedText = cleanedAttributedString; + } + + // Tell the display node superclasses that the cached layout is incorrect now + [self setNeedsLayout]; + + // Force display to create renderer with new size and redisplay with new string + [self setNeedsDisplay]; + + // Accessiblity + const auto currentAttributedText = self.attributedText; // Grab attributed string again in case it changed in the meantime + self.accessibilityLabel = self.defaultAccessibilityLabel; + + // We update the isAccessibilityElement setting if this node is not switching between strings. + if (oldAttributedText.length == 0 || currentAttributedText.length == 0) { + // We're an accessibility element by default if there is a string. + self.isAccessibilityElement = (currentAttributedText.length != 0); + } + +#if AS_TEXTNODE_RECORD_ATTRIBUTED_STRINGS + [ASTextNode _registerAttributedText:_attributedText]; +#endif +} + +#pragma mark - Text Layout + +- (void)setExclusionPaths:(NSArray *)exclusionPaths +{ + if (ASLockedSelfCompareAssignCopy(_exclusionPaths, exclusionPaths)) { + [self setNeedsLayout]; + [self setNeedsDisplay]; + } +} + +- (NSArray *)exclusionPaths +{ + return ASLockedSelf(_exclusionPaths); +} + +#pragma mark - Drawing + +- (NSObject *)drawParametersForAsyncLayer:(_ASDisplayLayer *)layer +{ + /// have to access tintColor outside of the lock to prevent dead lock when accessing up the view hierarchy + UIColor *tintColor = self.tintColor; + ASLockScopeSelf(); + if (_textColorFollowsTintColor) { + _cachedTintColor = tintColor; + } else { + _cachedTintColor = nil; + } + return [[ASTextNodeDrawParameter alloc] initWithRendererAttributes:[self _locked_rendererAttributes] + backgroundColor:self.backgroundColor + textContainerInsets:_textContainerInset + contentScale:_contentsScaleForDisplay + opaque:self.isOpaque + bounds:[self threadSafeBounds] + traitCollection:self.primitiveTraitCollection]; +} + ++ (UIImage *)displayWithParameters:(id)parameters isCancelled:(NS_NOESCAPE asdisplaynode_iscancelled_block_t)isCancelled +{ + ASTextNodeDrawParameter *drawParameter = (ASTextNodeDrawParameter *)parameters; + + if (drawParameter->_bounds.size.width <= 0 || drawParameter->_bounds.size.height <= 0) { + return nil; + } + + UIColor *backgroundColor = drawParameter->_backgroundColor; + UIEdgeInsets textContainerInsets = drawParameter ? drawParameter->_textContainerInsets : UIEdgeInsetsZero; + ASTextKitRenderer *renderer = [drawParameter rendererForBounds:drawParameter->_bounds]; + + UIImage *result = ASGraphicsCreateImage(drawParameter->_traitCollection, CGSizeMake(drawParameter->_bounds.size.width, drawParameter->_bounds.size.height), drawParameter->_opaque, drawParameter->_contentScale, nil, nil, ^{ + CGContextRef context = UIGraphicsGetCurrentContext(); + ASDisplayNodeAssert(context, @"This is no good without a context."); + + CGContextSaveGState(context); + CGContextTranslateCTM(context, textContainerInsets.left, textContainerInsets.top); + + // Fill background + if (backgroundColor != nil) { + [backgroundColor setFill]; + UIRectFillUsingBlendMode(CGContextGetClipBoundingBox(context), kCGBlendModeCopy); + } + + + // Draw text + [renderer drawInContext:context bounds:drawParameter->_bounds]; + CGContextRestoreGState(context); + }); + + return result; +} + +#pragma mark - Attributes + +- (id)linkAttributeValueAtPoint:(CGPoint)point + attributeName:(out NSString **)attributeNameOut + range:(out NSRange *)rangeOut +{ + return [self _linkAttributeValueAtPoint:point + attributeName:attributeNameOut + range:rangeOut + inAdditionalTruncationMessage:NULL + forHighlighting:NO]; +} + +- (id)_linkAttributeValueAtPoint:(CGPoint)point + attributeName:(out NSString * __autoreleasing *)attributeNameOut + range:(out NSRange *)rangeOut + inAdditionalTruncationMessage:(out BOOL *)inAdditionalTruncationMessageOut + forHighlighting:(BOOL)highlighting +{ + ASDisplayNodeAssertMainThread(); + + ASLockScopeSelf(); + + ASTextKitRenderer *renderer = [self _locked_renderer]; + NSRange visibleRange = renderer.firstVisibleRange; + NSAttributedString *attributedString = _attributedText; + NSRange clampedRange = NSIntersectionRange(visibleRange, NSMakeRange(0, attributedString.length)); + + // Check in a 9-point region around the actual touch point so we make sure + // we get the best attribute for the touch. + __block CGFloat minimumGlyphDistance = CGFLOAT_MAX; + + // Final output vars + __block id linkAttributeValue = nil; + __block BOOL inTruncationMessage = NO; + + [renderer enumerateTextIndexesAtPosition:point usingBlock:^(NSUInteger characterIndex, CGRect glyphBoundingRect, BOOL *stop) { + CGPoint glyphLocation = CGPointMake(CGRectGetMidX(glyphBoundingRect), CGRectGetMidY(glyphBoundingRect)); + CGFloat currentDistance = std::sqrt(std::pow(point.x - glyphLocation.x, 2.f) + std::pow(point.y - glyphLocation.y, 2.f)); + if (currentDistance >= minimumGlyphDistance) { + // If the distance computed from the touch to the glyph location is + // not the minimum among the located link attributes, we can just skip + // to the next location. + return; + } + + // Check if it's outside the visible range, if so, then we mark this touch + // as inside the truncation message, because in at least one of the touch + // points it was. + if (!(NSLocationInRange(characterIndex, visibleRange))) { + inTruncationMessage = YES; + } + + if (inAdditionalTruncationMessageOut != NULL) { + *inAdditionalTruncationMessageOut = inTruncationMessage; + } + + // Short circuit here if it's just in the truncation message. Since the + // truncation message may be beyond the scope of the actual input string, + // we have to make sure that we don't start asking for attributes on it. + if (inTruncationMessage) { + return; + } + + for (NSString *attributeName in self->_linkAttributeNames) { + NSRange range; + id value = [attributedString attribute:attributeName atIndex:characterIndex longestEffectiveRange:&range inRange:clampedRange]; + NSString *name = attributeName; + + if (value == nil || name == nil) { + // Didn't find anything + continue; + } + + // If highlighting, check with delegate first. If not implemented, assume YES. + if (highlighting + && [self->_delegate respondsToSelector:@selector(textNode:shouldHighlightLinkAttribute:value:atPoint:)] + && ![self->_delegate textNode:self shouldHighlightLinkAttribute:name value:value atPoint:point]) { + value = nil; + name = nil; + } + + if (value != nil || name != nil) { + // We found a minimum glyph distance link attribute, so set the min + // distance, and the out params. + minimumGlyphDistance = currentDistance; + + if (rangeOut != NULL && value != nil) { + *rangeOut = range; + // Limit to only the visible range, because the attributed string will + // return values outside the visible range. + if (NSMaxRange(*rangeOut) > NSMaxRange(visibleRange)) { + (*rangeOut).length = MAX(NSMaxRange(visibleRange) - (*rangeOut).location, 0); + } + } + + if (attributeNameOut != NULL) { + *attributeNameOut = name; + } + + // Set the values for the next iteration + linkAttributeValue = value; + + break; + } + } + }]; + + return linkAttributeValue; +} + +#pragma mark - UIGestureRecognizerDelegate + +- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer +{ + ASDisplayNodeAssertMainThread(); + + if (gestureRecognizer == _longPressGestureRecognizer) { + // Don't allow long press on truncation message + if ([self _pendingTruncationTap]) { + return NO; + } + + // Ask our delegate if a long-press on an attribute is relevant + if ([self _pendingLinkTap] && [_delegate respondsToSelector:@selector(textNode:shouldLongPressLinkAttribute:value:atPoint:)]) { + return [_delegate textNode:self + shouldLongPressLinkAttribute:_highlightedLinkAttributeName + value:_highlightedLinkAttributeValue + atPoint:[gestureRecognizer locationInView:self.view]]; + } + + // Otherwise we are good to go. + return YES; + } + + if (([self _pendingLinkTap] || [self _pendingTruncationTap]) + && [gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]] + && CGRectContainsPoint(self.threadSafeBounds, [gestureRecognizer locationInView:self.view])) { + return NO; + } + + return [super gestureRecognizerShouldBegin:gestureRecognizer]; +} + +#pragma mark - Highlighting + +- (ASTextNodeHighlightStyle)highlightStyle +{ + ASLockScopeSelf(); + + return _highlightStyle; +} + +- (void)setHighlightStyle:(ASTextNodeHighlightStyle)highlightStyle +{ + ASLockScopeSelf(); + + _highlightStyle = highlightStyle; +} + +- (NSRange)highlightRange +{ + ASDisplayNodeAssertMainThread(); + + return _highlightRange; +} + +- (void)setHighlightRange:(NSRange)highlightRange +{ + [self setHighlightRange:highlightRange animated:NO]; +} + +- (void)setHighlightRange:(NSRange)highlightRange animated:(BOOL)animated +{ + [self _setHighlightRange:highlightRange forAttributeName:nil value:nil animated:animated]; +} + +- (void)_setHighlightRange:(NSRange)highlightRange forAttributeName:(NSString *)highlightedAttributeName value:(id)highlightedAttributeValue animated:(BOOL)animated +{ + ASDisplayNodeAssertMainThread(); + ASLockScopeSelf(); + + _highlightedLinkAttributeName = highlightedAttributeName; + _highlightedLinkAttributeValue = highlightedAttributeValue; + + if (!NSEqualRanges(highlightRange, _highlightRange) && ((0 != highlightRange.length) || (0 != _highlightRange.length))) { + + _highlightRange = highlightRange; + + if (_activeHighlightLayer) { + if (animated) { + __weak CALayer *weakHighlightLayer = _activeHighlightLayer; + _activeHighlightLayer = nil; + + weakHighlightLayer.opacity = 0.0; + + CFTimeInterval beginTime = CACurrentMediaTime(); + CABasicAnimation *possibleFadeIn = (CABasicAnimation *)[weakHighlightLayer animationForKey:@"opacity"]; + if (possibleFadeIn) { + // Calculate when we should begin fading out based on the end of the fade in animation, + // Also check to make sure that the new begin time hasn't already passed + CGFloat newBeginTime = (possibleFadeIn.beginTime + possibleFadeIn.duration); + if (newBeginTime > beginTime) { + beginTime = newBeginTime; + } + } + + CABasicAnimation *fadeOut = [CABasicAnimation animationWithKeyPath:@"opacity"]; + fadeOut.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; + fadeOut.fromValue = possibleFadeIn.toValue ? : @(((CALayer *)weakHighlightLayer.presentationLayer).opacity); + fadeOut.toValue = @0.0; + fadeOut.fillMode = kCAFillModeBoth; + fadeOut.duration = ASTextNodeHighlightFadeOutDuration; + fadeOut.beginTime = beginTime; + + dispatch_block_t prev = [CATransaction completionBlock]; + [CATransaction setCompletionBlock:^{ + [weakHighlightLayer removeFromSuperlayer]; + }]; + + [weakHighlightLayer addAnimation:fadeOut forKey:fadeOut.keyPath]; + + [CATransaction setCompletionBlock:prev]; + + } else { + [_activeHighlightLayer removeFromSuperlayer]; + _activeHighlightLayer = nil; + } + } + if (0 != highlightRange.length) { + // Find layer in hierarchy that allows us to draw highlighting on. + CALayer *highlightTargetLayer = self.layer; + while (highlightTargetLayer != nil) { + if (highlightTargetLayer.as_allowsHighlightDrawing) { + break; + } + highlightTargetLayer = highlightTargetLayer.superlayer; + } + + if (highlightTargetLayer != nil) { + ASTextKitRenderer *renderer = [self _locked_renderer]; + + NSArray *highlightRects = [renderer rectsForTextRange:highlightRange measureOption:ASTextKitRendererMeasureOptionBlock]; + NSMutableArray *converted = [NSMutableArray arrayWithCapacity:highlightRects.count]; + for (NSValue *rectValue in highlightRects) { + UIEdgeInsets shadowPadding = renderer.shadower.shadowPadding; + CGRect rendererRect = ASTextNodeAdjustRenderRectForShadowPadding(rectValue.CGRectValue, shadowPadding); + + // The rects returned from renderer don't have `textContainerInset`, + // as well as they are using the `constrainedSize` for layout, + // so we can simply increase the rect by insets to get the full blown layout. + rendererRect.size.width += _textContainerInset.left + _textContainerInset.right; + rendererRect.size.height += _textContainerInset.top + _textContainerInset.bottom; + + CGRect highlightedRect = [self.layer convertRect:rendererRect toLayer:highlightTargetLayer]; + + // We set our overlay layer's frame to the bounds of the highlight target layer. + // Offset highlight rects to avoid double-counting target layer's bounds.origin. + highlightedRect.origin.x -= highlightTargetLayer.bounds.origin.x; + highlightedRect.origin.y -= highlightTargetLayer.bounds.origin.y; + [converted addObject:[NSValue valueWithCGRect:highlightedRect]]; + } + + ASHighlightOverlayLayer *overlayLayer = [[ASHighlightOverlayLayer alloc] initWithRects:converted]; + overlayLayer.highlightColor = [[self class] _highlightColorForStyle:self.highlightStyle]; + overlayLayer.frame = highlightTargetLayer.bounds; + overlayLayer.masksToBounds = NO; + overlayLayer.opacity = [[self class] _highlightOpacityForStyle:self.highlightStyle]; + [highlightTargetLayer addSublayer:overlayLayer]; + + if (animated) { + CABasicAnimation *fadeIn = [CABasicAnimation animationWithKeyPath:@"opacity"]; + fadeIn.fromValue = @0.0; + fadeIn.toValue = @(overlayLayer.opacity); + fadeIn.duration = ASTextNodeHighlightFadeInDuration; + fadeIn.beginTime = CACurrentMediaTime(); + + [overlayLayer addAnimation:fadeIn forKey:fadeIn.keyPath]; + } + + [overlayLayer setNeedsDisplay]; + + _activeHighlightLayer = overlayLayer; + } + } + } +} + +- (void)_clearHighlightIfNecessary +{ + ASDisplayNodeAssertMainThread(); + + if ([self _pendingLinkTap] || [self _pendingTruncationTap]) { + [self setHighlightRange:NSMakeRange(0, 0) animated:YES]; + } +} + ++ (CGColorRef)_highlightColorForStyle:(ASTextNodeHighlightStyle)style +{ + return [UIColor colorWithWhite:(style == ASTextNodeHighlightStyleLight ? 0.0 : 1.0) alpha:1.0].CGColor; +} + ++ (CGFloat)_highlightOpacityForStyle:(ASTextNodeHighlightStyle)style +{ + return (style == ASTextNodeHighlightStyleLight) ? ASTextNodeHighlightLightOpacity : ASTextNodeHighlightDarkOpacity; +} + +#pragma mark - Text rects + +static CGRect ASTextNodeAdjustRenderRectForShadowPadding(CGRect rendererRect, UIEdgeInsets shadowPadding) { + rendererRect.origin.x -= shadowPadding.left; + rendererRect.origin.y -= shadowPadding.top; + return rendererRect; +} + +- (NSArray *)rectsForTextRange:(NSRange)textRange +{ + return [self _rectsForTextRange:textRange measureOption:ASTextKitRendererMeasureOptionCapHeight]; +} + +- (NSArray *)highlightRectsForTextRange:(NSRange)textRange +{ + return [self _rectsForTextRange:textRange measureOption:ASTextKitRendererMeasureOptionBlock]; +} + +- (NSArray *)_rectsForTextRange:(NSRange)textRange measureOption:(ASTextKitRendererMeasureOption)measureOption +{ + ASLockScopeSelf(); + + NSArray *rects = [[self _locked_renderer] rectsForTextRange:textRange measureOption:measureOption]; + const auto adjustedRects = [[NSMutableArray alloc] init]; + + for (NSValue *rectValue in rects) { + CGRect rect = [rectValue CGRectValue]; + rect = ASTextNodeAdjustRenderRectForShadowPadding(rect, self.shadowPadding); + + NSValue *adjustedRectValue = [NSValue valueWithCGRect:rect]; + [adjustedRects addObject:adjustedRectValue]; + } + + return adjustedRects; +} + +- (CGRect)trailingRect +{ + ASLockScopeSelf(); + + CGRect rect = [[self _locked_renderer] trailingRect]; + return ASTextNodeAdjustRenderRectForShadowPadding(rect, self.shadowPadding); +} + +- (CGRect)frameForTextRange:(NSRange)textRange +{ + ASLockScopeSelf(); + + CGRect frame = [[self _locked_renderer] frameForTextRange:textRange]; + return ASTextNodeAdjustRenderRectForShadowPadding(frame, self.shadowPadding); +} + +#pragma mark - Tint Colors + + +- (void)tintColorDidChange +{ + [super tintColorDidChange]; + + [self _setNeedsDisplayOnTintedTextColor]; +} + +- (void)_setNeedsDisplayOnTintedTextColor +{ + BOOL textColorFollowsTintColor = NO; + { + AS::MutexLocker l(__instanceLock__); + textColorFollowsTintColor = _textColorFollowsTintColor; + } + + if (textColorFollowsTintColor) { + [self setNeedsDisplay]; + } +} + + +#pragma mark Interface State + +- (void)didEnterHierarchy +{ + [super didEnterHierarchy]; + + [self _setNeedsDisplayOnTintedTextColor]; +} + +#pragma mark - Placeholders + +- (UIColor *)placeholderColor +{ + return ASLockedSelf(_placeholderColor); +} + +- (void)setPlaceholderColor:(UIColor *)placeholderColor +{ + if (ASLockedSelfCompareAssignCopy(_placeholderColor, placeholderColor)) { + self.placeholderEnabled = CGColorGetAlpha(placeholderColor.CGColor) > 0; + } +} + +- (UIImage *)placeholderImage +{ + // FIXME: Replace this implementation with reusable CALayers that have .backgroundColor set. + // This would completely eliminate the memory and performance cost of the backing store. + CGSize size = self.calculatedSize; + if ((size.width * size.height) < CGFLOAT_EPSILON) { + return nil; + } + + ASLockScopeSelf(); + + UIGraphicsBeginImageContextWithOptions(size, NO, 1.0); + [self.placeholderColor setFill]; + + ASTextKitRenderer *renderer = [self _locked_renderer]; + NSRange visibleRange = renderer.firstVisibleRange; + + // cap height is both faster and creates less subpixel blending + NSArray *lineRects = [self _rectsForTextRange:visibleRange measureOption:ASTextKitRendererMeasureOptionLineHeight]; + + // fill each line with the placeholder color + for (NSValue *rectValue in lineRects) { + CGRect lineRect = [rectValue CGRectValue]; + CGRect fillBounds = CGRectIntegral(UIEdgeInsetsInsetRect(lineRect, self.placeholderInsets)); + + if (fillBounds.size.width > 0.0 && fillBounds.size.height > 0.0) { + UIRectFill(fillBounds); + } + } + + UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + return image; +} + +#pragma mark - Touch Handling + +- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event +{ + ASDisplayNodeAssertMainThread(); + ASLockScopeSelf(); // Protect usage of _passthroughNonlinkTouches and _alwaysHandleTruncationTokenTap ivars. + + if (!_passthroughNonlinkTouches) { + return [super pointInside:point withEvent:event]; + } + + if (_alwaysHandleTruncationTokenTap) { + return YES; + } + + NSRange range = NSMakeRange(0, 0); + NSString *linkAttributeName = nil; + BOOL inAdditionalTruncationMessage = NO; + + id linkAttributeValue = [self _linkAttributeValueAtPoint:point + attributeName:&linkAttributeName + range:&range + inAdditionalTruncationMessage:&inAdditionalTruncationMessage + forHighlighting:YES]; + + NSUInteger lastCharIndex = NSIntegerMax; + BOOL linkCrossesVisibleRange = (lastCharIndex > range.location) && (lastCharIndex < NSMaxRange(range) - 1); + + if (range.length > 0 && !linkCrossesVisibleRange && linkAttributeValue != nil && linkAttributeName != nil) { + return YES; + } else { + return NO; + } +} + +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event +{ + ASDisplayNodeAssertMainThread(); + + [super touchesBegan:touches withEvent:event]; + + CGPoint point = [[touches anyObject] locationInView:self.view]; + + NSRange range = NSMakeRange(0, 0); + NSString *linkAttributeName = nil; + BOOL inAdditionalTruncationMessage = NO; + + id linkAttributeValue = [self _linkAttributeValueAtPoint:point + attributeName:&linkAttributeName + range:&range + inAdditionalTruncationMessage:&inAdditionalTruncationMessage + forHighlighting:YES]; + + NSUInteger lastCharIndex = NSIntegerMax; + BOOL linkCrossesVisibleRange = (lastCharIndex > range.location) && (lastCharIndex < NSMaxRange(range) - 1); + + if (inAdditionalTruncationMessage) { + NSRange visibleRange = NSMakeRange(0, 0); + { + ASLockScopeSelf(); + visibleRange = [self _locked_renderer].firstVisibleRange; + } + NSRange truncationMessageRange = [self _additionalTruncationMessageRangeWithVisibleRange:visibleRange]; + [self _setHighlightRange:truncationMessageRange forAttributeName:ASTextNodeTruncationTokenAttributeName value:nil animated:YES]; + } else if (range.length > 0 && !linkCrossesVisibleRange && linkAttributeValue != nil && linkAttributeName != nil) { + [self _setHighlightRange:range forAttributeName:linkAttributeName value:linkAttributeValue animated:YES]; + } +} + + +- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event +{ + ASDisplayNodeAssertMainThread(); + [super touchesCancelled:touches withEvent:event]; + + [self _clearHighlightIfNecessary]; +} + +- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event +{ + ASDisplayNodeAssertMainThread(); + [super touchesEnded:touches withEvent:event]; + + if ([self _pendingLinkTap] && [_delegate respondsToSelector:@selector(textNode:tappedLinkAttribute:value:atPoint:textRange:)]) { + CGPoint point = [[touches anyObject] locationInView:self.view]; + [_delegate textNode:self tappedLinkAttribute:_highlightedLinkAttributeName value:_highlightedLinkAttributeValue atPoint:point textRange:_highlightRange]; + } + + if ([self _pendingTruncationTap]) { + if ([_delegate respondsToSelector:@selector(textNodeTappedTruncationToken:)]) { + [_delegate textNodeTappedTruncationToken:self]; + } + } + + [self _clearHighlightIfNecessary]; +} + +- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event +{ + ASDisplayNodeAssertMainThread(); + [super touchesMoved:touches withEvent:event]; + + UITouch *touch = [touches anyObject]; + CGPoint locationInView = [touch locationInView:self.view]; + // on 3D Touch enabled phones, this gets fired with changes in force, and usually will get fired immediately after touchesBegan:withEvent: + if (CGPointEqualToPoint([touch previousLocationInView:self.view], locationInView)) + return; + + // If touch has moved out of the current highlight range, clear the highlight. + if (_highlightRange.length > 0) { + NSRange range = NSMakeRange(0, 0); + [self _linkAttributeValueAtPoint:locationInView + attributeName:NULL + range:&range + inAdditionalTruncationMessage:NULL + forHighlighting:YES]; + + if (!NSEqualRanges(_highlightRange, range)) { + [self _clearHighlightIfNecessary]; + } + } +} + +- (void)_handleLongPress:(UILongPressGestureRecognizer *)longPressRecognizer +{ + ASDisplayNodeAssertMainThread(); + + // Respond to long-press when it begins, not when it ends. + if (longPressRecognizer.state == UIGestureRecognizerStateBegan) { + if ([self _pendingLinkTap] && [_delegate respondsToSelector:@selector(textNode:longPressedLinkAttribute:value:atPoint:textRange:)]) { + CGPoint touchPoint = [_longPressGestureRecognizer locationInView:self.view]; + [_delegate textNode:self longPressedLinkAttribute:_highlightedLinkAttributeName value:_highlightedLinkAttributeValue atPoint:touchPoint textRange:_highlightRange]; + } + } +} + +- (BOOL)_pendingLinkTap +{ + ASLockScopeSelf(); + + return (_highlightedLinkAttributeValue != nil && ![self _pendingTruncationTap]) && _delegate != nil; +} + +- (BOOL)_pendingTruncationTap +{ + ASLockScopeSelf(); + + return [_highlightedLinkAttributeName isEqualToString:ASTextNodeTruncationTokenAttributeName]; +} + +- (BOOL)alwaysHandleTruncationTokenTap +{ + ASLockScopeSelf(); + return _alwaysHandleTruncationTokenTap; +} + +- (void)setAlwaysHandleTruncationTokenTap:(BOOL)alwaysHandleTruncationTokenTap +{ + ASLockScopeSelf(); + _alwaysHandleTruncationTokenTap = alwaysHandleTruncationTokenTap; +} + +#pragma mark - Shadow Properties + +- (CGColorRef)shadowColor +{ + return ASLockedSelf(_shadowColor); +} + +- (void)setShadowColor:(CGColorRef)shadowColor +{ + [self lock]; + + if (_shadowColor != shadowColor && CGColorEqualToColor(shadowColor, _shadowColor) == NO) { + CGColorRelease(_shadowColor); + _shadowColor = CGColorRetain(shadowColor); + _cachedShadowUIColor = [UIColor colorWithCGColor:shadowColor]; + [self unlock]; + + [self setNeedsDisplay]; + return; + } + + [self unlock]; +} + +- (CGSize)shadowOffset +{ + return ASLockedSelf(_shadowOffset); +} + +- (void)setShadowOffset:(CGSize)shadowOffset +{ + if (ASLockedSelfCompareAssignCustom(_shadowOffset, shadowOffset, CGSizeEqualToSize)) { + [self setNeedsDisplay]; + } +} + +- (CGFloat)shadowOpacity +{ + return ASLockedSelf(_shadowOpacity); +} + +- (void)setShadowOpacity:(CGFloat)shadowOpacity +{ + if (ASLockedSelfCompareAssign(_shadowOpacity, shadowOpacity)) { + [self setNeedsDisplay]; + } +} + +- (CGFloat)shadowRadius +{ + return ASLockedSelf(_shadowRadius); +} + +- (void)setShadowRadius:(CGFloat)shadowRadius +{ + if (ASLockedSelfCompareAssign(_shadowRadius, shadowRadius)) { + [self setNeedsDisplay]; + } +} + +- (UIEdgeInsets)shadowPadding +{ + ASLockScopeSelf(); + return [self _locked_renderer].shadower.shadowPadding; +} + +#pragma mark - Truncation Message + +static NSAttributedString *DefaultTruncationAttributedString() +{ + static NSAttributedString *defaultTruncationAttributedString; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + defaultTruncationAttributedString = [[NSAttributedString alloc] initWithString:NSLocalizedString(@"\u2026", @"Default truncation string")]; + }); + return defaultTruncationAttributedString; +} + +- (NSAttributedString *)truncationAttributedText +{ + return ASLockedSelf(_truncationAttributedText); +} + +- (void)setTruncationAttributedText:(NSAttributedString *)truncationAttributedText +{ + if (ASLockedSelfCompareAssignCopy(_truncationAttributedText, truncationAttributedText)) { + [self _invalidateTruncationText]; + [self setNeedsDisplay]; + } +} + +- (void)setAdditionalTruncationMessage:(NSAttributedString *)additionalTruncationMessage +{ + if (ASLockedSelfCompareAssignCopy(_additionalTruncationMessage, additionalTruncationMessage)) { + [self _invalidateTruncationText]; + [self setNeedsDisplay]; + } +} + +- (NSAttributedString *)additionalTruncationMessage +{ + return ASLockedSelf(_additionalTruncationMessage); +} + +- (void)setTruncationMode:(NSLineBreakMode)truncationMode +{ + if (ASLockedSelfCompareAssign(_truncationMode, truncationMode)) { + [self setNeedsDisplay]; + } +} + +- (NSLineBreakMode)truncationMode +{ + return ASLockedSelf(_truncationMode); +} + +- (BOOL)isTruncated +{ + return ASLockedSelf([[self _locked_renderer] isTruncated]); +} + +- (BOOL)shouldTruncateForConstrainedSize:(ASSizeRange)constrainedSize +{ + return ASLockedSelf([[self _locked_rendererWithBounds:{.size = constrainedSize.max}] isTruncated]); +} + +- (void)setPointSizeScaleFactors:(NSArray *)pointSizeScaleFactors +{ + if (ASLockedSelfCompareAssignCopy(_pointSizeScaleFactors, pointSizeScaleFactors)) { + [self setNeedsDisplay]; + } +} + +- (NSArray *)pointSizeScaleFactors +{ + return ASLockedSelf(_pointSizeScaleFactors); +} + +- (void)setMaximumNumberOfLines:(NSUInteger)maximumNumberOfLines +{ + if (ASLockedSelfCompareAssign(_maximumNumberOfLines, maximumNumberOfLines)) { + [self setNeedsDisplay]; + } +} + +- (NSUInteger)maximumNumberOfLines +{ + return ASLockedSelf(_maximumNumberOfLines); +} + +- (NSUInteger)lineCount +{ + return ASLockedSelf([[self _locked_renderer] lineCount]); +} + +- (BOOL)textColorFollowsTintColor +{ + return ASLockedSelf(_textColorFollowsTintColor); +} + +#pragma mark - Truncation Message + +- (void)_invalidateTruncationText +{ + ASLockScopeSelf(); + [self _locked_invalidateTruncationText]; +} + +- (void)_locked_invalidateTruncationText +{ + _composedTruncationText = nil; +} + +/** + * @return the additional truncation message range within the as-rendered text. + * Must be called from main thread + */ +- (NSRange)_additionalTruncationMessageRangeWithVisibleRange:(NSRange)visibleRange +{ + ASLockScopeSelf(); + + // Check if we even have an additional truncation message. + if (!_additionalTruncationMessage) { + return NSMakeRange(NSNotFound, 0); + } + + // Character location of the unicode ellipsis (the first index after the visible range) + NSInteger truncationTokenIndex = NSMaxRange(visibleRange); + + NSUInteger additionalTruncationMessageLength = _additionalTruncationMessage.length; + // We get the location of the truncation token, then add the length of the + // truncation attributed string +1 for the space between. + return NSMakeRange(truncationTokenIndex + _truncationAttributedText.length + 1, additionalTruncationMessageLength); +} + +/** + * @return the truncation message for the string. If there are both an + * additional truncation message and a truncation attributed string, they will + * be properly composed. + */ +- (NSAttributedString *)_locked_composedTruncationText +{ + DISABLED_ASAssertLocked(__instanceLock__); + if (_composedTruncationText == nil) { + if (_truncationAttributedText != nil && _additionalTruncationMessage != nil) { + NSMutableAttributedString *newComposedTruncationString = [[NSMutableAttributedString alloc] initWithAttributedString:_truncationAttributedText]; + [newComposedTruncationString.mutableString appendString:@" "]; + [newComposedTruncationString appendAttributedString:_additionalTruncationMessage]; + _composedTruncationText = newComposedTruncationString; + } else if (_truncationAttributedText != nil) { + _composedTruncationText = _truncationAttributedText; + } else if (_additionalTruncationMessage != nil) { + _composedTruncationText = _additionalTruncationMessage; + } else { + _composedTruncationText = DefaultTruncationAttributedString(); + } + _composedTruncationText = [self _locked_prepareTruncationStringForDrawing:_composedTruncationText]; + } + return _composedTruncationText; +} + +/** + * - cleanses it of core text attributes so TextKit doesn't crash + * - Adds whole-string attributes so the truncation message matches the styling + * of the body text + */ +- (NSAttributedString *)_locked_prepareTruncationStringForDrawing:(NSAttributedString *)truncationString +{ + DISABLED_ASAssertLocked(__instanceLock__); + truncationString = ASCleanseAttributedStringOfCoreTextAttributes(truncationString); + NSMutableAttributedString *truncationMutableString = [truncationString mutableCopy]; + // Grab the attributes from the full string + if (_attributedText.length > 0) { + NSAttributedString *originalString = _attributedText; + NSInteger originalStringLength = _attributedText.length; + // Add any of the original string's attributes to the truncation string, + // but don't overwrite any of the truncation string's attributes + NSDictionary *originalStringAttributes = [originalString attributesAtIndex:originalStringLength-1 effectiveRange:NULL]; + [truncationString enumerateAttributesInRange:NSMakeRange(0, truncationString.length) options:0 usingBlock: + ^(NSDictionary *attributes, NSRange range, BOOL *stop) { + NSMutableDictionary *futureTruncationAttributes = [originalStringAttributes mutableCopy]; + [futureTruncationAttributes addEntriesFromDictionary:attributes]; + [truncationMutableString setAttributes:futureTruncationAttributes range:range]; + }]; + } + return truncationMutableString; +} + +#if AS_TEXTNODE_RECORD_ATTRIBUTED_STRINGS ++ (void)_registerAttributedText:(NSAttributedString *)str +{ + static NSMutableArray *array; + static NSLock *lock; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + lock = [NSLock new]; + array = [NSMutableArray new]; + }); + [lock lock]; + [array addObject:str]; + if (array.count % 20 == 0) { + NSLog(@"Got %d strings", (int)array.count); + } + if (array.count == 2000) { + NSString *path = [NSTemporaryDirectory() stringByAppendingPathComponent:@"AttributedStrings.plist"]; + NSAssert([NSKeyedArchiver archiveRootObject:array toFile:path], nil); + NSLog(@"Saved to %@", path); + } + [lock unlock]; +} +#endif + +// All direct descendants of ASTextNode get their superclass replaced by ASTextNode2. ++ (void)initialize +{ + // Texture requires that node subclasses call [super initialize] + [super initialize]; + + if (class_getSuperclass(self) == [ASTextNode class] + && ASActivateExperimentalFeature(ASExperimentalTextNode)) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + class_setSuperclass(self, [ASTextNode2 class]); +#pragma clang diagnostic pop + } +} + +// For direct allocations of ASTextNode itself, we override allocWithZone: ++ (id)allocWithZone:(struct _NSZone *)zone +{ + if (ASActivateExperimentalFeature(ASExperimentalTextNode)) { + return (ASTextNode *)[ASTextNode2 allocWithZone:zone]; + } else { + return [super allocWithZone:zone]; + } +} + +@end + +@implementation ASTextNode (Unsupported) + +- (void)setTextContainerLinePositionModifier:(id)textContainerLinePositionModifier +{ + AS_TEXT_ALERT_UNIMPLEMENTED_FEATURE(); +} + +- (id)textContainerLinePositionModifier +{ + AS_TEXT_ALERT_UNIMPLEMENTED_FEATURE(); + return nil; +} + +@end + +@implementation ASTextNode (Deprecated) + +- (void)setAttributedString:(NSAttributedString *)attributedString +{ + self.attributedText = attributedString; +} + +- (NSAttributedString *)attributedString +{ + return self.attributedText; +} + +- (void)setTruncationAttributedString:(NSAttributedString *)truncationAttributedString +{ + self.truncationAttributedText = truncationAttributedString; +} + +- (NSAttributedString *)truncationAttributedString +{ + return self.truncationAttributedText; +} + +@end + +#endif diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASTextNode2.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASTextNode2.h new file mode 100644 index 0000000..5848adc --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASTextNode2.h @@ -0,0 +1,256 @@ +// +// ASTextNode2.h +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import + +@protocol ASTextLinePositionModifier; + +NS_ASSUME_NONNULL_BEGIN + +/** + @abstract Draws interactive rich text. + @discussion Backed by the code in TextExperiment folder, on top of CoreText. + */ +#if AS_ENABLE_TEXTNODE +@interface ASTextNode2 : ASControlNode +#else +@interface ASTextNode : ASControlNode +#endif + +/** + @abstract The styled text displayed by the node. + @discussion Defaults to nil, no text is shown. + For inline image attachments, add an attribute of key NSAttachmentAttributeName, with a value of an NSTextAttachment. + */ +@property (nullable, copy) NSAttributedString *attributedText; + +#pragma mark - Truncation + +/** + @abstract The attributedText to use when the text must be truncated. + @discussion Defaults to a localized ellipsis character. + */ +@property (nullable, copy) NSAttributedString *truncationAttributedText; + +/** + @summary The second attributed string appended for truncation. + @discussion This string will be highlighted on touches. + @default nil + */ +@property (nullable, copy) NSAttributedString *additionalTruncationMessage; + +/** + @abstract Determines how the text is truncated to fit within the receiver's maximum size. + @discussion Defaults to NSLineBreakByWordWrapping. + @note Setting a truncationMode in attributedString will override the truncation mode set here. + */ +@property NSLineBreakMode truncationMode; + +/** + @abstract If the text node is truncated. Text must have been sized first. + */ +@property (readonly, getter=isTruncated) BOOL truncated; + +/** + @abstract The maximum number of lines to render of the text before truncation. + @default 0 (No limit) + */ +@property NSUInteger maximumNumberOfLines; + +/** + @abstract The number of lines in the text. Text must have been sized first. + */ +@property (readonly) NSUInteger lineCount; + +/** + * An array of path objects representing the regions where text should not be displayed. + * + * @discussion The default value of this property is an empty array. You can + * assign an array of UIBezierPath objects to exclude text from one or more regions in + * the text node's bounds. You can use this property to have text wrap around images, + * shapes or other text like a fancy magazine. + */ +@property (nullable, copy) NSArray *exclusionPaths; + +#pragma mark - Placeholders + +/** + * @abstract ASTextNode has a special placeholder behavior when placeholderEnabled is YES. + * + * @discussion Defaults to NO. When YES, it draws rectangles for each line of text, + * following the true shape of the text's wrapping. This visually mirrors the overall + * shape and weight of paragraphs, making the appearance of the finished text less jarring. + */ +@property BOOL placeholderEnabled; + +/** + @abstract The placeholder color. + */ +@property (nullable, copy) UIColor *placeholderColor; + +/** + @abstract Inset each line of the placeholder. + */ +@property UIEdgeInsets placeholderInsets; + +#pragma mark - Shadow + +/** + @abstract When you set these ASDisplayNode properties, they are composited into the bitmap instead of being applied by CA. + + @property (nonatomic) CGColorRef shadowColor; + @property (nonatomic) CGFloat shadowOpacity; + @property (nonatomic) CGSize shadowOffset; + @property (nonatomic) CGFloat shadowRadius; + */ + +/** + @abstract The number of pixels used for shadow padding on each side of the receiver. + @discussion Each inset will be less than or equal to zero, so that applying + UIEdgeInsetsRect(boundingRectForText, shadowPadding) + will return a CGRect large enough to fit both the text and the appropriate shadow padding. + */ +@property (nonatomic, readonly) UIEdgeInsets shadowPadding; + +#pragma mark - Positioning + +/** + @abstract Returns an array of rects bounding the characters in a given text range. + @param textRange A range of text. Must be valid for the receiver's string. + @discussion Use this method to detect all the different rectangles a given range of text occupies. + The rects returned are not guaranteed to be contiguous (for example, if the given text range spans + a line break, the rects returned will be on opposite sides and different lines). The rects returned + are in the coordinate system of the receiver. + */ +- (NSArray *)rectsForTextRange:(NSRange)textRange AS_WARN_UNUSED_RESULT; + +/** + @abstract Returns an array of rects used for highlighting the characters in a given text range. + @param textRange A range of text. Must be valid for the receiver's string. + @discussion Use this method to detect all the different rectangles the highlights of a given range of text occupies. + The rects returned are not guaranteed to be contiguous (for example, if the given text range spans + a line break, the rects returned will be on opposite sides and different lines). The rects returned + are in the coordinate system of the receiver. This method is useful for visual coordination with a + highlighted range of text. + */ +- (NSArray *)highlightRectsForTextRange:(NSRange)textRange AS_WARN_UNUSED_RESULT; + +/** + @abstract Returns a bounding rect for the given text range. + @param textRange A range of text. Must be valid for the receiver's string. + @discussion The height of the frame returned is that of the receiver's line-height; adjustment for + cap-height and descenders is not performed. This method raises an exception if textRange is not + a valid substring range of the receiver's string. + */ +- (CGRect)frameForTextRange:(NSRange)textRange AS_WARN_UNUSED_RESULT; + +/** + @abstract Returns the trailing rectangle of space in the receiver, after the final character. + @discussion Use this method to detect which portion of the receiver is not occupied by characters. + The rect returned is in the coordinate system of the receiver. + */ +- (CGRect)trailingRect AS_WARN_UNUSED_RESULT; + + +#pragma mark - Actions + +/** + @abstract The set of attribute names to consider links. Defaults to NSLinkAttributeName. + */ +@property (nonatomic, copy) NSArray *linkAttributeNames; + +/** + @abstract Indicates whether the receiver has an entity at a given point. + @param point The point, in the receiver's coordinate system. + @param attributeNameOut The name of the attribute at the point. Can be NULL. + @param rangeOut The ultimate range of the found text. Can be NULL. + @result YES if an entity exists at `point`; NO otherwise. + */ +- (nullable id)linkAttributeValueAtPoint:(CGPoint)point attributeName:(out NSString * _Nullable * _Nullable)attributeNameOut range:(out NSRange * _Nullable)rangeOut AS_WARN_UNUSED_RESULT; + +/** + @abstract The style to use when highlighting text. + */ +@property (nonatomic) ASTextNodeHighlightStyle highlightStyle; + +/** + @abstract The range of text highlighted by the receiver. Changes to this property are not animated by default. + */ +@property (nonatomic) NSRange highlightRange; + +/** + @abstract Set the range of text to highlight, with optional animation. + + @param highlightRange The range of text to highlight. + + @param animated Whether the text should be highlighted with an animation. + */ +- (void)setHighlightRange:(NSRange)highlightRange animated:(BOOL)animated; + +/** + @abstract Responds to actions from links in the text node. + @discussion The delegate must be set before the node is loaded, and implement + textNode:longPressedLinkAttribute:value:atPoint:textRange: in order for + the long press gesture recognizer to be installed. + */ +@property (weak) id delegate; + +/** + @abstract If YES and a long press is recognized, touches are cancelled. Default is NO + */ +@property (nonatomic) BOOL longPressCancelsTouches; + +/** + @abstract if YES will not intercept touches for non-link areas of the text. Default is NO. + @discussion If you still want to handle tap truncation action when passthroughNonlinkTouches is YES, + you should set the alwaysHandleTruncationTokenTap to YES. + */ +@property (nonatomic) BOOL passthroughNonlinkTouches; + +/** + @abstract Always handle tap truncationAction, even the passthroughNonlinkTouches is YES. Default is NO. + @discussion if this is set to YES, the [ASTextNodeDelegate textNodeTappedTruncationToken:] callback will be called. + */ +@property (nonatomic) BOOL alwaysHandleTruncationTokenTap; + +/** + @abstract if YES will use the value of `self.tintColor` if the foreground color of text is not defined. + @discussion This is mainly used from ASButtonNode since by default text nodes do not respect tintColor settings unless contained within a interactive control + */ +@property (nonatomic) BOOL textColorFollowsTintColor; + ++ (void)enableDebugging; + +#pragma mark - Layout and Sizing + +@property (nullable, nonatomic) id textContainerLinePositionModifier; + +@end + +#if AS_ENABLE_TEXTNODE +@interface ASTextNode2 (Unavailable) +#else +@interface ASTextNode (Unavailable) +#endif + +- (instancetype)initWithLayerBlock:(ASDisplayNodeLayerBlock)viewBlock didLoadBlock:(nullable ASDisplayNodeDidLoadBlock)didLoadBlock NS_UNAVAILABLE; + +- (instancetype)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock didLoadBlock:(nullable ASDisplayNodeDidLoadBlock)didLoadBlock NS_UNAVAILABLE; + +@end + +#if (!AS_ENABLE_TEXTNODE) +// For the time beeing remap ASTextNode2 to ASTextNode +#define ASTextNode2 ASTextNode +#endif + +NS_ASSUME_NONNULL_END + + diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASTextNode2.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASTextNode2.mm new file mode 100644 index 0000000..e268eca --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASTextNode2.mm @@ -0,0 +1,1512 @@ +// +// ASTextNode2.mm +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import // Definition of ASTextNodeDelegate + +#import +#import + +#import +#import +#import +#import +#import +#import + +#import +#import + +#import + +@interface ASTextCacheValue : NSObject { + @package + AS::Mutex _m; + std::deque> _layouts; +} +@end +@implementation ASTextCacheValue +@end + +/** + * If set, we will record all values set to attributedText into an array + * and once we get 2000, we'll write them all out into a plist file. + * + * This is useful for gathering realistic text data sets from apps for performance + * testing. + */ +#define AS_TEXTNODE2_RECORD_ATTRIBUTED_STRINGS 0 + +/** + * If it can't find a compatible layout, this method creates one. + * + * NOTE: Be careful to copy `text` if needed. + */ +static NS_RETURNS_RETAINED ASTextLayout *ASTextNodeCompatibleLayoutWithContainerAndText(ASTextContainer *container, NSAttributedString *text) { + static dispatch_once_t onceToken; + static AS::Mutex *layoutCacheLock; + static NSCache *textLayoutCache; + dispatch_once(&onceToken, ^{ + layoutCacheLock = new AS::Mutex(); + textLayoutCache = [[NSCache alloc] init]; + }); + + layoutCacheLock->lock(); + + ASTextCacheValue *cacheValue = [textLayoutCache objectForKey:text]; + if (cacheValue == nil) { + cacheValue = [[ASTextCacheValue alloc] init]; + [textLayoutCache setObject:cacheValue forKey:[text copy]]; + } + + // Lock the cache item for the rest of the method. Only after acquiring can we release the NSCache. + AS::MutexLocker lock(cacheValue->_m); + layoutCacheLock->unlock(); + + CGRect containerBounds = (CGRect){ .size = container.size }; + { + for (const auto &t : cacheValue->_layouts) { + CGSize constrainedSize = std::get<0>(t); + ASTextLayout *layout = std::get<1>(t); + + CGSize layoutSize = layout.textBoundingSize; + // 1. CoreText can return frames that are narrower than the constrained width, for obvious reasons. + // 2. CoreText can return frames that are slightly wider than the constrained width, for some reason. + // We have to trust that somehow it's OK to try and draw within our size constraint, despite the return value. + // 3. Thus, those two values (constrained width & returned width) form a range, where + // intermediate values in that range will be snapped. Thus, we can use a given layout as long as our + // width is in that range, between the min and max of those two values. + CGRect minRect = CGRectMake(0, 0, MIN(layoutSize.width, constrainedSize.width), MIN(layoutSize.height, constrainedSize.height)); + if (!CGRectContainsRect(containerBounds, minRect)) { + continue; + } + CGRect maxRect = CGRectMake(0, 0, MAX(layoutSize.width, constrainedSize.width), MAX(layoutSize.height, constrainedSize.height)); + if (!CGRectContainsRect(maxRect, containerBounds)) { + continue; + } + if (!CGSizeEqualToSize(container.size, constrainedSize)) { + continue; + } + + // Now check container params. + ASTextContainer *otherContainer = layout.container; + if (!UIEdgeInsetsEqualToEdgeInsets(container.insets, otherContainer.insets)) { + continue; + } + if (!ASObjectIsEqual(container.exclusionPaths, otherContainer.exclusionPaths)) { + continue; + } + if (container.maximumNumberOfRows != otherContainer.maximumNumberOfRows) { + continue; + } + if (container.truncationType != otherContainer.truncationType) { + continue; + } + if (!ASObjectIsEqual(container.truncationToken, otherContainer.truncationToken)) { + continue; + } + // TODO: When we get a cache hit, move this entry to the front (LRU). + return layout; + } + } + + // Cache Miss. Compute the text layout. + ASTextLayout *layout = [ASTextLayout layoutWithContainer:container text:text]; + + // Store the result in the cache. + { + // This is a critical section. However we also must hold the lock until this point, in case + // another thread requests this cache item while a layout is being calculated, so they don't race. + cacheValue->_layouts.push_front(std::make_tuple(container.size, layout)); + if (cacheValue->_layouts.size() > 3) { + cacheValue->_layouts.pop_back(); + } + } + + return layout; +} + +static const NSTimeInterval ASTextNodeHighlightFadeOutDuration = 0.15; +static const NSTimeInterval ASTextNodeHighlightFadeInDuration = 0.1; +static const CGFloat ASTextNodeHighlightLightOpacity = 0.11; +static const CGFloat ASTextNodeHighlightDarkOpacity = 0.22; +static NSString *ASTextNodeTruncationTokenAttributeName = @"ASTextNodeTruncationAttribute"; + +#if AS_ENABLE_TEXTNODE +#define AS_TN2_CLASSNAME ASTextNode2 +#else +#define AS_TN2_CLASSNAME ASTextNode +#endif + +@interface AS_TN2_CLASSNAME () + +@end + +@implementation AS_TN2_CLASSNAME { + ASTextContainer *_textContainer; + + CGSize _shadowOffset; + CGColorRef _shadowColor; + CGFloat _shadowOpacity; + CGFloat _shadowRadius; + + NSAttributedString *_attributedText; + NSAttributedString *_truncationAttributedText; + NSAttributedString *_additionalTruncationMessage; + NSArray *_pointSizeScaleFactors; + NSLineBreakMode _truncationMode; + + NSString *_highlightedLinkAttributeName; + id _highlightedLinkAttributeValue; + NSRange _highlightRange; + ASHighlightOverlayLayer *_activeHighlightLayer; + UIColor *_placeholderColor; + + UILongPressGestureRecognizer *_longPressGestureRecognizer; + ASTextNodeHighlightStyle _highlightStyle; + BOOL _longPressCancelsTouches; + BOOL _passthroughNonlinkTouches; + BOOL _alwaysHandleTruncationTokenTap; +} +@dynamic placeholderEnabled; + +static NSArray *DefaultLinkAttributeNames() { + static NSArray *names; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + names = @[ NSLinkAttributeName ]; + }); + return names; +} + +- (instancetype)init +{ + if (self = [super init]) { + _textContainer = [[ASTextContainer alloc] init]; + // Load default values from superclass. + _shadowOffset = [super shadowOffset]; + _shadowColor = CGColorRetain([super shadowColor]); + _shadowOpacity = [super shadowOpacity]; + _shadowRadius = [super shadowRadius]; + + // Disable user interaction for text node by default. + self.userInteractionEnabled = NO; + self.needsDisplayOnBoundsChange = YES; + + _textContainer.truncationType = ASTextTruncationTypeEnd; + + // The common case is for a text node to be non-opaque and blended over some background. + self.opaque = NO; + self.backgroundColor = [UIColor clearColor]; + + self.linkAttributeNames = DefaultLinkAttributeNames(); + + // Accessibility + self.isAccessibilityElement = YES; + self.accessibilityTraits = self.defaultAccessibilityTraits; + + // Placeholders + // Disabled by default in ASDisplayNode, but add a few options for those who toggle + // on the special placeholder behavior of ASTextNode. + _placeholderColor = ASDisplayNodeDefaultPlaceholderColor(); + _placeholderInsets = UIEdgeInsetsMake(1.0, 0.0, 1.0, 0.0); + } + + return self; +} + +- (void)dealloc +{ + CGColorRelease(_shadowColor); +} + +#pragma mark - Description + +- (NSString *)_plainStringForDescription +{ + NSString *plainString = [[self.attributedText string] stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]]; + if (plainString.length > 50) { + plainString = [[plainString substringToIndex:50] stringByAppendingString:@"…"]; + } + return plainString; +} + +- (NSMutableArray *)propertiesForDescription +{ + NSMutableArray *result = [super propertiesForDescription]; + NSString *plainString = [self _plainStringForDescription]; + if (plainString.length > 0) { + [result insertObject:@{ @"text" : ASStringWithQuotesIfMultiword(plainString) } atIndex:0]; + } + return result; +} + +- (NSMutableArray *)propertiesForDebugDescription +{ + NSMutableArray *result = [super propertiesForDebugDescription]; + NSString *plainString = [self _plainStringForDescription]; + if (plainString.length > 0) { + [result insertObject:@{ @"text" : ASStringWithQuotesIfMultiword(plainString) } atIndex:0]; + } + return result; +} + +#pragma mark - ASDisplayNode + +- (void)didLoad +{ + [super didLoad]; + + // If we are view-backed and the delegate cares, support the long-press callback. + // Locking is not needed, as all instance variables used are main-thread-only. + SEL longPressCallback = @selector(textNode:longPressedLinkAttribute:value:atPoint:textRange:); + if (!self.isLayerBacked && [self.delegate respondsToSelector:longPressCallback]) { + _longPressGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(_handleLongPress:)]; + _longPressGestureRecognizer.cancelsTouchesInView = self.longPressCancelsTouches; + _longPressGestureRecognizer.delegate = self; + [self.view addGestureRecognizer:_longPressGestureRecognizer]; + } +} + +- (BOOL)supportsLayerBacking +{ + if (!super.supportsLayerBacking) { + return NO; + } + + ASLockScopeSelf(); + // If the text contains any links, return NO. + NSAttributedString *attributedText = _attributedText; + NSRange range = NSMakeRange(0, attributedText.length); + for (NSString *linkAttributeName in _linkAttributeNames) { + __block BOOL hasLink = NO; + [attributedText enumerateAttribute:linkAttributeName inRange:range options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired usingBlock:^(id _Nullable value, NSRange range, BOOL * _Nonnull stop) { + if (value == nil) { + return; + } + hasLink = YES; + *stop = YES; + }]; + if (hasLink) { + return NO; + } + } + return YES; +} + +- (NSString *)defaultAccessibilityLabel +{ + ASLockScopeSelf(); + return _attributedText.string; +} + +- (UIAccessibilityTraits)defaultAccessibilityTraits +{ + return UIAccessibilityTraitStaticText; +} + +#pragma mark - Layout and Sizing + +- (void)setTextContainerInset:(UIEdgeInsets)textContainerInset +{ + ASLockScopeSelf(); + if (ASCompareAssignCustom(_textContainer.insets, textContainerInset, UIEdgeInsetsEqualToEdgeInsets)) { + [self setNeedsLayout]; + } +} + +- (UIEdgeInsets)textContainerInset +{ + // textContainer is invariant and has an atomic accessor. + return _textContainer.insets; +} + +- (void)setTextContainerLinePositionModifier:(id)modifier +{ + ASLockedSelfCompareAssignObjects(_textContainer.linePositionModifier, modifier); +} + +- (id)textContainerLinePositionModifier +{ + ASLockScopeSelf(); + return _textContainer.linePositionModifier; +} + +- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize +{ + ASDisplayNodeAssert(constrainedSize.width >= 0, @"Constrained width for text (%f) is too narrow", constrainedSize.width); + ASDisplayNodeAssert(constrainedSize.height >= 0, @"Constrained height for text (%f) is too short", constrainedSize.height); + + ASLockScopeSelf(); + + _textContainer.size = constrainedSize; + [self _ensureTruncationText]; + + // If the constrained size has a max/inf value on the text's forward direction, the text node is calculating its intrinsic size. + // Need to consider both width and height when determining if it is calculating instrinsic size. Even the constrained width is provided, the height can be inf + // it may provide a text that is longer than the width and require a wordWrapping line break mode and looking for the height to be calculated. + BOOL isCalculatingIntrinsicSize = (_textContainer.size.width >= ASTextContainerMaxSize.width) || (_textContainer.size.height >= ASTextContainerMaxSize.height); + + NSMutableAttributedString *mutableText = [_attributedText mutableCopy]; + [self prepareAttributedString:mutableText isForIntrinsicSize:isCalculatingIntrinsicSize]; + ASTextLayout *layout = ASTextNodeCompatibleLayoutWithContainerAndText(_textContainer, mutableText); + if (layout.truncatedLine != nil && layout.truncatedLine.size.width > layout.textBoundingSize.width) { + return (CGSize) {MIN(constrainedSize.width, layout.truncatedLine.size.width), layout.textBoundingSize.height}; + } + + return layout.textBoundingSize; +} + +#pragma mark - Modifying User Text + +// Returns the ascender of the first character in attributedString by also including the line height if specified in paragraph style. ++ (CGFloat)ascenderWithAttributedString:(NSAttributedString *)attributedString +{ + UIFont *font = [attributedString attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL]; + NSParagraphStyle *paragraphStyle = [attributedString attribute:NSParagraphStyleAttributeName atIndex:0 effectiveRange:NULL]; + if (!paragraphStyle) { + return font.ascender; + } + CGFloat lineHeight = MAX(font.lineHeight, paragraphStyle.minimumLineHeight); + if (paragraphStyle.maximumLineHeight > 0) { + lineHeight = MIN(lineHeight, paragraphStyle.maximumLineHeight); + } + return lineHeight + font.descender; +} + +- (NSAttributedString *)attributedText +{ + ASLockScopeSelf(); + return _attributedText; +} + +- (void)setAttributedText:(NSAttributedString *)attributedText +{ + if (attributedText == nil) { + attributedText = [[NSAttributedString alloc] initWithString:@"" attributes:nil]; + } + + // Many accessors in this method will acquire the lock (including ASDisplayNode methods). + // Holding it for the duration of the method is more efficient in this case. + ASLockScopeSelf(); + + NSAttributedString *oldAttributedText = _attributedText; + if (!ASCompareAssignCopy(_attributedText, attributedText)) { + return; + } + + // Since truncation text matches style of attributedText, invalidate it now. + [self _locked_invalidateTruncationText]; + + NSUInteger length = attributedText.length; + if (length > 0) { + ASLayoutElementStyle *style = [self _locked_style]; + style.ascender = [[self class] ascenderWithAttributedString:attributedText]; + style.descender = [[attributedText attribute:NSFontAttributeName atIndex:attributedText.length - 1 effectiveRange:NULL] descender]; + } + + // Tell the display node superclasses that the cached layout is incorrect now + [self setNeedsLayout]; + + // Force display to create renderer with new size and redisplay with new string + [self setNeedsDisplay]; + + // Accessiblity + self.accessibilityLabel = self.defaultAccessibilityLabel; + + // We update the isAccessibilityElement setting if this node is not switching between strings. + if (oldAttributedText.length == 0 || length == 0) { + // We're an accessibility element by default if there is a string. + self.isAccessibilityElement = (length != 0); + } + +#if AS_TEXTNODE2_RECORD_ATTRIBUTED_STRINGS + [ASTextNode _registerAttributedText:_attributedText]; +#endif +} + +#pragma mark - Text Layout + +- (void)setExclusionPaths:(NSArray *)exclusionPaths +{ + ASLockScopeSelf(); + _textContainer.exclusionPaths = exclusionPaths; + + [self setNeedsLayout]; + [self setNeedsDisplay]; +} + +- (NSArray *)exclusionPaths +{ + ASLockScopeSelf(); + return _textContainer.exclusionPaths; +} + +- (void)prepareAttributedString:(NSMutableAttributedString *)attributedString isForIntrinsicSize:(BOOL)isForIntrinsicSize +{ + ASLockScopeSelf(); + NSLineBreakMode innerMode; + switch (_truncationMode) { + case NSLineBreakByWordWrapping: + case NSLineBreakByCharWrapping: + case NSLineBreakByClipping: + innerMode = _truncationMode; + break; + default: + innerMode = NSLineBreakByWordWrapping; + } + + // Apply/Fix paragraph style if needed + [attributedString enumerateAttribute:NSParagraphStyleAttributeName inRange:NSMakeRange(0, attributedString.length) options:kNilOptions usingBlock:^(NSParagraphStyle *style, NSRange range, BOOL * _Nonnull stop) { + + BOOL applyTruncationMode = YES; + NSMutableParagraphStyle *paragraphStyle = nil; + // Only "left" and "justified" alignments are supported while calculating intrinsic size. + // Other alignments like "right", "center" and "natural" cause the size to be bigger than needed and thus should be ignored/overridden. + const BOOL forceLeftAlignment = (style != nil + && isForIntrinsicSize + && style.alignment != NSTextAlignmentLeft + && style.alignment != NSTextAlignmentJustified); + if (style != nil) { + if (innerMode == style.lineBreakMode) { + applyTruncationMode = NO; + } + paragraphStyle = [style mutableCopy]; + } else { + if (innerMode == NSLineBreakByWordWrapping) { + applyTruncationMode = NO; + } + paragraphStyle = [NSMutableParagraphStyle new]; + } + if (!applyTruncationMode && !forceLeftAlignment) { + return; + } + paragraphStyle.lineBreakMode = innerMode; + + if (applyTruncationMode) { + paragraphStyle.lineBreakMode = _truncationMode; + } + if (forceLeftAlignment) { + paragraphStyle.alignment = NSTextAlignmentLeft; + } + [attributedString addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:range]; + }]; + + // Apply shadow if needed + if (_shadowOpacity > 0 && (_shadowRadius != 0 || !CGSizeEqualToSize(_shadowOffset, CGSizeZero)) && CGColorGetAlpha(_shadowColor) > 0) { + NSShadow *shadow = [[NSShadow alloc] init]; + if (_shadowOpacity != 1) { + CGColorRef shadowColorRef = CGColorCreateCopyWithAlpha(_shadowColor, _shadowOpacity * CGColorGetAlpha(_shadowColor)); + shadow.shadowColor = [UIColor colorWithCGColor:shadowColorRef]; + CGColorRelease(shadowColorRef); + } else { + shadow.shadowColor = [UIColor colorWithCGColor:_shadowColor]; + } + shadow.shadowOffset = _shadowOffset; + shadow.shadowBlurRadius = _shadowRadius; + [attributedString addAttribute:NSShadowAttributeName value:shadow range:NSMakeRange(0, attributedString.length)]; + } +} + +#pragma mark - Drawing + +- (NSObject *)drawParametersForAsyncLayer:(_ASDisplayLayer *)layer +{ + ASTextContainer *copiedContainer; + NSMutableAttributedString *mutableText; + BOOL needsTintColor; + id bgColor; + { + // Wrapping all the other access here, because we can't lock while accessing tintColor. + ASLockScopeSelf(); + [self _ensureTruncationText]; + + // Unlike layout, here we must copy the container since drawing is asynchronous. + copiedContainer = [_textContainer copy]; + copiedContainer.size = self.bounds.size; + [copiedContainer makeImmutable]; + mutableText = [_attributedText mutableCopy] ?: [[NSMutableAttributedString alloc] init]; + + [self prepareAttributedString:mutableText isForIntrinsicSize:NO]; + needsTintColor = self.textColorFollowsTintColor && mutableText.length > 0; + bgColor = self.backgroundColor ?: [NSNull null]; + } + + // After all other attributes are set, apply tint color if needed and foreground color is not already specified + if (needsTintColor) { + // Apply tint color if specified and if foreground color is undefined for attributedString + NSRange limit = NSMakeRange(0, mutableText.length); + // Look for previous attributes that define foreground color + UIColor *attributeValue = (UIColor *)[mutableText attribute:NSForegroundColorAttributeName atIndex:limit.location effectiveRange:NULL]; + + // we need to unlock before accessing tintColor + UIColor *tintColor = self.tintColor; + if (attributeValue == nil && tintColor) { + // None are found, apply tint color if available. Fallback to "black" text color + [mutableText addAttributes:@{ NSForegroundColorAttributeName : tintColor } range:limit]; + } + } + + return @{ + @"container": copiedContainer, + @"text": mutableText, + @"bgColor": bgColor + }; +} + ++ (void)drawRect:(CGRect)bounds withParameters:(NSDictionary *)layoutDict isCancelled:(NS_NOESCAPE asdisplaynode_iscancelled_block_t)isCancelledBlock isRasterizing:(BOOL)isRasterizing +{ + ASTextContainer *container = layoutDict[@"container"]; + NSAttributedString *text = layoutDict[@"text"]; + UIColor *bgColor = layoutDict[@"bgColor"]; + ASTextLayout *layout = ASTextNodeCompatibleLayoutWithContainerAndText(container, text); + + if (isCancelledBlock()) { + return; + } + + // Fill background color. + if (bgColor == (id)[NSNull null]) { + bgColor = nil; + } + + // They may have already drawn into this context in the pre-context block + // so unfortunately we have to use the normal blend mode, not copy. + if (bgColor && CGColorGetAlpha(bgColor.CGColor) > 0) { + [bgColor setFill]; + UIRectFillUsingBlendMode(bounds, kCGBlendModeNormal); + } + + CGContextRef context = UIGraphicsGetCurrentContext(); + ASDisplayNodeAssert(context, @"This is no good without a context."); + + [layout drawInContext:context size:bounds.size point:bounds.origin view:nil layer:nil debug:[ASTextDebugOption sharedDebugOption] cancel:isCancelledBlock]; +} + +#pragma mark - Tint Color + +- (void)tintColorDidChange +{ + [super tintColorDidChange]; + + [self _setNeedsDisplayOnTintedTextColor]; +} + +- (void)_setNeedsDisplayOnTintedTextColor +{ + BOOL textColorFollowsTintColor = NO; + { + AS::MutexLocker l(__instanceLock__); + textColorFollowsTintColor = _textColorFollowsTintColor; + } + + if (textColorFollowsTintColor) { + [self setNeedsDisplay]; + } +} + + +#pragma mark Interface State + +- (void)didEnterHierarchy +{ + [super didEnterHierarchy]; + + [self _setNeedsDisplayOnTintedTextColor]; +} + +#pragma mark - Attributes + +- (id)linkAttributeValueAtPoint:(CGPoint)point + attributeName:(out NSString **)attributeNameOut + range:(out NSRange *)rangeOut +{ + return [self _linkAttributeValueAtPoint:point + attributeName:attributeNameOut + range:rangeOut + inAdditionalTruncationMessage:NULL + forHighlighting:NO]; +} + +- (id)_linkAttributeValueAtPoint:(CGPoint)point + attributeName:(out NSString **)attributeNameOut + range:(out NSRange *)rangeOut + inAdditionalTruncationMessage:(out BOOL *)inAdditionalTruncationMessageOut + forHighlighting:(BOOL)highlighting +{ + ASLockScopeSelf(); + + // TODO: The copy and application of size shouldn't be required, but it is currently. + // See discussion in https://github.com/TextureGroup/Texture/pull/396 + ASTextContainer *containerCopy = [_textContainer copy]; + containerCopy.size = self.calculatedSize; + ASTextLayout *layout = ASTextNodeCompatibleLayoutWithContainerAndText(containerCopy, _attributedText); + + if ([self _locked_pointInsideAdditionalTruncationMessage:point withLayout:layout]) { + if (inAdditionalTruncationMessageOut != NULL) { + *inAdditionalTruncationMessageOut = YES; + } + return nil; + } + + NSRange visibleRange = layout.visibleRange; + NSRange clampedRange = NSIntersectionRange(visibleRange, NSMakeRange(0, _attributedText.length)); + + // Search the 9 points of a 44x44 square around the touch until we find a link. + // Start from center, then do sides, then do top/bottom, then do corners. + static constexpr CGSize kRectOffsets[9] = { + { 0, 0 }, + { -22, 0 }, { 22, 0 }, + { 0, -22 }, { 0, 22 }, + { -22, -22 }, { -22, 22 }, + { 22, -22 }, { 22, 22 } + }; + + for (const CGSize &offset : kRectOffsets) { + const CGPoint testPoint = CGPointMake(point.x + offset.width, + point.y + offset.height); + ASTextPosition *pos = [layout closestPositionToPoint:testPoint]; + if (!pos || !NSLocationInRange(pos.offset, clampedRange)) { + continue; + } + for (NSString *attributeName in _linkAttributeNames) { + NSRange effectiveRange = NSMakeRange(0, 0); + id value = [_attributedText attribute:attributeName atIndex:pos.offset + longestEffectiveRange:&effectiveRange inRange:clampedRange]; + if (value == nil) { + // Didn't find any links specified with this attribute. + continue; + } + + // If highlighting, check with delegate first. If not implemented, assume YES. + if (highlighting + && [_delegate respondsToSelector:@selector(textNode:shouldHighlightLinkAttribute:value:atPoint:)] + && ![_delegate textNode:(ASTextNode *)self shouldHighlightLinkAttribute:attributeName + value:value atPoint:point]) { + continue; + } + + *rangeOut = NSIntersectionRange(visibleRange, effectiveRange); + + if (attributeNameOut != NULL) { + *attributeNameOut = attributeName; + } + + return value; + } + } + + return nil; +} + +- (BOOL)_locked_pointInsideAdditionalTruncationMessage:(CGPoint)point withLayout:(ASTextLayout *)layout +{ + // Check if the range is within the additional truncation range + BOOL inAdditionalTruncationMessage = NO; + + CTLineRef truncatedCTLine = layout.truncatedLine.CTLine; + if (truncatedCTLine != NULL && _additionalTruncationMessage != nil) { + CFIndex stringIndexForPosition = CTLineGetStringIndexForPosition(truncatedCTLine, point); + if (stringIndexForPosition != kCFNotFound) { + CFIndex truncatedCTLineGlyphCount = CTLineGetGlyphCount(truncatedCTLine); + + CTLineRef truncationTokenLine = CTLineCreateWithAttributedString((CFAttributedStringRef)_truncationAttributedText); + CFIndex truncationTokenLineGlyphCount = truncationTokenLine ? CTLineGetGlyphCount(truncationTokenLine) : 0; + + if (truncationTokenLine) { + CFRelease(truncationTokenLine); + } + + CTLineRef additionalTruncationTokenLine = CTLineCreateWithAttributedString((CFAttributedStringRef)_additionalTruncationMessage); + CFIndex additionalTruncationTokenLineGlyphCount = additionalTruncationTokenLine ? CTLineGetGlyphCount(additionalTruncationTokenLine) : 0; + + if (additionalTruncationTokenLine) { + CFRelease(additionalTruncationTokenLine); + } + + switch (_textContainer.truncationType) { + case ASTextTruncationTypeStart: { + CFIndex composedTruncationTextLineGlyphCount = truncationTokenLineGlyphCount + additionalTruncationTokenLineGlyphCount; + if (stringIndexForPosition > truncationTokenLineGlyphCount && + stringIndexForPosition < composedTruncationTextLineGlyphCount) { + inAdditionalTruncationMessage = YES; + } + break; + } + case ASTextTruncationTypeMiddle: { + CFIndex composedTruncationTextLineGlyphCount = truncationTokenLineGlyphCount + additionalTruncationTokenLineGlyphCount; + CFIndex firstTruncatedTokenIndex = (truncatedCTLineGlyphCount - composedTruncationTextLineGlyphCount) / 2.0; + if ((firstTruncatedTokenIndex + truncationTokenLineGlyphCount) < stringIndexForPosition && + stringIndexForPosition < (firstTruncatedTokenIndex + composedTruncationTextLineGlyphCount)) { + inAdditionalTruncationMessage = YES; + } + break; + } + case ASTextTruncationTypeEnd: { + if (stringIndexForPosition > (truncatedCTLineGlyphCount - additionalTruncationTokenLineGlyphCount)) { + inAdditionalTruncationMessage = YES; + } + break; + } + default: + // For now, assume that a tap inside this text, but outside the text range is a tap on the + // truncation token. + if (![layout textRangeAtPoint:point]) { + inAdditionalTruncationMessage = YES; + } + break; + } + } + } + + return inAdditionalTruncationMessage; +} + +#pragma mark - UIGestureRecognizerDelegate + +- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer +{ + ASDisplayNodeAssertMainThread(); + ASLockScopeSelf(); // Protect usage of _highlight* ivars. + + if (gestureRecognizer == _longPressGestureRecognizer) { + // Don't allow long press on truncation message + if ([self _pendingTruncationTap]) { + return NO; + } + + // Ask our delegate if a long-press on an attribute is relevant + id delegate = self.delegate; + if ([delegate respondsToSelector:@selector(textNode:shouldLongPressLinkAttribute:value:atPoint:)]) { + return [delegate textNode:(ASTextNode *)self + shouldLongPressLinkAttribute:_highlightedLinkAttributeName + value:_highlightedLinkAttributeValue + atPoint:[gestureRecognizer locationInView:self.view]]; + } + + // Otherwise we are good to go. + return YES; + } + + if (([self _pendingLinkTap] || [self _pendingTruncationTap]) + && [gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]] + && CGRectContainsPoint(self.threadSafeBounds, [gestureRecognizer locationInView:self.view])) { + return NO; + } + + return [super gestureRecognizerShouldBegin:gestureRecognizer]; +} + +#pragma mark - Highlighting + +- (ASTextNodeHighlightStyle)highlightStyle +{ + ASLockScopeSelf(); + + return _highlightStyle; +} + +- (void)setHighlightStyle:(ASTextNodeHighlightStyle)highlightStyle +{ + ASLockScopeSelf(); + + _highlightStyle = highlightStyle; +} + +- (NSRange)highlightRange +{ + ASLockScopeSelf(); + + return _highlightRange; +} + +- (void)setHighlightRange:(NSRange)highlightRange +{ + [self setHighlightRange:highlightRange animated:NO]; +} + +- (void)setHighlightRange:(NSRange)highlightRange animated:(BOOL)animated +{ + [self _setHighlightRange:highlightRange forAttributeName:nil value:nil animated:animated]; +} + +- (void)_setHighlightRange:(NSRange)highlightRange forAttributeName:(NSString *)highlightedAttributeName value:(id)highlightedAttributeValue animated:(BOOL)animated +{ + ASDisplayNodeAssertMainThread(); + ASLockScopeSelf(); // Protect usage of _highlight* ivars. + + // Set these so that link tapping works. + _highlightedLinkAttributeName = highlightedAttributeName; + _highlightedLinkAttributeValue = highlightedAttributeValue; + + if (!NSEqualRanges(highlightRange, _highlightRange) && ((0 != highlightRange.length) || (0 != _highlightRange.length))) { + + _highlightRange = highlightRange; + + if (_activeHighlightLayer) { + if (animated) { + __weak CALayer *weakHighlightLayer = _activeHighlightLayer; + _activeHighlightLayer = nil; + + weakHighlightLayer.opacity = 0.0; + + CFTimeInterval beginTime = CACurrentMediaTime(); + CABasicAnimation *possibleFadeIn = (CABasicAnimation *)[weakHighlightLayer animationForKey:@"opacity"]; + if (possibleFadeIn) { + // Calculate when we should begin fading out based on the end of the fade in animation, + // Also check to make sure that the new begin time hasn't already passed + CGFloat newBeginTime = (possibleFadeIn.beginTime + possibleFadeIn.duration); + if (newBeginTime > beginTime) { + beginTime = newBeginTime; + } + } + + CABasicAnimation *fadeOut = [CABasicAnimation animationWithKeyPath:@"opacity"]; + fadeOut.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; + fadeOut.fromValue = possibleFadeIn.toValue ? : @(((CALayer *)weakHighlightLayer.presentationLayer).opacity); + fadeOut.toValue = @0.0; + fadeOut.fillMode = kCAFillModeBoth; + fadeOut.duration = ASTextNodeHighlightFadeOutDuration; + fadeOut.beginTime = beginTime; + + dispatch_block_t prev = [CATransaction completionBlock]; + [CATransaction setCompletionBlock:^{ + [weakHighlightLayer removeFromSuperlayer]; + }]; + + [weakHighlightLayer addAnimation:fadeOut forKey:fadeOut.keyPath]; + + [CATransaction setCompletionBlock:prev]; + + } else { + [_activeHighlightLayer removeFromSuperlayer]; + _activeHighlightLayer = nil; + } + } + if (0 != highlightRange.length) { + // Find layer in hierarchy that allows us to draw highlighting on. + CALayer *highlightTargetLayer = self.layer; + while (highlightTargetLayer != nil) { + if (highlightTargetLayer.as_allowsHighlightDrawing) { + break; + } + highlightTargetLayer = highlightTargetLayer.superlayer; + } + + if (highlightTargetLayer != nil) { + // TODO: The copy and application of size shouldn't be required, but it is currently. + // See discussion in https://github.com/TextureGroup/Texture/pull/396 + ASTextContainer *textContainerCopy = [_textContainer copy]; + textContainerCopy.size = self.calculatedSize; + ASTextLayout *layout = ASTextNodeCompatibleLayoutWithContainerAndText(textContainerCopy, _attributedText); + + NSArray *highlightRects = [layout selectionRectsWithoutStartAndEndForRange:[ASTextRange rangeWithRange:highlightRange]]; + NSMutableArray *converted = [NSMutableArray arrayWithCapacity:highlightRects.count]; + + CALayer *layer = self.layer; + UIEdgeInsets shadowPadding = self.shadowPadding; + for (ASTextSelectionRect *rectValue in highlightRects) { + // Adjust shadow padding + CGRect rendererRect = ASTextNodeAdjustRenderRectForShadowPadding(rectValue.rect, shadowPadding); + CGRect highlightedRect = [layer convertRect:rendererRect toLayer:highlightTargetLayer]; + + // We set our overlay layer's frame to the bounds of the highlight target layer. + // Offset highlight rects to avoid double-counting target layer's bounds.origin. + highlightedRect.origin.x -= highlightTargetLayer.bounds.origin.x; + highlightedRect.origin.y -= highlightTargetLayer.bounds.origin.y; + [converted addObject:[NSValue valueWithCGRect:highlightedRect]]; + } + + ASHighlightOverlayLayer *overlayLayer = [[ASHighlightOverlayLayer alloc] initWithRects:converted]; + overlayLayer.highlightColor = [[self class] _highlightColorForStyle:self.highlightStyle]; + overlayLayer.frame = highlightTargetLayer.bounds; + overlayLayer.masksToBounds = NO; + overlayLayer.opacity = [[self class] _highlightOpacityForStyle:self.highlightStyle]; + [highlightTargetLayer addSublayer:overlayLayer]; + + if (animated) { + CABasicAnimation *fadeIn = [CABasicAnimation animationWithKeyPath:@"opacity"]; + fadeIn.fromValue = @0.0; + fadeIn.toValue = @(overlayLayer.opacity); + fadeIn.duration = ASTextNodeHighlightFadeInDuration; + fadeIn.beginTime = CACurrentMediaTime(); + + [overlayLayer addAnimation:fadeIn forKey:fadeIn.keyPath]; + } + + [overlayLayer setNeedsDisplay]; + + _activeHighlightLayer = overlayLayer; + } + } + } +} + +- (void)_clearHighlightIfNecessary +{ + ASDisplayNodeAssertMainThread(); + + if ([self _pendingLinkTap] || [self _pendingTruncationTap]) { + [self setHighlightRange:NSMakeRange(0, 0) animated:YES]; + } +} + ++ (CGColorRef)_highlightColorForStyle:(ASTextNodeHighlightStyle)style +{ + return [UIColor colorWithWhite:(style == ASTextNodeHighlightStyleLight ? 0.0 : 1.0) alpha:1.0].CGColor; +} + ++ (CGFloat)_highlightOpacityForStyle:(ASTextNodeHighlightStyle)style +{ + return (style == ASTextNodeHighlightStyleLight) ? ASTextNodeHighlightLightOpacity : ASTextNodeHighlightDarkOpacity; +} + +#pragma mark - Text rects + +static CGRect ASTextNodeAdjustRenderRectForShadowPadding(CGRect rendererRect, UIEdgeInsets shadowPadding) { + rendererRect.origin.x -= shadowPadding.left; + rendererRect.origin.y -= shadowPadding.top; + return rendererRect; +} + +- (NSArray *)rectsForTextRange:(NSRange)textRange +{ + AS_TEXT_ALERT_UNIMPLEMENTED_FEATURE(); + return @[]; +} + +- (NSArray *)highlightRectsForTextRange:(NSRange)textRange +{ + AS_TEXT_ALERT_UNIMPLEMENTED_FEATURE(); + return @[]; +} + +- (CGRect)trailingRect +{ + AS_TEXT_ALERT_UNIMPLEMENTED_FEATURE(); + return CGRectZero; +} + +- (CGRect)frameForTextRange:(NSRange)textRange +{ + AS_TEXT_ALERT_UNIMPLEMENTED_FEATURE(); + return CGRectZero; +} + +#pragma mark - Placeholders + +- (UIColor *)placeholderColor +{ + return ASLockedSelf(_placeholderColor); +} + +- (void)setPlaceholderColor:(UIColor *)placeholderColor +{ + ASLockScopeSelf(); + if (ASCompareAssignCopy(_placeholderColor, placeholderColor)) { + self.placeholderEnabled = CGColorGetAlpha(placeholderColor.CGColor) > 0; + } +} + +- (UIImage *)placeholderImage +{ + AS_TEXT_ALERT_UNIMPLEMENTED_FEATURE(); + return nil; +} + +#pragma mark - Touch Handling + +- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event +{ + ASDisplayNodeAssertMainThread(); + ASLockScopeSelf(); // Protect usage of _passthroughNonlinkTouches and _alwaysHandleTruncationTokenTap ivars. + + if (!_passthroughNonlinkTouches) { + return [super pointInside:point withEvent:event]; + } + + if (_alwaysHandleTruncationTokenTap) { + return YES; + } + + NSRange range = NSMakeRange(0, 0); + NSString *linkAttributeName = nil; + BOOL inAdditionalTruncationMessage = NO; + + id linkAttributeValue = [self _linkAttributeValueAtPoint:point + attributeName:&linkAttributeName + range:&range + inAdditionalTruncationMessage:&inAdditionalTruncationMessage + forHighlighting:YES]; + + NSUInteger lastCharIndex = NSIntegerMax; + BOOL linkCrossesVisibleRange = (lastCharIndex > range.location) && (lastCharIndex < NSMaxRange(range) - 1); + + if (range.length > 0 && !linkCrossesVisibleRange && linkAttributeValue != nil && linkAttributeName != nil) { + return YES; + } else { + return NO; + } +} + +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event +{ + ASDisplayNodeAssertMainThread(); + + [super touchesBegan:touches withEvent:event]; + + CGPoint point = [[touches anyObject] locationInView:self.view]; + + NSRange range = NSMakeRange(0, 0); + NSString *linkAttributeName = nil; + BOOL inAdditionalTruncationMessage = NO; + + id linkAttributeValue = [self _linkAttributeValueAtPoint:point + attributeName:&linkAttributeName + range:&range + inAdditionalTruncationMessage:&inAdditionalTruncationMessage + forHighlighting:YES]; + + NSUInteger lastCharIndex = NSIntegerMax; + BOOL linkCrossesVisibleRange = (lastCharIndex > range.location) && (lastCharIndex < NSMaxRange(range) - 1); + + if (inAdditionalTruncationMessage) { + NSRange visibleRange = NSMakeRange(0, 0); + { + ASLockScopeSelf(); + // TODO: The copy and application of size shouldn't be required, but it is currently. + // See discussion in https://github.com/TextureGroup/Texture/pull/396 + ASTextContainer *containerCopy = [_textContainer copy]; + containerCopy.size = self.calculatedSize; + ASTextLayout *layout = ASTextNodeCompatibleLayoutWithContainerAndText(containerCopy, _attributedText); + visibleRange = layout.visibleRange; + } + NSRange truncationMessageRange = [self _additionalTruncationMessageRangeWithVisibleRange:visibleRange]; + [self _setHighlightRange:truncationMessageRange forAttributeName:ASTextNodeTruncationTokenAttributeName value:nil animated:YES]; + } else if (range.length > 0 && !linkCrossesVisibleRange && linkAttributeValue != nil && linkAttributeName != nil) { + [self _setHighlightRange:range forAttributeName:linkAttributeName value:linkAttributeValue animated:YES]; + } + + return; +} + + +- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event +{ + ASDisplayNodeAssertMainThread(); + [super touchesCancelled:touches withEvent:event]; + + [self _clearHighlightIfNecessary]; +} + +- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event +{ + ASDisplayNodeAssertMainThread(); + [super touchesEnded:touches withEvent:event]; + + ASLockScopeSelf(); // Protect usage of _highlight* ivars. + id delegate = self.delegate; + if ([self _pendingLinkTap] && [delegate respondsToSelector:@selector(textNode:tappedLinkAttribute:value:atPoint:textRange:)]) { + CGPoint point = [[touches anyObject] locationInView:self.view]; + [delegate textNode:(ASTextNode *)self tappedLinkAttribute:_highlightedLinkAttributeName value:_highlightedLinkAttributeValue atPoint:point textRange:_highlightRange]; + } + + if ([self _pendingTruncationTap]) { + if ([delegate respondsToSelector:@selector(textNodeTappedTruncationToken:)]) { + [delegate textNodeTappedTruncationToken:(ASTextNode *)self]; + } + } + + [self _clearHighlightIfNecessary]; +} + +- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event +{ + ASDisplayNodeAssertMainThread(); + [super touchesMoved:touches withEvent:event]; + + ASLockScopeSelf(); // Protect usage of _highlight* ivars. + UITouch *touch = [touches anyObject]; + CGPoint locationInView = [touch locationInView:self.view]; + // on 3D Touch enabled phones, this gets fired with changes in force, and usually will get fired immediately after touchesBegan:withEvent: + if (CGPointEqualToPoint([touch previousLocationInView:self.view], locationInView)) + return; + + // If touch has moved out of the current highlight range, clear the highlight. + if (_highlightRange.length > 0) { + NSRange range = NSMakeRange(0, 0); + [self _linkAttributeValueAtPoint:locationInView + attributeName:NULL + range:&range + inAdditionalTruncationMessage:NULL + forHighlighting:YES]; + + if (!NSEqualRanges(_highlightRange, range)) { + [self _clearHighlightIfNecessary]; + } + } +} + +- (void)_handleLongPress:(UILongPressGestureRecognizer *)longPressRecognizer +{ + ASDisplayNodeAssertMainThread(); + + // Respond to long-press when it begins, not when it ends. + if (longPressRecognizer.state == UIGestureRecognizerStateBegan) { + id delegate = self.delegate; + if ([delegate respondsToSelector:@selector(textNode:longPressedLinkAttribute:value:atPoint:textRange:)]) { + ASLockScopeSelf(); // Protect usage of _highlight* ivars. + CGPoint touchPoint = [_longPressGestureRecognizer locationInView:self.view]; + [delegate textNode:(ASTextNode *)self longPressedLinkAttribute:_highlightedLinkAttributeName value:_highlightedLinkAttributeValue atPoint:touchPoint textRange:_highlightRange]; + } + } +} + +- (BOOL)_pendingLinkTap +{ + ASLockScopeSelf(); + + return (_highlightedLinkAttributeValue != nil && ![self _pendingTruncationTap]) && self.delegate != nil; +} + +- (BOOL)_pendingTruncationTap +{ + return [ASLockedSelf(_highlightedLinkAttributeName) isEqualToString:ASTextNodeTruncationTokenAttributeName]; +} + +- (BOOL)alwaysHandleTruncationTokenTap +{ + ASLockScopeSelf(); + return _alwaysHandleTruncationTokenTap; +} + +- (void)setAlwaysHandleTruncationTokenTap:(BOOL)alwaysHandleTruncationTokenTap +{ + ASLockScopeSelf(); + _alwaysHandleTruncationTokenTap = alwaysHandleTruncationTokenTap; +} + +#pragma mark - Shadow Properties + +/** + * Note about shadowed text: + * + * Shadowed text is pretty rare, and we are a framework that targets serious developers. + * We should probably ignore these properties and tell developers to set the shadow into their attributed text instead. + */ +- (CGColorRef)shadowColor +{ + return ASLockedSelf(_shadowColor); +} + +- (void)setShadowColor:(CGColorRef)shadowColor +{ + ASLockScopeSelf(); + if (_shadowColor != shadowColor && CGColorEqualToColor(shadowColor, _shadowColor) == NO) { + CGColorRelease(_shadowColor); + _shadowColor = CGColorRetain(shadowColor); + [self setNeedsDisplay]; + } +} + +- (CGSize)shadowOffset +{ + return ASLockedSelf(_shadowOffset); +} + +- (void)setShadowOffset:(CGSize)shadowOffset +{ + ASLockScopeSelf(); + if (ASCompareAssignCustom(_shadowOffset, shadowOffset, CGSizeEqualToSize)) { + [self setNeedsDisplay]; + } +} + +- (CGFloat)shadowOpacity +{ + return ASLockedSelf(_shadowOpacity); +} + +- (void)setShadowOpacity:(CGFloat)shadowOpacity +{ + ASLockScopeSelf(); + if (ASCompareAssign(_shadowOpacity, shadowOpacity)) { + [self setNeedsDisplay]; + } +} + +- (CGFloat)shadowRadius +{ + return ASLockedSelf(_shadowRadius); +} + +- (void)setShadowRadius:(CGFloat)shadowRadius +{ + ASLockScopeSelf(); + if (ASCompareAssign(_shadowRadius, shadowRadius)) { + [self setNeedsDisplay]; + } +} + +- (UIEdgeInsets)shadowPadding +{ + AS_TEXT_ALERT_UNIMPLEMENTED_FEATURE(); + return UIEdgeInsetsZero; +} + +- (void)setPointSizeScaleFactors:(NSArray *)scaleFactors +{ + AS_TEXT_ALERT_UNIMPLEMENTED_FEATURE(); + ASLockScopeSelf(); + if (ASCompareAssignCopy(_pointSizeScaleFactors, scaleFactors)) { + [self setNeedsLayout]; + } +} + +- (NSArray *)pointSizeScaleFactors +{ + return ASLockedSelf(_pointSizeScaleFactors); +} + +#pragma mark - Truncation Message + +static NSAttributedString *DefaultTruncationAttributedString() +{ + static NSAttributedString *defaultTruncationAttributedString; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + defaultTruncationAttributedString = [[NSAttributedString alloc] initWithString:NSLocalizedString(@"\u2026", @"Default truncation string")]; + }); + return defaultTruncationAttributedString; +} + +- (void)_ensureTruncationText +{ + ASLockScopeSelf(); + if (_textContainer.truncationToken == nil) { + _textContainer.truncationToken = [self _locked_composedTruncationText]; + } +} + +- (NSAttributedString *)truncationAttributedText +{ + return ASLockedSelf(_truncationAttributedText); +} + +- (void)setTruncationAttributedText:(NSAttributedString *)truncationAttributedText +{ + ASLockScopeSelf(); + if (ASCompareAssignCopy(_truncationAttributedText, truncationAttributedText)) { + [self _invalidateTruncationText]; + } +} + +- (NSAttributedString *)additionalTruncationMessage +{ + return ASLockedSelf(_additionalTruncationMessage); +} + +- (void)setAdditionalTruncationMessage:(NSAttributedString *)additionalTruncationMessage +{ + ASLockScopeSelf(); + if (ASCompareAssignCopy(_additionalTruncationMessage, additionalTruncationMessage)) { + [self _invalidateTruncationText]; + } +} + +- (NSLineBreakMode)truncationMode +{ + return ASLockedSelf(_truncationMode); +} + +- (void)setTruncationMode:(NSLineBreakMode)truncationMode +{ + ASLockScopeSelf(); + if (ASCompareAssign(_truncationMode, truncationMode)) { + ASTextTruncationType truncationType; + switch (truncationMode) { + case NSLineBreakByTruncatingHead: + truncationType = ASTextTruncationTypeStart; + break; + case NSLineBreakByTruncatingTail: + truncationType = ASTextTruncationTypeEnd; + break; + case NSLineBreakByTruncatingMiddle: + truncationType = ASTextTruncationTypeMiddle; + break; + default: + truncationType = ASTextTruncationTypeNone; + } + + _textContainer.truncationType = truncationType; + + [self setNeedsDisplay]; + } +} + +- (BOOL)isTruncated +{ + return ASLockedSelf([self locked_textLayoutForSize:[self _locked_threadSafeBounds].size].truncatedLine != nil); +} + +- (BOOL)shouldTruncateForConstrainedSize:(ASSizeRange)constrainedSize +{ + return ASLockedSelf([self locked_textLayoutForSize:constrainedSize.max].truncatedLine != nil); +} + +- (ASTextLayout *)locked_textLayoutForSize:(CGSize)size +{ + ASTextContainer *container = [_textContainer copy]; + container.size = size; + return ASTextNodeCompatibleLayoutWithContainerAndText(container, _attributedText); +} + +- (NSUInteger)maximumNumberOfLines +{ + // _textContainer is invariant and this is just atomic access. + return _textContainer.maximumNumberOfRows; +} + +- (void)setMaximumNumberOfLines:(NSUInteger)maximumNumberOfLines +{ + ASLockScopeSelf(); + if (ASCompareAssign(_textContainer.maximumNumberOfRows, maximumNumberOfLines)) { + [self setNeedsDisplay]; + } +} + +- (NSUInteger)lineCount +{ + ASLockScopeSelf(); + AS_TEXT_ALERT_UNIMPLEMENTED_FEATURE(); + return 0; +} + +#pragma mark - Truncation Message + +- (void)_invalidateTruncationText +{ + ASLockScopeSelf(); + [self _locked_invalidateTruncationText]; + [self setNeedsDisplay]; +} + +- (void)_locked_invalidateTruncationText +{ + _textContainer.truncationToken = nil; +} + +/** + * @return the additional truncation message range within the as-rendered text. + * Must be called from main thread + */ +- (NSRange)_additionalTruncationMessageRangeWithVisibleRange:(NSRange)visibleRange +{ + ASLockScopeSelf(); + + // Check if we even have an additional truncation message. + if (!_additionalTruncationMessage) { + return NSMakeRange(NSNotFound, 0); + } + + // Character location of the unicode ellipsis (the first index after the visible range) + NSInteger truncationTokenIndex = NSMaxRange(visibleRange); + + NSUInteger additionalTruncationMessageLength = _additionalTruncationMessage.length; + // We get the location of the truncation token, then add the length of the + // truncation attributed string +1 for the space between. + return NSMakeRange(truncationTokenIndex + _truncationAttributedText.length + 1, additionalTruncationMessageLength); +} + +/** + * @return the truncation message for the string. If there are both an + * additional truncation message and a truncation attributed string, they will + * be properly composed. + */ +- (NSAttributedString *)_locked_composedTruncationText +{ + DISABLED_ASAssertLocked(__instanceLock__); + NSAttributedString *composedTruncationText = nil; + if (_truncationAttributedText != nil && _additionalTruncationMessage != nil) { + NSMutableAttributedString *newComposedTruncationString = [[NSMutableAttributedString alloc] initWithAttributedString:_truncationAttributedText]; + [newComposedTruncationString.mutableString appendString:@" "]; + [newComposedTruncationString appendAttributedString:_additionalTruncationMessage]; + composedTruncationText = newComposedTruncationString; + } else if (_truncationAttributedText != nil) { + composedTruncationText = _truncationAttributedText; + } else if (_additionalTruncationMessage != nil) { + composedTruncationText = _additionalTruncationMessage; + } else { + composedTruncationText = DefaultTruncationAttributedString(); + } + return [self _locked_prepareTruncationStringForDrawing:composedTruncationText]; +} + +/** + * - cleanses it of core text attributes so TextKit doesn't crash + * - Adds whole-string attributes so the truncation message matches the styling + * of the body text + */ +- (NSAttributedString *)_locked_prepareTruncationStringForDrawing:(NSAttributedString *)truncationString +{ + DISABLED_ASAssertLocked(__instanceLock__); + NSMutableAttributedString *truncationMutableString = [truncationString mutableCopy]; + // Grab the attributes from the full string + if (_attributedText.length > 0) { + NSAttributedString *originalString = _attributedText; + NSInteger originalStringLength = _attributedText.length; + // Add any of the original string's attributes to the truncation string, + // but don't overwrite any of the truncation string's attributes + NSDictionary *originalStringAttributes = [originalString attributesAtIndex:originalStringLength-1 effectiveRange:NULL]; + [truncationString enumerateAttributesInRange:NSMakeRange(0, truncationString.length) options:0 usingBlock: + ^(NSDictionary *attributes, NSRange range, BOOL *stop) { + NSMutableDictionary *futureTruncationAttributes = [originalStringAttributes mutableCopy]; + [futureTruncationAttributes addEntriesFromDictionary:attributes]; + [truncationMutableString setAttributes:futureTruncationAttributes range:range]; + }]; + } + return truncationMutableString; +} + +#if AS_TEXTNODE2_RECORD_ATTRIBUTED_STRINGS ++ (void)_registerAttributedText:(NSAttributedString *)str +{ + static NSMutableArray *array; + static NSLock *lock; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + lock = [NSLock new]; + array = [NSMutableArray new]; + }); + [lock lock]; + [array addObject:str]; + if (array.count % 20 == 0) { + NSLog(@"Got %d strings", (int)array.count); + } + if (array.count == 2000) { + NSString *path = [NSTemporaryDirectory() stringByAppendingPathComponent:@"AttributedStrings.plist"]; + NSAssert([NSKeyedArchiver archiveRootObject:array toFile:path], nil); + NSLog(@"Saved to %@", path); + } + [lock unlock]; +} +#endif + ++ (void)enableDebugging +{ + ASTextDebugOption *debugOption = [[ASTextDebugOption alloc] init]; + debugOption.CTLineFillColor = [UIColor colorWithRed:0 green:0.3 blue:1 alpha:0.1]; + [ASTextDebugOption setSharedDebugOption:debugOption]; +} + +- (BOOL)usingExperiment +{ + return YES; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASTextNodeCommon.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASTextNodeCommon.h new file mode 100644 index 0000000..beee262 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASTextNodeCommon.h @@ -0,0 +1,91 @@ +// +// ASTextNodeCommon.h +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import + +@class ASTextNode; + +#define AS_TEXT_ALERT_UNIMPLEMENTED_FEATURE() { \ + static dispatch_once_t onceToken; \ + dispatch_once(&onceToken, ^{ \ + NSLog(@"[Texture] Warning: Feature %@ is unimplemented in %@.", NSStringFromSelector(_cmd), NSStringFromClass(self.class)); \ + });\ +} + +/** + * Highlight styles. + */ +typedef NS_ENUM(unsigned char, ASTextNodeHighlightStyle) { + /** + * Highlight style for text on a light background. + */ + ASTextNodeHighlightStyleLight, + + /** + * Highlight style for text on a dark background. + */ + ASTextNodeHighlightStyleDark +}; + +/** + * @abstract Text node delegate. + */ +@protocol ASTextNodeDelegate +@optional + +/** + @abstract Indicates to the delegate that a link was tapped within a text node. + @param textNode The ASTextNode containing the link that was tapped. + @param attribute The attribute that was tapped. Will not be nil. + @param value The value of the tapped attribute. + @param point The point within textNode, in textNode's coordinate system, that was tapped. + @param textRange The range of highlighted text. + */ +- (void)textNode:(ASTextNode *)textNode tappedLinkAttribute:(NSString *)attribute value:(id)value atPoint:(CGPoint)point textRange:(NSRange)textRange; + +/** + @abstract Indicates to the delegate that a link was tapped within a text node. + @param textNode The ASTextNode containing the link that was tapped. + @param attribute The attribute that was tapped. Will not be nil. + @param value The value of the tapped attribute. + @param point The point within textNode, in textNode's coordinate system, that was tapped. + @param textRange The range of highlighted text. + @discussion In addition to implementing this method, the delegate must be set on the text + node before it is loaded (the recognizer is created in -didLoad) + */ +- (void)textNode:(ASTextNode *)textNode longPressedLinkAttribute:(NSString *)attribute value:(id)value atPoint:(CGPoint)point textRange:(NSRange)textRange; + +//! @abstract Called when the text node's truncation string has been tapped. +- (void)textNodeTappedTruncationToken:(ASTextNode *)textNode; + +/** + @abstract Indicates to the text node if an attribute should be considered a link. + @param textNode The text node containing the entity attribute. + @param attribute The attribute that was tapped. Will not be nil. + @param value The value of the tapped attribute. + @param point The point within textNode, in textNode's coordinate system, that was touched to trigger a highlight. + @discussion If not implemented, the default value is YES. + @return YES if the entity attribute should be a link, NO otherwise. + */ +- (BOOL)textNode:(ASTextNode *)textNode shouldHighlightLinkAttribute:(NSString *)attribute value:(id)value atPoint:(CGPoint)point; + +/** + @abstract Indicates to the text node if an attribute is a valid long-press target + @param textNode The text node containing the entity attribute. + @param attribute The attribute that was tapped. Will not be nil. + @param value The value of the tapped attribute. + @param point The point within textNode, in textNode's coordinate system, that was long-pressed. + @discussion If not implemented, the default value is NO. + @return YES if the entity attribute should be treated as a long-press target, NO otherwise. + */ +- (BOOL)textNode:(ASTextNode *)textNode shouldLongPressLinkAttribute:(NSString *)attribute value:(id)value atPoint:(CGPoint)point; + +@end + diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASVideoNode.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASVideoNode.h new file mode 100644 index 0000000..440c71c --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASVideoNode.h @@ -0,0 +1,171 @@ +// +// ASVideoNode.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +#if AS_USE_VIDEO + +@class AVAsset, AVPlayer, AVPlayerLayer, AVPlayerItem, AVVideoComposition, AVAudioMix; +@protocol ASVideoNodeDelegate; + +typedef NS_ENUM(NSInteger, ASVideoNodePlayerState) { + ASVideoNodePlayerStateUnknown, + ASVideoNodePlayerStateInitialLoading, + ASVideoNodePlayerStateReadyToPlay, + ASVideoNodePlayerStatePlaybackLikelyToKeepUpButNotPlaying, + ASVideoNodePlayerStatePlaying, + ASVideoNodePlayerStateLoading, + ASVideoNodePlayerStatePaused, + ASVideoNodePlayerStateFinished +}; + +NS_ASSUME_NONNULL_BEGIN + +// IMPORTANT NOTES: +// 1. Applications using ASVideoNode must link AVFoundation! (this provides the AV* classes below) +// 2. This is a relatively new component of AsyncDisplayKit. It has many useful features, but +// there is room for further expansion and optimization. Please report any issues or requests +// in an issue on GitHub: https://github.com/facebook/AsyncDisplayKit/issues + +@interface ASVideoNode : ASNetworkImageNode + +- (void)play; +- (void)pause; +- (BOOL)isPlaying; +- (void)resetToPlaceholder; + +// TODO: copy +@property (nullable) AVAsset *asset; + +/** + ** @abstract The URL with which the asset was initialized. + ** @discussion Setting the URL will override the current asset with a newly created AVURLAsset created from the given URL, and AVAsset *asset will point to that newly created AVURLAsset. Please don't set both assetURL and asset. + ** @return Current URL the asset was initialized or nil if no URL was given. + **/ +@property (nullable, copy) NSURL *assetURL; + +// TODO: copy both of these. +@property (nullable) AVVideoComposition *videoComposition; +@property (nullable) AVAudioMix *audioMix; + +@property (nullable, readonly) AVPlayer *player; + +// TODO: copy +@property (nullable, readonly) AVPlayerItem *currentItem; + +@property (nullable, nonatomic, readonly) AVPlayerLayer *playerLayer; + + +/** + * When shouldAutoplay is set to true, a video node will play when it has both loaded and entered the "visible" interfaceState. + * If it leaves the visible interfaceState it will pause but will resume once it has returned. + */ +@property BOOL shouldAutoplay; +@property BOOL shouldAutorepeat; + +@property BOOL muted; +@property BOOL shouldAggressivelyRecoverFromStall; + +@property (readonly) ASVideoNodePlayerState playerState; +//! Defaults to 10000 +@property int32_t periodicTimeObserverTimescale; + +//! Defaults to AVLayerVideoGravityResizeAspect +@property (null_resettable, copy) NSString *gravity; + +@property (nullable, weak) id delegate; + +@end + +@protocol ASVideoNodeDelegate +@optional +/** + * @abstract Delegate method invoked when the node's video has played to its end time. + * @param videoNode The video node has played to its end time. + */ +- (void)videoDidPlayToEnd:(ASVideoNode *)videoNode; +/** + * @abstract Delegate method invoked the node is tapped. + * @param videoNode The video node that was tapped. + * @discussion The video's play state is toggled if this method is not implemented. + */ +- (void)didTapVideoNode:(ASVideoNode *)videoNode; +/** + * @abstract Delegate method invoked when player changes state. + * @param videoNode The video node. + * @param state player state before this change. + * @param toState player new state. + * @discussion This method is called after each state change + */ +- (void)videoNode:(ASVideoNode *)videoNode willChangePlayerState:(ASVideoNodePlayerState)state toState:(ASVideoNodePlayerState)toState; +/** + * @abstract Asks delegate if state change is allowed + * ASVideoNodePlayerStatePlaying or ASVideoNodePlayerStatePaused. + * asks delegate if state change is allowed. + * @param videoNode The video node. + * @param state player state that is going to be set. + * @discussion Delegate method invoked when player changes it's state to + * ASVideoNodePlayerStatePlaying or ASVideoNodePlayerStatePaused + * and asks delegate if state change is valid + */ +- (BOOL)videoNode:(ASVideoNode*)videoNode shouldChangePlayerStateTo:(ASVideoNodePlayerState)state; +/** + * @abstract Delegate method invoked when player playback time is updated. + * @param videoNode The video node. + * @param timeInterval current playback time in seconds. + */ +- (void)videoNode:(ASVideoNode *)videoNode didPlayToTimeInterval:(NSTimeInterval)timeInterval; +/** + * @abstract Delegate method invoked when the video player stalls. + * @param videoNode The video node that has experienced the stall + * @param timeInterval Current playback time when the stall happens + */ +- (void)videoNode:(ASVideoNode *)videoNode didStallAtTimeInterval:(NSTimeInterval)timeInterval; +/** + * @abstract Delegate method invoked when the video player starts the inital asset loading + * @param videoNode The videoNode + */ +- (void)videoNodeDidStartInitialLoading:(ASVideoNode *)videoNode; +/** + * @abstract Delegate method invoked when the video is done loading the asset and can start the playback + * @param videoNode The videoNode + */ +- (void)videoNodeDidFinishInitialLoading:(ASVideoNode *)videoNode; +/** + * @abstract Delegate method invoked when the AVPlayerItem for the asset has been set up and can be accessed throught currentItem. + * @param videoNode The videoNode. + * @param currentItem The AVPlayerItem that was constructed from the asset. + */ +- (void)videoNode:(ASVideoNode *)videoNode didSetCurrentItem:(AVPlayerItem *)currentItem; +/** + * @abstract Delegate method invoked when the video node has recovered from the stall + * @param videoNode The videoNode + */ +- (void)videoNodeDidRecoverFromStall:(ASVideoNode *)videoNode; +/** + * @abstract Delegate method invoked when an error occurs while trying to load an asset + * @param videoNode The videoNode. + * @param key The key of value that failed to load. + * @param asset The asset. + * @param error The error that occurs. + */ +- (void)videoNode:(ASVideoNode *)videoNode didFailToLoadValueForKey:(NSString *)key asset:(AVAsset *)asset error:(NSError *)error; + +@end + +@interface ASVideoNode (Unavailable) + +- (instancetype)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock didLoadBlock:(nullable ASDisplayNodeDidLoadBlock)didLoadBlock NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASVideoNode.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASVideoNode.mm new file mode 100644 index 0000000..141ca2e --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASVideoNode.mm @@ -0,0 +1,861 @@ +// +// ASVideoNode.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#if AS_USE_VIDEO + +#import +#import +#import +#import +#import +#import + +static BOOL ASAssetIsEqual(AVAsset *asset1, AVAsset *asset2) { + return ASObjectIsEqual(asset1, asset2) + || ([asset1 isKindOfClass:[AVURLAsset class]] + && [asset2 isKindOfClass:[AVURLAsset class]] + && ASObjectIsEqual(((AVURLAsset *)asset1).URL, ((AVURLAsset *)asset2).URL)); +} + +static UIViewContentMode ASContentModeFromVideoGravity(NSString *videoGravity) { + if ([videoGravity isEqualToString:AVLayerVideoGravityResizeAspectFill]) { + return UIViewContentModeScaleAspectFill; + } else if ([videoGravity isEqualToString:AVLayerVideoGravityResize]) { + return UIViewContentModeScaleToFill; + } else { + return UIViewContentModeScaleAspectFit; + } +} + +static void *ASVideoNodeContext = &ASVideoNodeContext; +static NSString * const kPlaybackLikelyToKeepUpKey = @"playbackLikelyToKeepUp"; +static NSString * const kplaybackBufferEmpty = @"playbackBufferEmpty"; +static NSString * const kStatus = @"status"; +static NSString * const kRate = @"rate"; + +@interface ASVideoNode () +{ + struct { + unsigned int delegateVideNodeShouldChangePlayerStateTo:1; + unsigned int delegateVideoDidPlayToEnd:1; + unsigned int delegateDidTapVideoNode:1; + unsigned int delegateVideoNodeWillChangePlayerStateToState:1; + unsigned int delegateVideoNodeDidPlayToTimeInterval:1; + unsigned int delegateVideoNodeDidStartInitialLoading:1; + unsigned int delegateVideoNodeDidFinishInitialLoading:1; + unsigned int delegateVideoNodeDidSetCurrentItem:1; + unsigned int delegateVideoNodeDidStallAtTimeInterval:1; + unsigned int delegateVideoNodeDidRecoverFromStall:1; + unsigned int delegateVideoNodeDidFailToLoadValueForKey:1; + } _delegateFlags; + + BOOL _shouldBePlaying; + + BOOL _shouldAutorepeat; + BOOL _shouldAutoplay; + BOOL _shouldAggressivelyRecoverFromStall; + BOOL _muted; + + ASVideoNodePlayerState _playerState; + + AVAsset *_asset; + NSURL *_assetURL; + AVVideoComposition *_videoComposition; + AVAudioMix *_audioMix; + + AVPlayerItem *_currentPlayerItem; + AVPlayer *_player; + + id _timeObserver; + int32_t _periodicTimeObserverTimescale; + CMTime _timeObserverInterval; + + CMTime _lastPlaybackTime; + + ASDisplayNode *_playerNode; + NSString *_gravity; +} + +@end + +@implementation ASVideoNode + +@dynamic delegate; + +// TODO: Support preview images with HTTP Live Streaming videos. + +#pragma mark - Construction and Layout + +- (instancetype)initWithCache:(id)cache downloader:(id)downloader +{ + if (!(self = [super initWithCache:cache downloader:downloader])) { + return nil; + } + + self.gravity = AVLayerVideoGravityResizeAspect; + _periodicTimeObserverTimescale = 10000; + [self addTarget:self action:@selector(tapped) forControlEvents:ASControlNodeEventTouchUpInside]; + _lastPlaybackTime = kCMTimeZero; + + NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; + [notificationCenter addObserver:self selector:@selector(applicationDidBecomeActive:) name:UIApplicationDidBecomeActiveNotification object:nil]; + + return self; +} + +- (ASDisplayNode *)constructPlayerNode +{ + ASVideoNode * __weak weakSelf = self; + + return [[ASDisplayNode alloc] initWithLayerBlock:^CALayer *{ + AVPlayerLayer *playerLayer = [[AVPlayerLayer alloc] init]; + playerLayer.player = weakSelf.player; + playerLayer.videoGravity = weakSelf.gravity; + return playerLayer; + }]; +} + +- (AVPlayerItem *)constructPlayerItem +{ + ASDisplayNodeAssertMainThread(); + ASLockScopeSelf(); + + AVPlayerItem *playerItem = nil; + if (_assetURL != nil) { + playerItem = [[AVPlayerItem alloc] initWithURL:_assetURL]; + _asset = [playerItem asset]; + } else { + playerItem = [[AVPlayerItem alloc] initWithAsset:_asset]; + } + + playerItem.videoComposition = _videoComposition; + playerItem.audioMix = _audioMix; + return playerItem; +} + +- (void)prepareToPlayAsset:(AVAsset *)asset withKeys:(NSArray *)requestedKeys +{ + ASDisplayNodeAssertMainThread(); + + for (NSString *key in requestedKeys) { + NSError *error = nil; + AVKeyValueStatus keyStatus = [asset statusOfValueForKey:key error:&error]; + if (keyStatus == AVKeyValueStatusFailed) { + NSLog(@"Asset loading failed with error: %@", error); + if (_delegateFlags.delegateVideoNodeDidFailToLoadValueForKey) { + [self.delegate videoNode:self didFailToLoadValueForKey:key asset:asset error:error]; + } + } + } + + if ([asset isPlayable] == NO) { + NSLog(@"Asset is not playable."); + return; + } + + AVPlayerItem *playerItem = [self constructPlayerItem]; + [self setCurrentItem:playerItem]; + + if (_player != nil) { + [_player replaceCurrentItemWithPlayerItem:playerItem]; + } else { + self.player = [AVPlayer playerWithPlayerItem:playerItem]; + } + + if (_delegateFlags.delegateVideoNodeDidSetCurrentItem) { + [self.delegate videoNode:self didSetCurrentItem:playerItem]; + } + + if (self.image == nil && self.URL == nil) { + [self generatePlaceholderImage]; + } +} + +- (void)addPlayerItemObservers:(AVPlayerItem *)playerItem +{ + if (playerItem == nil) { + return; + } + + [playerItem addObserver:self forKeyPath:kStatus options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew context:ASVideoNodeContext]; + [playerItem addObserver:self forKeyPath:kPlaybackLikelyToKeepUpKey options:NSKeyValueObservingOptionNew context:ASVideoNodeContext]; + [playerItem addObserver:self forKeyPath:kplaybackBufferEmpty options:NSKeyValueObservingOptionNew context:ASVideoNodeContext]; + + NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; + [notificationCenter addObserver:self selector:@selector(didPlayToEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:playerItem]; + [notificationCenter addObserver:self selector:@selector(videoNodeDidStall:) name:AVPlayerItemPlaybackStalledNotification object:playerItem]; + [notificationCenter addObserver:self selector:@selector(errorWhilePlaying:) name:AVPlayerItemFailedToPlayToEndTimeNotification object:playerItem]; + [notificationCenter addObserver:self selector:@selector(errorWhilePlaying:) name:AVPlayerItemNewErrorLogEntryNotification object:playerItem]; +} + +- (void)removePlayerItemObservers:(AVPlayerItem *)playerItem +{ + @try { + [playerItem removeObserver:self forKeyPath:kStatus context:ASVideoNodeContext]; + [playerItem removeObserver:self forKeyPath:kPlaybackLikelyToKeepUpKey context:ASVideoNodeContext]; + [playerItem removeObserver:self forKeyPath:kplaybackBufferEmpty context:ASVideoNodeContext]; + } + @catch (NSException * __unused exception) { + NSLog(@"Unnecessary KVO removal"); + } + + NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; + [notificationCenter removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:playerItem]; + [notificationCenter removeObserver:self name: AVPlayerItemPlaybackStalledNotification object:playerItem]; + [notificationCenter removeObserver:self name:AVPlayerItemFailedToPlayToEndTimeNotification object:playerItem]; + [notificationCenter removeObserver:self name:AVPlayerItemNewErrorLogEntryNotification object:playerItem]; +} + +- (void)addPlayerObservers:(AVPlayer *)player +{ + if (player == nil) { + return; + } + + [player addObserver:self forKeyPath:kRate options:NSKeyValueObservingOptionNew context:ASVideoNodeContext]; + + __weak __typeof(self) weakSelf = self; + _timeObserverInterval = CMTimeMake(1, _periodicTimeObserverTimescale); + _timeObserver = [player addPeriodicTimeObserverForInterval:_timeObserverInterval queue:NULL usingBlock:^(CMTime time){ + [weakSelf periodicTimeObserver:time]; + }]; +} + +- (void) removePlayerObservers:(AVPlayer *)player +{ + if (_timeObserver != nil) { + [player removeTimeObserver:_timeObserver]; + _timeObserver = nil; + } + + @try { + [player removeObserver:self forKeyPath:kRate context:ASVideoNodeContext]; + } + @catch (NSException * __unused exception) { + NSLog(@"Unnecessary KVO removal"); + } +} + +- (void)layout +{ + [super layout]; + // The _playerNode wraps AVPlayerLayer, and therefore should extend across the entire bounds. + _playerNode.frame = self.bounds; +} + +- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize +{ + ASDisplayNode *playerNode = ASLockedSelf(_playerNode); + + CGSize calculatedSize = constrainedSize; + + // Prevent crashes through if infinite width or height + if (isinf(calculatedSize.width) || isinf(calculatedSize.height)) { + ASDisplayNodeAssert(NO, @"Infinite width or height in ASVideoNode"); + calculatedSize = CGSizeZero; + } + + if (playerNode != nil) { + playerNode.style.preferredSize = calculatedSize; + [playerNode layoutThatFits:ASSizeRangeMake(CGSizeZero, calculatedSize)]; + } + + return calculatedSize; +} + +- (void)generatePlaceholderImage +{ + ASVideoNode * __weak weakSelf = self; + AVAsset *asset = self.asset; + + [self imageAtTime:kCMTimeZero completionHandler:^(UIImage *image) { + ASPerformBlockOnMainThread(^{ + // Ensure the asset hasn't changed since the image request was made + if (ASAssetIsEqual(weakSelf.asset, asset)) { + [weakSelf setVideoPlaceholderImage:image]; + } + }); + }]; +} + +- (void)imageAtTime:(CMTime)imageTime completionHandler:(void(^)(UIImage *image))completionHandler +{ + ASPerformBlockOnBackgroundThread(^{ + AVAsset *asset = self->_asset; + + // Skip the asset image generation if we don't have any tracks available that are capable of supporting it + NSArray* visualAssetArray = [asset tracksWithMediaCharacteristic:AVMediaCharacteristicVisual]; + if (visualAssetArray.count == 0) { + completionHandler(nil); + return; + } + + AVAssetImageGenerator *previewImageGenerator = [AVAssetImageGenerator assetImageGeneratorWithAsset:asset]; + previewImageGenerator.appliesPreferredTrackTransform = YES; + previewImageGenerator.videoComposition = self->_videoComposition; + + [previewImageGenerator generateCGImagesAsynchronouslyForTimes:@[[NSValue valueWithCMTime:imageTime]] + completionHandler:^(CMTime requestedTime, CGImageRef image, CMTime actualTime, AVAssetImageGeneratorResult result, NSError *error) { + if (error != nil && result != AVAssetImageGeneratorCancelled) { + NSLog(@"Asset preview image generation failed with error: %@", error); + } + completionHandler(image ? [UIImage imageWithCGImage:image] : nil); + }]; + }); +} + +- (void)setVideoPlaceholderImage:(UIImage *)image +{ + NSString *gravity = self.gravity; + + if (image != nil) { + self.contentMode = ASContentModeFromVideoGravity(gravity); + } + self.image = image; +} + +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context +{ + AS::UniqueLock l(__instanceLock__); + + if (object == _currentPlayerItem) { + if ([keyPath isEqualToString:kStatus]) { + if ([change[NSKeyValueChangeNewKey] integerValue] == AVPlayerItemStatusReadyToPlay) { + if (self.playerState != ASVideoNodePlayerStatePlaying) { + self.playerState = ASVideoNodePlayerStateReadyToPlay; + if (_shouldBePlaying && ASInterfaceStateIncludesVisible(self.interfaceState)) { + l.unlock(); + [self play]; + l.lock(); + } + } + // If we don't yet have a placeholder image update it now that we should have data available for it + if (self.image == nil && self.URL == nil) { + [self generatePlaceholderImage]; + } + } + } else if ([keyPath isEqualToString:kPlaybackLikelyToKeepUpKey]) { + BOOL likelyToKeepUp = [change[NSKeyValueChangeNewKey] boolValue]; + if (likelyToKeepUp && self.playerState == ASVideoNodePlayerStatePlaying) { + return; + } + if (!likelyToKeepUp) { + self.playerState = ASVideoNodePlayerStateLoading; + } else if (self.playerState != ASVideoNodePlayerStateFinished) { + self.playerState = ASVideoNodePlayerStatePlaybackLikelyToKeepUpButNotPlaying; + } + if (_shouldBePlaying && (_shouldAggressivelyRecoverFromStall || likelyToKeepUp) && ASInterfaceStateIncludesVisible(self.interfaceState)) { + if (self.playerState == ASVideoNodePlayerStateLoading && _delegateFlags.delegateVideoNodeDidRecoverFromStall) { + [self.delegate videoNodeDidRecoverFromStall:self]; + } + + l.unlock(); + [self play]; // autoresume after buffer catches up + // NOTE: Early return without re-locking. + return; + } + } else if ([keyPath isEqualToString:kplaybackBufferEmpty]) { + if (_shouldBePlaying && [change[NSKeyValueChangeNewKey] boolValue] == YES && ASInterfaceStateIncludesVisible(self.interfaceState)) { + self.playerState = ASVideoNodePlayerStateLoading; + } + } + } else if (object == _player) { + if ([keyPath isEqualToString:kRate]) { + if ([change[NSKeyValueChangeNewKey] floatValue] == 0.0) { + if (self.playerState == ASVideoNodePlayerStatePlaying) { + self.playerState = ASVideoNodePlayerStatePaused; + } + } else { + self.playerState = ASVideoNodePlayerStatePlaying; + } + } + } + + // NOTE: Early return above. +} + +- (void)tapped +{ + if (_delegateFlags.delegateDidTapVideoNode) { + [self.delegate didTapVideoNode:self]; + + } else { + if (_shouldBePlaying) { + [self pause]; + } else { + [self play]; + } + } +} + +- (void)didEnterPreloadState +{ + [super didEnterPreloadState]; + + ASLockScopeSelf(); + AVAsset *asset = self.asset; + // Return immediately if the asset is nil; + if (asset == nil || self.playerState != ASVideoNodePlayerStateUnknown) { + return; + } + + self.playerState = ASVideoNodePlayerStateLoading; + if (_delegateFlags.delegateVideoNodeDidStartInitialLoading) { + [self.delegate videoNodeDidStartInitialLoading:self]; + } + + NSArray *requestedKeys = @[@"playable"]; + [asset loadValuesAsynchronouslyForKeys:requestedKeys completionHandler:^{ + ASPerformBlockOnMainThread(^{ + if (self->_delegateFlags.delegateVideoNodeDidFinishInitialLoading) { + [self.delegate videoNodeDidFinishInitialLoading:self]; + } + [self prepareToPlayAsset:asset withKeys:requestedKeys]; + }); + }]; +} + +- (void)periodicTimeObserver:(CMTime)time +{ + NSTimeInterval timeInSeconds = CMTimeGetSeconds(time); + if (timeInSeconds <= 0) { + return; + } + + if (_delegateFlags.delegateVideoNodeDidPlayToTimeInterval) { + [self.delegate videoNode:self didPlayToTimeInterval:timeInSeconds]; + + } +} + +- (void)didExitPreloadState +{ + [super didExitPreloadState]; + + { + ASLockScopeSelf(); + + self.player = nil; + self.currentItem = nil; + self.playerState = ASVideoNodePlayerStateUnknown; + } +} + +- (void)didEnterVisibleState +{ + [super didEnterVisibleState]; + + BOOL shouldPlay = NO; + { + ASLockScopeSelf(); + if (_shouldBePlaying || _shouldAutoplay) { + if (_player != nil && CMTIME_IS_VALID(_lastPlaybackTime)) { + [_player seekToTime:_lastPlaybackTime]; + } + shouldPlay = YES; + } + } + + if (shouldPlay) { + [self play]; + } +} + +- (void)didExitVisibleState +{ + [super didExitVisibleState]; + + ASLockScopeSelf(); + + if (_shouldBePlaying) { + [self pause]; + if (_player != nil && CMTIME_IS_VALID(_player.currentTime)) { + _lastPlaybackTime = _player.currentTime; + } + _shouldBePlaying = YES; + } +} + +#pragma mark - Video Properties + +- (void)setPlayerState:(ASVideoNodePlayerState)playerState +{ + ASLockScopeSelf(); + + ASVideoNodePlayerState oldState = _playerState; + + if (oldState == playerState) { + return; + } + + if (_delegateFlags.delegateVideoNodeWillChangePlayerStateToState) { + [self.delegate videoNode:self willChangePlayerState:oldState toState:playerState]; + } + + _playerState = playerState; +} + +- (void)setAssetURL:(NSURL *)assetURL +{ + ASDisplayNodeAssertMainThread(); + + if (ASObjectIsEqual(assetURL, self.assetURL) == NO) { + [self setAndFetchAsset:[AVURLAsset assetWithURL:assetURL] url:assetURL]; + } +} + +- (NSURL *)assetURL +{ + ASLockScopeSelf(); + + if (_assetURL != nil) { + return _assetURL; + } else if ([_asset isKindOfClass:AVURLAsset.class]) { + return ((AVURLAsset *)_asset).URL; + } + + return nil; +} + +- (void)setAsset:(AVAsset *)asset +{ + ASDisplayNodeAssertMainThread(); + + if (ASAssetIsEqual(asset, self.asset) == NO) { + [self setAndFetchAsset:asset url:nil]; + } +} + +- (AVAsset *)asset +{ + ASLockScopeSelf(); + return _asset; +} + +- (void)setAndFetchAsset:(AVAsset *)asset url:(NSURL *)assetURL +{ + ASDisplayNodeAssertMainThread(); + + [self didExitPreloadState]; + + { + ASLockScopeSelf(); + self.videoPlaceholderImage = nil; + _asset = asset; + _assetURL = assetURL; + } + + [self setNeedsPreload]; +} + +- (void)setVideoComposition:(AVVideoComposition *)videoComposition +{ + ASLockScopeSelf(); + + _videoComposition = videoComposition; + _currentPlayerItem.videoComposition = videoComposition; +} + +- (AVVideoComposition *)videoComposition +{ + ASLockScopeSelf(); + return _videoComposition; +} + +- (void)setAudioMix:(AVAudioMix *)audioMix +{ + ASLockScopeSelf(); + + _audioMix = audioMix; + _currentPlayerItem.audioMix = audioMix; +} + +- (AVAudioMix *)audioMix +{ + ASLockScopeSelf(); + return _audioMix; +} + +- (AVPlayer *)player +{ + ASLockScopeSelf(); + return _player; +} + +- (AVPlayerLayer *)playerLayer +{ + ASLockScopeSelf(); + return (AVPlayerLayer *)_playerNode.layer; +} + +- (void)setDelegate:(id)delegate +{ + [super setDelegate:delegate]; + + if (delegate == nil) { + memset(&_delegateFlags, 0, sizeof(_delegateFlags)); + } else { + _delegateFlags.delegateVideNodeShouldChangePlayerStateTo = [delegate respondsToSelector:@selector(videoNode:shouldChangePlayerStateTo:)]; + _delegateFlags.delegateVideoDidPlayToEnd = [delegate respondsToSelector:@selector(videoDidPlayToEnd:)]; + _delegateFlags.delegateDidTapVideoNode = [delegate respondsToSelector:@selector(didTapVideoNode:)]; + _delegateFlags.delegateVideoNodeWillChangePlayerStateToState = [delegate respondsToSelector:@selector(videoNode:willChangePlayerState:toState:)]; + _delegateFlags.delegateVideoNodeDidPlayToTimeInterval = [delegate respondsToSelector:@selector(videoNode:didPlayToTimeInterval:)]; + _delegateFlags.delegateVideoNodeDidStartInitialLoading = [delegate respondsToSelector:@selector(videoNodeDidStartInitialLoading:)]; + _delegateFlags.delegateVideoNodeDidFinishInitialLoading = [delegate respondsToSelector:@selector(videoNodeDidFinishInitialLoading:)]; + _delegateFlags.delegateVideoNodeDidSetCurrentItem = [delegate respondsToSelector:@selector(videoNode:didSetCurrentItem:)]; + _delegateFlags.delegateVideoNodeDidStallAtTimeInterval = [delegate respondsToSelector:@selector(videoNode:didStallAtTimeInterval:)]; + _delegateFlags.delegateVideoNodeDidRecoverFromStall = [delegate respondsToSelector:@selector(videoNodeDidRecoverFromStall:)]; + _delegateFlags.delegateVideoNodeDidFailToLoadValueForKey = [delegate respondsToSelector:@selector(videoNode:didFailToLoadValueForKey:asset:error:)]; + } +} + +- (void)setGravity:(NSString *)gravity +{ + ASLockScopeSelf(); + if (!gravity) { + gravity = AVLayerVideoGravityResizeAspect; + } + + if (!ASCompareAssignObjects(_gravity, gravity)) { + return; + } + + if (_playerNode.isNodeLoaded) { + ((AVPlayerLayer *)_playerNode.layer).videoGravity = gravity; + } + self.contentMode = ASContentModeFromVideoGravity(gravity); + _gravity = gravity; +} + +- (NSString *)gravity +{ + ASLockScopeSelf(); + return _gravity; +} + +- (BOOL)muted +{ + ASLockScopeSelf(); + return _muted; +} + +- (void)setMuted:(BOOL)muted +{ + ASLockScopeSelf(); + + _player.muted = muted; + _muted = muted; +} + +#pragma mark - Video Playback + +- (void)play +{ + ASLockScopeSelf(); + + if (![self isStateChangeValid:ASVideoNodePlayerStatePlaying]) { + return; + } + + if (_player == nil) { + ASUnlockScope(self); + [self setNeedsPreload]; + } + + if (_playerNode == nil) { + _playerNode = [self constructPlayerNode]; + + { + ASUnlockScope(self); + [self addSubnode:_playerNode]; + } + + [self setNeedsLayout]; + } + + + [_player play]; + _shouldBePlaying = YES; +} + +- (BOOL)ready +{ + return _currentPlayerItem.status == AVPlayerItemStatusReadyToPlay; +} + +- (void)pause +{ + ASLockScopeSelf(); + if (![self isStateChangeValid:ASVideoNodePlayerStatePaused]) { + return; + } + [_player pause]; + _shouldBePlaying = NO; +} + +- (BOOL)isPlaying +{ + ASLockScopeSelf(); + + return (_player.rate > 0 && !_player.error); +} + +- (BOOL)isStateChangeValid:(ASVideoNodePlayerState)state +{ + if (_delegateFlags.delegateVideNodeShouldChangePlayerStateTo) { + if (![self.delegate videoNode:self shouldChangePlayerStateTo:state]) { + return NO; + } + } + return YES; +} + +- (void)resetToPlaceholder +{ + ASLockScopeSelf(); + + if (_playerNode != nil) { + [_playerNode removeFromSupernode]; + _playerNode = nil; + } + + [_player seekToTime:kCMTimeZero]; + [self pause]; +} + + +#pragma mark - Playback observers + +- (void)applicationDidBecomeActive:(NSNotification *)notification +{ + if (self.shouldBePlaying && self.isVisible) { + [self play]; + } +} + +- (void)didPlayToEnd:(NSNotification *)notification +{ + self.playerState = ASVideoNodePlayerStateFinished; + if (_delegateFlags.delegateVideoDidPlayToEnd) { + [self.delegate videoDidPlayToEnd:self]; + } + + if (_shouldAutorepeat) { + [_player seekToTime:kCMTimeZero]; + [self play]; + } else { + [self pause]; + } +} + +- (void)videoNodeDidStall:(NSNotification *)notification +{ + self.playerState = ASVideoNodePlayerStateLoading; + if (_delegateFlags.delegateVideoNodeDidStallAtTimeInterval) { + [self.delegate videoNode:self didStallAtTimeInterval:CMTimeGetSeconds(_player.currentItem.currentTime)]; + } +} + +- (void)errorWhilePlaying:(NSNotification *)notification +{ + if ([notification.name isEqualToString:AVPlayerItemFailedToPlayToEndTimeNotification]) { + NSLog(@"Failed to play video"); + } else if ([notification.name isEqualToString:AVPlayerItemNewErrorLogEntryNotification]) { + AVPlayerItem *item = (AVPlayerItem *)notification.object; + AVPlayerItemErrorLogEvent *logEvent = item.errorLog.events.lastObject; + NSLog(@"AVPlayerItem error log entry added for video with error %@ status %@", item.error, + (item.status == AVPlayerItemStatusFailed ? @"FAILED" : [NSString stringWithFormat:@"%ld", (long)item.status])); + NSLog(@"Item is %@", item); + + if (logEvent) { + NSLog(@"Log code %ld domain %@ comment %@", (long)logEvent.errorStatusCode, logEvent.errorDomain, logEvent.errorComment); + } + } +} + +#pragma mark - Internal Properties + +- (AVPlayerItem *)currentItem +{ + ASLockScopeSelf(); + return _currentPlayerItem; +} + +- (void)setCurrentItem:(AVPlayerItem *)currentItem +{ + ASLockScopeSelf(); + + [self removePlayerItemObservers:_currentPlayerItem]; + + _currentPlayerItem = currentItem; + + if (currentItem != nil) { + [self addPlayerItemObservers:currentItem]; + } +} + +- (ASDisplayNode *)playerNode +{ + ASLockScopeSelf(); + return _playerNode; +} + +- (void)setPlayerNode:(ASDisplayNode *)playerNode +{ + { + ASLockScopeSelf(); + _playerNode = playerNode; + } + + [self setNeedsLayout]; +} + +- (void)setPlayer:(AVPlayer *)player +{ + ASLockScopeSelf(); + + [self removePlayerObservers:_player]; + + _player = player; + player.muted = _muted; + ((AVPlayerLayer *)_playerNode.layer).player = player; + + if (player != nil) { + [self addPlayerObservers:player]; + } +} + +- (BOOL)shouldBePlaying +{ + ASLockScopeSelf(); + return _shouldBePlaying; +} + +- (void)setShouldBePlaying:(BOOL)shouldBePlaying +{ + ASLockScopeSelf(); + _shouldBePlaying = shouldBePlaying; +} + +#pragma mark - Lifecycle + +- (void)dealloc +{ + [self removePlayerItemObservers:_currentPlayerItem]; + [self removePlayerObservers:_player]; + + NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; + [notificationCenter removeObserver:self name:UIApplicationDidBecomeActiveNotification object:nil]; +} + +@end + +#endif diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASVideoPlayerNode.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASVideoPlayerNode.h new file mode 100644 index 0000000..4483f60 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASVideoPlayerNode.h @@ -0,0 +1,225 @@ +// +// ASVideoPlayerNode.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#if AS_USE_VIDEO + +#if TARGET_OS_IOS +#import +#import +#import +#import + +@class AVAsset; +@class ASButtonNode; +@protocol ASVideoPlayerNodeDelegate; + +typedef NS_ENUM(NSInteger, ASVideoPlayerNodeControlType) { + ASVideoPlayerNodeControlTypePlaybackButton, + ASVideoPlayerNodeControlTypeElapsedText, + ASVideoPlayerNodeControlTypeDurationText, + ASVideoPlayerNodeControlTypeScrubber, + ASVideoPlayerNodeControlTypeFullScreenButton, + ASVideoPlayerNodeControlTypeFlexGrowSpacer, +}; + +NS_ASSUME_NONNULL_BEGIN + +@interface ASVideoPlayerNode : ASDisplayNode + +@property (nullable, nonatomic, weak) id delegate; + +@property (nonatomic, readonly) CMTime duration; + +@property (nonatomic) BOOL controlsDisabled; + +#pragma mark - ASVideoNode property proxy +/** + * When shouldAutoplay is set to true, a video node will play when it has both loaded and entered the "visible" interfaceState. + * If it leaves the visible interfaceState it will pause but will resume once it has returned. + */ +@property (nonatomic) BOOL shouldAutoPlay; +@property (nonatomic) BOOL shouldAutoRepeat; +@property (nonatomic) BOOL muted; +@property (nonatomic, readonly) ASVideoNodePlayerState playerState; +@property (nonatomic) BOOL shouldAggressivelyRecoverFromStall; +@property (nullable, nonatomic) NSURL *placeholderImageURL; + +@property (nullable, nonatomic) AVAsset *asset; +/** + ** @abstract The URL with which the asset was initialized. + ** @discussion Setting the URL will override the current asset with a newly created AVURLAsset created from the given URL, and AVAsset *asset will point to that newly created AVURLAsset. Please don't set both assetURL and asset. + ** @return Current URL the asset was initialized or nil if no URL was given. + **/ +@property (nullable, nonatomic) NSURL *assetURL; + +/// You should never set any value on the backing video node. Use exclusivively the video player node to set properties +@property (nonatomic, readonly) ASVideoNode *videoNode; + +//! Defaults to 10000 +@property (nonatomic) int32_t periodicTimeObserverTimescale; +//! Defaults to AVLayerVideoGravityResizeAspect +@property (nonatomic, copy) NSString *gravity; + +#pragma mark - Lifecycle +- (instancetype)initWithURL:(NSURL *)URL; +- (instancetype)initWithAsset:(AVAsset *)asset; +- (instancetype)initWithAsset:(AVAsset *)asset videoComposition:(AVVideoComposition *)videoComposition audioMix:(AVAudioMix *)audioMix; + +#pragma mark - Public API +- (void)seekToTime:(CGFloat)percentComplete; +- (void)play; +- (void)pause; +- (BOOL)isPlaying; +- (void)resetToPlaceholder; + +@end + +#pragma mark - ASVideoPlayerNodeDelegate - +@protocol ASVideoPlayerNodeDelegate +@optional +/** + * @abstract Delegate method invoked before creating controlbar controls + * @param videoPlayer The sender + */ +- (NSArray *)videoPlayerNodeNeededDefaultControls:(ASVideoPlayerNode*)videoPlayer; + +/** + * @abstract Delegate method invoked before creating default controls, asks delegate for custom controls dictionary. + * This dictionary must constain only ASDisplayNode subclass objects. + * @param videoPlayer The sender + * @discussion - This method is invoked only when developer implements videoPlayerNodeLayoutSpec:forControls:forMaximumSize: + * and gives ability to add custom constrols to ASVideoPlayerNode, for example mute button. + */ +- (NSDictionary *)videoPlayerNodeCustomControls:(ASVideoPlayerNode*)videoPlayer; + +/** + * @abstract Delegate method invoked in layoutSpecThatFits: + * @param videoPlayer The sender + * @param controls - Dictionary of controls which are used in videoPlayer; Dictionary keys are ASVideoPlayerNodeControlType + * @param maxSize - Maximum size for ASVideoPlayerNode + * @discussion - Developer can layout whole ASVideoPlayerNode as he wants. ASVideoNode is locked and it can't be changed + */ +- (ASLayoutSpec *)videoPlayerNodeLayoutSpec:(ASVideoPlayerNode *)videoPlayer + forControls:(NSDictionary *)controls + forMaximumSize:(CGSize)maxSize; + +#pragma mark Text delegate methods +/** + * @abstract Delegate method invoked before creating ASVideoPlayerNodeControlTypeElapsedText and ASVideoPlayerNodeControlTypeDurationText + * @param videoPlayer The sender + * @param timeLabelType The of the time label + */ +- (NSDictionary *)videoPlayerNodeTimeLabelAttributes:(ASVideoPlayerNode *)videoPlayer timeLabelType:(ASVideoPlayerNodeControlType)timeLabelType; +- (NSString *)videoPlayerNode:(ASVideoPlayerNode *)videoPlayerNode + timeStringForTimeLabelType:(ASVideoPlayerNodeControlType)timeLabelType + forTime:(CMTime)time; + +#pragma mark Scrubber delegate methods +- (UIColor *)videoPlayerNodeScrubberMaximumTrackTint:(ASVideoPlayerNode *)videoPlayer; +- (UIColor *)videoPlayerNodeScrubberMinimumTrackTint:(ASVideoPlayerNode *)videoPlayer; +- (UIColor *)videoPlayerNodeScrubberThumbTint:(ASVideoPlayerNode *)videoPlayer; +- (UIImage *)videoPlayerNodeScrubberThumbImage:(ASVideoPlayerNode *)videoPlayer; + +#pragma mark - Spinner delegate methods +- (UIColor *)videoPlayerNodeSpinnerTint:(ASVideoPlayerNode *)videoPlayer; +- (UIActivityIndicatorViewStyle)videoPlayerNodeSpinnerStyle:(ASVideoPlayerNode *)videoPlayer; + +#pragma mark - Playback button delegate methods +- (UIColor *)videoPlayerNodePlaybackButtonTint:(ASVideoPlayerNode *)videoPlayer; + +#pragma mark - Fullscreen button delegate methods + +- (UIImage *)videoPlayerNodeFullScreenButtonImage:(ASVideoPlayerNode *)videoPlayer; + + +#pragma mark ASVideoNodeDelegate proxy methods +/** + * @abstract Delegate method invoked when ASVideoPlayerNode is taped. + * @param videoPlayer The ASVideoPlayerNode that was tapped. + */ +- (void)didTapVideoPlayerNode:(ASVideoPlayerNode *)videoPlayer; + +/** + * @abstract Delegate method invoked when fullcreen button is taped. + * @param buttonNode The fullscreen button node that was tapped. + */ +- (void)didTapFullScreenButtonNode:(ASButtonNode *)buttonNode; + +/** + * @abstract Delegate method invoked when ASVideoNode playback time is updated. + * @param videoPlayer The video player node + * @param time current playback time. + */ +- (void)videoPlayerNode:(ASVideoPlayerNode *)videoPlayer didPlayToTime:(CMTime)time; + +/** + * @abstract Delegate method invoked when ASVideoNode changes state. + * @param videoPlayer The ASVideoPlayerNode whose ASVideoNode is changing state. + * @param state ASVideoNode state before this change. + * @param toState ASVideoNode new state. + * @discussion This method is called after each state change + */ +- (void)videoPlayerNode:(ASVideoPlayerNode *)videoPlayer willChangeVideoNodeState:(ASVideoNodePlayerState)state toVideoNodeState:(ASVideoNodePlayerState)toState; + +/** + * @abstract Delegate method is invoked when ASVideoNode decides to change state. + * @param videoPlayer The ASVideoPlayerNode whose ASVideoNode is changing state. + * @param state ASVideoNode that is going to be set. + * @discussion Delegate method invoked when player changes it's state to + * ASVideoNodePlayerStatePlaying or ASVideoNodePlayerStatePaused + * and asks delegate if state change is valid + */ +- (BOOL)videoPlayerNode:(ASVideoPlayerNode*)videoPlayer shouldChangeVideoNodeStateTo:(ASVideoNodePlayerState)state; + +/** + * @abstract Delegate method invoked when the ASVideoNode has played to its end time. + * @param videoPlayer The video node has played to its end time. + */ +- (void)videoPlayerNodeDidPlayToEnd:(ASVideoPlayerNode *)videoPlayer; + +/** + * @abstract Delegate method invoked when the ASVideoNode has constructed its AVPlayerItem for the asset. + * @param videoPlayer The video player node. + * @param currentItem The AVPlayerItem that was constructed from the asset. + */ +- (void)videoPlayerNode:(ASVideoPlayerNode *)videoPlayer didSetCurrentItem:(AVPlayerItem *)currentItem; + +/** + * @abstract Delegate method invoked when the ASVideoNode stalls. + * @param videoPlayer The video player node that has experienced the stall + * @param timeInterval Current playback time when the stall happens + */ +- (void)videoPlayerNode:(ASVideoPlayerNode *)videoPlayer didStallAtTimeInterval:(NSTimeInterval)timeInterval; + +/** + * @abstract Delegate method invoked when the ASVideoNode starts the inital asset loading + * @param videoPlayer The videoPlayer + */ +- (void)videoPlayerNodeDidStartInitialLoading:(ASVideoPlayerNode *)videoPlayer; + +/** + * @abstract Delegate method invoked when the ASVideoNode is done loading the asset and can start the playback + * @param videoPlayer The videoPlayer + */ +- (void)videoPlayerNodeDidFinishInitialLoading:(ASVideoPlayerNode *)videoPlayer; + +/** + * @abstract Delegate method invoked when the ASVideoNode has recovered from the stall + * @param videoPlayer The videoplayer + */ +- (void)videoPlayerNodeDidRecoverFromStall:(ASVideoPlayerNode *)videoPlayer; + + +@end +NS_ASSUME_NONNULL_END +#endif // TARGET_OS_IOS + +#endif diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASVideoPlayerNode.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASVideoPlayerNode.mm new file mode 100644 index 0000000..36b4a3f --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASVideoPlayerNode.mm @@ -0,0 +1,1017 @@ +// +// ASVideoPlayerNode.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#if AS_USE_VIDEO +#if TARGET_OS_IOS + +#import + +#import +#import +#import + +static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext; + +@interface ASVideoPlayerNode() +{ + __weak id _delegate; + + struct { + unsigned int delegateNeededDefaultControls:1; + unsigned int delegateCustomControls:1; + unsigned int delegateSpinnerTintColor:1; + unsigned int delegateSpinnerStyle:1; + unsigned int delegatePlaybackButtonTint:1; + unsigned int delegateFullScreenButtonImage:1; + unsigned int delegateScrubberMaximumTrackTintColor:1; + unsigned int delegateScrubberMinimumTrackTintColor:1; + unsigned int delegateScrubberThumbTintColor:1; + unsigned int delegateScrubberThumbImage:1; + unsigned int delegateTimeLabelAttributes:1; + unsigned int delegateTimeLabelAttributedString:1; + unsigned int delegateLayoutSpecForControls:1; + unsigned int delegateVideoNodeDidPlayToTime:1; + unsigned int delegateVideoNodeWillChangeState:1; + unsigned int delegateVideoNodeShouldChangeState:1; + unsigned int delegateVideoNodePlaybackDidFinish:1; + unsigned int delegateDidTapVideoPlayerNode:1; + unsigned int delegateDidTapFullScreenButtonNode:1; + unsigned int delegateVideoPlayerNodeDidSetCurrentItem:1; + unsigned int delegateVideoPlayerNodeDidStallAtTimeInterval:1; + unsigned int delegateVideoPlayerNodeDidStartInitialLoading:1; + unsigned int delegateVideoPlayerNodeDidFinishInitialLoading:1; + unsigned int delegateVideoPlayerNodeDidRecoverFromStall:1; + } _delegateFlags; + + // The asset passed in the initializer will be assigned as pending asset. As soon as the first + // preload state happened all further asset handling is made by using the asset of the backing + // video node + AVAsset *_pendingAsset; + + // The backing video node. Ideally this is the source of truth and the video player node should + // not handle anything related to asset management + ASVideoNode *_videoNode; + + NSArray *_neededDefaultControls; + + NSMutableDictionary *_cachedControls; + + ASDefaultPlaybackButton *_playbackButtonNode; + ASButtonNode *_fullScreenButtonNode; + ASTextNode *_elapsedTextNode; + ASTextNode *_durationTextNode; + ASDisplayNode *_scrubberNode; + ASStackLayoutSpec *_controlFlexGrowSpacerSpec; + ASDisplayNode *_spinnerNode; + + BOOL _isSeeking; + CMTime _duration; + + BOOL _controlsDisabled; + + BOOL _shouldAutoPlay; + BOOL _shouldAutoRepeat; + BOOL _muted; + int32_t _periodicTimeObserverTimescale; + NSString *_gravity; + + BOOL _shouldAggressivelyRecoverFromStall; + + UIColor *_defaultControlsColor; +} + +@end + +@implementation ASVideoPlayerNode + +@dynamic placeholderImageURL; + +#pragma mark - Lifecycle + +- (instancetype)init +{ + if (!(self = [super init])) { + return nil; + } + + [self _initControlsAndVideoNode]; + + return self; +} + +- (instancetype)initWithAsset:(AVAsset *)asset +{ + if (!(self = [self init])) { + return nil; + } + + _pendingAsset = asset; + + return self; +} + +- (instancetype)initWithURL:(NSURL *)URL +{ + return [self initWithAsset:[AVAsset assetWithURL:URL]]; +} + +- (instancetype)initWithAsset:(AVAsset *)asset videoComposition:(AVVideoComposition *)videoComposition audioMix:(AVAudioMix *)audioMix +{ + if (!(self = [self initWithAsset:asset])) { + return nil; + } + + _videoNode.videoComposition = videoComposition; + _videoNode.audioMix = audioMix; + + return self; +} + +- (void)_initControlsAndVideoNode +{ + _defaultControlsColor = [UIColor whiteColor]; + _cachedControls = [[NSMutableDictionary alloc] init]; + + _videoNode = [[ASVideoNode alloc] init]; + _videoNode.delegate = self; + [self addSubnode:_videoNode]; +} + +#pragma mark - Setter / Getter + +- (void)setAssetURL:(NSURL *)assetURL +{ + ASDisplayNodeAssertMainThread(); + + self.asset = [AVAsset assetWithURL:assetURL]; +} + +- (NSURL *)assetURL +{ + NSURL *url = nil; + { + ASLockScopeSelf(); + if ([_pendingAsset isKindOfClass:AVURLAsset.class]) { + url = ((AVURLAsset *)_pendingAsset).URL; + } + } + + return url ?: _videoNode.assetURL; +} + +- (void)setAsset:(AVAsset *)asset +{ + ASDisplayNodeAssertMainThread(); + + [self lock]; + + // Clean out pending asset + _pendingAsset = nil; + + // Set asset based on interface state + if ((ASInterfaceStateIncludesPreload(self.interfaceState))) { + // Don't hold the lock while accessing the subnode + [self unlock]; + _videoNode.asset = asset; + return; + } + + _pendingAsset = asset; + [self unlock]; +} + +- (AVAsset *)asset +{ + return ASLockedSelf(_pendingAsset) ?: _videoNode.asset; +} + +#pragma mark - ASDisplayNode + +- (void)didLoad +{ + [super didLoad]; + + [self createControls]; +} + +- (void)didEnterPreloadState +{ + [super didEnterPreloadState]; + + AVAsset *pendingAsset = nil; + { + ASLockScopeSelf(); + pendingAsset = _pendingAsset; + _pendingAsset = nil; + } + + // If we enter preload state we apply the pending asset to load to the video node so it can start and fetch the asset + if (pendingAsset != nil && _videoNode.asset != pendingAsset) { + _videoNode.asset = pendingAsset; + } +} + +- (BOOL)supportsLayerBacking +{ + return NO; +} + +#pragma mark - UI + +- (void)createControls +{ + { + ASLockScopeSelf(); + + if (_controlsDisabled) { + return; + } + + if (_neededDefaultControls == nil) { + _neededDefaultControls = [self createDefaultControlElementArray]; + } + + if (_cachedControls == nil) { + _cachedControls = [[NSMutableDictionary alloc] init]; + } + + for (id object in _neededDefaultControls) { + ASVideoPlayerNodeControlType type = (ASVideoPlayerNodeControlType)[object integerValue]; + switch (type) { + case ASVideoPlayerNodeControlTypePlaybackButton: + [self _locked_createPlaybackButton]; + break; + case ASVideoPlayerNodeControlTypeElapsedText: + [self _locked_createElapsedTextField]; + break; + case ASVideoPlayerNodeControlTypeDurationText: + [self _locked_createDurationTextField]; + break; + case ASVideoPlayerNodeControlTypeScrubber: + [self _locked_createScrubber]; + break; + case ASVideoPlayerNodeControlTypeFullScreenButton: + [self _locked_createFullScreenButton]; + break; + case ASVideoPlayerNodeControlTypeFlexGrowSpacer: + [self _locked_createControlFlexGrowSpacer]; + break; + default: + break; + } + } + + if (_delegateFlags.delegateCustomControls && _delegateFlags.delegateLayoutSpecForControls) { + NSDictionary *customControls = [_delegate videoPlayerNodeCustomControls:self]; + std::vector subnodes; + for (id key in customControls) { + id node = customControls[key]; + if (![node isKindOfClass:[ASDisplayNode class]]) { + continue; + } + + subnodes.push_back(node); + [_cachedControls setObject:node forKey:key]; + } + + { + ASUnlockScope(self); + for (ASDisplayNode *subnode : subnodes) { + [self addSubnode:subnode]; + } + } + } + } + + ASPerformBlockOnMainThread(^{ + [self setNeedsLayout]; + }); +} + +- (NSArray *)createDefaultControlElementArray +{ + if (_delegateFlags.delegateNeededDefaultControls) { + return [_delegate videoPlayerNodeNeededDefaultControls:self]; + } + + return @[ @(ASVideoPlayerNodeControlTypePlaybackButton), + @(ASVideoPlayerNodeControlTypeElapsedText), + @(ASVideoPlayerNodeControlTypeScrubber), + @(ASVideoPlayerNodeControlTypeDurationText) ]; +} + +- (void)removeControls +{ + NSMutableDictionary *cachedControls = nil; + { + ASLockScope(self); + + // Grab the cached controls for removing it + cachedControls = [_cachedControls copy]; + [self _locked_cleanCachedControls]; + } + + for (ASDisplayNode *node in [cachedControls objectEnumerator]) { + [node removeFromSupernode]; + } +} + +- (void)_locked_cleanCachedControls +{ + [_cachedControls removeAllObjects]; + + _playbackButtonNode = nil; + _fullScreenButtonNode = nil; + _elapsedTextNode = nil; + _durationTextNode = nil; + _scrubberNode = nil; +} + +- (void)_locked_createPlaybackButton +{ + DISABLED_ASAssertLocked(__instanceLock__); + + if (_playbackButtonNode == nil) { + _playbackButtonNode = [[ASDefaultPlaybackButton alloc] init]; + _playbackButtonNode.style.preferredSize = CGSizeMake(16.0, 22.0); + + if (_delegateFlags.delegatePlaybackButtonTint) { + _playbackButtonNode.tintColor = [_delegate videoPlayerNodePlaybackButtonTint:self]; + } else { + _playbackButtonNode.tintColor = _defaultControlsColor; + } + + if (_videoNode.playerState == ASVideoNodePlayerStatePlaying) { + _playbackButtonNode.buttonType = ASDefaultPlaybackButtonTypePause; + } + + [_playbackButtonNode addTarget:self action:@selector(didTapPlaybackButton:) forControlEvents:ASControlNodeEventTouchUpInside]; + [_cachedControls setObject:_playbackButtonNode forKey:@(ASVideoPlayerNodeControlTypePlaybackButton)]; + } + + { + ASUnlockScope(self); + [self addSubnode:_playbackButtonNode]; + } +} + +- (void)_locked_createFullScreenButton +{ + DISABLED_ASAssertLocked(__instanceLock__); + + if (_fullScreenButtonNode == nil) { + _fullScreenButtonNode = [[ASButtonNode alloc] init]; + _fullScreenButtonNode.style.preferredSize = CGSizeMake(16.0, 22.0); + + if (_delegateFlags.delegateFullScreenButtonImage) { + [_fullScreenButtonNode setImage:[_delegate videoPlayerNodeFullScreenButtonImage:self] forState:UIControlStateNormal]; + } + + [_fullScreenButtonNode addTarget:self action:@selector(didTapFullScreenButton:) forControlEvents:ASControlNodeEventTouchUpInside]; + [_cachedControls setObject:_fullScreenButtonNode forKey:@(ASVideoPlayerNodeControlTypeFullScreenButton)]; + } + + { + ASUnlockScope(self); + [self addSubnode:_fullScreenButtonNode]; + } +} + +- (void)_locked_createElapsedTextField +{ + DISABLED_ASAssertLocked(__instanceLock__); + + if (_elapsedTextNode == nil) { + _elapsedTextNode = [[ASTextNode alloc] init]; + _elapsedTextNode.attributedText = [self timeLabelAttributedStringForString:@"00:00" + forControlType:ASVideoPlayerNodeControlTypeElapsedText]; + _elapsedTextNode.truncationMode = NSLineBreakByClipping; + + [_cachedControls setObject:_elapsedTextNode forKey:@(ASVideoPlayerNodeControlTypeElapsedText)]; + } + { + ASUnlockScope(self); + [self addSubnode:_elapsedTextNode]; + } +} + +- (void)_locked_createDurationTextField +{ + DISABLED_ASAssertLocked(__instanceLock__); + + if (_durationTextNode == nil) { + _durationTextNode = [[ASTextNode alloc] init]; + _durationTextNode.attributedText = [self timeLabelAttributedStringForString:@"00:00" + forControlType:ASVideoPlayerNodeControlTypeDurationText]; + _durationTextNode.truncationMode = NSLineBreakByClipping; + + [_cachedControls setObject:_durationTextNode forKey:@(ASVideoPlayerNodeControlTypeDurationText)]; + } + [self updateDurationTimeLabel]; + { + ASUnlockScope(self); + [self addSubnode:_durationTextNode]; + } +} + +- (void)_locked_createScrubber +{ + DISABLED_ASAssertLocked(__instanceLock__); + + if (_scrubberNode == nil) { + __weak __typeof__(self) weakSelf = self; + _scrubberNode = [[ASDisplayNode alloc] initWithViewBlock:^UIView * _Nonnull { + __typeof__(self) strongSelf = weakSelf; + + UISlider *slider = [[UISlider alloc] initWithFrame:CGRectZero]; + slider.minimumValue = 0.0; + slider.maximumValue = 1.0; + + if (strongSelf->_delegateFlags.delegateScrubberMinimumTrackTintColor) { + slider.minimumTrackTintColor = [strongSelf.delegate videoPlayerNodeScrubberMinimumTrackTint:strongSelf]; + } + + if (strongSelf->_delegateFlags.delegateScrubberMaximumTrackTintColor) { + slider.maximumTrackTintColor = [strongSelf.delegate videoPlayerNodeScrubberMaximumTrackTint:strongSelf]; + } + + if (strongSelf->_delegateFlags.delegateScrubberThumbTintColor) { + slider.thumbTintColor = [strongSelf.delegate videoPlayerNodeScrubberThumbTint:strongSelf]; + } + + if (strongSelf->_delegateFlags.delegateScrubberThumbImage) { + UIImage *thumbImage = [strongSelf.delegate videoPlayerNodeScrubberThumbImage:strongSelf]; + [slider setThumbImage:thumbImage forState:UIControlStateNormal]; + } + + + [slider addTarget:strongSelf action:@selector(beginSeek) forControlEvents:UIControlEventTouchDown]; + [slider addTarget:strongSelf action:@selector(endSeek) forControlEvents:UIControlEventTouchUpInside|UIControlEventTouchUpOutside|UIControlEventTouchCancel]; + [slider addTarget:strongSelf action:@selector(seekTimeDidChange:) forControlEvents:UIControlEventValueChanged]; + + return slider; + }]; + + _scrubberNode.style.flexShrink = 1; + + [_cachedControls setObject:_scrubberNode forKey:@(ASVideoPlayerNodeControlTypeScrubber)]; + } + { + ASUnlockScope(self); + [self addSubnode:_scrubberNode]; + } +} + +- (void)_locked_createControlFlexGrowSpacer +{ + DISABLED_ASAssertLocked(__instanceLock__); + + if (_controlFlexGrowSpacerSpec == nil) { + _controlFlexGrowSpacerSpec = [[ASStackLayoutSpec alloc] init]; + _controlFlexGrowSpacerSpec.style.flexGrow = 1.0; + } + + [_cachedControls setObject:_controlFlexGrowSpacerSpec forKey:@(ASVideoPlayerNodeControlTypeFlexGrowSpacer)]; +} + +- (void)updateDurationTimeLabel +{ + if (!_durationTextNode) { + return; + } + NSString *formattedDuration = [self timeStringForCMTime:_duration forTimeLabelType:ASVideoPlayerNodeControlTypeDurationText]; + _durationTextNode.attributedText = [self timeLabelAttributedStringForString:formattedDuration forControlType:ASVideoPlayerNodeControlTypeDurationText]; +} + +- (void)updateElapsedTimeLabel:(NSTimeInterval)seconds +{ + if (!_elapsedTextNode) { + return; + } + NSString *formattedElapsed = [self timeStringForCMTime:CMTimeMakeWithSeconds( seconds, _videoNode.periodicTimeObserverTimescale ) forTimeLabelType:ASVideoPlayerNodeControlTypeElapsedText]; + _elapsedTextNode.attributedText = [self timeLabelAttributedStringForString:formattedElapsed forControlType:ASVideoPlayerNodeControlTypeElapsedText]; +} + +- (NSAttributedString*)timeLabelAttributedStringForString:(NSString*)string forControlType:(ASVideoPlayerNodeControlType)controlType +{ + NSDictionary *options; + if (_delegateFlags.delegateTimeLabelAttributes) { + options = [_delegate videoPlayerNodeTimeLabelAttributes:self timeLabelType:controlType]; + } else { + options = @{ + NSFontAttributeName : [UIFont systemFontOfSize:12.0], + NSForegroundColorAttributeName: _defaultControlsColor + }; + } + + + NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:string attributes:options]; + + return attributedString; +} + +#pragma mark - ASVideoNodeDelegate +- (void)videoNode:(ASVideoNode *)videoNode willChangePlayerState:(ASVideoNodePlayerState)state toState:(ASVideoNodePlayerState)toState +{ + if (_delegateFlags.delegateVideoNodeWillChangeState) { + [_delegate videoPlayerNode:self willChangeVideoNodeState:state toVideoNodeState:toState]; + } + + if (toState == ASVideoNodePlayerStateReadyToPlay) { + _duration = _videoNode.currentItem.duration; + [self updateDurationTimeLabel]; + } + + if (toState == ASVideoNodePlayerStatePlaying) { + _playbackButtonNode.buttonType = ASDefaultPlaybackButtonTypePause; + [self removeSpinner]; + } else if (toState != ASVideoNodePlayerStatePlaybackLikelyToKeepUpButNotPlaying && toState != ASVideoNodePlayerStateReadyToPlay) { + _playbackButtonNode.buttonType = ASDefaultPlaybackButtonTypePlay; + } + + if (toState == ASVideoNodePlayerStateLoading || toState == ASVideoNodePlayerStateInitialLoading) { + [self showSpinner]; + } + + if (toState == ASVideoNodePlayerStateReadyToPlay || toState == ASVideoNodePlayerStatePaused || toState == ASVideoNodePlayerStatePlaybackLikelyToKeepUpButNotPlaying) { + [self removeSpinner]; + } +} + +- (BOOL)videoNode:(ASVideoNode *)videoNode shouldChangePlayerStateTo:(ASVideoNodePlayerState)state +{ + if (_delegateFlags.delegateVideoNodeShouldChangeState) { + return [_delegate videoPlayerNode:self shouldChangeVideoNodeStateTo:state]; + } + return YES; +} + +- (void)videoNode:(ASVideoNode *)videoNode didPlayToTimeInterval:(NSTimeInterval)timeInterval +{ + if (_delegateFlags.delegateVideoNodeDidPlayToTime) { + [_delegate videoPlayerNode:self didPlayToTime:_videoNode.player.currentTime]; + } + + if (_isSeeking) { + return; + } + + if (_elapsedTextNode) { + [self updateElapsedTimeLabel:timeInterval]; + } + + if (_scrubberNode) { + [(UISlider*)_scrubberNode.view setValue:( timeInterval / CMTimeGetSeconds(_duration) ) animated:NO]; + } +} + +- (void)videoDidPlayToEnd:(ASVideoNode *)videoNode +{ + if (_delegateFlags.delegateVideoNodePlaybackDidFinish) { + [_delegate videoPlayerNodeDidPlayToEnd:self]; + } +} + +- (void)didTapVideoNode:(ASVideoNode *)videoNode +{ + if (_delegateFlags.delegateDidTapVideoPlayerNode) { + [_delegate didTapVideoPlayerNode:self]; + } else { + [self togglePlayPause]; + } +} + +- (void)videoNode:(ASVideoNode *)videoNode didSetCurrentItem:(AVPlayerItem *)currentItem +{ + if (_delegateFlags.delegateVideoPlayerNodeDidSetCurrentItem) { + [_delegate videoPlayerNode:self didSetCurrentItem:currentItem]; + } +} + +- (void)videoNode:(ASVideoNode *)videoNode didStallAtTimeInterval:(NSTimeInterval)timeInterval +{ + if (_delegateFlags.delegateVideoPlayerNodeDidStallAtTimeInterval) { + [_delegate videoPlayerNode:self didStallAtTimeInterval:timeInterval]; + } +} + +- (void)videoNodeDidStartInitialLoading:(ASVideoNode *)videoNode +{ + if (_delegateFlags.delegateVideoPlayerNodeDidStartInitialLoading) { + [_delegate videoPlayerNodeDidStartInitialLoading:self]; + } +} + +- (void)videoNodeDidFinishInitialLoading:(ASVideoNode *)videoNode +{ + if (_delegateFlags.delegateVideoPlayerNodeDidFinishInitialLoading) { + [_delegate videoPlayerNodeDidFinishInitialLoading:self]; + } +} + +- (void)videoNodeDidRecoverFromStall:(ASVideoNode *)videoNode +{ + if (_delegateFlags.delegateVideoPlayerNodeDidRecoverFromStall) { + [_delegate videoPlayerNodeDidRecoverFromStall:self]; + } +} + +#pragma mark - Actions +- (void)togglePlayPause +{ + if (_videoNode.playerState == ASVideoNodePlayerStatePlaying) { + [_videoNode pause]; + } else { + [_videoNode play]; + } +} + +- (void)showSpinner +{ + ASLockScopeSelf(); + + if (!_spinnerNode) { + __weak __typeof__(self) weakSelf = self; + _spinnerNode = [[ASDisplayNode alloc] initWithViewBlock:^UIView *{ + __typeof__(self) strongSelf = weakSelf; + UIActivityIndicatorView *spinnnerView = [[UIActivityIndicatorView alloc] init]; + spinnnerView.backgroundColor = [UIColor clearColor]; + + if (strongSelf->_delegateFlags.delegateSpinnerTintColor) { + spinnnerView.color = [strongSelf->_delegate videoPlayerNodeSpinnerTint:strongSelf]; + } else { + spinnnerView.color = strongSelf->_defaultControlsColor; + } + + if (strongSelf->_delegateFlags.delegateSpinnerStyle) { + spinnnerView.activityIndicatorViewStyle = [strongSelf->_delegate videoPlayerNodeSpinnerStyle:strongSelf]; + } + + return spinnnerView; + }]; + _spinnerNode.style.preferredSize = CGSizeMake(44.0, 44.0); + + const auto spinnerNode = _spinnerNode; + { + ASUnlockScope(self); + [self addSubnode:spinnerNode]; + [self setNeedsLayout]; + } + } + [(UIActivityIndicatorView *)_spinnerNode.view startAnimating]; +} + +- (void)removeSpinner +{ + ASDisplayNode *spinnerNode = nil; + { + ASLockScopeSelf(); + if (!_spinnerNode) { + return; + } + + spinnerNode = _spinnerNode; + _spinnerNode = nil; + } + + [spinnerNode removeFromSupernode]; +} + +- (void)didTapPlaybackButton:(ASControlNode*)node +{ + [self togglePlayPause]; +} + +- (void)didTapFullScreenButton:(ASButtonNode*)node +{ + if (_delegateFlags.delegateDidTapFullScreenButtonNode) { + [_delegate didTapFullScreenButtonNode:node]; + } +} + +- (void)beginSeek +{ + _isSeeking = YES; +} + +- (void)endSeek +{ + _isSeeking = NO; +} + +- (void)seekTimeDidChange:(UISlider*)slider +{ + CGFloat percentage = slider.value * 100; + [self seekToTime:percentage]; +} + +#pragma mark - Public API +- (void)seekToTime:(CGFloat)percentComplete +{ + CGFloat seconds = ( CMTimeGetSeconds(_duration) * percentComplete ) / 100; + + [self updateElapsedTimeLabel:seconds]; + [_videoNode.player seekToTime:CMTimeMakeWithSeconds(seconds, _videoNode.periodicTimeObserverTimescale)]; + + if (_videoNode.playerState != ASVideoNodePlayerStatePlaying) { + [self togglePlayPause]; + } +} + +- (void)play +{ + [_videoNode play]; +} + +- (void)pause +{ + [_videoNode pause]; +} + +- (BOOL)isPlaying +{ + return [_videoNode isPlaying]; +} + +- (void)resetToPlaceholder +{ + [_videoNode resetToPlaceholder]; +} + +- (NSArray *)controlsForLayoutSpec +{ + NSMutableArray *controls = [[NSMutableArray alloc] initWithCapacity:_cachedControls.count]; + + if (_cachedControls[ @(ASVideoPlayerNodeControlTypePlaybackButton) ]) { + [controls addObject:_cachedControls[ @(ASVideoPlayerNodeControlTypePlaybackButton) ]]; + } + + if (_cachedControls[ @(ASVideoPlayerNodeControlTypeElapsedText) ]) { + [controls addObject:_cachedControls[ @(ASVideoPlayerNodeControlTypeElapsedText) ]]; + } + + if (_cachedControls[ @(ASVideoPlayerNodeControlTypeScrubber) ]) { + [controls addObject:_cachedControls[ @(ASVideoPlayerNodeControlTypeScrubber) ]]; + } + + if (_cachedControls[ @(ASVideoPlayerNodeControlTypeDurationText) ]) { + [controls addObject:_cachedControls[ @(ASVideoPlayerNodeControlTypeDurationText) ]]; + } + + if (_cachedControls[ @(ASVideoPlayerNodeControlTypeFullScreenButton) ]) { + [controls addObject:_cachedControls[ @(ASVideoPlayerNodeControlTypeFullScreenButton) ]]; + } + + return controls; +} + + +#pragma mark - Layout + +- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize +{ + CGSize maxSize = constrainedSize.max; + + // Prevent crashes through if infinite width or height + if (isinf(maxSize.width) || isinf(maxSize.height)) { + ASDisplayNodeAssert(NO, @"Infinite width or height in ASVideoPlayerNode"); + maxSize = CGSizeZero; + } + + _videoNode.style.preferredSize = maxSize; + + ASLayoutSpec *layoutSpec; + if (_delegateFlags.delegateLayoutSpecForControls) { + layoutSpec = [_delegate videoPlayerNodeLayoutSpec:self forControls:_cachedControls forMaximumSize:maxSize]; + } else { + layoutSpec = [self defaultLayoutSpecThatFits:maxSize]; + } + + NSMutableArray *children = [[NSMutableArray alloc] init]; + + if (_spinnerNode) { + ASCenterLayoutSpec *centerLayoutSpec = [ASCenterLayoutSpec centerLayoutSpecWithCenteringOptions:ASCenterLayoutSpecCenteringXY sizingOptions:ASCenterLayoutSpecSizingOptionDefault child:_spinnerNode]; + centerLayoutSpec.style.preferredSize = maxSize; + [children addObject:centerLayoutSpec]; + } + + ASOverlayLayoutSpec *overlaySpec = [ASOverlayLayoutSpec overlayLayoutSpecWithChild:_videoNode overlay:layoutSpec]; + overlaySpec.style.preferredSize = maxSize; + [children addObject:overlaySpec]; + + return [ASAbsoluteLayoutSpec absoluteLayoutSpecWithChildren:children]; +} + +- (ASLayoutSpec *)defaultLayoutSpecThatFits:(CGSize)maxSize +{ + _scrubberNode.style.preferredSize = CGSizeMake(maxSize.width, 44.0); + + ASLayoutSpec *spacer = [[ASLayoutSpec alloc] init]; + spacer.style.flexGrow = 1.0; + + ASStackLayoutSpec *controlbarSpec = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal + spacing:10.0 + justifyContent:ASStackLayoutJustifyContentStart + alignItems:ASStackLayoutAlignItemsCenter + children: [self controlsForLayoutSpec] ]; + controlbarSpec.style.alignSelf = ASStackLayoutAlignSelfStretch; + + UIEdgeInsets insets = UIEdgeInsetsMake(10.0, 10.0, 10.0, 10.0); + + ASInsetLayoutSpec *controlbarInsetSpec = [ASInsetLayoutSpec insetLayoutSpecWithInsets:insets child:controlbarSpec]; + + controlbarInsetSpec.style.alignSelf = ASStackLayoutAlignSelfStretch; + + ASStackLayoutSpec *mainVerticalStack = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical + spacing:0.0 + justifyContent:ASStackLayoutJustifyContentStart + alignItems:ASStackLayoutAlignItemsStart + children:@[spacer,controlbarInsetSpec]]; + + return mainVerticalStack; +} + +#pragma mark - Properties +- (id)delegate +{ + return _delegate; +} + +- (void)setDelegate:(id)delegate +{ + if (delegate == _delegate) { + return; + } + + _delegate = delegate; + + if (_delegate == nil) { + memset(&_delegateFlags, 0, sizeof(_delegateFlags)); + } else { + _delegateFlags.delegateNeededDefaultControls = [_delegate respondsToSelector:@selector(videoPlayerNodeNeededDefaultControls:)]; + _delegateFlags.delegateCustomControls = [_delegate respondsToSelector:@selector(videoPlayerNodeCustomControls:)]; + _delegateFlags.delegateSpinnerTintColor = [_delegate respondsToSelector:@selector(videoPlayerNodeSpinnerTint:)]; + _delegateFlags.delegateSpinnerStyle = [_delegate respondsToSelector:@selector(videoPlayerNodeSpinnerStyle:)]; + _delegateFlags.delegateScrubberMaximumTrackTintColor = [_delegate respondsToSelector:@selector(videoPlayerNodeScrubberMaximumTrackTint:)]; + _delegateFlags.delegateScrubberMinimumTrackTintColor = [_delegate respondsToSelector:@selector(videoPlayerNodeScrubberMinimumTrackTint:)]; + _delegateFlags.delegateScrubberThumbTintColor = [_delegate respondsToSelector:@selector(videoPlayerNodeScrubberThumbTint:)]; + _delegateFlags.delegateScrubberThumbImage = [_delegate respondsToSelector:@selector(videoPlayerNodeScrubberThumbImage:)]; + _delegateFlags.delegateTimeLabelAttributes = [_delegate respondsToSelector:@selector(videoPlayerNodeTimeLabelAttributes:timeLabelType:)]; + _delegateFlags.delegateLayoutSpecForControls = [_delegate respondsToSelector:@selector(videoPlayerNodeLayoutSpec:forControls:forMaximumSize:)]; + _delegateFlags.delegateVideoNodeDidPlayToTime = [_delegate respondsToSelector:@selector(videoPlayerNode:didPlayToTime:)]; + _delegateFlags.delegateVideoNodeWillChangeState = [_delegate respondsToSelector:@selector(videoPlayerNode:willChangeVideoNodeState:toVideoNodeState:)]; + _delegateFlags.delegateVideoNodePlaybackDidFinish = [_delegate respondsToSelector:@selector(videoPlayerNodeDidPlayToEnd:)]; + _delegateFlags.delegateVideoNodeShouldChangeState = [_delegate respondsToSelector:@selector(videoPlayerNode:shouldChangeVideoNodeStateTo:)]; + _delegateFlags.delegateTimeLabelAttributedString = [_delegate respondsToSelector:@selector(videoPlayerNode:timeStringForTimeLabelType:forTime:)]; + _delegateFlags.delegatePlaybackButtonTint = [_delegate respondsToSelector:@selector(videoPlayerNodePlaybackButtonTint:)]; + _delegateFlags.delegateFullScreenButtonImage = [_delegate respondsToSelector:@selector(videoPlayerNodeFullScreenButtonImage:)]; + _delegateFlags.delegateDidTapVideoPlayerNode = [_delegate respondsToSelector:@selector(didTapVideoPlayerNode:)]; + _delegateFlags.delegateDidTapFullScreenButtonNode = [_delegate respondsToSelector:@selector(didTapFullScreenButtonNode:)]; + _delegateFlags.delegateVideoPlayerNodeDidSetCurrentItem = [_delegate respondsToSelector:@selector(videoPlayerNode:didSetCurrentItem:)]; + _delegateFlags.delegateVideoPlayerNodeDidStallAtTimeInterval = [_delegate respondsToSelector:@selector(videoPlayerNode:didStallAtTimeInterval:)]; + _delegateFlags.delegateVideoPlayerNodeDidStartInitialLoading = [_delegate respondsToSelector:@selector(videoPlayerNodeDidStartInitialLoading:)]; + _delegateFlags.delegateVideoPlayerNodeDidFinishInitialLoading = [_delegate respondsToSelector:@selector(videoPlayerNodeDidFinishInitialLoading:)]; + _delegateFlags.delegateVideoPlayerNodeDidRecoverFromStall = [_delegate respondsToSelector:@selector(videoPlayerNodeDidRecoverFromStall:)]; + } +} + +- (void)setControlsDisabled:(BOOL)controlsDisabled +{ + if (_controlsDisabled == controlsDisabled) { + return; + } + + _controlsDisabled = controlsDisabled; + + if (_controlsDisabled && _cachedControls.count > 0) { + [self removeControls]; + } else if (!_controlsDisabled) { + [self createControls]; + } +} + +- (void)setShouldAutoPlay:(BOOL)shouldAutoPlay +{ + if (_shouldAutoPlay == shouldAutoPlay) { + return; + } + _shouldAutoPlay = shouldAutoPlay; + _videoNode.shouldAutoplay = _shouldAutoPlay; +} + +- (void)setShouldAutoRepeat:(BOOL)shouldAutoRepeat +{ + if (_shouldAutoRepeat == shouldAutoRepeat) { + return; + } + _shouldAutoRepeat = shouldAutoRepeat; + _videoNode.shouldAutorepeat = _shouldAutoRepeat; +} + +- (void)setMuted:(BOOL)muted +{ + if (_muted == muted) { + return; + } + _muted = muted; + _videoNode.muted = _muted; +} + +- (void)setPeriodicTimeObserverTimescale:(int32_t)periodicTimeObserverTimescale +{ + if (_periodicTimeObserverTimescale == periodicTimeObserverTimescale) { + return; + } + _periodicTimeObserverTimescale = periodicTimeObserverTimescale; + _videoNode.periodicTimeObserverTimescale = _periodicTimeObserverTimescale; +} + +- (NSString *)gravity +{ + if (_gravity == nil) { + _gravity = _videoNode.gravity; + } + return _gravity; +} + +- (void)setGravity:(NSString *)gravity +{ + if (_gravity == gravity) { + return; + } + _gravity = gravity; + _videoNode.gravity = _gravity; +} + +- (ASVideoNodePlayerState)playerState +{ + return _videoNode.playerState; +} + +- (BOOL)shouldAggressivelyRecoverFromStall +{ + return _videoNode.shouldAggressivelyRecoverFromStall; +} + +- (void) setPlaceholderImageURL:(NSURL *)placeholderImageURL +{ + _videoNode.URL = placeholderImageURL; +} + +- (NSURL*) placeholderImageURL +{ + return _videoNode.URL; +} + +- (ASVideoNode*)videoNode +{ + return _videoNode; +} + +- (void)setShouldAggressivelyRecoverFromStall:(BOOL)shouldAggressivelyRecoverFromStall +{ + if (_shouldAggressivelyRecoverFromStall == shouldAggressivelyRecoverFromStall) { + return; + } + _shouldAggressivelyRecoverFromStall = shouldAggressivelyRecoverFromStall; + _videoNode.shouldAggressivelyRecoverFromStall = _shouldAggressivelyRecoverFromStall; +} + +#pragma mark - Helpers +- (NSString *)timeStringForCMTime:(CMTime)time forTimeLabelType:(ASVideoPlayerNodeControlType)type +{ + if (!CMTIME_IS_VALID(time)) { + return @"00:00"; + } + if (_delegateFlags.delegateTimeLabelAttributedString) { + return [_delegate videoPlayerNode:self timeStringForTimeLabelType:type forTime:time]; + } + + NSUInteger dTotalSeconds = CMTimeGetSeconds(time); + + NSUInteger dHours = floor(dTotalSeconds / 3600); + NSUInteger dMinutes = floor(dTotalSeconds % 3600 / 60); + NSUInteger dSeconds = floor(dTotalSeconds % 3600 % 60); + + NSString *videoDurationText; + if (dHours > 0) { + videoDurationText = [NSString stringWithFormat:@"%i:%02i:%02i", (int)dHours, (int)dMinutes, (int)dSeconds]; + } else { + videoDurationText = [NSString stringWithFormat:@"%02i:%02i", (int)dMinutes, (int)dSeconds]; + } + return videoDurationText; +} + +@end + +#endif // TARGET_OS_IOS + +#endif diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASVisibilityProtocols.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASVisibilityProtocols.h new file mode 100644 index 0000000..b9f5c7b --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASVisibilityProtocols.h @@ -0,0 +1,145 @@ +// +// ASVisibilityProtocols.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@class UIViewController; + +ASDK_EXTERN ASLayoutRangeMode ASLayoutRangeModeForVisibilityDepth(NSUInteger visibilityDepth); + +/** + * ASVisibilityDepth + * + * @discussion "Visibility Depth" represents the number of user actions required to make an ASDisplayNode or + * ASDKViewController visibile. AsyncDisplayKit uses this information to intelligently manage memory and focus + * resources where they are most visible to the user. + * + * The ASVisibilityDepth protocol describes how custom view controllers can integrate with this system. + * + * Parent view controllers should also implement @c ASManagesChildVisibilityDepth + * + * @see ASManagesChildVisibilityDepth + */ + +@protocol ASVisibilityDepth + +/** + * Visibility depth + * + * @discussion Represents the number of user actions necessary to reach the view controller. An increased visibility + * depth indicates a higher number of user interactions for the view controller to be visible again. For example, + * an onscreen navigation controller's top view controller should have a visibility depth of 0. The view controller + * one from the top should have a visibility depth of 1 as should the root view controller in the stack (because + * the user can hold the back button to pop to the root view controller). + * + * Visibility depth is used to automatically adjust ranges on range controllers (and thus free up memory) and can + * be used to reduce memory usage of other items as well. + */ +- (NSInteger)visibilityDepth; + +/** + * Called when visibility depth changes + * + * @discussion @c visibilityDepthDidChange is called whenever the visibility depth of the represented view controller + * has changed. + * + * If implemented by a view controller container, use this method to notify child view controllers that their view + * depth has changed @see ASNavigationController.m + * + * If implemented on an ASDKViewController, use this method to reduce or increase the resources that your + * view controller uses. A higher visibility depth view controller should decrease it's resource usage, a lower + * visibility depth controller should pre-warm resources in preperation for a display at 0 depth. + * + * ASDKViewController implements this method and reduces / increases range mode of supporting nodes (such as ASCollectionNode + * and ASTableNode). + * + * @see visibilityDepth + */ +- (void)visibilityDepthDidChange; + +@end + +/** + * ASManagesChildVisibilityDepth + * + * @discussion A protocol which should be implemented by container view controllers to allow proper + * propagation of visibility depth + * + * @see ASVisibilityDepth + */ +@protocol ASManagesChildVisibilityDepth + +/** + * @abstract Container view controllers should adopt this protocol to indicate that they will manage their child's + * visibilityDepth. For example, ASNavigationController adopts this protocol and manages its childrens visibility + * depth. + * + * If you adopt this protocol, you *must* also emit visibilityDepthDidChange messages to child view controllers. + * + * @param childViewController Expected to return the visibility depth of the child view controller. + */ +- (NSInteger)visibilityDepthOfChildViewController:(UIViewController *)childViewController; + +@end + +#define ASVisibilitySetVisibilityDepth \ +- (void)setVisibilityDepth:(NSUInteger)visibilityDepth \ +{ \ + if (_visibilityDepth == visibilityDepth) { \ + return; \ + } \ + _visibilityDepth = visibilityDepth; \ + [self visibilityDepthDidChange]; \ +} + +#define ASVisibilityDepthImplementation \ +- (NSInteger)visibilityDepth \ +{ \ + if (self.parentViewController && _parentManagesVisibilityDepth == NO) { \ + _parentManagesVisibilityDepth = [self.parentViewController conformsToProtocol:@protocol(ASManagesChildVisibilityDepth)]; \ + } \ + \ + if (_parentManagesVisibilityDepth) { \ + return [(id )self.parentViewController visibilityDepthOfChildViewController:self]; \ + } \ + return _visibilityDepth; \ +} + +#define ASVisibilityViewDidDisappearImplementation \ +- (void)viewDidDisappear:(BOOL)animated \ +{ \ + [super viewDidDisappear:animated]; \ + \ + if (_parentManagesVisibilityDepth == NO) { \ + [self setVisibilityDepth:1]; \ + } \ +} + +#define ASVisibilityViewWillAppear \ +- (void)viewWillAppear:(BOOL)animated \ +{ \ + [super viewWillAppear:animated]; \ + \ + if (_parentManagesVisibilityDepth == NO) { \ + [self setVisibilityDepth:0]; \ + } \ +} + +#define ASVisibilityDidMoveToParentViewController \ +- (void)didMoveToParentViewController:(UIViewController *)parent \ +{ \ + [super didMoveToParentViewController:parent]; \ + _parentManagesVisibilityDepth = NO; \ + [self visibilityDepthDidChange]; \ +} + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASVisibilityProtocols.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASVisibilityProtocols.mm new file mode 100644 index 0000000..b13a683 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/ASVisibilityProtocols.mm @@ -0,0 +1,22 @@ +// +// ASVisibilityProtocols.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +ASLayoutRangeMode ASLayoutRangeModeForVisibilityDepth(NSUInteger visibilityDepth) +{ + if (visibilityDepth == 0) { + return ASLayoutRangeModeFull; + } else if (visibilityDepth == 1) { + return ASLayoutRangeModeMinimum; + } else if (visibilityDepth == 2) { + return ASLayoutRangeModeVisibleOnly; + } + return ASLayoutRangeModeLowMemory; +} diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/AsyncDisplayKit+IGListKitMethods.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/AsyncDisplayKit+IGListKitMethods.h new file mode 100644 index 0000000..aa0de42 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/AsyncDisplayKit+IGListKitMethods.h @@ -0,0 +1,64 @@ +// +// AsyncDisplayKit+IGListKitMethods.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#if AS_IG_LIST_KIT + +#import +#import +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * If you are using AsyncDisplayKit with IGListKit, you should use + * these methods to provide implementations for methods like + * -cellForItemAtIndex: that don't apply when used with AsyncDisplayKit. + * + * Your section controllers should also conform to @c ASSectionController and your + * supplementary view sources should conform to @c ASSupplementaryNodeSource. + */ + +AS_SUBCLASSING_RESTRICTED +@interface ASIGListSectionControllerMethods : NSObject + +/** + * Call this for your section controller's @c cellForItemAtIndex: method. + */ ++ (__kindof UICollectionViewCell *)cellForItemAtIndex:(NSInteger)index sectionController:(IGListSectionController *)sectionController; + +/** + * Call this for your section controller's @c sizeForItemAtIndex: method. + */ ++ (CGSize)sizeForItemAtIndex:(NSInteger)index; + +@end + +AS_SUBCLASSING_RESTRICTED +@interface ASIGListSupplementaryViewSourceMethods : NSObject + +/** + * Call this for your supplementary source's @c viewForSupplementaryElementOfKind:atIndex: method. + */ ++ (__kindof UICollectionReusableView *)viewForSupplementaryElementOfKind:(NSString *)elementKind + atIndex:(NSInteger)index + sectionController:(IGListSectionController *)sectionController; + +/** + * Call this for your supplementary source's @c sizeForSupplementaryViewOfKind:atIndex: method. + */ ++ (CGSize)sizeForSupplementaryViewOfKind:(NSString *)elementKind atIndex:(NSInteger)index; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/AsyncDisplayKit+IGListKitMethods.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/AsyncDisplayKit+IGListKitMethods.mm new file mode 100644 index 0000000..50d7a21 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/AsyncDisplayKit+IGListKitMethods.mm @@ -0,0 +1,53 @@ +// +// AsyncDisplayKit+IGListKitMethods.m +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#if AS_IG_LIST_KIT + +#import "AsyncDisplayKit+IGListKitMethods.h" +#import +#import +#import + + +@implementation ASIGListSectionControllerMethods + ++ (__kindof UICollectionViewCell *)cellForItemAtIndex:(NSInteger)index sectionController:(IGListSectionController *)sectionController +{ + // Cast to id for backwards-compatibility until 3.0.0 is officially released – IGListSectionType was removed. This is safe. + return [sectionController.collectionContext dequeueReusableCellOfClass:[_ASCollectionViewCell class] forSectionController:(id)sectionController atIndex:index]; +} + ++ (CGSize)sizeForItemAtIndex:(NSInteger)index +{ + ASDisplayNodeFailAssert(@"Did not expect %@ to be called.", NSStringFromSelector(_cmd)); + return CGSizeZero; +} + +@end + +@implementation ASIGListSupplementaryViewSourceMethods + ++ (__kindof UICollectionReusableView *)viewForSupplementaryElementOfKind:(NSString *)elementKind + atIndex:(NSInteger)index + sectionController:(IGListSectionController *)sectionController +{ + return [sectionController.collectionContext dequeueReusableSupplementaryViewOfKind:elementKind forSectionController:(id)sectionController class:[_ASCollectionReusableView class] atIndex:index]; +} + ++ (CGSize)sizeForSupplementaryViewOfKind:(NSString *)elementKind atIndex:(NSInteger)index +{ + ASDisplayNodeFailAssert(@"Did not expect %@ to be called.", NSStringFromSelector(_cmd)); + return CGSizeZero; +} + +@end + +#endif // AS_IG_LIST_KIT diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/AsyncDisplayKit.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/AsyncDisplayKit.h new file mode 100644 index 0000000..2ac6e9b --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/AsyncDisplayKit.h @@ -0,0 +1,133 @@ +// +// AsyncDisplayKit.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import +#import +#import +#import +#import +#import +#import + +#import +#import +#import +#import +#import +#import +#import + +#import +#import + +#import +#import +#import +#import +#import +#import +#import + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + +#import +#import +#import +#import +#import +#import + +#import +#import + +#import + +#import +#import +#import + +#import +#import +#import +#import +#import + +#import + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + +#import +#import +#import +#import +#import +#import +#import +#import + +#import +#import + +#import +#import +#import +#import diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Base/ASAssert.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Base/ASAssert.h new file mode 100644 index 0000000..4445b24 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Base/ASAssert.h @@ -0,0 +1,101 @@ +// +// ASAssert.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#pragma once + +#import +#import +#import + +#if !defined(NS_BLOCK_ASSERTIONS) + #define ASDISPLAYNODE_ASSERTIONS_ENABLED 1 +#else + #define ASDISPLAYNODE_ASSERTIONS_ENABLED 0 +#endif + +/** + * Note: In some cases it would be sufficient to do e.g.: + * ASDisplayNodeAssert(...) NSAssert(__VA_ARGS__) + * but we prefer not to, because we want to match the autocomplete behavior of NSAssert. + * The construction listed above does not show the user what arguments are required and what are optional. + */ + +#define ASDisplayNodeAssert(condition, desc, ...) NSAssert(condition, desc, ##__VA_ARGS__) +#define ASDisplayNodeCAssert(condition, desc, ...) NSCAssert(condition, desc, ##__VA_ARGS__) + +#define ASDisplayNodeAssertNil(condition, desc, ...) ASDisplayNodeAssert((condition) == nil, desc, ##__VA_ARGS__) +#define ASDisplayNodeCAssertNil(condition, desc, ...) ASDisplayNodeCAssert((condition) == nil, desc, ##__VA_ARGS__) + +#define ASDisplayNodeAssertNotNil(condition, desc, ...) ASDisplayNodeAssert((condition) != nil, desc, ##__VA_ARGS__) +#define ASDisplayNodeCAssertNotNil(condition, desc, ...) ASDisplayNodeCAssert((condition) != nil, desc, ##__VA_ARGS__) + +#define ASDisplayNodeAssertImplementedBySubclass() ASDisplayNodeAssert(NO, @"This method must be implemented by subclass %@", [self class]); +#define ASDisplayNodeAssertNotInstantiable() ASDisplayNodeAssert(NO, nil, @"This class is not instantiable."); +#define ASDisplayNodeAssertNotSupported() ASDisplayNodeAssert(NO, nil, @"This method is not supported by class %@", [self class]); + +#define ASDisplayNodeAssertMainThread() ASDisplayNodeAssert(ASMainThreadAssertionsAreDisabled() || 0 != pthread_main_np(), @"This method must be called on the main thread") +#define ASDisplayNodeCAssertMainThread() ASDisplayNodeCAssert(ASMainThreadAssertionsAreDisabled() || 0 != pthread_main_np(), @"This function must be called on the main thread") + +#define ASDisplayNodeAssertNotMainThread() ASDisplayNodeAssert(0 == pthread_main_np(), @"This method must be called off the main thread") +#define ASDisplayNodeCAssertNotMainThread() ASDisplayNodeCAssert(0 == pthread_main_np(), @"This function must be called off the main thread") + +#define ASDisplayNodeAssertFlag(X, desc, ...) ASDisplayNodeAssert((1 == __builtin_popcount(X)), desc, ##__VA_ARGS__) +#define ASDisplayNodeCAssertFlag(X, desc, ...) ASDisplayNodeCAssert((1 == __builtin_popcount(X)), desc, ##__VA_ARGS__) + +#define ASDisplayNodeAssertTrue(condition) ASDisplayNodeAssert((condition), @"Expected %s to be true.", #condition) +#define ASDisplayNodeCAssertTrue(condition) ASDisplayNodeCAssert((condition), @"Expected %s to be true.", #condition) + +#define ASDisplayNodeAssertFalse(condition) ASDisplayNodeAssert(!(condition), @"Expected %s to be false.", #condition) +#define ASDisplayNodeCAssertFalse(condition) ASDisplayNodeCAssert(!(condition), @"Expected %s to be false.", #condition) + +#define ASDisplayNodeFailAssert(desc, ...) ASDisplayNodeAssert(NO, desc, ##__VA_ARGS__) +#define ASDisplayNodeCFailAssert(desc, ...) ASDisplayNodeCAssert(NO, desc, ##__VA_ARGS__) + +#define ASDisplayNodeConditionalAssert(shouldTestCondition, condition, desc, ...) ASDisplayNodeAssert((!(shouldTestCondition) || (condition)), desc, ##__VA_ARGS__) +#define ASDisplayNodeConditionalCAssert(shouldTestCondition, condition, desc, ...) ASDisplayNodeCAssert((!(shouldTestCondition) || (condition)), desc, ##__VA_ARGS__) + +#define ASDisplayNodeCAssertPositiveReal(description, num) ASDisplayNodeCAssert(num >= 0 && num <= CGFLOAT_MAX, @"%@ must be a real positive integer: %f.", description, (CGFloat)num) +#define ASDisplayNodeCAssertInfOrPositiveReal(description, num) ASDisplayNodeCAssert(isinf(num) || (num >= 0 && num <= CGFLOAT_MAX), @"%@ must be infinite or a real positive integer: %f.", description, (CGFloat)num) + +#define ASDisplayNodeCAssertPermanent(object) ASDisplayNodeCAssert(CFGetRetainCount((__bridge CFTypeRef)(object)) == CFGetRetainCount(kCFNull), @"Expected %s to be a permanent object.", #object) +#define ASDisplayNodeErrorDomain @"ASDisplayNodeErrorDomain" +#define ASDisplayNodeNonFatalErrorCode 1 + +/** + * In debug methods, it can be useful to disable main thread assertions to get valuable information, + * even if it means violating threading requirements. These functions are used in -debugDescription and let + * threads decide to suppress/re-enable main thread assertions. + */ +#pragma mark - Main Thread Assertions Disabling + +ASDK_EXTERN BOOL ASMainThreadAssertionsAreDisabled(void); + +ASDK_EXTERN void ASPushMainThreadAssertionsDisabled(void); + +ASDK_EXTERN void ASPopMainThreadAssertionsDisabled(void); + +#pragma mark - Non-Fatal Assertions + +/// Returns YES if assertion passed, NO otherwise. +#define ASDisplayNodeAssertNonFatal(condition, desc, ...) ({ \ + BOOL __evaluated = condition; \ + if (__evaluated == NO) { \ + ASDisplayNodeFailAssert(desc, ##__VA_ARGS__); \ + ASDisplayNodeNonFatalErrorBlock block = [ASDisplayNode nonFatalErrorBlock]; \ + if (block != nil) { \ + NSDictionary *userInfo = nil; \ + if (desc.length > 0) { \ + userInfo = @{ NSLocalizedDescriptionKey : desc }; \ + } \ + NSError *error = [NSError errorWithDomain:ASDisplayNodeErrorDomain code:ASDisplayNodeNonFatalErrorCode userInfo:userInfo]; \ + block(error); \ + } \ + } \ + __evaluated; \ +}) \ diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Base/ASAssert.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Base/ASAssert.mm new file mode 100644 index 0000000..6a78b06 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Base/ASAssert.mm @@ -0,0 +1,58 @@ +// +// ASAssert.mm +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +#if AS_TLS_AVAILABLE + +static _Thread_local int tls_mainThreadAssertionsDisabledCount; +BOOL ASMainThreadAssertionsAreDisabled() { + return tls_mainThreadAssertionsDisabledCount > 0; +} + +void ASPushMainThreadAssertionsDisabled() { + tls_mainThreadAssertionsDisabledCount += 1; +} + +void ASPopMainThreadAssertionsDisabled() { + tls_mainThreadAssertionsDisabledCount -= 1; + ASDisplayNodeCAssert(tls_mainThreadAssertionsDisabledCount >= 0, @"Attempt to pop thread assertion-disabling without corresponding push."); +} + +#else + +#import + +static pthread_key_t ASMainThreadAssertionsDisabledKey() { + static pthread_key_t k; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + pthread_key_create(&k, NULL); + }); + return k; +} + +BOOL ASMainThreadAssertionsAreDisabled() { + return (nullptr != pthread_getspecific(ASMainThreadAssertionsDisabledKey())); +} + +void ASPushMainThreadAssertionsDisabled() { + const auto key = ASMainThreadAssertionsDisabledKey(); + const auto oldVal = (intptr_t)pthread_getspecific(key); + pthread_setspecific(key, (void *)(oldVal + 1)); +} + +void ASPopMainThreadAssertionsDisabled() { + const auto key = ASMainThreadAssertionsDisabledKey(); + const auto oldVal = (intptr_t)pthread_getspecific(key); + pthread_setspecific(key, (void *)(oldVal - 1)); + ASDisplayNodeCAssert(oldVal > 0, @"Attempt to pop thread assertion-disabling without corresponding push."); +} + +#endif // AS_TLS_AVAILABLE diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Base/ASAvailability.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Base/ASAvailability.h new file mode 100644 index 0000000..b210397 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Base/ASAvailability.h @@ -0,0 +1,95 @@ +// +// ASAvailability.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#pragma once + +#ifdef __i386__ + #define AS_TLS_AVAILABLE 0 +#else + #define AS_TLS_AVAILABLE 1 +#endif + +#ifndef AS_ENABLE_TEXTNODE + #define AS_ENABLE_TEXTNODE 1 // Enable old TextNode by default +#endif + +// This needs to stay in sync with Weaver +#ifndef AS_USE_VIDEO + #define AS_USE_VIDEO 0 +#endif + +#ifndef AS_USE_PHOTOS + #define AS_USE_PHOTOS 0 +#endif + +#ifndef AS_USE_MAPKIT + #define AS_USE_MAPKIT 0 +#endif + +#ifndef AS_USE_ASSETS_LIBRARY + #define AS_USE_ASSETS_LIBRARY 0 +#endif + +#ifndef kCFCoreFoundationVersionNumber_iOS_10_0 + #define kCFCoreFoundationVersionNumber_iOS_10_0 1348.00 +#endif + +#ifndef kCFCoreFoundationVersionNumber_iOS_11_0 + #define kCFCoreFoundationVersionNumber_iOS_11_0 1438.10 +#endif + +#ifndef __IPHONE_11_0 + #define __IPHONE_11_0 110000 +#endif + +#ifndef __IPHONE_13_0 + #define __IPHONE_13_0 130000 +#endif + +#define AS_AT_LEAST_IOS10 (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_10_0) +#define AS_AT_LEAST_IOS11 (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_11_0) +#define AS_AT_LEAST_IOS13 (__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0) + +// Use __builtin_available if we're on Xcode >= 9, AS_AT_LEAST otherwise. +#if __has_builtin(__builtin_available) + #define AS_AVAILABLE_IOS(ver) __builtin_available(iOS ver, *) + #define AS_AVAILABLE_TVOS(ver) __builtin_available(tvOS ver, *) + #define AS_AVAILABLE_IOS_TVOS(ver1, ver2) __builtin_available(iOS ver1, tvOS ver2, *) +#else + #define AS_AVAILABLE_IOS(ver) (TARGET_OS_IOS && AS_AT_LEAST_IOS##ver) + #define AS_AVAILABLE_TVOS(ver) (TARGET_OS_TV && AS_AT_LEAST_IOS##ver) + #define AS_AVAILABLE_IOS_TVOS(ver1, ver2) (AS_AVAILABLE_IOS(ver1) || AS_AVAILABLE_TVOS(ver2)) +#endif + +// If Yoga is available, make it available anywhere we use ASAvailability. +// This reduces Yoga-specific code in other files. +// NOTE: Yoga integration is experimental and not fully tested. Use with caution and test layouts carefully. +#ifndef YOGA_HEADER_PATH + #define YOGA_HEADER_PATH +#endif + +#ifndef YOGA + #define YOGA __has_include(YOGA_HEADER_PATH) +#endif + +#ifdef ASTEXTNODE_EXPERIMENT_GLOBAL_ENABLE + #error "ASTEXTNODE_EXPERIMENT_GLOBAL_ENABLE is unavailable. See ASConfiguration.h." +#endif + +#define AS_PIN_REMOTE_IMAGE __has_include() +#define AS_IG_LIST_KIT __has_include() +#define AS_IG_LIST_DIFF_KIT __has_include() + +/** + * For IGListKit versions < 3.0, you have to use IGListCollectionView. + * For 3.0 and later, that class is removed and you use UICollectionView. + */ +#define IG_LIST_COLLECTION_VIEW __has_include() diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Base/ASBaseDefines.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Base/ASBaseDefines.h new file mode 100644 index 0000000..53ea66a --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Base/ASBaseDefines.h @@ -0,0 +1,237 @@ +// +// ASBaseDefines.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#define ASDK_EXTERN FOUNDATION_EXTERN +#define unowned __unsafe_unretained + +/** + * Decorates methods that clients can implement in categories on our base class. These methods + * will be stubbed with an empty implementation if no implementation is provided. + */ +#define AS_CATEGORY_IMPLEMENTABLE + +#ifdef __GNUC__ +# define ASDISPLAYNODE_GNUC(major, minor) \ +(__GNUC__ > (major) || (__GNUC__ == (major) && __GNUC_MINOR__ >= (minor))) +#else +# define ASDISPLAYNODE_GNUC(major, minor) 0 +#endif + +#ifndef ASDISPLAYNODE_INLINE +# if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +# define ASDISPLAYNODE_INLINE static inline +# elif defined (__MWERKS__) || defined (__cplusplus) +# define ASDISPLAYNODE_INLINE static inline +# elif ASDISPLAYNODE_GNUC (3, 0) +# define ASDISPLAYNODE_INLINE static __inline__ __attribute__ ((always_inline)) +# else +# define ASDISPLAYNODE_INLINE static +# endif +#endif + +#ifndef ASDISPLAYNODE_WARN_DEPRECATED +# define ASDISPLAYNODE_WARN_DEPRECATED 1 +#endif + +#ifndef ASDISPLAYNODE_DEPRECATED +# if ASDISPLAYNODE_GNUC (3, 0) && ASDISPLAYNODE_WARN_DEPRECATED +# define ASDISPLAYNODE_DEPRECATED __attribute__ ((deprecated)) +# else +# define ASDISPLAYNODE_DEPRECATED +# endif +#endif + +#ifndef ASDISPLAYNODE_DEPRECATED_MSG +# if ASDISPLAYNODE_GNUC (3, 0) && ASDISPLAYNODE_WARN_DEPRECATED +# define ASDISPLAYNODE_DEPRECATED_MSG(msg) __deprecated_msg(msg) +# else +# define ASDISPLAYNODE_DEPRECATED_MSG(msg) +# endif +#endif + +#ifndef AS_ENABLE_TIPS +#define AS_ENABLE_TIPS 0 +#endif + +#ifndef __has_feature // Optional. +#define __has_feature(x) 0 // Compatibility with non-clang compilers. +#endif + +#ifndef __has_attribute // Optional. +#define __has_attribute(x) 0 // Compatibility with non-clang compilers. +#endif + +#ifndef NS_RETURNS_RETAINED +#if __has_feature(attribute_ns_returns_retained) +#define NS_RETURNS_RETAINED __attribute__((ns_returns_retained)) +#else +#define NS_RETURNS_RETAINED +#endif +#endif + +#ifndef CF_RETURNS_RETAINED +#if __has_feature(attribute_cf_returns_retained) +#define CF_RETURNS_RETAINED __attribute__((cf_returns_retained)) +#else +#define CF_RETURNS_RETAINED +#endif +#endif + +#ifndef ASDISPLAYNODE_REQUIRES_SUPER +#if __has_attribute(objc_requires_super) +#define ASDISPLAYNODE_REQUIRES_SUPER __attribute__((objc_requires_super)) +#else +#define ASDISPLAYNODE_REQUIRES_SUPER +#endif +#endif + +#ifndef AS_UNAVAILABLE +#if __has_attribute(unavailable) +#define AS_UNAVAILABLE(message) __attribute__((unavailable(message))) +#else +#define AS_UNAVAILABLE(message) +#endif +#endif + +#ifndef AS_WARN_UNUSED_RESULT +#if __has_attribute(warn_unused_result) +#define AS_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) +#else +#define AS_WARN_UNUSED_RESULT +#endif +#endif + +#define ASOVERLOADABLE __attribute__((overloadable)) + +/// Xcode >= 10. +#define AS_HAS_OS_SIGNPOST __has_include() + +#if __has_attribute(noescape) +#define AS_NOESCAPE __attribute__((noescape)) +#else +#define AS_NOESCAPE +#endif + +#if __has_attribute(objc_subclassing_restricted) +#define AS_SUBCLASSING_RESTRICTED __attribute__((objc_subclassing_restricted)) +#else +#define AS_SUBCLASSING_RESTRICTED +#endif + +#define AS_ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) + +#define ASCreateOnce(expr) ({ \ + static dispatch_once_t onceToken; \ + static __typeof__(expr) staticVar; \ + dispatch_once(&onceToken, ^{ \ + staticVar = expr; \ + }); \ + staticVar; \ +}) + +/// Ensure that class is of certain kind +#define ASDynamicCast(x, c) ({ \ + id __val = x;\ + ((c *) ([__val isKindOfClass:[c class]] ? __val : nil));\ +}) + +/// Ensure that class is of certain kind, assuming it is subclass restricted +#define ASDynamicCastStrict(x, c) ({ \ + id __val = x;\ + ((c *) ([__val class] == [c class] ? __val : nil));\ +}) + +// Compare two primitives, assign if different. Returns whether the assignment happened. +#define ASCompareAssign(lvalue, newValue) ({ \ + BOOL result = (lvalue != newValue); \ + if (result) { lvalue = newValue; } \ + result; \ +}) + +#define ASCompareAssignObjects(lvalue, newValue) \ + ASCompareAssignCustom(lvalue, newValue, ASObjectIsEqual) + +// e.g. ASCompareAssignCustom(_myInsets, insets, UIEdgeInsetsEqualToEdgeInsets) +#define ASCompareAssignCustom(lvalue, newValue, isequal) ({ \ + BOOL result = !(isequal(lvalue, newValue)); \ + if (result) { lvalue = newValue; } \ + result; \ +}) + +#define ASCompareAssignCopy(lvalue, newValue) ({ \ + BOOL result = !ASObjectIsEqual(lvalue, newValue); \ + if (result) { lvalue = [newValue copyWithZone:NULL]; } \ + result; \ +}) + +/** + * Create a new set by mapping `collection` over `work`, ignoring nil. + */ +#define ASSetByFlatMapping(collection, decl, work) ({ \ + NSMutableSet *s = [[NSMutableSet alloc] init]; \ + for (decl in collection) {\ + id result = work; \ + if (result != nil) { \ + [s addObject:result]; \ + } \ + } \ + s; \ +}) + +/** + * Create a new ObjectPointerPersonality NSHashTable by mapping `collection` over `work`, ignoring nil. + * + * capacity: 0 is taken from +hashTableWithOptions. + */ +#define ASPointerTableByFlatMapping(collection, decl, work) ({ \ + NSHashTable *t = [[NSHashTable alloc] initWithOptions:NSHashTableObjectPointerPersonality capacity:0]; \ + for (decl in collection) {\ + id result = work; \ + if (result != nil) { \ + [t addObject:result]; \ + } \ + } \ + t; \ +}) + +/** + * Create a new array by mapping `collection` over `work`, ignoring nil. + */ +#define ASArrayByFlatMapping(collectionArg, decl, work) ({ \ + id __collection = collectionArg; \ + NSArray *__result; \ + if (__collection) { \ + id __buf[[__collection count]]; \ + NSUInteger __i = 0; \ + for (decl in __collection) {\ + if ((__buf[__i] = work)) { \ + __i++; \ + } \ + } \ + __result = [NSArray arrayByTransferring:__buf count:__i]; \ + } \ + __result; \ +}) + +/** + * Capture-and-clear a strong reference without the intervening retain/release pair. + * + * E.g. const auto localVar = ASTransferStrong(_myIvar); + * Post-condition: localVar has the strong value from _myIvar and _myIvar is nil. + * No retain/release is emitted when the optimizer is on. + */ +#define ASTransferStrong(lvalue) ({ \ + CFTypeRef *__rawPtr = (CFTypeRef *)(void *)(&(lvalue)); \ + CFTypeRef __cfValue = *__rawPtr; \ + *__rawPtr = NULL; \ + __typeof(lvalue) __result = (__bridge_transfer __typeof(lvalue))__cfValue; \ + __result; \ +}) diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Base/ASDisplayNode+Ancestry.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Base/ASDisplayNode+Ancestry.h new file mode 100644 index 0000000..2c61cee --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Base/ASDisplayNode+Ancestry.h @@ -0,0 +1,55 @@ +// +// ASDisplayNode+Ancestry.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface ASDisplayNode (Ancestry) + +/** + * Returns an object to enumerate the supernode ancestry of this node, starting with its supernode. + * + * For instance, you could write: + * for (ASDisplayNode *node in self.supernodes) { + * if ([node.backgroundColor isEqual:[UIColor blueColor]]) { + * node.hidden = YES; + * } + * } + * + * Note: If this property is read on the main thread, the enumeration will attempt to go up + * the layer hierarchy if it finds a break in the display node hierarchy. + */ +@property (readonly) id supernodes; + +/** + * Same as `supernodes` but begins the enumeration with self. + */ +@property (readonly) id supernodesIncludingSelf; + +/** + * Searches the supernodes of this node for one matching the given class. + * + * @param supernodeClass The class of node you're looking for. + * @param includeSelf Whether to include self in the search. + * @return A node of the given class that is an ancestor of this node, or nil. + * + * @note See the documentation on `supernodes` for details about the upward traversal. + */ +- (nullable __kindof ASDisplayNode *)supernodeOfClass:(Class)supernodeClass includingSelf:(BOOL)includeSelf; + +/** + * e.g. "(, , )" + */ +@property (copy, readonly) NSString *ancestryDescription; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Base/ASDisplayNode+Ancestry.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Base/ASDisplayNode+Ancestry.mm new file mode 100644 index 0000000..7003064 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Base/ASDisplayNode+Ancestry.mm @@ -0,0 +1,90 @@ +// +// ASDisplayNode+Ancestry.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import "ASDisplayNode+Ancestry.h" +#import +#import + +AS_SUBCLASSING_RESTRICTED +@interface ASNodeAncestryEnumerator : NSEnumerator +@end + +@implementation ASNodeAncestryEnumerator { + ASDisplayNode *_lastNode; // This needs to be strong because enumeration will not retain the current batch of objects + BOOL _initialState; +} + +- (instancetype)initWithNode:(ASDisplayNode *)node +{ + if (self = [super init]) { + _initialState = YES; + _lastNode = node; + } + return self; +} + +- (id)nextObject +{ + if (_initialState) { + _initialState = NO; + return _lastNode; + } + + ASDisplayNode *nextNode = _lastNode.supernode; + if (nextNode == nil && ASDisplayNodeThreadIsMain()) { + CALayer *layer = _lastNode.nodeLoaded ? _lastNode.layer.superlayer : nil; + while (layer != nil) { + nextNode = ASLayerToDisplayNode(layer); + if (nextNode != nil) { + break; + } + layer = layer.superlayer; + } + } + _lastNode = nextNode; + return nextNode; +} + +@end + +@implementation ASDisplayNode (Ancestry) + +- (id)supernodes +{ + NSEnumerator *result = [[ASNodeAncestryEnumerator alloc] initWithNode:self]; + [result nextObject]; // discard first object (self) + return result; +} + +- (id)supernodesIncludingSelf +{ + return [[ASNodeAncestryEnumerator alloc] initWithNode:self]; +} + +- (nullable __kindof ASDisplayNode *)supernodeOfClass:(Class)supernodeClass includingSelf:(BOOL)includeSelf +{ + id chain = includeSelf ? self.supernodesIncludingSelf : self.supernodes; + for (ASDisplayNode *ancestor in chain) { + if ([ancestor isKindOfClass:supernodeClass]) { + return ancestor; + } + } + return nil; +} + +- (NSString *)ancestryDescription +{ + NSMutableArray *strings = [NSMutableArray array]; + for (ASDisplayNode *node in self.supernodes) { + [strings addObject:ASObjectDescriptionMakeTiny(node)]; + } + return strings.description; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Base/ASEqualityHelpers.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Base/ASEqualityHelpers.h new file mode 100644 index 0000000..6024595 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Base/ASEqualityHelpers.h @@ -0,0 +1,21 @@ +// +// ASEqualityHelpers.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +/** + @abstract Correctly equates two objects, including cases where both objects are nil. The latter is a case where `isEqual:` fails. + @param obj The first object in the comparison. Can be nil. + @param otherObj The second object in the comparison. Can be nil. + @result YES if the objects are equal, including cases where both object are nil. + */ +ASDISPLAYNODE_INLINE BOOL ASObjectIsEqual(id obj, id otherObj) +{ + return obj == otherObj || [obj isEqual:otherObj]; +} diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Base/ASLog.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Base/ASLog.h new file mode 100644 index 0000000..fb0eb57 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Base/ASLog.h @@ -0,0 +1,131 @@ +// +// ASLog.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import +#import +#import + +#ifndef ASEnableVerboseLogging + #define ASEnableVerboseLogging 0 +#endif + +/** + * Disable all logging. + * + * You should only use this function if the default log level is + * annoying during development. By default, logging is run at + * the appropriate system log level (see the os_log_* functions), + * so you do not need to worry generally about the performance + * implications of log messages. + * + * For example, virtually all log messages generated by Texture + * are at the `debug` log level, which the system + * disables in production. + */ +ASDK_EXTERN void ASDisableLogging(void); + +/** + * Restore logging that has been runtime-disabled via ASDisableLogging(). + * + * Logging can be disabled at runtime using the ASDisableLogging() function. + * This command restores logging to the level provided in the build + * configuration. This can be used in conjunction with ASDisableLogging() + * to allow logging to be toggled off and back on at runtime. + */ +ASDK_EXTERN void ASEnableLogging(void); + +/// Log for general node events e.g. interfaceState, didLoad. +#define ASNodeLogEnabled 1 +ASDK_EXTERN os_log_t ASNodeLog(void); + +/// Log for layout-specific events e.g. calculateLayout. +#define ASLayoutLogEnabled 1 +ASDK_EXTERN os_log_t ASLayoutLog(void); + +/// Log for display-specific events e.g. display queue batches. +#define ASDisplayLogEnabled 1 +ASDK_EXTERN os_log_t ASDisplayLog(void); + +/// Log for collection events e.g. reloadData, performBatchUpdates. +#define ASCollectionLogEnabled 1 +ASDK_EXTERN os_log_t ASCollectionLog(void); + +/// Log for ASNetworkImageNode and ASMultiplexImageNode events. +#define ASImageLoadingLogEnabled 1 +ASDK_EXTERN os_log_t ASImageLoadingLog(void); + +/// Specialized log for our main thread deallocation trampoline. +#define ASMainThreadDeallocationLogEnabled 0 +ASDK_EXTERN os_log_t ASMainThreadDeallocationLog(void); + +#define ASLockingLogEnabled 0 +ASDK_EXTERN os_log_t ASLockingLog(void); + +#if AS_HAS_OS_SIGNPOST +/// Uses the special POI category for Instruments. Used by ASSignpost.h. +ASDK_EXTERN os_log_t ASPointsOfInterestLog(void); +#endif + +/** + * The activity tracing system changed a lot between iOS 9 and 10. + * In iOS 10, the system was merged with logging and became much more powerful + * and adopted a new API. + * + * The legacy API is visible, but its functionality is extremely limited and the API is so different + * that we don't bother with it. For example, activities described by os_activity_start/end are not + * reflected in the log whereas activities described by the newer + * os_activity_scope are. So unfortunately we must use these iOS 10 + * APIs to get meaningful logging data. + * + * NOTE: Creating and tearing down activities require inter-process communication and can + * take dozens of microseconds on an A8. We do it quite often. Enable activities only during debugging. + */ +#if DEBUG && OS_LOG_TARGET_HAS_10_12_FEATURES + +#define OS_ACTIVITY_NULLABLE nullable +#define AS_ACTIVITY_CURRENT OS_ACTIVITY_CURRENT +#define as_activity_scope(activity) os_activity_scope(activity) +#define as_activity_apply(activity, block) os_activity_apply(activity, block) +#define as_activity_create(description, parent_activity, flags) os_activity_create(description, parent_activity, flags) +#define as_activity_scope_enter(activity, statePtr) os_activity_scope_enter(activity, statePtr) +#define as_activity_scope_leave(statePtr) os_activity_scope_leave(statePtr) +#define as_activity_get_identifier(activity, outParentID) os_activity_get_identifier(activity, outParentID) + +#else + +#define OS_ACTIVITY_NULLABLE +#define AS_ACTIVITY_CURRENT OS_ACTIVITY_NULL +#define as_activity_scope(activity) +#define as_activity_apply(activity, block) +#define as_activity_create(description, parent_activity, flags) OS_ACTIVITY_NULL +#define as_activity_scope_enter(activity, statePtr) +#define as_activity_scope_leave(statePtr) +#define as_activity_get_identifier(activity, outParentID) (os_activity_id_t)0 + +#endif // OS_LOG_TARGET_HAS_10_12_FEATURES + +// Create activities only when verbose enabled. Doesn't materially impact performance, but good if we're cluttering up +// activity scopes and reducing readability. +#if ASEnableVerboseLogging + #define as_activity_scope_verbose(activity) as_activity_scope(activity) +#else + #define as_activity_scope_verbose(activity) +#endif + +// Convenience for: as_activity_scope(as_activity_create(description, AS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT)) +#define as_activity_create_for_scope(description) \ + as_activity_scope(as_activity_create(description, AS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT)) + +#if ASEnableVerboseLogging + #define as_log_verbose(log, format, ...) os_log_debug(log, format, ##__VA_ARGS__) +#else + #define as_log_verbose(log, format, ...) +#endif diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Base/ASLog.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Base/ASLog.mm new file mode 100644 index 0000000..e2014e2 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Base/ASLog.mm @@ -0,0 +1,61 @@ +// +// ASLog.mm +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#if AS_HAS_OS_SIGNPOST +#import +#endif + +static atomic_bool __ASLogEnabled = ATOMIC_VAR_INIT(YES); + +void ASDisableLogging() { + atomic_store(&__ASLogEnabled, NO); +} + +void ASEnableLogging() { + atomic_store(&__ASLogEnabled, YES); +} + +ASDISPLAYNODE_INLINE BOOL ASLoggingIsEnabled() { + return atomic_load(&__ASLogEnabled); +} + +os_log_t ASNodeLog() { + return (ASNodeLogEnabled && ASLoggingIsEnabled()) ? ASCreateOnce(os_log_create("org.TextureGroup.Texture", "Node")) : OS_LOG_DISABLED; +} + +os_log_t ASLayoutLog() { + return (ASLayoutLogEnabled && ASLoggingIsEnabled()) ? ASCreateOnce(os_log_create("org.TextureGroup.Texture", "Layout")) : OS_LOG_DISABLED; +} + +os_log_t ASCollectionLog() { + return (ASCollectionLogEnabled && ASLoggingIsEnabled()) ?ASCreateOnce(os_log_create("org.TextureGroup.Texture", "Collection")) : OS_LOG_DISABLED; +} + +os_log_t ASDisplayLog() { + return (ASDisplayLogEnabled && ASLoggingIsEnabled()) ?ASCreateOnce(os_log_create("org.TextureGroup.Texture", "Display")) : OS_LOG_DISABLED; +} + +os_log_t ASImageLoadingLog() { + return (ASImageLoadingLogEnabled && ASLoggingIsEnabled()) ? ASCreateOnce(os_log_create("org.TextureGroup.Texture", "ImageLoading")) : OS_LOG_DISABLED; +} + +os_log_t ASMainThreadDeallocationLog() { + return (ASMainThreadDeallocationLogEnabled && ASLoggingIsEnabled()) ? ASCreateOnce(os_log_create("org.TextureGroup.Texture", "MainDealloc")) : OS_LOG_DISABLED; +} + +os_log_t ASLockingLog() { + return (ASLockingLogEnabled && ASLoggingIsEnabled()) ? ASCreateOnce(os_log_create("org.TextureGroup.Texture", "Locking")) : OS_LOG_DISABLED; +} + +#if AS_HAS_OS_SIGNPOST +os_log_t ASPointsOfInterestLog() { + return ASCreateOnce(os_log_create("org.TextureGroup.Texture", OS_LOG_CATEGORY_POINTS_OF_INTEREST)); +} +#endif diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Base/ASSignpost.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Base/ASSignpost.h new file mode 100644 index 0000000..27a44af --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Base/ASSignpost.h @@ -0,0 +1,106 @@ +// +// ASSignpost.h +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +/// The signposts we use. Signposts are grouped by color. The SystemTrace.tracetemplate file +/// should be kept up-to-date with these values. +typedef NS_ENUM(uint32_t, ASSignpostName) { + // Collection/Table + ASSignpostDataControllerBatch = 300, // Alloc/layout nodes before collection update. + ASSignpostRangeControllerUpdate, // Ranges update pass. + + // Rendering + ASSignpostLayerDisplay = 325, // Client display callout. + ASSignpostRunLoopQueueBatch, // One batch of ASRunLoopQueue. + + // Layout + ASSignpostCalculateLayout = 350, // Start of calculateLayoutThatFits to end. Max 1 per thread. + + // Misc + ASSignpostDeallocQueueDrain = 375, // One chunk of dealloc queue work. arg0 is count. + ASSignpostOrientationChange, // From WillChangeStatusBarOrientation to animation end. +}; + +#ifdef PROFILE + #define AS_SIGNPOST_ENABLE 1 +#else + #define AS_SIGNPOST_ENABLE 0 +#endif + +#if AS_SIGNPOST_ENABLE + +#import +#if AS_HAS_OS_SIGNPOST +#import +#endif + +// These definitions are required to build the backward-compatible kdebug trace +// on the iOS 10 SDK. The kdebug_trace function crashes if run on iOS 9 and earlier. +// It's valuable to support trace signposts on iOS 9, because A5 devices don't support iOS 10. +#ifndef DBG_MACH_CHUD +#define DBG_MACH_CHUD 0x0A +#define DBG_FUNC_NONE 0 +#define DBG_FUNC_START 1 +#define DBG_FUNC_END 2 +#define DBG_APPS 33 +#define SYS_kdebug_trace 180 +#define KDBG_CODE(Class, SubClass, code) (((Class & 0xff) << 24) | ((SubClass & 0xff) << 16) | ((code & 0x3fff) << 2)) +#define APPSDBG_CODE(SubClass,code) KDBG_CODE(DBG_APPS, SubClass, code) +#endif + +#if AS_HAS_OS_SIGNPOST + +#define ASSignpostStart(name, identifier, format, ...) ({\ + if (AS_AVAILABLE_IOS_TVOS(12, 12)) { \ + unowned os_log_t log = ASPointsOfInterestLog(); \ + os_signpost_id_t spid = os_signpost_id_make_with_id(log, identifier); \ + os_signpost_interval_begin(log, spid, #name, format, ##__VA_ARGS__); \ + } else if (AS_AVAILABLE_IOS_TVOS(10, 10)) { \ + kdebug_signpost_start(ASSignpost##name, (uintptr_t)identifier, 0, 0, 0); \ + } else { \ + syscall(SYS_kdebug_trace, APPSDBG_CODE(DBG_MACH_CHUD, ASSignpost##name) | DBG_FUNC_START, (uintptr_t)identifier, 0, 0, 0); \ + } \ +}) + +#define ASSignpostEnd(name, identifier, format, ...) ({\ + if (AS_AVAILABLE_IOS_TVOS(12, 12)) { \ + unowned os_log_t log = ASPointsOfInterestLog(); \ + os_signpost_id_t spid = os_signpost_id_make_with_id(log, identifier); \ + os_signpost_interval_end(log, spid, #name, format, ##__VA_ARGS__); \ + } else if (AS_AVAILABLE_IOS_TVOS(10, 10)) { \ + kdebug_signpost_end(ASSignpost##name, (uintptr_t)identifier, 0, 0, 0); \ + } else { \ + syscall(SYS_kdebug_trace, APPSDBG_CODE(DBG_MACH_CHUD, ASSignpost##name) | DBG_FUNC_END, (uintptr_t)identifier, 0, 0, 0); \ + } \ +}) + +#else // !AS_HAS_OS_SIGNPOST + +#define ASSignpostStart(name, identifier, format, ...) ({\ + if (AS_AVAILABLE_IOS_TVOS(10, 10)) { \ + kdebug_signpost_start(ASSignpost##name, (uintptr_t)identifier, 0, 0, 0); \ + } else { \ + syscall(SYS_kdebug_trace, APPSDBG_CODE(DBG_MACH_CHUD, ASSignpost##name) | DBG_FUNC_START, (uintptr_t)identifier, 0, 0, 0); \ + } \ +}) + +#define ASSignpostEnd(name, identifier, format, ...) ({\ + if (AS_AVAILABLE_IOS_TVOS(10, 10)) { \ + kdebug_signpost_end(ASSignpost##name, (uintptr_t)identifier, 0, 0, 0); \ + } else { \ + syscall(SYS_kdebug_trace, APPSDBG_CODE(DBG_MACH_CHUD, ASSignpost##name) | DBG_FUNC_END, (uintptr_t)identifier, 0, 0, 0); \ + } \ +}) + +#endif + +#else // !AS_SIGNPOST_ENABLE + +#define ASSignpostStart(name, identifier, format, ...) +#define ASSignpostEnd(name, identifier, format, ...) + +#endif diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Debug/AsyncDisplayKit+Debug.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Debug/AsyncDisplayKit+Debug.h new file mode 100644 index 0000000..6dc5137 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Debug/AsyncDisplayKit+Debug.h @@ -0,0 +1,53 @@ +// +// AsyncDisplayKit+Debug.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface ASImageNode (Debugging) + +/** + * Enables an ASImageNode debug label that shows the ratio of pixels in the source image to those in + * the displayed bounds (including cropRect). This helps detect excessive image fetching / downscaling, + * as well as upscaling (such as providing a URL not suitable for a Retina device). For dev purposes only. + * Specify YES to show the label on all ASImageNodes with non-1.0x source-to-bounds pixel ratio. + */ +@property (class, nonatomic) BOOL shouldShowImageScalingOverlay; + +@end + +@interface ASControlNode (Debugging) + +/** + * Class method to enable a visualization overlay of the tappable area on the ASControlNode. For app debugging purposes only. + * NOTE: GESTURE RECOGNIZERS, (including tap gesture recognizers on a control node) WILL NOT BE VISUALIZED!!! + * Overlay = translucent GREEN color, + * edges that are clipped by the tappable area of any parent (their bounds + hitTestSlop) in the hierarchy = DARK GREEN BORDERED EDGE, + * edges that are clipped by clipToBounds = YES of any parent in the hierarchy = ORANGE BORDERED EDGE (may still receive touches beyond + * overlay rect, but can't be visualized). + * Specify YES to make this debug feature enabled when messaging the ASControlNode class. + */ +@property (class, nonatomic) BOOL enableHitTestDebug; + +@end + +@interface ASDisplayNode (RangeDebugging) + +/** + * Enable a visualization overlay of the all table/collection tuning parameters. For dev purposes only. + * To use, set this in the AppDelegate --> ASDisplayNode.shouldShowRangeDebugOverlay = YES + */ +@property (class, nonatomic) BOOL shouldShowRangeDebugOverlay; + +@end + + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Debug/AsyncDisplayKit+Debug.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Debug/AsyncDisplayKit+Debug.mm new file mode 100644 index 0000000..965b265 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Debug/AsyncDisplayKit+Debug.mm @@ -0,0 +1,753 @@ +// +// AsyncDisplayKit+Debug.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + + +#pragma mark - ASImageNode (Debugging) + +static BOOL __shouldShowImageScalingOverlay = NO; + +@implementation ASImageNode (Debugging) + ++ (void)setShouldShowImageScalingOverlay:(BOOL)show; +{ + __shouldShowImageScalingOverlay = show; +} + ++ (BOOL)shouldShowImageScalingOverlay +{ + return __shouldShowImageScalingOverlay; +} + +@end + +#pragma mark - ASControlNode (DebuggingInternal) + +static BOOL __enableHitTestDebug = NO; + +@interface ASControlNode (DebuggingInternal) + +- (ASImageNode *)debugHighlightOverlay; + +@end + +@implementation ASControlNode (Debugging) + ++ (void)setEnableHitTestDebug:(BOOL)enable +{ + __enableHitTestDebug = enable; +} + ++ (BOOL)enableHitTestDebug +{ + return __enableHitTestDebug; +} + +// layout method required ONLY when hitTestDebug is enabled +- (void)layout +{ + [super layout]; + + if ([ASControlNode enableHitTestDebug]) { + + // Construct hitTestDebug highlight overlay frame indicating tappable area of a node, which can be restricted by two things: + + // (1) Any parent's tapable area (its own bounds + hitTestSlop) may restrict the desired tappable area expansion using + // hitTestSlop of a child as UIKit event delivery (hitTest:) will not search sub-hierarchies if one of our parents does + // not return YES for pointInside:. To circumvent this restriction, a developer will need to set / adjust the hitTestSlop + // on the limiting parent. This is indicated in the overlay by a dark GREEN edge. This is an ACTUAL restriction. + + // (2) Any parent's .clipToBounds. If a parent is clipping, we cannot show the overlay outside that area + // (although it still respond to touch). To indicate that the overlay cannot accurately display the true tappable area, + // the overlay will have an ORANGE edge. This is a VISUALIZATION restriction. + + CGRect intersectRect = UIEdgeInsetsInsetRect(self.bounds, [self hitTestSlop]); + UIRectEdge clippedEdges = UIRectEdgeNone; + UIRectEdge clipsToBoundsClippedEdges = UIRectEdgeNone; + CALayer *layer = self.layer; + CALayer *intersectLayer = layer; + CALayer *intersectSuperlayer = layer.superlayer; + + // FIXED: Stop climbing hierarchy if UIScrollView is encountered (its offset bounds origin may make it seem like our events + // will be clipped when scrolling will actually reveal them (because this process will not re-run due to scrolling)) + while (intersectSuperlayer && ![intersectSuperlayer.delegate respondsToSelector:@selector(contentOffset)]) { + + // Get parent's tappable area + CGRect parentHitRect = intersectSuperlayer.bounds; + BOOL parentClipsToBounds = NO; + + // If parent is a node, tappable area may be expanded by hitTestSlop + ASDisplayNode *parentNode = ASLayerToDisplayNode(intersectSuperlayer); + if (parentNode) { + UIEdgeInsets parentSlop = [parentNode hitTestSlop]; + + // If parent has hitTestSlop, expand tappable area (if parent doesn't clipToBounds) + if (!UIEdgeInsetsEqualToEdgeInsets(UIEdgeInsetsZero, parentSlop)) { + parentClipsToBounds = parentNode.clipsToBounds; + if (!parentClipsToBounds) { + parentHitRect = UIEdgeInsetsInsetRect(parentHitRect, [parentNode hitTestSlop]); + } + } + } + + // Convert our current rect to parent coordinates + CGRect intersectRectInParentCoordinates = [intersectSuperlayer convertRect:intersectRect fromLayer:intersectLayer]; + + // Intersect rect with the parent's tappable area rect + intersectRect = CGRectIntersection(parentHitRect, intersectRectInParentCoordinates); + if (!CGSizeEqualToSize(parentHitRect.size, intersectRectInParentCoordinates.size)) { + clippedEdges = [self setEdgesOfIntersectionForChildRect:intersectRectInParentCoordinates + parentRect:parentHitRect rectEdge:clippedEdges]; + if (parentClipsToBounds) { + clipsToBoundsClippedEdges = [self setEdgesOfIntersectionForChildRect:intersectRectInParentCoordinates + parentRect:parentHitRect rectEdge:clipsToBoundsClippedEdges]; + } + } + + // move up hierarchy + intersectLayer = intersectSuperlayer; + intersectSuperlayer = intersectLayer.superlayer; + } + + // produce final overlay image (or fill background if edges aren't restricted) + CGRect finalRect = [intersectLayer convertRect:intersectRect toLayer:layer]; + UIColor *fillColor = [[UIColor greenColor] colorWithAlphaComponent:0.4]; + + ASImageNode *debugOverlay = [self debugHighlightOverlay]; + + // determine if edges are clipped and if so, highlight the restricted edges + if (clippedEdges == UIRectEdgeNone) { + debugOverlay.backgroundColor = fillColor; + } else { + const CGFloat borderWidth = 2.0; + UIColor *borderColor = [[UIColor orangeColor] colorWithAlphaComponent:0.8]; + UIColor *clipsBorderColor = [UIColor colorWithRed:30/255.0 green:90/255.0 blue:50/255.0 alpha:0.7]; + CGRect imgRect = CGRectMake(0, 0, 2.0 * borderWidth + 1.0, 2.0 * borderWidth + 1.0); + + UIImage *debugHighlightImage = ASGraphicsCreateImage(self.primitiveTraitCollection, imgRect.size, NO, 1, nil, nil, ^{ + [fillColor setFill]; + UIRectFill(imgRect); + + [self drawEdgeIfClippedWithEdges:clippedEdges color:clipsBorderColor borderWidth:borderWidth imgRect:imgRect]; + [self drawEdgeIfClippedWithEdges:clipsToBoundsClippedEdges color:borderColor borderWidth:borderWidth imgRect:imgRect]; + }); + + UIEdgeInsets edgeInsets = UIEdgeInsetsMake(borderWidth, borderWidth, borderWidth, borderWidth); + debugOverlay.image = [debugHighlightImage resizableImageWithCapInsets:edgeInsets resizingMode:UIImageResizingModeStretch]; + debugOverlay.backgroundColor = nil; + } + + debugOverlay.frame = finalRect; + } +} + +- (UIRectEdge)setEdgesOfIntersectionForChildRect:(CGRect)childRect parentRect:(CGRect)parentRect rectEdge:(UIRectEdge)rectEdge +{ + // determine which edges of childRect are outside parentRect (and thus will be clipped) + if (childRect.origin.y < parentRect.origin.y) { + rectEdge |= UIRectEdgeTop; + } + if (childRect.origin.x < parentRect.origin.x) { + rectEdge |= UIRectEdgeLeft; + } + if (CGRectGetMaxY(childRect) > CGRectGetMaxY(parentRect)) { + rectEdge |= UIRectEdgeBottom; + } + if (CGRectGetMaxX(childRect) > CGRectGetMaxX(parentRect)) { + rectEdge |= UIRectEdgeRight; + } + + return rectEdge; +} + +- (void)drawEdgeIfClippedWithEdges:(UIRectEdge)rectEdge color:(UIColor *)color borderWidth:(CGFloat)borderWidth imgRect:(CGRect)imgRect +{ + [color setFill]; + + // highlight individual edges of overlay if edge is restricted by parentRect + // so that the developer is aware that increasing hitTestSlop will not result in an expanded tappable area + if (rectEdge & UIRectEdgeTop) { + UIRectFill(CGRectMake(0.0, 0.0, imgRect.size.width, borderWidth)); + } + if (rectEdge & UIRectEdgeLeft) { + UIRectFill(CGRectMake(0.0, 0.0, borderWidth, imgRect.size.height)); + } + if (rectEdge & UIRectEdgeBottom) { + UIRectFill(CGRectMake(0.0, imgRect.size.height - borderWidth, imgRect.size.width, borderWidth)); + } + if (rectEdge & UIRectEdgeRight) { + UIRectFill(CGRectMake(imgRect.size.width - borderWidth, 0.0, borderWidth, imgRect.size.height)); + } +} + +@end + +#pragma mark - ASRangeController (Debugging) + +@interface _ASRangeDebugOverlayView : UIView + ++ (instancetype)sharedInstance NS_RETURNS_RETAINED; + +- (void)addRangeController:(ASRangeController *)rangeController; + +- (void)updateRangeController:(ASRangeController *)controller + withScrollableDirections:(ASScrollDirection)scrollableDirections + scrollDirection:(ASScrollDirection)direction + rangeMode:(ASLayoutRangeMode)mode + displayTuningParameters:(ASRangeTuningParameters)displayTuningParameters + preloadTuningParameters:(ASRangeTuningParameters)preloadTuningParameters + interfaceState:(ASInterfaceState)interfaceState; + +@end + +@interface _ASRangeDebugBarView : UIView + +@property (nonatomic, weak) ASRangeController *rangeController; +@property (nonatomic) BOOL destroyOnLayout; +@property (nonatomic) NSString *debugString; + +- (instancetype)initWithRangeController:(ASRangeController *)rangeController; + +- (void)updateWithVisibleRatio:(CGFloat)visibleRatio + displayRatio:(CGFloat)displayRatio + leadingDisplayRatio:(CGFloat)leadingDisplayRatio + preloadRatio:(CGFloat)preloadRatio + leadingpreloadRatio:(CGFloat)leadingpreloadRatio + direction:(ASScrollDirection)direction; + +@end + +static BOOL __shouldShowRangeDebugOverlay = NO; + +@implementation ASDisplayNode (RangeDebugging) + ++ (void)setShouldShowRangeDebugOverlay:(BOOL)show +{ + __shouldShowRangeDebugOverlay = show; +} + ++ (BOOL)shouldShowRangeDebugOverlay +{ + return __shouldShowRangeDebugOverlay; +} + +@end + +@implementation ASRangeController (DebugInternal) + ++ (void)layoutDebugOverlayIfNeeded +{ + [[_ASRangeDebugOverlayView sharedInstance] setNeedsLayout]; +} + +- (void)addRangeControllerToRangeDebugOverlay +{ + [[_ASRangeDebugOverlayView sharedInstance] addRangeController:self]; +} + +- (void)updateRangeController:(ASRangeController *)controller + withScrollableDirections:(ASScrollDirection)scrollableDirections + scrollDirection:(ASScrollDirection)direction + rangeMode:(ASLayoutRangeMode)mode + displayTuningParameters:(ASRangeTuningParameters)displayTuningParameters + preloadTuningParameters:(ASRangeTuningParameters)preloadTuningParameters + interfaceState:(ASInterfaceState)interfaceState +{ + [[_ASRangeDebugOverlayView sharedInstance] updateRangeController:controller + withScrollableDirections:scrollableDirections + scrollDirection:direction + rangeMode:mode + displayTuningParameters:displayTuningParameters + preloadTuningParameters:preloadTuningParameters + interfaceState:interfaceState]; +} + +@end + + +#pragma mark _ASRangeDebugOverlayView + +@interface _ASRangeDebugOverlayView () +@end + +@implementation _ASRangeDebugOverlayView +{ + NSMutableArray *_rangeControllerViews; + NSInteger _newControllerCount; + NSInteger _removeControllerCount; + BOOL _animating; +} + ++ (UIWindow *)keyWindow +{ + // hack to work around app extensions not having UIApplication...not sure of a better way to do this? + return [[NSClassFromString(@"UIApplication") sharedApplication] keyWindow]; +} + ++ (_ASRangeDebugOverlayView *)sharedInstance NS_RETURNS_RETAINED +{ + static _ASRangeDebugOverlayView *__rangeDebugOverlay = nil; + + if (!__rangeDebugOverlay && ASDisplayNode.shouldShowRangeDebugOverlay) { + __rangeDebugOverlay = [[self alloc] initWithFrame:CGRectZero]; + [[self keyWindow] addSubview:__rangeDebugOverlay]; + } + + return __rangeDebugOverlay; +} + +#define OVERLAY_INSET 10 +#define OVERLAY_SCALE 3 +- (instancetype)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + + if (self) { + _rangeControllerViews = [[NSMutableArray alloc] init]; + self.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.4]; + self.layer.zPosition = 1000; + self.clipsToBounds = YES; + + CGSize windowSize = [[[self class] keyWindow] bounds].size; + self.frame = CGRectMake(windowSize.width - (windowSize.width / OVERLAY_SCALE) - OVERLAY_INSET, windowSize.height - OVERLAY_INSET, + windowSize.width / OVERLAY_SCALE, 0.0); + + UIPanGestureRecognizer *panGR = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(rangeDebugOverlayWasPanned:)]; + [self addGestureRecognizer:panGR]; + } + + return self; +} + +#define BAR_THICKNESS 24 + +- (void)layoutSubviews +{ + [super layoutSubviews]; + [UIView animateWithDuration:0.2 delay:0.0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{ + [self layoutToFitAllBarsExcept:0]; + } completion:^(BOOL finished) { + + }]; +} + +- (void)layoutToFitAllBarsExcept:(NSInteger)barsToClip +{ + CGSize boundsSize = self.bounds.size; + CGFloat totalHeight = 0.0; + + CGRect barRect = CGRectMake(0, boundsSize.height - BAR_THICKNESS, self.bounds.size.width, BAR_THICKNESS); + NSMutableArray *displayedBars = [NSMutableArray array]; + + for (_ASRangeDebugBarView *barView in [_rangeControllerViews copy]) { + barView.frame = barRect; + + ASInterfaceState interfaceState = [barView.rangeController.dataSource interfaceStateForRangeController:barView.rangeController]; + + if (!(interfaceState & (ASInterfaceStateVisible))) { + if (barView.destroyOnLayout && barView.alpha == 0.0) { + [_rangeControllerViews removeObjectIdenticalTo:barView]; + [barView removeFromSuperview]; + } else { + barView.alpha = 0.0; + } + } else { + assert(!barView.destroyOnLayout); // In this case we should not have a visible interfaceState + barView.alpha = 1.0; + totalHeight += BAR_THICKNESS; + barRect.origin.y -= BAR_THICKNESS; + [displayedBars addObject:barView]; + } + } + + if (totalHeight > 0) { + totalHeight -= (BAR_THICKNESS * barsToClip); + } + + if (barsToClip == 0) { + CGRect overlayFrame = self.frame; + CGFloat heightChange = (overlayFrame.size.height - totalHeight); + + overlayFrame.origin.y += heightChange; + overlayFrame.size.height = totalHeight; + self.frame = overlayFrame; + + for (_ASRangeDebugBarView *barView in displayedBars) { + [self offsetYOrigin:-heightChange forView:barView]; + } + } +} + +- (void)setOrigin:(CGPoint)origin forView:(UIView *)view +{ + CGRect newFrame = view.frame; + newFrame.origin = origin; + view.frame = newFrame; +} + +- (void)offsetYOrigin:(CGFloat)offset forView:(UIView *)view +{ + CGRect newFrame = view.frame; + newFrame.origin = CGPointMake(newFrame.origin.x, newFrame.origin.y + offset); + view.frame = newFrame; +} + +- (void)addRangeController:(ASRangeController *)rangeController +{ + for (_ASRangeDebugBarView *rangeView in _rangeControllerViews) { + if (rangeView.rangeController == rangeController) { + return; + } + } + _ASRangeDebugBarView *rangeView = [[_ASRangeDebugBarView alloc] initWithRangeController:rangeController]; + [_rangeControllerViews addObject:rangeView]; + [self addSubview:rangeView]; + + if (!_animating) { + [self layoutToFitAllBarsExcept:1]; + } + + [UIView animateWithDuration:0.2 delay:0.0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{ + self->_animating = YES; + [self layoutToFitAllBarsExcept:0]; + } completion:^(BOOL finished) { + self->_animating = NO; + }]; +} + +- (void)updateRangeController:(ASRangeController *)controller + withScrollableDirections:(ASScrollDirection)scrollableDirections + scrollDirection:(ASScrollDirection)scrollDirection + rangeMode:(ASLayoutRangeMode)rangeMode + displayTuningParameters:(ASRangeTuningParameters)displayTuningParameters + preloadTuningParameters:(ASRangeTuningParameters)preloadTuningParameters + interfaceState:(ASInterfaceState)interfaceState; +{ + _ASRangeDebugBarView *viewToUpdate = [self barViewForRangeController:controller]; + + CGRect boundsRect = self.bounds; + CGRect visibleRect = CGRectExpandToRangeWithScrollableDirections(boundsRect, ASRangeTuningParametersZero, scrollableDirections, scrollDirection); + CGRect displayRect = CGRectExpandToRangeWithScrollableDirections(boundsRect, displayTuningParameters, scrollableDirections, scrollDirection); + CGRect preloadRect = CGRectExpandToRangeWithScrollableDirections(boundsRect, preloadTuningParameters, scrollableDirections, scrollDirection); + + // figure out which is biggest and assume that is full bounds + BOOL displayRangeLargerThanPreload = NO; + CGFloat visibleRatio = 0; + CGFloat displayRatio = 0; + CGFloat preloadRatio = 0; + CGFloat leadingDisplayTuningRatio = 0; + CGFloat leadingPreloadTuningRatio = 0; + + if (displayTuningParameters.leadingBufferScreenfuls + displayTuningParameters.trailingBufferScreenfuls != 0) { + leadingDisplayTuningRatio = displayTuningParameters.leadingBufferScreenfuls / (displayTuningParameters.leadingBufferScreenfuls + displayTuningParameters.trailingBufferScreenfuls); + } + if (preloadTuningParameters.leadingBufferScreenfuls + preloadTuningParameters.trailingBufferScreenfuls != 0) { + leadingPreloadTuningRatio = preloadTuningParameters.leadingBufferScreenfuls / (preloadTuningParameters.leadingBufferScreenfuls + preloadTuningParameters.trailingBufferScreenfuls); + } + + if (ASScrollDirectionContainsVerticalDirection(scrollDirection)) { + + if (displayRect.size.height >= preloadRect.size.height) { + displayRangeLargerThanPreload = YES; + } else { + displayRangeLargerThanPreload = NO; + } + + if (displayRangeLargerThanPreload) { + visibleRatio = visibleRect.size.height / displayRect.size.height; + displayRatio = 1.0; + preloadRatio = preloadRect.size.height / displayRect.size.height; + } else { + visibleRatio = visibleRect.size.height / preloadRect.size.height; + displayRatio = displayRect.size.height / preloadRect.size.height; + preloadRatio = 1.0; + } + + } else { + + if (displayRect.size.width >= preloadRect.size.width) { + displayRangeLargerThanPreload = YES; + } else { + displayRangeLargerThanPreload = NO; + } + + if (displayRangeLargerThanPreload) { + visibleRatio = visibleRect.size.width / displayRect.size.width; + displayRatio = 1.0; + preloadRatio = preloadRect.size.width / displayRect.size.width; + } else { + visibleRatio = visibleRect.size.width / preloadRect.size.width; + displayRatio = displayRect.size.width / preloadRect.size.width; + preloadRatio = 1.0; + } + } + + [viewToUpdate updateWithVisibleRatio:visibleRatio + displayRatio:displayRatio + leadingDisplayRatio:leadingDisplayTuningRatio + preloadRatio:preloadRatio + leadingpreloadRatio:leadingPreloadTuningRatio + direction:scrollDirection]; + + [self setNeedsLayout]; +} + +- (_ASRangeDebugBarView *)barViewForRangeController:(ASRangeController *)controller +{ + _ASRangeDebugBarView *rangeControllerBarView = nil; + + for (_ASRangeDebugBarView *rangeView in [[_rangeControllerViews reverseObjectEnumerator] allObjects]) { + // remove barView if its rangeController has been deleted + if (!rangeView.rangeController) { + rangeView.destroyOnLayout = YES; + [self setNeedsLayout]; + } + ASInterfaceState interfaceState = [rangeView.rangeController.dataSource interfaceStateForRangeController:rangeView.rangeController]; + if (!(interfaceState & (ASInterfaceStateVisible | ASInterfaceStateDisplay))) { + [self setNeedsLayout]; + } + + if ([rangeView.rangeController isEqual:controller]) { + rangeControllerBarView = rangeView; + } + } + + return rangeControllerBarView; +} + +#define MIN_VISIBLE_INSET 40 +- (void)rangeDebugOverlayWasPanned:(UIPanGestureRecognizer *)recognizer +{ + CGPoint translation = [recognizer translationInView:recognizer.view]; + CGFloat newCenterX = recognizer.view.center.x + translation.x; + CGFloat newCenterY = recognizer.view.center.y + translation.y; + CGSize boundsSize = recognizer.view.bounds.size; + CGSize superBoundsSize = recognizer.view.superview.bounds.size; + CGFloat minAllowableX = -boundsSize.width / 2.0 + MIN_VISIBLE_INSET; + CGFloat maxAllowableX = superBoundsSize.width + boundsSize.width / 2.0 - MIN_VISIBLE_INSET; + + if (newCenterX > maxAllowableX) { + newCenterX = maxAllowableX; + } else if (newCenterX < minAllowableX) { + newCenterX = minAllowableX; + } + + CGFloat minAllowableY = -boundsSize.height / 2.0 + MIN_VISIBLE_INSET; + CGFloat maxAllowableY = superBoundsSize.height + boundsSize.height / 2.0 - MIN_VISIBLE_INSET; + + if (newCenterY > maxAllowableY) { + newCenterY = maxAllowableY; + } else if (newCenterY < minAllowableY) { + newCenterY = minAllowableY; + } + + recognizer.view.center = CGPointMake(newCenterX, newCenterY); + [recognizer setTranslation:CGPointMake(0, 0) inView:recognizer.view]; +} + +@end + +#pragma mark _ASRangeDebugBarView + +@implementation _ASRangeDebugBarView +{ + ASTextNode *_debugText; + ASTextNode *_leftDebugText; + ASTextNode *_rightDebugText; + ASImageNode *_visibleRect; + ASImageNode *_displayRect; + ASImageNode *_preloadRect; + CGFloat _visibleRatio; + CGFloat _displayRatio; + CGFloat _preloadRatio; + CGFloat _leadingDisplayRatio; + CGFloat _leadingpreloadRatio; + ASScrollDirection _scrollDirection; + BOOL _firstLayoutOfRects; +} + +- (instancetype)initWithRangeController:(ASRangeController *)rangeController +{ + self = [super initWithFrame:CGRectZero]; + if (self) { + _firstLayoutOfRects = YES; + _rangeController = rangeController; + _debugText = [self createDebugTextNode]; + _leftDebugText = [self createDebugTextNode]; + _rightDebugText = [self createDebugTextNode]; + _preloadRect = [self createRangeNodeWithColor:[UIColor orangeColor]]; + _displayRect = [self createRangeNodeWithColor:[UIColor yellowColor]]; + _visibleRect = [self createRangeNodeWithColor:[UIColor greenColor]]; + } + + return self; +} + +#define HORIZONTAL_INSET 10 +- (void)layoutSubviews +{ + [super layoutSubviews]; + + CGSize boundsSize = self.bounds.size; + CGFloat subCellHeight = 9.0; + [self setBarDebugLabelsWithSize:subCellHeight]; + [self setBarSubviewOrder]; + + CGRect rect = CGRectIntegral(CGRectMake(0, 0, boundsSize.width, floorf(boundsSize.height / 2.0))); + rect.size = [_debugText layoutThatFits:ASSizeRangeMake(CGSizeZero, CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX))].size; + rect.origin.x = (boundsSize.width - rect.size.width) / 2.0; + _debugText.frame = rect; + rect.origin.y += rect.size.height; + + rect.origin.x = 0; + rect.size = CGSizeMake(HORIZONTAL_INSET, boundsSize.height / 2.0); + _leftDebugText.frame = rect; + + rect.origin.x = boundsSize.width - HORIZONTAL_INSET; + _rightDebugText.frame = rect; + + CGFloat visibleDimension = (boundsSize.width - 2 * HORIZONTAL_INSET) * _visibleRatio; + CGFloat displayDimension = (boundsSize.width - 2 * HORIZONTAL_INSET) * _displayRatio; + CGFloat preloadDimension = (boundsSize.width - 2 * HORIZONTAL_INSET) * _preloadRatio; + CGFloat visiblePoint = 0; + CGFloat displayPoint = 0; + CGFloat preloadPoint = 0; + + BOOL displayLargerThanPreload = (_displayRatio == 1.0) ? YES : NO; + + if (ASScrollDirectionContainsLeft(_scrollDirection) || ASScrollDirectionContainsUp(_scrollDirection)) { + + if (displayLargerThanPreload) { + visiblePoint = (displayDimension - visibleDimension) * _leadingDisplayRatio; + preloadPoint = visiblePoint - (preloadDimension - visibleDimension) * _leadingpreloadRatio; + } else { + visiblePoint = (preloadDimension - visibleDimension) * _leadingpreloadRatio; + displayPoint = visiblePoint - (displayDimension - visibleDimension) * _leadingDisplayRatio; + } + } else if (ASScrollDirectionContainsRight(_scrollDirection) || ASScrollDirectionContainsDown(_scrollDirection)) { + + if (displayLargerThanPreload) { + visiblePoint = (displayDimension - visibleDimension) * (1 - _leadingDisplayRatio); + preloadPoint = visiblePoint - (preloadDimension - visibleDimension) * (1 - _leadingpreloadRatio); + } else { + visiblePoint = (preloadDimension - visibleDimension) * (1 - _leadingpreloadRatio); + displayPoint = visiblePoint - (displayDimension - visibleDimension) * (1 - _leadingDisplayRatio); + } + } + + BOOL animate = !_firstLayoutOfRects; + [UIView animateWithDuration:animate ? 0.3 : 0.0 delay:0.0 options:UIViewAnimationOptionLayoutSubviews animations:^{ + self->_visibleRect.frame = CGRectMake(HORIZONTAL_INSET + visiblePoint, rect.origin.y, visibleDimension, subCellHeight); + self->_displayRect.frame = CGRectMake(HORIZONTAL_INSET + displayPoint, rect.origin.y, displayDimension, subCellHeight); + self->_preloadRect.frame = CGRectMake(HORIZONTAL_INSET + preloadPoint, rect.origin.y, preloadDimension, subCellHeight); + } completion:^(BOOL finished) {}]; + + if (!animate) { + _visibleRect.alpha = _displayRect.alpha = _preloadRect.alpha = 0; + [UIView animateWithDuration:0.3 animations:^{ + self->_visibleRect.alpha = self->_displayRect.alpha = self->_preloadRect.alpha = 1; + }]; + } + + _firstLayoutOfRects = NO; +} + +- (void)updateWithVisibleRatio:(CGFloat)visibleRatio + displayRatio:(CGFloat)displayRatio + leadingDisplayRatio:(CGFloat)leadingDisplayRatio + preloadRatio:(CGFloat)preloadRatio + leadingpreloadRatio:(CGFloat)leadingpreloadRatio + direction:(ASScrollDirection)scrollDirection +{ + _visibleRatio = visibleRatio; + _displayRatio = displayRatio; + _leadingDisplayRatio = leadingDisplayRatio; + _preloadRatio = preloadRatio; + _leadingpreloadRatio = leadingpreloadRatio; + _scrollDirection = scrollDirection; + + [self setNeedsLayout]; +} + +- (void)setBarSubviewOrder +{ + if (_preloadRatio == 1.0) { + [self sendSubviewToBack:_preloadRect.view]; + } else { + [self sendSubviewToBack:_displayRect.view]; + } + + [self bringSubviewToFront:_visibleRect.view]; +} + +- (void)setBarDebugLabelsWithSize:(CGFloat)size +{ + if (!_debugString) { + _debugString = [[_rangeController dataSource] nameForRangeControllerDataSource]; + } + if (_debugString) { + _debugText.attributedText = [_ASRangeDebugBarView whiteAttributedStringFromString:_debugString withSize:size]; + } + + if (ASScrollDirectionContainsVerticalDirection(_scrollDirection)) { + _leftDebugText.attributedText = [_ASRangeDebugBarView whiteAttributedStringFromString:@"▲" withSize:size]; + _rightDebugText.attributedText = [_ASRangeDebugBarView whiteAttributedStringFromString:@"▼" withSize:size]; + } else if (ASScrollDirectionContainsHorizontalDirection(_scrollDirection)) { + _leftDebugText.attributedText = [_ASRangeDebugBarView whiteAttributedStringFromString:@"◀︎" withSize:size]; + _rightDebugText.attributedText = [_ASRangeDebugBarView whiteAttributedStringFromString:@"▶︎" withSize:size]; + } + + _leftDebugText.hidden = (_scrollDirection != ASScrollDirectionLeft && _scrollDirection != ASScrollDirectionUp); + _rightDebugText.hidden = (_scrollDirection != ASScrollDirectionRight && _scrollDirection != ASScrollDirectionDown); +} + +- (ASTextNode *)createDebugTextNode +{ + ASTextNode *label = [[ASTextNode alloc] init]; + [self addSubnode:label]; + return label; +} + +#define RANGE_BAR_CORNER_RADIUS 3 +#define RANGE_BAR_BORDER_WIDTH 1 +- (ASImageNode *)createRangeNodeWithColor:(UIColor *)color +{ + ASImageNode *rangeBarImageNode = [[ASImageNode alloc] init]; + ASPrimitiveTraitCollection primitiveTraitCollection = ASPrimitiveTraitCollectionFromUITraitCollection(self.traitCollection); + rangeBarImageNode.image = [UIImage as_resizableRoundedImageWithCornerRadius:RANGE_BAR_CORNER_RADIUS + cornerColor:[UIColor clearColor] + fillColor:[color colorWithAlphaComponent:0.5] + borderColor:[[UIColor blackColor] colorWithAlphaComponent:0.9] + borderWidth:RANGE_BAR_BORDER_WIDTH + roundedCorners:UIRectCornerAllCorners + scale:[[UIScreen mainScreen] scale] + traitCollection:primitiveTraitCollection]; + [self addSubnode:rangeBarImageNode]; + + return rangeBarImageNode; +} + ++ (NSAttributedString *)whiteAttributedStringFromString:(NSString *)string withSize:(CGFloat)size NS_RETURNS_RETAINED +{ + NSDictionary *attributes = @{NSForegroundColorAttributeName : [UIColor whiteColor], + NSFontAttributeName : [UIFont systemFontOfSize:size]}; + return [[NSAttributedString alloc] initWithString:string attributes:attributes]; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Debug/AsyncDisplayKit+Tips.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Debug/AsyncDisplayKit+Tips.h new file mode 100644 index 0000000..6232746 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Debug/AsyncDisplayKit+Tips.h @@ -0,0 +1,43 @@ +// +// AsyncDisplayKit+Tips.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +typedef void(^ASTipDisplayBlock)(ASDisplayNode *node, NSString *message); + +/** + * The methods added to ASDisplayNode to control the tips system. + * + * To enable tips, define AS_ENABLE_TIPS=1 (e.g. modify ASBaseDefines.h). + */ +@interface ASDisplayNode (Tips) + +/** + * Whether this class should have tips active. Default YES. + * + * NOTE: This property is for _disabling_ tips on a per-class basis, + * if they become annoying or have false-positives. The tips system + * is completely disabled unless you define AS_ENABLE_TIPS=1. + */ +@property (class) BOOL enableTips; + +/** + * A block to be run on the main thread to show text when a tip is tapped. + * + * If nil, the default, the message is just logged to the console with the + * ancestry of the node. + */ +@property (class, nonatomic, null_resettable) ASTipDisplayBlock tipDisplayBlock; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Debug/AsyncDisplayKit+Tips.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Debug/AsyncDisplayKit+Tips.mm new file mode 100644 index 0000000..f1a3ec8 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Debug/AsyncDisplayKit+Tips.mm @@ -0,0 +1,48 @@ +// +// AsyncDisplayKit+Tips.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +@implementation ASDisplayNode (Tips) + +static char ASDisplayNodeEnableTipsKey; +static ASTipDisplayBlock _Nullable __tipDisplayBlock; + +/** + * Use associated objects with NSNumbers. This is a debug property - simplicity is king. + */ ++ (void)setEnableTips:(BOOL)enableTips +{ + objc_setAssociatedObject(self, &ASDisplayNodeEnableTipsKey, @(enableTips), OBJC_ASSOCIATION_COPY); +} + ++ (BOOL)enableTips +{ + NSNumber *result = objc_getAssociatedObject(self, &ASDisplayNodeEnableTipsKey); + if (result == nil) { + return YES; + } + return result.boolValue; +} + + ++ (void)setTipDisplayBlock:(ASTipDisplayBlock)tipDisplayBlock +{ + __tipDisplayBlock = tipDisplayBlock; +} + ++ (ASTipDisplayBlock)tipDisplayBlock +{ + return __tipDisplayBlock ?: ^(ASDisplayNode *node, NSString *string) { + NSLog(@"%@. Node ancestry: %@", string, node.ancestryDescription); + }; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASAbstractLayoutController.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASAbstractLayoutController.h new file mode 100644 index 0000000..4cc7b3a --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASAbstractLayoutController.h @@ -0,0 +1,33 @@ +// +// ASAbstractLayoutController.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +ASDK_EXTERN ASDirectionalScreenfulBuffer ASDirectionalScreenfulBufferHorizontal(ASScrollDirection scrollDirection, ASRangeTuningParameters rangeTuningParameters); + +ASDK_EXTERN ASDirectionalScreenfulBuffer ASDirectionalScreenfulBufferVertical(ASScrollDirection scrollDirection, ASRangeTuningParameters rangeTuningParameters); + +ASDK_EXTERN CGRect CGRectExpandToRangeWithScrollableDirections(CGRect rect, ASRangeTuningParameters tuningParameters, ASScrollDirection scrollableDirections, ASScrollDirection scrollDirection); + +@interface ASAbstractLayoutController : NSObject + +@end + +@interface ASAbstractLayoutController (Unavailable) + +- (NSHashTable *)indexPathsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType __unavailable; + +- (void)allIndexPathsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode displaySet:(NSHashTable * _Nullable * _Nullable)displaySet preloadSet:(NSHashTable * _Nullable * _Nullable)preloadSet __unavailable; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASAbstractLayoutController.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASAbstractLayoutController.mm new file mode 100644 index 0000000..9d6b61f --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASAbstractLayoutController.mm @@ -0,0 +1,185 @@ +// +// ASAbstractLayoutController.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import + +ASRangeTuningParameters const ASRangeTuningParametersZero = {}; + +BOOL ASRangeTuningParametersEqualToRangeTuningParameters(ASRangeTuningParameters lhs, ASRangeTuningParameters rhs) +{ + return lhs.leadingBufferScreenfuls == rhs.leadingBufferScreenfuls && lhs.trailingBufferScreenfuls == rhs.trailingBufferScreenfuls; +} + +ASDirectionalScreenfulBuffer ASDirectionalScreenfulBufferHorizontal(ASScrollDirection scrollDirection, + ASRangeTuningParameters rangeTuningParameters) +{ + ASDirectionalScreenfulBuffer horizontalBuffer = {0, 0}; + BOOL movingRight = ASScrollDirectionContainsRight(scrollDirection); + + horizontalBuffer.positiveDirection = movingRight ? rangeTuningParameters.leadingBufferScreenfuls + : rangeTuningParameters.trailingBufferScreenfuls; + horizontalBuffer.negativeDirection = movingRight ? rangeTuningParameters.trailingBufferScreenfuls + : rangeTuningParameters.leadingBufferScreenfuls; + return horizontalBuffer; +} + +ASDirectionalScreenfulBuffer ASDirectionalScreenfulBufferVertical(ASScrollDirection scrollDirection, + ASRangeTuningParameters rangeTuningParameters) +{ + ASDirectionalScreenfulBuffer verticalBuffer = {0, 0}; + BOOL movingDown = ASScrollDirectionContainsDown(scrollDirection); + + verticalBuffer.positiveDirection = movingDown ? rangeTuningParameters.leadingBufferScreenfuls + : rangeTuningParameters.trailingBufferScreenfuls; + verticalBuffer.negativeDirection = movingDown ? rangeTuningParameters.trailingBufferScreenfuls + : rangeTuningParameters.leadingBufferScreenfuls; + return verticalBuffer; +} + +CGRect CGRectExpandHorizontally(CGRect rect, ASDirectionalScreenfulBuffer buffer) +{ + CGFloat negativeDirectionWidth = buffer.negativeDirection * rect.size.width; + CGFloat positiveDirectionWidth = buffer.positiveDirection * rect.size.width; + rect.size.width = negativeDirectionWidth + rect.size.width + positiveDirectionWidth; + rect.origin.x -= negativeDirectionWidth; + return rect; +} + +CGRect CGRectExpandVertically(CGRect rect, ASDirectionalScreenfulBuffer buffer) +{ + CGFloat negativeDirectionHeight = buffer.negativeDirection * rect.size.height; + CGFloat positiveDirectionHeight = buffer.positiveDirection * rect.size.height; + rect.size.height = negativeDirectionHeight + rect.size.height + positiveDirectionHeight; + rect.origin.y -= negativeDirectionHeight; + return rect; +} + +CGRect CGRectExpandToRangeWithScrollableDirections(CGRect rect, ASRangeTuningParameters tuningParameters, + ASScrollDirection scrollableDirections, ASScrollDirection scrollDirection) +{ + // Can scroll horizontally - expand the range appropriately + if (ASScrollDirectionContainsHorizontalDirection(scrollableDirections)) { + ASDirectionalScreenfulBuffer horizontalBuffer = ASDirectionalScreenfulBufferHorizontal(scrollDirection, tuningParameters); + rect = CGRectExpandHorizontally(rect, horizontalBuffer); + } + + // Can scroll vertically - expand the range appropriately + if (ASScrollDirectionContainsVerticalDirection(scrollableDirections)) { + ASDirectionalScreenfulBuffer verticalBuffer = ASDirectionalScreenfulBufferVertical(scrollDirection, tuningParameters); + rect = CGRectExpandVertically(rect, verticalBuffer); + } + + return rect; +} + +@interface ASAbstractLayoutController () { + std::vector> _tuningParameters; +} +@end + +@implementation ASAbstractLayoutController + ++ (std::vector>)defaultTuningParameters +{ + auto tuningParameters = std::vector> (ASLayoutRangeModeCount, std::vector (ASLayoutRangeTypeCount)); + + tuningParameters[ASLayoutRangeModeFull][ASLayoutRangeTypeDisplay] = { + .leadingBufferScreenfuls = 1.0, + .trailingBufferScreenfuls = 0.5 + }; + + tuningParameters[ASLayoutRangeModeFull][ASLayoutRangeTypePreload] = { + .leadingBufferScreenfuls = 2.5, + .trailingBufferScreenfuls = 1.5 + }; + + tuningParameters[ASLayoutRangeModeMinimum][ASLayoutRangeTypeDisplay] = { + .leadingBufferScreenfuls = 0.25, + .trailingBufferScreenfuls = 0.25 + }; + tuningParameters[ASLayoutRangeModeMinimum][ASLayoutRangeTypePreload] = { + .leadingBufferScreenfuls = 0.5, + .trailingBufferScreenfuls = 0.25 + }; + + tuningParameters[ASLayoutRangeModeVisibleOnly][ASLayoutRangeTypeDisplay] = { + .leadingBufferScreenfuls = 0, + .trailingBufferScreenfuls = 0 + }; + tuningParameters[ASLayoutRangeModeVisibleOnly][ASLayoutRangeTypePreload] = { + .leadingBufferScreenfuls = 0, + .trailingBufferScreenfuls = 0 + }; + + // The Low Memory range mode has special handling. Because a zero range still includes the visible area / bounds, + // in order to implement the behavior of releasing all graphics memory (backing stores), ASRangeController must check + // for this range mode and use an empty set for displayIndexPaths rather than querying the ASLayoutController for the indexPaths. + tuningParameters[ASLayoutRangeModeLowMemory][ASLayoutRangeTypeDisplay] = { + .leadingBufferScreenfuls = 0, + .trailingBufferScreenfuls = 0 + }; + tuningParameters[ASLayoutRangeModeLowMemory][ASLayoutRangeTypePreload] = { + .leadingBufferScreenfuls = 0, + .trailingBufferScreenfuls = 0 + }; + return tuningParameters; +} + +- (instancetype)init +{ + if (!(self = [super init])) { + return nil; + } + ASDisplayNodeAssert(self.class != [ASAbstractLayoutController class], @"Should never create instances of abstract class ASAbstractLayoutController."); + + _tuningParameters = [[self class] defaultTuningParameters]; + + return self; +} + +#pragma mark - Tuning Parameters + +- (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType +{ + return [self tuningParametersForRangeMode:ASLayoutRangeModeFull rangeType:rangeType]; +} + +- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType +{ + return [self setTuningParameters:tuningParameters forRangeMode:ASLayoutRangeModeFull rangeType:rangeType]; +} + +- (ASRangeTuningParameters)tuningParametersForRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType +{ + ASDisplayNodeAssert(rangeMode < _tuningParameters.size() && rangeType < _tuningParameters[rangeMode].size(), @"Requesting a range that is OOB for the configured tuning parameters"); + return _tuningParameters[rangeMode][rangeType]; +} + +- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType +{ + ASDisplayNodeAssert(rangeMode < _tuningParameters.size() && rangeType < _tuningParameters[rangeMode].size(), @"Setting a range that is OOB for the configured tuning parameters"); + _tuningParameters[rangeMode][rangeType] = tuningParameters; +} + +#pragma mark - Abstract Index Path Range Support + +- (NSHashTable *)elementsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType map:(ASElementMap *)map +{ + ASDisplayNodeAssertNotSupported(); + return nil; +} + +- (void)allElementsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode displaySet:(NSHashTable *__autoreleasing _Nullable *)displaySet preloadSet:(NSHashTable *__autoreleasing _Nullable *)preloadSet map:(ASElementMap *)map +{ + ASDisplayNodeAssertNotSupported(); +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASBasicImageDownloader.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASBasicImageDownloader.h new file mode 100644 index 0000000..7667bf8 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASBasicImageDownloader.h @@ -0,0 +1,36 @@ +// +// ASBasicImageDownloader.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * @abstract Simple NSURLSession-based image downloader. + */ +@interface ASBasicImageDownloader : NSObject + +/** + * A shared image downloader which can be used by @c ASNetworkImageNodes and @c ASMultiplexImageNodes. + * The userInfo provided by this downloader is `nil`. + * + * This is a very basic image downloader. It does not support caching, progressive downloading and likely + * isn't something you should use in production. If you'd like something production ready, see @c ASPINRemoteImageDownloader + * + * @note It is strongly recommended you include PINRemoteImage and use @c ASPINRemoteImageDownloader instead. + */ +@property (class, readonly) ASBasicImageDownloader *sharedImageDownloader; ++ (ASBasicImageDownloader *)sharedImageDownloader NS_RETURNS_RETAINED; + ++ (instancetype)new __attribute__((unavailable("+[ASBasicImageDownloader sharedImageDownloader] must be used."))); +- (instancetype)init __attribute__((unavailable("+[ASBasicImageDownloader sharedImageDownloader] must be used."))); + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASBasicImageDownloader.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASBasicImageDownloader.mm new file mode 100644 index 0000000..f577eb1 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASBasicImageDownloader.mm @@ -0,0 +1,352 @@ +// +// ASBasicImageDownloader.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import + +#import +#import +#import + +using AS::MutexLocker; + +#pragma mark - +/** + * Collection of properties associated with a download request. + */ + +NSString * const kASBasicImageDownloaderContextCallbackQueue = @"kASBasicImageDownloaderContextCallbackQueue"; +NSString * const kASBasicImageDownloaderContextProgressBlock = @"kASBasicImageDownloaderContextProgressBlock"; +NSString * const kASBasicImageDownloaderContextCompletionBlock = @"kASBasicImageDownloaderContextCompletionBlock"; + +static inline float NSURLSessionTaskPriorityWithImageDownloaderPriority(ASImageDownloaderPriority priority) { + switch (priority) { + case ASImageDownloaderPriorityPreload: + return NSURLSessionTaskPriorityLow; + + case ASImageDownloaderPriorityImminent: + return NSURLSessionTaskPriorityDefault; + + case ASImageDownloaderPriorityVisible: + return NSURLSessionTaskPriorityHigh; + } +} + +@interface ASBasicImageDownloaderContext () +{ + BOOL _invalid; + AS::RecursiveMutex __instanceLock__; +} + +@property (nonatomic) NSMutableArray *callbackDatas; + +@end + +@implementation ASBasicImageDownloaderContext + +static NSMutableDictionary *currentRequests = nil; + ++ (AS::Mutex *)currentRequestLock +{ + static dispatch_once_t onceToken; + static AS::Mutex *currentRequestsLock; + dispatch_once(&onceToken, ^{ + currentRequestsLock = new AS::Mutex(); + }); + return currentRequestsLock; +} + ++ (ASBasicImageDownloaderContext *)contextForURL:(NSURL *)URL +{ + MutexLocker l(*self.currentRequestLock); + if (!currentRequests) { + currentRequests = [[NSMutableDictionary alloc] init]; + } + ASBasicImageDownloaderContext *context = currentRequests[URL]; + if (!context) { + context = [[ASBasicImageDownloaderContext alloc] initWithURL:URL]; + currentRequests[URL] = context; + } + return context; +} + ++ (void)cancelContextWithURL:(NSURL *)URL +{ + MutexLocker l(*self.currentRequestLock); + if (currentRequests) { + [currentRequests removeObjectForKey:URL]; + } +} + +- (instancetype)initWithURL:(NSURL *)URL +{ + if (self = [super init]) { + _URL = URL; + _callbackDatas = [NSMutableArray array]; + } + return self; +} + +- (void)cancel +{ + MutexLocker l(__instanceLock__); + + NSURLSessionTask *sessionTask = self.sessionTask; + if (sessionTask) { + [sessionTask cancel]; + self.sessionTask = nil; + } + + _invalid = YES; + [self.class cancelContextWithURL:self.URL]; +} + +- (BOOL)isCancelled +{ + MutexLocker l(__instanceLock__); + return _invalid; +} + +- (void)addCallbackData:(NSDictionary *)callbackData +{ + MutexLocker l(__instanceLock__); + [self.callbackDatas addObject:callbackData]; +} + +- (void)performProgressBlocks:(CGFloat)progress +{ + MutexLocker l(__instanceLock__); + for (NSDictionary *callbackData in self.callbackDatas) { + ASImageDownloaderProgress progressBlock = callbackData[kASBasicImageDownloaderContextProgressBlock]; + dispatch_queue_t callbackQueue = callbackData[kASBasicImageDownloaderContextCallbackQueue]; + + if (progressBlock) { + dispatch_async(callbackQueue, ^{ + progressBlock(progress); + }); + } + } +} + +- (void)completeWithImage:(UIImage *)image error:(NSError *)error +{ + MutexLocker l(__instanceLock__); + for (NSDictionary *callbackData in self.callbackDatas) { + ASImageDownloaderCompletion completionBlock = callbackData[kASBasicImageDownloaderContextCompletionBlock]; + dispatch_queue_t callbackQueue = callbackData[kASBasicImageDownloaderContextCallbackQueue]; + + if (completionBlock) { + dispatch_async(callbackQueue, ^{ + completionBlock(image, error, nil, nil); + }); + } + } + + self.sessionTask = nil; + [self.callbackDatas removeAllObjects]; +} + +- (NSURLSessionTask *)createSessionTaskIfNecessaryWithBlock:(NSURLSessionTask *(^)())creationBlock { + { + MutexLocker l(__instanceLock__); + + if (self.isCancelled) { + return nil; + } + + if (self.sessionTask && (self.sessionTask.state == NSURLSessionTaskStateRunning)) { + return nil; + } + } + + NSURLSessionTask *newTask = creationBlock(); + + { + MutexLocker l(__instanceLock__); + + if (self.isCancelled) { + return nil; + } + + if (self.sessionTask && (self.sessionTask.state == NSURLSessionTaskStateRunning)) { + return nil; + } + + self.sessionTask = newTask; + + return self.sessionTask; + } +} + +@end + + +#pragma mark - +/** + * NSURLSessionDownloadTask lacks a `userInfo` property, so add this association ourselves. + */ +@interface NSURLRequest (ASBasicImageDownloader) +@property (nonatomic) ASBasicImageDownloaderContext *asyncdisplaykit_context; +@end + +@implementation NSURLRequest (ASBasicImageDownloader) + +static const void *ContextKey() { + return @selector(asyncdisplaykit_context); +} + +- (void)setAsyncdisplaykit_context:(ASBasicImageDownloaderContext *)asyncdisplaykit_context +{ + objc_setAssociatedObject(self, ContextKey(), asyncdisplaykit_context, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} +- (ASBasicImageDownloader *)asyncdisplaykit_context +{ + return objc_getAssociatedObject(self, ContextKey()); +} +@end + + +#pragma mark - +@interface ASBasicImageDownloader () +{ + NSOperationQueue *_sessionDelegateQueue; + NSURLSession *_session; +} + +@end + +@implementation ASBasicImageDownloader + ++ (ASBasicImageDownloader *)sharedImageDownloader +{ + static ASBasicImageDownloader *sharedImageDownloader = nil; + static dispatch_once_t once = 0; + dispatch_once(&once, ^{ + sharedImageDownloader = [[ASBasicImageDownloader alloc] _init]; + }); + return sharedImageDownloader; +} + +#pragma mark Lifecycle. + +- (instancetype)_init +{ + if (!(self = [super init])) + return nil; + + _sessionDelegateQueue = [[NSOperationQueue alloc] init]; + _session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] + delegate:self + delegateQueue:_sessionDelegateQueue]; + + return self; +} + + +#pragma mark ASImageDownloaderProtocol. + +- (nullable id)downloadImageWithURL:(NSURL *)URL + callbackQueue:(dispatch_queue_t)callbackQueue + downloadProgress:(nullable ASImageDownloaderProgress)downloadProgress + completion:(ASImageDownloaderCompletion)completion +{ + return [self downloadImageWithURL:URL + priority:ASImageDownloaderPriorityImminent // maps to default priority + callbackQueue:callbackQueue + downloadProgress:downloadProgress + completion:completion]; +} + +- (nullable id)downloadImageWithURL:(NSURL *)URL + priority:(ASImageDownloaderPriority)priority + callbackQueue:(dispatch_queue_t)callbackQueue + downloadProgress:(ASImageDownloaderProgress)downloadProgress + completion:(ASImageDownloaderCompletion)completion +{ + ASBasicImageDownloaderContext *context = [ASBasicImageDownloaderContext contextForURL:URL]; + + // NSURLSessionDownloadTask will do file I/O to create a temp directory. If called on the main thread this will + // cause significant performance issues. + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + // associate metadata with it + const auto callbackData = [[NSMutableDictionary alloc] init]; + callbackData[kASBasicImageDownloaderContextCallbackQueue] = callbackQueue ? : dispatch_get_main_queue(); + + if (downloadProgress) { + callbackData[kASBasicImageDownloaderContextProgressBlock] = [downloadProgress copy]; + } + + if (completion) { + callbackData[kASBasicImageDownloaderContextCompletionBlock] = [completion copy]; + } + + [context addCallbackData:[[NSDictionary alloc] initWithDictionary:callbackData]]; + + // Create new task if necessary + NSURLSessionDownloadTask *task = (NSURLSessionDownloadTask *)[context createSessionTaskIfNecessaryWithBlock:^(){return [self->_session downloadTaskWithURL:URL];}]; + + if (task) { + task.priority = NSURLSessionTaskPriorityWithImageDownloaderPriority(priority); + task.originalRequest.asyncdisplaykit_context = context; + + // start downloading + [task resume]; + } + }); + + return context; +} + +- (void)cancelImageDownloadForIdentifier:(id)downloadIdentifier +{ + ASDisplayNodeAssert([downloadIdentifier isKindOfClass:ASBasicImageDownloaderContext.class], @"unexpected downloadIdentifier"); + ASBasicImageDownloaderContext *context = (ASBasicImageDownloaderContext *)downloadIdentifier; + + [context cancel]; +} + + +#pragma mark NSURLSessionDownloadDelegate. + +- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask + didWriteData:(int64_t)bytesWritten + totalBytesWritten:(int64_t)totalBytesWritten + totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite +{ + ASBasicImageDownloaderContext *context = downloadTask.originalRequest.asyncdisplaykit_context; + [context performProgressBlocks:(CGFloat)totalBytesWritten / (CGFloat)totalBytesExpectedToWrite]; +} + +// invoked if the download succeeded with no error +- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask + didFinishDownloadingToURL:(NSURL *)location +{ + ASBasicImageDownloaderContext *context = downloadTask.originalRequest.asyncdisplaykit_context; + if ([context isCancelled]) { + return; + } + + if (context) { + UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:location]]; + [context completeWithImage:image error:nil]; + } +} + +// invoked unconditionally +- (void)URLSession:(NSURLSession *)session task:(NSURLSessionDownloadTask *)task + didCompleteWithError:(NSError *)error +{ + ASBasicImageDownloaderContext *context = task.originalRequest.asyncdisplaykit_context; + if (context && error) { + [context completeWithImage:nil error:error]; + } +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASBatchContext.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASBatchContext.h new file mode 100644 index 0000000..2c71e03 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASBatchContext.h @@ -0,0 +1,65 @@ +// +// ASBatchContext.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * @abstract A context object to notify when batch fetches are finished or cancelled. + */ +@interface ASBatchContext : NSObject + +/** + * Retrieve the state of the current batch process. + * + * @return A boolean reflecting if the owner of the context object is fetching another batch. + */ +- (BOOL)isFetching; + +/** + * Let the context object know that a batch fetch was completed. + * + * @param didComplete A boolean that states whether or not the batch fetch completed. + * + * @discussion Only by passing YES will the owner of the context know to attempt another batch update when necessary. + * For instance, when a table has reached the end of its data, a batch fetch will be attempted unless the context + * object thinks that it is still fetching. + */ +- (void)completeBatchFetching:(BOOL)didComplete; + +/** + * Ask the context object if the batch fetching process was cancelled by the context owner. + * + * @discussion If an error occurs in the context owner, the batch fetching may become out of sync and need to be + * cancelled. For best practices, pass the return value of -batchWasCancelled to -completeBatchFetch:. + * + * @return A boolean reflecting if the context object owner had to cancel the batch process. + */ +- (BOOL)batchFetchingWasCancelled; + +/** + * Notify the context object that something has interrupted the batch fetching process. + * + * @discussion Call this method only when something has corrupted the batch fetching process. Calling this method should + * be left to the owner of the batch process unless there is a specific purpose. + */ +- (void)cancelBatchFetching; + +/** + * Notify the context object that fetching has started. + * + * @discussion Call this method only when you are beginning a fetch process. This should really only be called by the + * context object's owner. Calling this method should be paired with -completeBatchFetching:. + */ +- (void)beginBatchFetching; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASBatchContext.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASBatchContext.mm new file mode 100644 index 0000000..a3395f6 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASBatchContext.mm @@ -0,0 +1,61 @@ +// +// ASBatchContext.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import +#import + +typedef NS_ENUM(NSInteger, ASBatchContextState) { + ASBatchContextStateFetching, + ASBatchContextStateCancelled, + ASBatchContextStateCompleted +}; + +@implementation ASBatchContext { + atomic_int _state; +} + +- (instancetype)init +{ + if (self = [super init]) { + _state = ATOMIC_VAR_INIT(ASBatchContextStateCompleted); + } + return self; +} + +- (BOOL)isFetching +{ + return atomic_load(&_state) == ASBatchContextStateFetching; +} + +- (BOOL)batchFetchingWasCancelled +{ + return atomic_load(&_state) == ASBatchContextStateCancelled; +} + +- (void)beginBatchFetching +{ + atomic_store(&_state, ASBatchContextStateFetching); +} + +- (void)completeBatchFetching:(BOOL)didComplete +{ + if (didComplete) { + os_log_debug(ASCollectionLog(), "Completed batch fetch with context %@", self); + atomic_store(&_state, ASBatchContextStateCompleted); + } +} + +- (void)cancelBatchFetching +{ + atomic_store(&_state, ASBatchContextStateCancelled); +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASBatchFetchingDelegate.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASBatchFetchingDelegate.h new file mode 100644 index 0000000..3b35115 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASBatchFetchingDelegate.h @@ -0,0 +1,23 @@ +// +// ASBatchFetchingDelegate.h +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +@protocol ASBatchFetchingDelegate + +/** + * @abstract Determine if batch fetching should begin based on the remaining time. + * If the delegate doesn't have enough information to confidently decide, it can take the given hint. + * + * @param remainingTime The amount of time left for user to reach the end of the scroll view's content. + * + * @param hint A hint for the delegate to fallback to. + */ +- (BOOL)shouldFetchBatchWithRemainingTime:(NSTimeInterval)remainingTime hint:(BOOL)hint; + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionElement.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionElement.h new file mode 100644 index 0000000..b128dd6 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionElement.h @@ -0,0 +1,47 @@ +// +// ASCollectionElement.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +@class ASDisplayNode; +@protocol ASRangeManagingNode; + +NS_ASSUME_NONNULL_BEGIN + +AS_SUBCLASSING_RESTRICTED +@interface ASCollectionElement : NSObject + +@property (nullable, nonatomic, copy, readonly) NSString *supplementaryElementKind; +@property (nonatomic) ASSizeRange constrainedSize; +@property (nonatomic, weak, readonly) id owningNode; +@property (nonatomic) ASPrimitiveTraitCollection traitCollection; +@property (nullable, nonatomic, readonly) id nodeModel; + +- (instancetype)initWithNodeModel:(nullable id)nodeModel + nodeBlock:(ASCellNodeBlock)nodeBlock + supplementaryElementKind:(nullable NSString *)supplementaryElementKind + constrainedSize:(ASSizeRange)constrainedSize + owningNode:(id)owningNode + traitCollection:(ASPrimitiveTraitCollection)traitCollection; + +/** + * @return The node, running the node block if necessary. The node block will be discarded + * after the first time it is run. + */ +@property (readonly) ASCellNode *node; + +/** + * @return The node, if the node block has been run already. + */ +@property (nullable, readonly) ASCellNode *nodeIfAllocated; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionElement.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionElement.mm new file mode 100644 index 0000000..9f9a05c --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionElement.mm @@ -0,0 +1,87 @@ +// +// ASCollectionElement.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import + +@interface ASCollectionElement () + +/// Required node block used to allocate a cell node. Nil after the first execution. +@property (nonatomic) ASCellNodeBlock nodeBlock; + +@end + +@implementation ASCollectionElement { + AS::Mutex _lock; + ASCellNode *_node; +} + +- (instancetype)initWithNodeModel:(id)nodeModel + nodeBlock:(ASCellNodeBlock)nodeBlock + supplementaryElementKind:(NSString *)supplementaryElementKind + constrainedSize:(ASSizeRange)constrainedSize + owningNode:(id)owningNode + traitCollection:(ASPrimitiveTraitCollection)traitCollection +{ + NSAssert(nodeBlock != nil, @"Node block must not be nil"); + self = [super init]; + if (self) { + _nodeModel = nodeModel; + _nodeBlock = nodeBlock; + _supplementaryElementKind = [supplementaryElementKind copy]; + _constrainedSize = constrainedSize; + _owningNode = owningNode; + _traitCollection = traitCollection; + } + return self; +} + +- (ASCellNode *)node +{ + AS::MutexLocker l(_lock); + if (_nodeBlock != nil) { + ASCellNode *node = _nodeBlock(); + _nodeBlock = nil; + if (node == nil) { + ASDisplayNodeFailAssert(@"Node block returned nil node!"); + node = [[ASCellNode alloc] init]; + } + node.collectionElement = self; + ASTraitCollectionPropagateDown(node, _traitCollection); + node.nodeModel = _nodeModel; + _node = node; + } + return _node; +} + +- (ASCellNode *)nodeIfAllocated +{ + AS::MutexLocker l(_lock); + return _node; +} + +- (void)setTraitCollection:(ASPrimitiveTraitCollection)traitCollection +{ + ASCellNode *nodeIfNeedsPropagation; + + { + AS::MutexLocker l(_lock); + if (! ASPrimitiveTraitCollectionIsEqualToASPrimitiveTraitCollection(_traitCollection, traitCollection)) { + _traitCollection = traitCollection; + nodeIfNeedsPropagation = _node; + } + } + + if (nodeIfNeedsPropagation != nil) { + ASTraitCollectionPropagateDown(nodeIfNeedsPropagation, traitCollection); + } +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionFlowLayoutDelegate.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionFlowLayoutDelegate.h new file mode 100644 index 0000000..d22e9bb --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionFlowLayoutDelegate.h @@ -0,0 +1,28 @@ +// +// ASCollectionFlowLayoutDelegate.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +AS_SUBCLASSING_RESTRICTED + +/** + * A thread-safe, high performant layout delegate that arranges items into a flow layout. + * It uses a concurrent and multi-line ASStackLayoutSpec under the hood. Thus, per-child flex properties (i.e alignSelf, + * flexShrink, flexGrow, etc - see @ASStackLayoutElement) can be set directly on cell nodes to be used + * to calculate the final collection layout. + */ +@interface ASCollectionFlowLayoutDelegate : NSObject + +- (instancetype)initWithScrollableDirections:(ASScrollDirection)scrollableDirections NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionFlowLayoutDelegate.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionFlowLayoutDelegate.mm new file mode 100644 index 0000000..7a2796c --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionFlowLayoutDelegate.mm @@ -0,0 +1,78 @@ +// +// ASCollectionFlowLayoutDelegate.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import +#import +#import +#import +#import +#import +#import +#import +#import + +@implementation ASCollectionFlowLayoutDelegate { + ASScrollDirection _scrollableDirections; +} + +- (instancetype)init +{ + return [self initWithScrollableDirections:ASScrollDirectionVerticalDirections]; +} + +- (instancetype)initWithScrollableDirections:(ASScrollDirection)scrollableDirections +{ + self = [super init]; + if (self) { + _scrollableDirections = scrollableDirections; + } + return self; +} + +- (ASScrollDirection)scrollableDirections +{ + ASDisplayNodeAssertMainThread(); + return _scrollableDirections; +} + +- (id)additionalInfoForLayoutWithElements:(ASElementMap *)elements +{ + ASDisplayNodeAssertMainThread(); + return nil; +} + ++ (ASCollectionLayoutState *)calculateLayoutWithContext:(ASCollectionLayoutContext *)context +{ + ASElementMap *elements = context.elements; + NSArray *children = ASArrayByFlatMapping(elements.itemElements, ASCollectionElement *element, element.node); + if (children.count == 0) { + return [[ASCollectionLayoutState alloc] initWithContext:context]; + } + + ASStackLayoutSpec *stackSpec = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal + spacing:0 + justifyContent:ASStackLayoutJustifyContentStart + alignItems:ASStackLayoutAlignItemsStart + flexWrap:ASStackLayoutFlexWrapWrap + alignContent:ASStackLayoutAlignContentStart + children:children]; + stackSpec.concurrent = YES; + + ASSizeRange sizeRange = ASSizeRangeForCollectionLayoutThatFitsViewportSize(context.viewportSize, context.scrollableDirections); + ASLayout *layout = [stackSpec layoutThatFits:sizeRange]; + + return [[ASCollectionLayoutState alloc] initWithContext:context layout:layout getElementBlock:^ASCollectionElement * _Nullable(ASLayout * _Nonnull sublayout) { + ASCellNode *node = ASDynamicCast(sublayout.layoutElement, ASCellNode); + return node ? node.collectionElement : nil; + }]; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionGalleryLayoutDelegate.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionGalleryLayoutDelegate.h new file mode 100644 index 0000000..064891c --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionGalleryLayoutDelegate.h @@ -0,0 +1,106 @@ +// +// ASCollectionGalleryLayoutDelegate.h +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +@class ASElementMap; +@class ASCollectionGalleryLayoutDelegate; + +NS_ASSUME_NONNULL_BEGIN + +@protocol ASCollectionGalleryLayoutPropertiesProviding + +/** + * Returns the fixed size of each and every element. + * + * @discussion This method will only be called on main thread. + * + * @param delegate The calling object. + * + * @param elements All elements to be sized. + * + * @return The elements' size + */ +- (CGSize)galleryLayoutDelegate:(ASCollectionGalleryLayoutDelegate *)delegate sizeForElements:(ASElementMap *)elements; + +@optional + +/** + * Returns the minumum spacing to use between lines of items. + * + * @discussion This method will only be called on main thread. + * + * @discussion For a vertically scrolling layout, this value represents the minimum spacing between rows. + * For a horizontally scrolling one, it represents the minimum spacing between columns. + * It is not applied between the first line and the header, or between the last line and the footer. + * This is the same behavior as UICollectionViewFlowLayout's minimumLineSpacing. + * + * @param delegate The calling object. + * + * @param elements All elements in the layout. + * + * @return The interitem spacing + */ +- (CGFloat)galleryLayoutDelegate:(ASCollectionGalleryLayoutDelegate *)delegate minimumLineSpacingForElements:(ASElementMap *)elements; + +/** + * Returns the minumum spacing to use between items in the same row or column, depending on the scroll directions. + * + * @discussion This method will only be called on main thread. + * + * @discussion For a vertically scrolling layout, this value represents the minimum spacing between items in the same row. + * For a horizontally scrolling one, it represents the minimum spacing between items in the same column. + * It is considered while fitting items into lines, but the actual final spacing between some items might be larger. + * This is the same behavior as UICollectionViewFlowLayout's minimumInteritemSpacing. + * + * @param delegate The calling object. + * + * @param elements All elements in the layout. + * + * @return The interitem spacing + */ +- (CGFloat)galleryLayoutDelegate:(ASCollectionGalleryLayoutDelegate *)delegate minimumInteritemSpacingForElements:(ASElementMap *)elements; + +/** + * Returns the margins of each section. + * + * @discussion This method will only be called on main thread. + * + * @param delegate The calling object. + * + * @param elements All elements in the layout. + * + * @return The margins used to layout content in a section + */ +- (UIEdgeInsets)galleryLayoutDelegate:(ASCollectionGalleryLayoutDelegate *)delegate sectionInsetForElements:(ASElementMap *)elements; + +@end + +/** + * A thread-safe layout delegate that arranges items with the same size into a flow layout. + * + * @note Supplemenraty elements are not supported. + */ +AS_SUBCLASSING_RESTRICTED +@interface ASCollectionGalleryLayoutDelegate : NSObject + +@property (nonatomic, weak) id propertiesProvider; + +/** + * Designated initializer. + * + * @param scrollableDirections The scrollable directions of this layout. Must be either vertical or horizontal directions. + */ +- (instancetype)initWithScrollableDirections:(ASScrollDirection)scrollableDirections NS_DESIGNATED_INITIALIZER; + +- (instancetype)init __unavailable; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionGalleryLayoutDelegate.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionGalleryLayoutDelegate.mm new file mode 100644 index 0000000..5ba74d6 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionGalleryLayoutDelegate.mm @@ -0,0 +1,135 @@ +// +// ASCollectionGalleryLayoutDelegate.mm +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + +#pragma mark - ASCollectionGalleryLayoutDelegate + +@implementation ASCollectionGalleryLayoutDelegate { + ASScrollDirection _scrollableDirections; + + struct { + unsigned int minimumLineSpacingForElements:1; + unsigned int minimumInteritemSpacingForElements:1; + unsigned int sectionInsetForElements:1; + } _propertiesProviderFlags; +} + +- (instancetype)initWithScrollableDirections:(ASScrollDirection)scrollableDirections +{ + self = [super init]; + if (self) { + // Scrollable directions must be either vertical or horizontal, but not both + ASDisplayNodeAssertTrue(ASScrollDirectionContainsVerticalDirection(scrollableDirections) + || ASScrollDirectionContainsHorizontalDirection(scrollableDirections)); + ASDisplayNodeAssertFalse(ASScrollDirectionContainsVerticalDirection(scrollableDirections) + && ASScrollDirectionContainsHorizontalDirection(scrollableDirections)); + _scrollableDirections = scrollableDirections; + } + return self; +} + +- (ASScrollDirection)scrollableDirections +{ + ASDisplayNodeAssertMainThread(); + return _scrollableDirections; +} + +- (void)setPropertiesProvider:(id)propertiesProvider +{ + ASDisplayNodeAssertMainThread(); + if (propertiesProvider == nil) { + _propertiesProvider = nil; + _propertiesProviderFlags = {}; + } else { + _propertiesProvider = propertiesProvider; + _propertiesProviderFlags.minimumLineSpacingForElements = [_propertiesProvider respondsToSelector:@selector(galleryLayoutDelegate:minimumLineSpacingForElements:)]; + _propertiesProviderFlags.minimumInteritemSpacingForElements = [_propertiesProvider respondsToSelector:@selector(galleryLayoutDelegate:minimumInteritemSpacingForElements:)]; + _propertiesProviderFlags.sectionInsetForElements = [_propertiesProvider respondsToSelector:@selector(galleryLayoutDelegate:sectionInsetForElements:)]; + } +} + +- (id)additionalInfoForLayoutWithElements:(ASElementMap *)elements +{ + ASDisplayNodeAssertMainThread(); + id propertiesProvider = _propertiesProvider; + if (propertiesProvider == nil) { + return nil; + } + + CGSize itemSize = [propertiesProvider galleryLayoutDelegate:self sizeForElements:elements]; + UIEdgeInsets sectionInset = _propertiesProviderFlags.sectionInsetForElements ? [propertiesProvider galleryLayoutDelegate:self sectionInsetForElements:elements] : UIEdgeInsetsZero; + CGFloat lineSpacing = _propertiesProviderFlags.minimumLineSpacingForElements ? [propertiesProvider galleryLayoutDelegate:self minimumLineSpacingForElements:elements] : 0.0; + CGFloat interitemSpacing = _propertiesProviderFlags.minimumInteritemSpacingForElements ? [propertiesProvider galleryLayoutDelegate:self minimumInteritemSpacingForElements:elements] : 0.0; + return [[_ASCollectionGalleryLayoutInfo alloc] initWithItemSize:itemSize + minimumLineSpacing:lineSpacing + minimumInteritemSpacing:interitemSpacing + sectionInset:sectionInset]; +} + ++ (ASCollectionLayoutState *)calculateLayoutWithContext:(ASCollectionLayoutContext *)context +{ + ASElementMap *elements = context.elements; + CGSize pageSize = context.viewportSize; + ASScrollDirection scrollableDirections = context.scrollableDirections; + + _ASCollectionGalleryLayoutInfo *info = ASDynamicCast(context.additionalInfo, _ASCollectionGalleryLayoutInfo); + CGSize itemSize = info.itemSize; + if (info == nil || CGSizeEqualToSize(CGSizeZero, itemSize)) { + return [[ASCollectionLayoutState alloc] initWithContext:context]; + } + + NSArray<_ASGalleryLayoutItem *> *children = ASArrayByFlatMapping(elements.itemElements, + ASCollectionElement *element, + [[_ASGalleryLayoutItem alloc] initWithItemSize:itemSize collectionElement:element]); + if (children.count == 0) { + return [[ASCollectionLayoutState alloc] initWithContext:context]; + } + + // Use a stack spec to calculate layout content size and frames of all elements without actually measuring each element + ASStackLayoutDirection stackDirection = ASScrollDirectionContainsVerticalDirection(scrollableDirections) + ? ASStackLayoutDirectionHorizontal + : ASStackLayoutDirectionVertical; + ASStackLayoutSpec *stackSpec = [ASStackLayoutSpec stackLayoutSpecWithDirection:stackDirection + spacing:info.minimumInteritemSpacing + justifyContent:ASStackLayoutJustifyContentStart + alignItems:ASStackLayoutAlignItemsStart + flexWrap:ASStackLayoutFlexWrapWrap + alignContent:ASStackLayoutAlignContentStart + lineSpacing:info.minimumLineSpacing + children:children]; + stackSpec.concurrent = YES; + + ASLayoutSpec *finalSpec = stackSpec; + UIEdgeInsets sectionInset = info.sectionInset; + if (UIEdgeInsetsEqualToEdgeInsets(sectionInset, UIEdgeInsetsZero) == NO) { + finalSpec = [ASInsetLayoutSpec insetLayoutSpecWithInsets:sectionInset child:stackSpec]; + } + + ASLayout *layout = [finalSpec layoutThatFits:ASSizeRangeForCollectionLayoutThatFitsViewportSize(pageSize, scrollableDirections)]; + + return [[ASCollectionLayoutState alloc] initWithContext:context layout:layout getElementBlock:^ASCollectionElement * _Nullable(ASLayout * _Nonnull sublayout) { + _ASGalleryLayoutItem *item = ASDynamicCast(sublayout.layoutElement, _ASGalleryLayoutItem); + return item ? item.collectionElement : nil; + }]; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionInternal.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionInternal.h new file mode 100644 index 0000000..42f651a --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionInternal.h @@ -0,0 +1,64 @@ +// +// ASCollectionInternal.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@protocol ASCollectionViewLayoutFacilitatorProtocol; +@class ASCollectionNode; +@class ASDataController; +@class ASRangeController; + +@interface ASCollectionView () +- (instancetype)_initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout layoutFacilitator:(nullable id)layoutFacilitator owningNode:(nullable ASCollectionNode *)owningNode; + +@property (nonatomic, weak) ASCollectionNode *collectionNode; +@property (nonatomic, readonly) ASDataController *dataController; +@property (nonatomic, readonly) ASRangeController *rangeController; + +/** + * The change set that we're currently building, if any. + */ +@property (nonatomic, nullable, readonly) _ASHierarchyChangeSet *changeSet; + +/** + * @see ASCollectionNode+Beta.h for full documentation. + */ +@property (nonatomic) ASCellLayoutMode cellLayoutMode; + +/** + * Attempt to get the view-layer index path for the item with the given index path. + * + * @param indexPath The index path of the item. + * @param wait If the item hasn't reached the view yet, this attempts to wait for updates to commit. + */ +- (nullable NSIndexPath *)convertIndexPathFromCollectionNode:(NSIndexPath *)indexPath waitingIfNeeded:(BOOL)wait; + +/** + * Attempt to get the node index path given the view-layer index path. + * + * @param indexPath The index path of the row. + */ +- (nullable NSIndexPath *)convertIndexPathToCollectionNode:(NSIndexPath *)indexPath; + +/** + * Attempt to get the node index paths given the view-layer index paths. + * + * @param indexPaths An array of index paths in the view space + */ +- (nullable NSArray *)convertIndexPathsToCollectionNode:(nullable NSArray *)indexPaths; + +- (void)beginUpdates; + +- (void)endUpdatesAnimated:(BOOL)animated completion:(nullable void (^)(BOOL))completion; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionLayoutContext.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionLayoutContext.h new file mode 100644 index 0000000..4c25838 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionLayoutContext.h @@ -0,0 +1,31 @@ +// +// ASCollectionLayoutContext.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import + +@class ASElementMap; + +NS_ASSUME_NONNULL_BEGIN + +AS_SUBCLASSING_RESTRICTED +@interface ASCollectionLayoutContext : NSObject + +@property (nonatomic, readonly) CGSize viewportSize; +@property (nonatomic, readonly) CGPoint initialContentOffset; +@property (nonatomic, readonly) ASScrollDirection scrollableDirections; +@property (nonatomic, weak, readonly) ASElementMap *elements; +@property (nullable, nonatomic, readonly) id additionalInfo; + +- (instancetype)init NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionLayoutContext.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionLayoutContext.mm new file mode 100644 index 0000000..6f2ba71 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionLayoutContext.mm @@ -0,0 +1,102 @@ +// +// ASCollectionLayoutContext.mm +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +#import +#import +#import +#import +#import + +@implementation ASCollectionLayoutContext { + Class _layoutDelegateClass; + __weak ASCollectionLayoutCache *_layoutCache; +} + +- (instancetype)initWithViewportSize:(CGSize)viewportSize + initialContentOffset:(CGPoint)initialContentOffset + scrollableDirections:(ASScrollDirection)scrollableDirections + elements:(ASElementMap *)elements + layoutDelegateClass:(Class)layoutDelegateClass + layoutCache:(ASCollectionLayoutCache *)layoutCache + additionalInfo:(id)additionalInfo +{ + self = [super init]; + if (self) { + _viewportSize = viewportSize; + _initialContentOffset = initialContentOffset; + _scrollableDirections = scrollableDirections; + _elements = elements; + _layoutDelegateClass = layoutDelegateClass; + _layoutCache = layoutCache; + _additionalInfo = additionalInfo; + } + return self; +} + +- (Class)layoutDelegateClass +{ + return _layoutDelegateClass; +} + +- (ASCollectionLayoutCache *)layoutCache +{ + return _layoutCache; +} + +// NOTE: Some properties, like initialContentOffset and layoutCache are ignored in -isEqualToContext: and -hash. +// That is because contexts can be equal regardless of the content offsets or layout caches. +- (BOOL)isEqualToContext:(ASCollectionLayoutContext *)context +{ + if (context == nil) { + return NO; + } + + // NOTE: ASObjectIsEqual returns YES when both objects are nil. + // So don't use ASObjectIsEqual on _elements. + // It is a weak property and 2 layouts generated from different sets of elements + // should never be considered the same even if they are nil now. + return CGSizeEqualToSize(_viewportSize, context.viewportSize) + && _scrollableDirections == context.scrollableDirections + && [_elements isEqual:context.elements] + && _layoutDelegateClass == context.layoutDelegateClass + && ASObjectIsEqual(_additionalInfo, context.additionalInfo); +} + +- (BOOL)isEqual:(id)other +{ + if (self == other) { + return YES; + } + if (! [other isKindOfClass:[ASCollectionLayoutContext class]]) { + return NO; + } + return [self isEqualToContext:other]; +} + +- (NSUInteger)hash +{ + struct { + CGSize viewportSize; + ASScrollDirection scrollableDirections; + NSUInteger elementsHash; + NSUInteger layoutDelegateClassHash; + NSUInteger additionalInfoHash; + } data = { + _viewportSize, + _scrollableDirections, + _elements.hash, + _layoutDelegateClass.hash, + [_additionalInfo hash] + }; + return ASHashBytes(&data, sizeof(data)); +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionLayoutDelegate.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionLayoutDelegate.h new file mode 100644 index 0000000..c53e22a --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionLayoutDelegate.h @@ -0,0 +1,59 @@ +// +// ASCollectionLayoutDelegate.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import + +@class ASElementMap, ASCollectionLayoutContext, ASCollectionLayoutState; + +NS_ASSUME_NONNULL_BEGIN + +@protocol ASCollectionLayoutDelegate + +/** + * @abstract Returns the scrollable directions of the coming layout (@see @c -calculateLayoutWithContext:). + * It will be available in the context parameter in +calculateLayoutWithContext: + * + * @return The scrollable directions. + * + * @discusstion This method will be called on main thread. + */ +- (ASScrollDirection)scrollableDirections; + +/** + * @abstract Returns any additional information needed for a coming layout pass (@see @c -calculateLayoutWithContext:) with the given elements. + * + * @param elements The elements to be laid out later. + * + * @discussion The returned object must support equality and hashing (i.e `-isEqual:` and `-hash` must be properly implemented). + * It should contain all the information needed for the layout pass to perform. It will be available in the context parameter in +calculateLayoutWithContext: + * + * This method will be called on main thread. + */ +- (nullable id)additionalInfoForLayoutWithElements:(ASElementMap *)elements; + +/** + * @abstract Prepares and returns a new layout for given context. + * + * @param context A context that contains all elements to be laid out and any additional information needed. + * + * @return The new layout calculated for the given context. + * + * @discussion This method is called ahead of time, i.e before the underlying collection/table view is aware of the provided elements. + * As a result, clients must solely rely on the given context and should not reach out to other objects for information not available in the context. + * + * This method can be called on background theads. It must be thread-safe and should not change any internal state of this delegate. + * It must block the calling thread but can dispatch to other theads to reduce total blocking time. + */ ++ (ASCollectionLayoutState *)calculateLayoutWithContext:(ASCollectionLayoutContext *)context; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionLayoutState.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionLayoutState.h new file mode 100644 index 0000000..c362033 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionLayoutState.h @@ -0,0 +1,111 @@ +// +// ASCollectionLayoutState.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import + +@class ASCollectionLayoutContext, ASLayout, ASCollectionElement; + +NS_ASSUME_NONNULL_BEGIN + +typedef ASCollectionElement * _Nullable (^ASCollectionLayoutStateGetElementBlock)(ASLayout *); + +@interface NSMapTable (ASCollectionLayoutConvenience) + ++ (NSMapTable *)elementToLayoutAttributesTable; + +@end + +AS_SUBCLASSING_RESTRICTED + +/// An immutable state of the collection layout +@interface ASCollectionLayoutState : NSObject + +/// The context used to calculate this object +@property (readonly) ASCollectionLayoutContext *context; + +/// The final content size of the collection's layout +@property (readonly) CGSize contentSize; + +- (instancetype)init NS_UNAVAILABLE; + +/** + * Designated initializer. + * + * @param context The context used to calculate this object + * + * @param contentSize The content size of the collection's layout + * + * @param table A map between elements to their layout attributes. It must contain all elements. + * It should have NSMapTableObjectPointerPersonality and NSMapTableWeakMemory as key options. + */ +- (instancetype)initWithContext:(ASCollectionLayoutContext *)context + contentSize:(CGSize)contentSize + elementToLayoutAttributesTable:(NSMapTable *)table NS_DESIGNATED_INITIALIZER; + +/** + * Convenience initializer. Returns an object with zero content size and an empty table. + * + * @param context The context used to calculate this object + */ +- (instancetype)initWithContext:(ASCollectionLayoutContext *)context; + +/** + * Convenience initializer. + * + * @param context The context used to calculate this object + * + * @param layout The layout describes size and position of all elements. + * + * @param getElementBlock A block that can retrieve the collection element from a sublayout of the root layout. + */ +- (instancetype)initWithContext:(ASCollectionLayoutContext *)context + layout:(ASLayout *)layout + getElementBlock:(ASCollectionLayoutStateGetElementBlock)getElementBlock; + +/** + * Returns all layout attributes present in this object. + */ +- (NSArray *)allLayoutAttributes; + +/** + * Returns layout attributes of elements in the specified rect. + * + * @param rect The rect containing the target elements. + */ +- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect; + +/** + * Returns layout attributes of the element at the specified index path. + * + * @param indexPath The index path of the item. + */ +- (nullable UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath; + +/** + * Returns layout attributes of the specified supplementary element. + * + * @param kind A string that identifies the type of the supplementary element. + * + * @param indexPath The index path of the element. + */ +- (nullable UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryElementOfKind:(NSString *)kind + atIndexPath:(NSIndexPath *)indexPath; + +/** + * Returns layout attributes of the specified element. + * + * @element The element. + */ +- (nullable UICollectionViewLayoutAttributes *)layoutAttributesForElement:(ASCollectionElement *)element; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionLayoutState.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionLayoutState.mm new file mode 100644 index 0000000..0f98b21 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionLayoutState.mm @@ -0,0 +1,253 @@ +// +// ASCollectionLayoutState.mm +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import +#import +#import +#import +#import +#import +#import +#import +#import + +#import + +@implementation NSMapTable (ASCollectionLayoutConvenience) + ++ (NSMapTable *)elementToLayoutAttributesTable +{ + return [NSMapTable mapTableWithKeyOptions:(NSMapTableWeakMemory | NSMapTableObjectPointerPersonality) valueOptions:NSMapTableStrongMemory]; +} + +@end + +@implementation ASCollectionLayoutState { + AS::Mutex __instanceLock__; + CGSize _contentSize; + ASCollectionLayoutContext *_context; + NSMapTable *_elementToLayoutAttributesTable; + ASPageToLayoutAttributesTable *_pageToLayoutAttributesTable; + ASPageToLayoutAttributesTable *_unmeasuredPageToLayoutAttributesTable; +} + +- (instancetype)initWithContext:(ASCollectionLayoutContext *)context +{ + return [self initWithContext:context + contentSize:CGSizeZero +elementToLayoutAttributesTable:[NSMapTable elementToLayoutAttributesTable]]; +} + +- (instancetype)initWithContext:(ASCollectionLayoutContext *)context + layout:(ASLayout *)layout + getElementBlock:(ASCollectionLayoutStateGetElementBlock)getElementBlock +{ + ASElementMap *elements = context.elements; + NSMapTable *table = [NSMapTable elementToLayoutAttributesTable]; + + // Traverse the given layout tree in breadth first fashion. Generate layout attributes for all included elements along the way. + struct Context { + ASLayout *layout; + CGPoint absolutePosition; + }; + + std::queue queue; + queue.push({layout, CGPointZero}); + + while (!queue.empty()) { + Context context = queue.front(); + queue.pop(); + + ASLayout *layout = context.layout; + const CGPoint absolutePosition = context.absolutePosition; + + ASCollectionElement *element = getElementBlock(layout); + if (element != nil) { + NSIndexPath *indexPath = [elements indexPathForElement:element]; + NSString *supplementaryElementKind = element.supplementaryElementKind; + + UICollectionViewLayoutAttributes *attrs; + if (supplementaryElementKind == nil) { + attrs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath]; + } else { + attrs = [UICollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:supplementaryElementKind withIndexPath:indexPath]; + } + + CGRect frame = layout.frame; + frame.origin = absolutePosition; + attrs.frame = frame; + [table setObject:attrs forKey:element]; + } + + // Add all sublayouts to process in next step + for (ASLayout *sublayout in layout.sublayouts) { + queue.push({sublayout, absolutePosition + sublayout.position}); + } + } + + return [self initWithContext:context contentSize:layout.size elementToLayoutAttributesTable:table]; +} + +- (instancetype)initWithContext:(ASCollectionLayoutContext *)context + contentSize:(CGSize)contentSize + elementToLayoutAttributesTable:(NSMapTable *)table +{ + self = [super init]; + if (self) { + _context = context; + _contentSize = contentSize; + _elementToLayoutAttributesTable = [table copy]; // Copy the given table to make sure clients can't mutate it after this point. + CGSize pageSize = context.viewportSize; + _pageToLayoutAttributesTable = [ASPageTable pageTableWithLayoutAttributes:table.objectEnumerator contentSize:contentSize pageSize:pageSize]; + _unmeasuredPageToLayoutAttributesTable = [ASCollectionLayoutState _unmeasuredLayoutAttributesTableFromTable:table contentSize:contentSize pageSize:pageSize]; + } + return self; +} + +- (ASCollectionLayoutContext *)context +{ + return _context; +} + +- (CGSize)contentSize +{ + return _contentSize; +} + +- (NSArray *)allLayoutAttributes +{ + return [_elementToLayoutAttributesTable.objectEnumerator allObjects]; +} + +- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath +{ + ASCollectionElement *element = [_context.elements elementForItemAtIndexPath:indexPath]; + return [_elementToLayoutAttributesTable objectForKey:element]; +} + +- (UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryElementOfKind:(NSString *)elementKind + atIndexPath:(NSIndexPath *)indexPath +{ + ASCollectionElement *element = [_context.elements supplementaryElementOfKind:elementKind atIndexPath:indexPath]; + return [_elementToLayoutAttributesTable objectForKey:element]; +} + +- (UICollectionViewLayoutAttributes *)layoutAttributesForElement:(ASCollectionElement *)element +{ + return [_elementToLayoutAttributesTable objectForKey:element]; +} + +- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect +{ + CGSize pageSize = _context.viewportSize; + NSPointerArray *pages = ASPageCoordinatesForPagesThatIntersectRect(rect, _contentSize, pageSize); + if (pages.count == 0) { + return @[]; + } + + // Use a set here because some items may span multiple pages + const auto result = [[NSMutableSet alloc] init]; + for (id pagePtr in pages) { + ASPageCoordinate page = (ASPageCoordinate)pagePtr; + NSArray *allAttrs = [_pageToLayoutAttributesTable objectForPage:page]; + if (allAttrs.count > 0) { + CGRect pageRect = ASPageCoordinateGetPageRect(page, pageSize); + + if (CGRectContainsRect(rect, pageRect)) { + [result addObjectsFromArray:allAttrs]; + } else { + for (UICollectionViewLayoutAttributes *attrs in allAttrs) { + if (CGRectIntersectsRect(rect, attrs.frame)) { + [result addObject:attrs]; + } + } + } + } + } + + return [result allObjects]; +} + +- (ASPageToLayoutAttributesTable *)getAndRemoveUnmeasuredLayoutAttributesPageTableInRect:(CGRect)rect +{ + CGSize pageSize = _context.viewportSize; + CGSize contentSize = _contentSize; + + AS::MutexLocker l(__instanceLock__); + if (_unmeasuredPageToLayoutAttributesTable.count == 0 || CGRectIsNull(rect) || CGRectIsEmpty(rect) || CGSizeEqualToSize(CGSizeZero, contentSize) || CGSizeEqualToSize(CGSizeZero, pageSize)) { + return nil; + } + + // Step 1: Determine all the pages that intersect the specified rect + NSPointerArray *pagesInRect = ASPageCoordinatesForPagesThatIntersectRect(rect, contentSize, pageSize); + if (pagesInRect.count == 0) { + return nil; + } + + // Step 2: Filter out attributes in these pages that intersect the specified rect. + ASPageToLayoutAttributesTable *result = nil; + for (id pagePtr in pagesInRect) { + ASPageCoordinate page = (ASPageCoordinate)pagePtr; + NSMutableArray *attrsInPage = [_unmeasuredPageToLayoutAttributesTable objectForPage:page]; + if (attrsInPage.count == 0) { + continue; + } + + NSMutableArray *intersectingAttrsInPage = nil; + CGRect pageRect = ASPageCoordinateGetPageRect(page, pageSize); + if (CGRectContainsRect(rect, pageRect)) { + // This page fits well within the specified rect. Simply return all of its attributes. + intersectingAttrsInPage = attrsInPage; + } else { + // The page intersects the specified rect. Some attributes in this page are returned, some are not. + for (UICollectionViewLayoutAttributes *attrs in attrsInPage) { + if (CGRectIntersectsRect(rect, attrs.frame)) { + if (intersectingAttrsInPage == nil) { + intersectingAttrsInPage = [[NSMutableArray alloc] init]; + } + [intersectingAttrsInPage addObject:attrs]; + } + } + } + + if (intersectingAttrsInPage.count > 0) { + if (attrsInPage.count == intersectingAttrsInPage.count) { + [_unmeasuredPageToLayoutAttributesTable removeObjectForPage:page]; + } else { + [attrsInPage removeObjectsInArray:intersectingAttrsInPage]; + } + if (result == nil) { + result = [ASPageTable pageTableForStrongObjectPointers]; + } + [result setObject:intersectingAttrsInPage forPage:page]; + } + } + + return result; +} + +#pragma mark - Private methods + ++ (ASPageToLayoutAttributesTable *)_unmeasuredLayoutAttributesTableFromTable:(NSMapTable *)table + contentSize:(CGSize)contentSize + pageSize:(CGSize)pageSize +{ + NSMutableArray *unmeasuredAttrs = [[NSMutableArray alloc] init]; + for (ASCollectionElement *element in table) { + UICollectionViewLayoutAttributes *attrs = [table objectForKey:element]; + if (element.nodeIfAllocated == nil || CGSizeEqualToSize(element.nodeIfAllocated.calculatedSize, attrs.frame.size) == NO) { + [unmeasuredAttrs addObject:attrs]; + } + } + + return [ASPageTable pageTableWithLayoutAttributes:unmeasuredAttrs contentSize:contentSize pageSize:pageSize]; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionViewLayoutController.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionViewLayoutController.h new file mode 100644 index 0000000..6311e0a --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionViewLayoutController.h @@ -0,0 +1,23 @@ +// +// ASCollectionViewLayoutController.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@class ASCollectionView; + +AS_SUBCLASSING_RESTRICTED +@interface ASCollectionViewLayoutController : ASAbstractLayoutController + +- (instancetype)initWithCollectionView:(ASCollectionView *)collectionView; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionViewLayoutController.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionViewLayoutController.mm new file mode 100644 index 0000000..b31bb12 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionViewLayoutController.mm @@ -0,0 +1,126 @@ +// +// ASCollectionViewLayoutController.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import +#import +#import + +struct ASRangeGeometry { + CGRect rangeBounds; + CGRect updateBounds; +}; +typedef struct ASRangeGeometry ASRangeGeometry; + +#pragma mark - +#pragma mark ASCollectionViewLayoutController + +@interface ASCollectionViewLayoutController () +{ + @package + ASCollectionView * __weak _collectionView; + UICollectionViewLayout * __strong _collectionViewLayout; +} +@end + +@implementation ASCollectionViewLayoutController + +- (instancetype)initWithCollectionView:(ASCollectionView *)collectionView +{ + if (!(self = [super init])) { + return nil; + } + + _collectionView = collectionView; + _collectionViewLayout = [collectionView collectionViewLayout]; + return self; +} + +- (NSHashTable *)elementsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType map:(ASElementMap *)map +{ + ASRangeTuningParameters tuningParameters = [self tuningParametersForRangeMode:rangeMode rangeType:rangeType]; + CGRect rangeBounds = [self rangeBoundsWithScrollDirection:scrollDirection rangeTuningParameters:tuningParameters]; + return [self elementsWithinRangeBounds:rangeBounds map:map]; +} + +- (void)allElementsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode displaySet:(NSHashTable *__autoreleasing _Nullable *)displaySet preloadSet:(NSHashTable *__autoreleasing _Nullable *)preloadSet map:(ASElementMap *)map +{ + if (displaySet == NULL || preloadSet == NULL) { + return; + } + + ASRangeTuningParameters displayParams = [self tuningParametersForRangeMode:rangeMode rangeType:ASLayoutRangeTypeDisplay]; + ASRangeTuningParameters preloadParams = [self tuningParametersForRangeMode:rangeMode rangeType:ASLayoutRangeTypePreload]; + CGRect displayBounds = [self rangeBoundsWithScrollDirection:scrollDirection rangeTuningParameters:displayParams]; + CGRect preloadBounds = [self rangeBoundsWithScrollDirection:scrollDirection rangeTuningParameters:preloadParams]; + + CGRect unionBounds = CGRectUnion(displayBounds, preloadBounds); + NSArray *layoutAttributes = [_collectionViewLayout layoutAttributesForElementsInRect:unionBounds]; + NSInteger count = layoutAttributes.count; + + __auto_type display = [[NSHashTable alloc] initWithOptions:NSHashTableObjectPointerPersonality capacity:count]; + __auto_type preload = [[NSHashTable alloc] initWithOptions:NSHashTableObjectPointerPersonality capacity:count]; + + for (UICollectionViewLayoutAttributes *la in layoutAttributes) { + // Manually filter out elements that don't intersect the range bounds. + // See comment in elementsForItemsWithinRangeBounds: + // This is re-implemented here so that the iteration over layoutAttributes can be done once to check both ranges. + CGRect frame = la.frame; + BOOL intersectsDisplay = CGRectIntersectsRect(displayBounds, frame); + BOOL intersectsPreload = CGRectIntersectsRect(preloadBounds, frame); + if (intersectsDisplay == NO && intersectsPreload == NO && CATransform3DIsIdentity(la.transform3D) == YES) { + // Questionable why the element would be included here, but it doesn't belong. + continue; + } + + // Avoid excessive retains and releases, as well as property calls. We know the element is kept alive by map. + unowned ASCollectionElement *e = [map elementForLayoutAttributes:la]; + if (e != nil && intersectsDisplay) { + [display addObject:e]; + } + if (e != nil && intersectsPreload) { + [preload addObject:e]; + } + } + + *displaySet = display; + *preloadSet = preload; + return; +} + +- (NSHashTable *)elementsWithinRangeBounds:(CGRect)rangeBounds map:(ASElementMap *)map +{ + NSArray *layoutAttributes = [_collectionViewLayout layoutAttributesForElementsInRect:rangeBounds]; + NSHashTable *elementSet = [[NSHashTable alloc] initWithOptions:NSHashTableObjectPointerPersonality capacity:layoutAttributes.count]; + + for (UICollectionViewLayoutAttributes *la in layoutAttributes) { + // Manually filter out elements that don't intersect the range bounds. + // If a layout returns elements outside the requested rect this can be a huge problem. + // For instance in a paging flow, you may only want to preload 3 pages (one center, one on each side) + // but if flow layout includes the 4th page (which it does! as of iOS 9&10), you will preload a 4th + // page as well. + if (CATransform3DIsIdentity(la.transform3D) && CGRectIntersectsRect(la.frame, rangeBounds) == NO) { + continue; + } + [elementSet addObject:[map elementForLayoutAttributes:la]]; + } + + return elementSet; +} + +- (CGRect)rangeBoundsWithScrollDirection:(ASScrollDirection)scrollDirection + rangeTuningParameters:(ASRangeTuningParameters)tuningParameters +{ + CGRect rect = _collectionView.bounds; + + return CGRectExpandToRangeWithScrollableDirections(rect, tuningParameters, [_collectionView scrollableDirections], scrollDirection); +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionViewLayoutInspector.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionViewLayoutInspector.h new file mode 100644 index 0000000..4e58890 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionViewLayoutInspector.h @@ -0,0 +1,83 @@ +// +// ASCollectionViewLayoutInspector.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import + +@class ASCollectionView; +@protocol ASCollectionDataSource; +@protocol ASCollectionDelegate; + +NS_ASSUME_NONNULL_BEGIN + +ASDK_EXTERN ASSizeRange NodeConstrainedSizeForScrollDirection(ASCollectionView *collectionView); + +@protocol ASCollectionViewLayoutInspecting + +/** + * Asks the inspector to provide a constrained size range for the given collection view node. + */ +- (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath; + +/** + * Return the directions in which your collection view can scroll + */ +- (ASScrollDirection)scrollableDirections; + +@optional + +/** + * Asks the inspector to provide a constrained size range for the given supplementary node. + */ +- (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath; + +/** + * Asks the inspector for the number of supplementary views for the given kind in the specified section. + */ +- (NSUInteger)collectionView:(ASCollectionView *)collectionView supplementaryNodesOfKind:(NSString *)kind inSection:(NSUInteger)section; + +/** + * Allow the inspector to respond to delegate changes. + * + * @discussion A great time to update perform selector caches! + */ +- (void)didChangeCollectionViewDelegate:(nullable id)delegate; + +/** + * Allow the inspector to respond to dataSource changes. + * + * @discussion A great time to update perform selector caches! + */ +- (void)didChangeCollectionViewDataSource:(nullable id)dataSource; + +#pragma mark Deprecated Methods + +/** + * Asks the inspector for the number of supplementary sections in the collection view for the given kind. + * + * @deprecated This method will not be called, and it is only deprecated as a reminder to remove it. + * Supplementary elements must exist in the same sections as regular collection view items i.e. -numberOfSectionsInCollectionView: + */ +- (NSUInteger)collectionView:(ASCollectionView *)collectionView numberOfSectionsForSupplementaryNodeOfKind:(NSString *)kind ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode's method instead."); + +@end + +/** + * A layout inspector for non-flow layouts that returns a constrained size to let the cells layout itself as + * far as possible based on the scrollable direction of the collection view. + * It doesn't support supplementary nodes and therefore doesn't implement delegate methods + * that are related to supplementary node's management. + * + * @warning This class is not meant to be subclassed and will be restricted in the future. + */ +@interface ASCollectionViewLayoutInspector : NSObject +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionViewLayoutInspector.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionViewLayoutInspector.mm new file mode 100644 index 0000000..1747c46 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASCollectionViewLayoutInspector.mm @@ -0,0 +1,76 @@ +// +// ASCollectionViewLayoutInspector.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import +#import +#import + +#pragma mark - Helper Functions + +// Returns a constrained size to let the cells layout itself as far as possible based on the scrollable direction +// of the collection view +ASSizeRange NodeConstrainedSizeForScrollDirection(ASCollectionView *collectionView) { + CGSize maxSize = collectionView.bounds.size; + UIEdgeInsets contentInset = collectionView.contentInset; + if (ASScrollDirectionContainsHorizontalDirection(collectionView.scrollableDirections)) { + maxSize.width = CGFLOAT_MAX; + maxSize.height -= (contentInset.top + contentInset.bottom); + } else { + maxSize.width -= (contentInset.left + contentInset.right); + maxSize.height = CGFLOAT_MAX; + } + return ASSizeRangeMake(CGSizeZero, maxSize); +} + +#pragma mark - ASCollectionViewLayoutInspector + +@implementation ASCollectionViewLayoutInspector { + struct { + unsigned int implementsConstrainedSizeForNodeAtIndexPathDeprecated:1; + unsigned int implementsConstrainedSizeForNodeAtIndexPath:1; + } _delegateFlags; +} + +#pragma mark ASCollectionViewLayoutInspecting + +- (void)didChangeCollectionViewDelegate:(id)delegate +{ + if (delegate == nil) { + memset(&_delegateFlags, 0, sizeof(_delegateFlags)); + } else { + _delegateFlags.implementsConstrainedSizeForNodeAtIndexPathDeprecated = [delegate respondsToSelector:@selector(collectionView:constrainedSizeForNodeAtIndexPath:)]; + _delegateFlags.implementsConstrainedSizeForNodeAtIndexPath = [delegate respondsToSelector:@selector(collectionNode:constrainedSizeForItemAtIndexPath:)]; + } +} + +- (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath +{ + if (_delegateFlags.implementsConstrainedSizeForNodeAtIndexPath) { + return [collectionView.asyncDelegate collectionNode:collectionView.collectionNode constrainedSizeForItemAtIndexPath:indexPath]; + } else if (_delegateFlags.implementsConstrainedSizeForNodeAtIndexPathDeprecated) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + return [collectionView.asyncDelegate collectionView:collectionView constrainedSizeForNodeAtIndexPath:indexPath]; +#pragma clang diagnostic pop + } else { + // With 2.0 `collectionView:constrainedSizeForNodeAtIndexPath:` was moved to the delegate. Assert if not implemented on the delegate but on the data source + ASDisplayNodeAssert([collectionView.asyncDataSource respondsToSelector:@selector(collectionView:constrainedSizeForNodeAtIndexPath:)] == NO, @"collectionView:constrainedSizeForNodeAtIndexPath: was moved from the ASCollectionDataSource to the ASCollectionDelegate."); + } + + return NodeConstrainedSizeForScrollDirection(collectionView); +} + +- (ASScrollDirection)scrollableDirections +{ + return ASScrollDirectionNone; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASDataController.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASDataController.h new file mode 100644 index 0000000..88150cb --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASDataController.h @@ -0,0 +1,275 @@ +// +// ASDataController.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#pragma once + +#import +#import +#import +#ifdef __cplusplus +#import +#endif + +NS_ASSUME_NONNULL_BEGIN + +@class ASCellNode; +@class ASCollectionElement; +@class ASCollectionLayoutContext; +@class ASCollectionLayoutState; +@class ASDataController; +@class ASElementMap; +@class ASLayout; +@class _ASHierarchyChangeSet; +@protocol ASRangeManagingNode; +@protocol ASTraitEnvironment; +@protocol ASSectionContext; + +typedef NSUInteger ASDataControllerAnimationOptions; + +ASDK_EXTERN NSString * const ASDataControllerRowNodeKind; +ASDK_EXTERN NSString * const ASCollectionInvalidUpdateException; + +/** + Data source for data controller + It will be invoked in the same thread as the api call of ASDataController. + */ + +@protocol ASDataControllerSource + +/** + Fetch the ASCellNode block for specific index path. This block should return the ASCellNode for the specified index path. + */ +- (ASCellNodeBlock)dataController:(ASDataController *)dataController nodeBlockAtIndexPath:(NSIndexPath *)indexPath shouldAsyncLayout:(BOOL *)shouldAsyncLayout; + +/** + Fetch the number of rows in specific section. + */ +- (NSUInteger)dataController:(ASDataController *)dataController rowsInSection:(NSUInteger)section; + +/** + Fetch the number of sections. + */ +- (NSUInteger)numberOfSectionsInDataController:(ASDataController *)dataController; + +/** + Returns if the collection element size matches a given size. + @precondition The element is present in the data controller's visible map. + */ +- (BOOL)dataController:(ASDataController *)dataController presentedSizeForElement:(ASCollectionElement *)element matchesSize:(CGSize)size; + +- (nullable id)dataController:(ASDataController *)dataController nodeModelForItemAtIndexPath:(NSIndexPath *)indexPath; + +/** + * Called just after dispatching ASCellNode allocation and layout to the concurrent background queue. + * In some cases, for example on the first content load for a screen, it may be desirable to call + * -waitUntilAllUpdatesAreProcessed at this point. + * + * Returning YES will cause the ASDataController to wait on the background queue, and this ensures + * that any new / changed cells are in the hierarchy by the very next CATransaction / frame draw. + */ +- (BOOL)dataController:(ASDataController *)dataController shouldSynchronouslyProcessChangeSet:(_ASHierarchyChangeSet *)changeSet; +- (BOOL)dataController:(ASDataController *)dataController shouldEagerlyLayoutNode:(ASCellNode *)node; +- (BOOL)dataControllerShouldSerializeNodeCreation:(ASDataController *)dataController; + +@optional + +/** + The constrained size range for layout. Called only if collection layout delegate is not provided. + */ +- (ASSizeRange)dataController:(ASDataController *)dataController constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath; + +- (NSArray *)dataController:(ASDataController *)dataController supplementaryNodeKindsInSections:(NSIndexSet *)sections; + +- (NSUInteger)dataController:(ASDataController *)dataController supplementaryNodesOfKind:(NSString *)kind inSection:(NSUInteger)section; + +- (ASCellNodeBlock)dataController:(ASDataController *)dataController supplementaryNodeBlockOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath shouldAsyncLayout:(BOOL *)shouldAsyncLayout; + +/** + The constrained size range for layout. Called only if no data controller layout delegate is provided. + */ +- (ASSizeRange)dataController:(ASDataController *)dataController constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath; + +- (nullable id)dataController:(ASDataController *)dataController contextForSection:(NSInteger)section; + +@end + +/** + Delegate for notify the data updating of data controller. + These methods will be invoked from main thread right now, but it may be moved to background thread in the future. + */ +@protocol ASDataControllerDelegate + +/** + * Called for change set updates. + * + * @param changeSet The change set that includes all updates + * + * @param updates The block that performs relevant data updates. + * + * @discussion The updates block must always be executed or the data controller will get into a bad state. + * It should be called at the time the backing view is ready to process the updates, + * i.e inside the updates block of `-[UICollectionView performBatchUpdates:completion:] or after calling `-[UITableView beginUpdates]`. + */ +- (void)dataController:(ASDataController *)dataController updateWithChangeSet:(_ASHierarchyChangeSet *)changeSet updates:(dispatch_block_t)updates; + +@end + +@protocol ASDataControllerLayoutDelegate + +/** + * @abstract Returns a layout context needed for a coming layout pass with the given elements. + * The context should contain the elements and any additional information needed. + * + * @discussion This method will be called on main thread. + */ +- (ASCollectionLayoutContext *)layoutContextWithElements:(ASElementMap *)elements; + +/** + * @abstract Prepares and returns a new layout for given context. + * + * @param context A context that was previously returned by `-layoutContextWithElements:`. + * + * @return The new layout calculated for the given context. + * + * @discussion This method is called ahead of time, i.e before the underlying collection/table view is aware of the provided elements. + * As a result, clients must solely rely on the given context and should not reach out to other objects for information not available in the context. + * + * This method will be called on background theads. It must be thread-safe and should not change any internal state of the conforming object. + * It must block the calling thread but can dispatch to other theads to reduce total blocking time. + */ ++ (ASCollectionLayoutState *)calculateLayoutWithContext:(ASCollectionLayoutContext *)context; + +@end + +/** + * Controller to layout data in background, and managed data updating. + * + * All operations are asynchronous and thread safe. You can call it from background thread (it is recommendated) and the data + * will be updated asynchronously. The dataSource must be updated to reflect the changes before these methods has been called. + * For each data updating, the corresponding methods in delegate will be called. + */ +@interface ASDataController : NSObject + +- (instancetype)initWithDataSource:(id)dataSource node:(nullable id)node NS_DESIGNATED_INITIALIZER; + +- (instancetype)init NS_UNAVAILABLE; + +/** + * The node that owns this data controller, if any. + * + * NOTE: Soon we will drop support for using ASTableView/ASCollectionView without the node, so this will be non-null. + */ +@property (nullable, nonatomic, weak, readonly) id node; + +/** + * The map that is currently displayed. The "UIKit index space." + * + * This property will only be changed on the main thread. + */ +@property (copy, readonly) ASElementMap *visibleMap; + +/** + * The latest map fetched from the data source. May be more recent than @c visibleMap. + * + * This property will only be changed on the main thread. + */ +@property (copy, readonly) ASElementMap *pendingMap; + +/** + Data source for fetching data info. + */ +@property (nonatomic, weak, readonly) id dataSource; + +/** + An object that will be included in the backtrace of any update validation exceptions that occur. + */ +@property (nonatomic, weak) id validationErrorSource; + +/** + Delegate to notify when data is updated. + */ +@property (nonatomic, weak) id delegate; + +/** + * Delegate for preparing layouts. Main thead only. + */ +@property (nonatomic, weak) id layoutDelegate; + +#ifdef __cplusplus +/** + * Returns the most recently gathered item counts from the data source. If the counts + * have been invalidated, this synchronously queries the data source and saves the result. + * + * This must be called on the main thread. + */ +- (std::vector)itemCountsFromDataSource; +#endif + +/** + * Returns YES if reloadData has been called at least once. Before this point it is + * important to ignore/suppress some operations. For example, inserting a section + * before the initial data load should have no effect. + * + * This must be called on the main thread. + */ +@property (nonatomic, readonly) BOOL initialReloadDataHasBeenCalled; + +/** @name Data Updating */ + +- (void)updateWithChangeSet:(_ASHierarchyChangeSet *)changeSet; + +/** + * Re-measures all loaded nodes in the backing store. + * + * @discussion Used to respond to a change in size of the containing view + * (e.g. ASTableView or ASCollectionView after an orientation change). + * + * The invalidationBlock is called after flushing the ASMainSerialQueue, which ensures that any in-progress + * layout calculations have been applied. The block will not be called if data hasn't been loaded. + */ +- (void)relayoutAllNodesWithInvalidationBlock:(nullable void (^)(void))invalidationBlock; + +/** + * Re-measures given nodes in the backing store. + * + * @discussion Used to respond to setNeedsLayout calls in ASCellNode + */ +- (void)relayoutNodes:(id)nodes nodesSizeChanged:(NSMutableArray *)nodesSizesChanged; + +/** + * See ASCollectionNode.h for full documentation of these methods. + */ +@property (nonatomic, readonly) BOOL isProcessingUpdates; +- (void)onDidFinishProcessingUpdates:(void (^)(void))completion; +- (void)waitUntilAllUpdatesAreProcessed; + +/** + * See ASCollectionNode.h for full documentation of these methods. + */ +@property (nonatomic, readonly, getter=isSynchronized) BOOL synchronized; +- (void)onDidFinishSynchronizing:(void (^)(void))completion; + +/** + * Notifies the data controller object that its environment has changed. The object will request its environment delegate for new information + * and propagate the information to all visible elements, including ones that are being prepared in background. + * + * @discussion If called before the initial @c reloadData, this method will do nothing and the trait collection of the initial load will be requested from the environment delegate. + * + * @discussion This method can be called on any threads. + */ +- (void)environmentDidChange; + +/** + * Reset visibleMap and pendingMap when asyncDataSource and asyncDelegate of collection view become nil. + */ +- (void)clearData; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASDataController.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASDataController.mm new file mode 100644 index 0000000..76e58d8 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASDataController.mm @@ -0,0 +1,934 @@ +// +// ASDataController.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + +#import +#import +#import +#import + +//#define LOG(...) NSLog(__VA_ARGS__) +#define LOG(...) + +#define ASSERT_ON_EDITING_QUEUE ASDisplayNodeAssertNotNil(dispatch_get_specific(&kASDataControllerEditingQueueKey), @"%@ must be called on the editing transaction queue.", NSStringFromSelector(_cmd)) + +const static char * kASDataControllerEditingQueueKey = "kASDataControllerEditingQueueKey"; +const static char * kASDataControllerEditingQueueContext = "kASDataControllerEditingQueueContext"; + +NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind"; +NSString * const ASCollectionInvalidUpdateException = @"ASCollectionInvalidUpdateException"; + +typedef dispatch_block_t ASDataControllerCompletionBlock; + +typedef void (^ASDataControllerSynchronizationBlock)(); + +@interface ASDataController () { + id _layoutDelegate; + + NSInteger _nextSectionID; + + BOOL _itemCountsFromDataSourceAreValid; // Main thread only. + std::vector _itemCountsFromDataSource; // Main thread only. + + ASMainSerialQueue *_mainSerialQueue; + + dispatch_queue_t _editingTransactionQueue; // Serial background queue. Dispatches concurrent layout and manages _editingNodes. + dispatch_group_t _editingTransactionGroup; // Group of all edit transaction blocks. Useful for waiting. + std::atomic _editingTransactionGroupCount; + + BOOL _initialReloadDataHasBeenCalled; + + BOOL _synchronized; + NSMutableSet *_onDidFinishSynchronizingBlocks; + + struct { + unsigned int supplementaryNodeKindsInSections:1; + unsigned int supplementaryNodesOfKindInSection:1; + unsigned int supplementaryNodeBlockOfKindAtIndexPath:1; + unsigned int constrainedSizeForNodeAtIndexPath:1; + unsigned int constrainedSizeForSupplementaryNodeOfKindAtIndexPath:1; + unsigned int contextForSection:1; + } _dataSourceFlags; +} + +@property (copy) ASElementMap *pendingMap; +@property (copy) ASElementMap *visibleMap; +@end + +@implementation ASDataController + +#pragma mark - Lifecycle + +- (instancetype)initWithDataSource:(id)dataSource node:(nullable id)node +{ + if (!(self = [super init])) { + return nil; + } + + _node = node; + _dataSource = dataSource; + + _dataSourceFlags.supplementaryNodeKindsInSections = [_dataSource respondsToSelector:@selector(dataController:supplementaryNodeKindsInSections:)]; + _dataSourceFlags.supplementaryNodesOfKindInSection = [_dataSource respondsToSelector:@selector(dataController:supplementaryNodesOfKind:inSection:)]; + _dataSourceFlags.supplementaryNodeBlockOfKindAtIndexPath = [_dataSource respondsToSelector:@selector(dataController:supplementaryNodeBlockOfKind:atIndexPath:shouldAsyncLayout:)]; + _dataSourceFlags.constrainedSizeForNodeAtIndexPath = [_dataSource respondsToSelector:@selector(dataController:constrainedSizeForNodeAtIndexPath:)]; + _dataSourceFlags.constrainedSizeForSupplementaryNodeOfKindAtIndexPath = [_dataSource respondsToSelector:@selector(dataController:constrainedSizeForSupplementaryNodeOfKind:atIndexPath:)]; + _dataSourceFlags.contextForSection = [_dataSource respondsToSelector:@selector(dataController:contextForSection:)]; + + self.visibleMap = self.pendingMap = [[ASElementMap alloc] init]; + + _nextSectionID = 0; + + _mainSerialQueue = [[ASMainSerialQueue alloc] init]; + + _synchronized = YES; + _onDidFinishSynchronizingBlocks = [[NSMutableSet alloc] init]; + + const char *queueName = [[NSString stringWithFormat:@"org.AsyncDisplayKit.ASDataController.editingTransactionQueue:%p", self] cStringUsingEncoding:NSASCIIStringEncoding]; + _editingTransactionQueue = dispatch_queue_create(queueName, DISPATCH_QUEUE_SERIAL); + dispatch_queue_set_specific(_editingTransactionQueue, &kASDataControllerEditingQueueKey, &kASDataControllerEditingQueueContext, NULL); + _editingTransactionGroup = dispatch_group_create(); + + return self; +} + +- (id)layoutDelegate +{ + ASDisplayNodeAssertMainThread(); + return _layoutDelegate; +} + +- (void)setLayoutDelegate:(id)layoutDelegate +{ + ASDisplayNodeAssertMainThread(); + if (layoutDelegate != _layoutDelegate) { + _layoutDelegate = layoutDelegate; + } +} + +#pragma mark - Cell Layout + +- (void)_allocateNodesFromElements:(NSArray *)elements +{ + ASSERT_ON_EDITING_QUEUE; + + NSUInteger nodeCount = elements.count; + __weak id weakDataSource = _dataSource; + if (nodeCount == 0 || weakDataSource == nil) { + return; + } + + ASSignpostStart(DataControllerBatch, self, "%@", ASObjectDescriptionMakeTiny(weakDataSource)); + + { + as_activity_create_for_scope("Data controller batch"); + + dispatch_queue_t queue = dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0); + NSUInteger threadCount = 0; + if ([_dataSource dataControllerShouldSerializeNodeCreation:self]) { + threadCount = 1; + } + ASDispatchApply(nodeCount, queue, threadCount, ^(size_t i) { + __strong id strongDataSource = weakDataSource; + if (strongDataSource == nil) { + return; + } + + unowned ASCollectionElement *element = elements[i]; + + NSMutableDictionary *dict = [[NSThread currentThread] threadDictionary]; + dict[ASThreadDictMaxConstraintSizeKey] = + [NSValue valueWithCGSize:element.constrainedSize.max]; + unowned ASCellNode *node = element.node; + [dict removeObjectForKey:ASThreadDictMaxConstraintSizeKey]; + + // Layout the node if the size range is valid. + ASSizeRange sizeRange = element.constrainedSize; + if (ASSizeRangeHasSignificantArea(sizeRange)) { + [self _layoutNode:node withConstrainedSize:sizeRange]; + } + }); + } + + ASSignpostEnd(DataControllerBatch, self, "count: %lu", (unsigned long)nodeCount); +} + +/** + * Measure and layout the given node with the constrained size range. + */ +- (void)_layoutNode:(ASCellNode *)node withConstrainedSize:(ASSizeRange)constrainedSize +{ + if (![_dataSource dataController:self shouldEagerlyLayoutNode:node]) { + return; + } + + ASDisplayNodeAssert(ASSizeRangeHasSignificantArea(constrainedSize), @"Attempt to layout cell node with invalid size range %@", NSStringFromASSizeRange(constrainedSize)); + + CGRect frame = CGRectZero; + frame.size = [node layoutThatFits:constrainedSize].size; + node.frame = frame; +} + +#pragma mark - Data Source Access (Calling _dataSource) + +- (NSArray *)_allIndexPathsForItemsOfKind:(NSString *)kind inSections:(NSIndexSet *)sections +{ + ASDisplayNodeAssertMainThread(); + + if (sections.count == 0 || _dataSource == nil) { + return @[]; + } + + const auto indexPaths = [[NSMutableArray alloc] init]; + if ([kind isEqualToString:ASDataControllerRowNodeKind]) { + std::vector counts = [self itemCountsFromDataSource]; + [sections enumerateRangesUsingBlock:^(NSRange range, BOOL * _Nonnull stop) { + for (NSUInteger sectionIndex = range.location; sectionIndex < NSMaxRange(range); sectionIndex++) { + NSUInteger itemCount = counts[sectionIndex]; + for (NSUInteger i = 0; i < itemCount; i++) { + [indexPaths addObject:[NSIndexPath indexPathForItem:i inSection:sectionIndex]]; + } + } + }]; + } else if (_dataSourceFlags.supplementaryNodesOfKindInSection) { + id dataSource = _dataSource; + [sections enumerateRangesUsingBlock:^(NSRange range, BOOL * _Nonnull stop) { + for (NSUInteger sectionIndex = range.location; sectionIndex < NSMaxRange(range); sectionIndex++) { + NSUInteger itemCount = [dataSource dataController:self supplementaryNodesOfKind:kind inSection:sectionIndex]; + for (NSUInteger i = 0; i < itemCount; i++) { + [indexPaths addObject:[NSIndexPath indexPathForItem:i inSection:sectionIndex]]; + } + } + }]; + } + + return indexPaths; +} + +/** + * Agressively repopulates supplementary nodes of all kinds for sections that contains some given index paths. + * + * @param map The element map into which to apply the change. + * @param indexPaths The index paths belongs to sections whose supplementary nodes need to be repopulated. + * @param changeSet The changeset that triggered this repopulation. + * @param traitCollection The trait collection needed to initialize elements + * @param indexPathsAreNew YES if index paths are "after the update," NO otherwise. + * @param shouldFetchSizeRanges Whether constrained sizes should be fetched from data source + */ +- (void)_repopulateSupplementaryNodesIntoMap:(ASMutableElementMap *)map + forSectionsContainingIndexPaths:(NSArray *)indexPaths + changeSet:(_ASHierarchyChangeSet *)changeSet + traitCollection:(ASPrimitiveTraitCollection)traitCollection + indexPathsAreNew:(BOOL)indexPathsAreNew + shouldFetchSizeRanges:(BOOL)shouldFetchSizeRanges + previousMap:(ASElementMap *)previousMap +{ + ASDisplayNodeAssertMainThread(); + + if (indexPaths.count == 0) { + return; + } + + // Remove all old supplementaries from these sections + NSIndexSet *oldSections = [NSIndexSet as_sectionsFromIndexPaths:indexPaths]; + + // Add in new ones with the new kinds. + NSIndexSet *newSections; + if (indexPathsAreNew) { + newSections = oldSections; + } else { + newSections = [oldSections as_indexesByMapping:^NSUInteger(NSUInteger oldSection) { + return [changeSet newSectionForOldSection:oldSection]; + }]; + } + + for (NSString *kind in [self supplementaryKindsInSections:newSections]) { + [self _insertElementsIntoMap:map kind:kind forSections:newSections traitCollection:traitCollection shouldFetchSizeRanges:shouldFetchSizeRanges changeSet:changeSet previousMap:previousMap]; + } +} + +/** + * Update supplementary nodes of all kinds for sections. + * + * @param map The element map into which to apply the change. + * @param traitCollection The trait collection needed to initialize elements + * @param shouldFetchSizeRanges Whether constrained sizes should be fetched from data source + */ +- (void)_updateSupplementaryNodesIntoMap:(ASMutableElementMap *)map + traitCollection:(ASPrimitiveTraitCollection)traitCollection + shouldFetchSizeRanges:(BOOL)shouldFetchSizeRanges + previousMap:(ASElementMap *)previousMap +{ + ASDisplayNodeAssertMainThread(); + if (self.layoutDelegate != nil) { + // TODO: https://github.com/TextureGroup/Texture/issues/948 + return; + } + NSUInteger sectionCount = [self itemCountsFromDataSource].size(); + if (sectionCount > 0) { + NSIndexSet *sectionIndexes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, sectionCount)]; + ASSizeRange newSizeRange = ASSizeRangeZero; + for (NSString *kind in [self supplementaryKindsInSections:sectionIndexes]) { + NSArray *indexPaths = [self _allIndexPathsForItemsOfKind:kind inSections:sectionIndexes]; + NSMutableArray *indexPathsToDeleteForKind = [[NSMutableArray alloc] init]; + NSMutableArray *indexPathsToInsertForKind = [[NSMutableArray alloc] init]; + // If supplementary node does exist and size is now zero, remove it. + // If supplementary node doesn't exist and size is now non-zero, insert one. + for (NSIndexPath *indexPath in indexPaths) { + ASCollectionElement *previousElement = [previousMap supplementaryElementOfKind:kind atIndexPath:indexPath]; + newSizeRange = [self constrainedSizeForNodeOfKind:kind atIndexPath:indexPath]; + BOOL sizeRangeIsZero = ASSizeRangeEqualToSizeRange(ASSizeRangeZero, newSizeRange); + if (previousElement != nil && sizeRangeIsZero) { + [indexPathsToDeleteForKind addObject:indexPath]; + } else if (previousElement == nil && !sizeRangeIsZero) { + [indexPathsToInsertForKind addObject:indexPath]; + } + } + + [map removeSupplementaryElementsAtIndexPaths:indexPathsToDeleteForKind kind:kind]; + [self _insertElementsIntoMap:map kind:kind atIndexPaths:indexPathsToInsertForKind traitCollection:traitCollection shouldFetchSizeRanges:shouldFetchSizeRanges changeSet:nil previousMap:previousMap]; + } + } +} + +/** + * Inserts new elements of a certain kind for some sections + * + * @param kind The kind of the elements, e.g ASDataControllerRowNodeKind + * @param sections The sections that should be populated by new elements + * @param traitCollection The trait collection needed to initialize elements + * @param shouldFetchSizeRanges Whether constrained sizes should be fetched from data source + */ +- (void)_insertElementsIntoMap:(ASMutableElementMap *)map + kind:(NSString *)kind + forSections:(NSIndexSet *)sections + traitCollection:(ASPrimitiveTraitCollection)traitCollection + shouldFetchSizeRanges:(BOOL)shouldFetchSizeRanges + changeSet:(_ASHierarchyChangeSet *)changeSet + previousMap:(ASElementMap *)previousMap +{ + ASDisplayNodeAssertMainThread(); + + if (sections.count == 0 || _dataSource == nil) { + return; + } + + NSArray *indexPaths = [self _allIndexPathsForItemsOfKind:kind inSections:sections]; + [self _insertElementsIntoMap:map kind:kind atIndexPaths:indexPaths traitCollection:traitCollection shouldFetchSizeRanges:shouldFetchSizeRanges changeSet:changeSet previousMap:previousMap]; +} + +/** + * Inserts new elements of a certain kind at some index paths + * + * @param map The map to insert the elements into. + * @param kind The kind of the elements, e.g ASDataControllerRowNodeKind + * @param indexPaths The index paths at which new elements should be populated + * @param traitCollection The trait collection needed to initialize elements + * @param shouldFetchSizeRanges Whether constrained sizes should be fetched from data source + */ +- (void)_insertElementsIntoMap:(ASMutableElementMap *)map + kind:(NSString *)kind + atIndexPaths:(NSArray *)indexPaths + traitCollection:(ASPrimitiveTraitCollection)traitCollection + shouldFetchSizeRanges:(BOOL)shouldFetchSizeRanges + changeSet:(_ASHierarchyChangeSet *)changeSet + previousMap:(ASElementMap *)previousMap +{ + ASDisplayNodeAssertMainThread(); + + if (indexPaths.count == 0 || _dataSource == nil) { + return; + } + + BOOL isRowKind = [kind isEqualToString:ASDataControllerRowNodeKind]; + if (!isRowKind && !_dataSourceFlags.supplementaryNodeBlockOfKindAtIndexPath) { + // Populating supplementary elements but data source doesn't support. + return; + } + + LOG(@"Populating elements of kind: %@, for index paths: %@", kind, indexPaths); + id dataSource = self.dataSource; + id node = self.node; + BOOL shouldAsyncLayout = YES; + for (NSIndexPath *indexPath in indexPaths) { + ASCellNodeBlock nodeBlock; + id nodeModel; + if (isRowKind) { + nodeModel = [dataSource dataController:self nodeModelForItemAtIndexPath:indexPath]; + + // Get the prior element and attempt to update the existing cell node. + if (nodeModel != nil && !changeSet.includesReloadData) { + NSIndexPath *oldIndexPath = [changeSet oldIndexPathForNewIndexPath:indexPath]; + if (oldIndexPath != nil) { + ASCollectionElement *oldElement = [previousMap elementForItemAtIndexPath:oldIndexPath]; + ASCellNode *oldNode = oldElement.node; + if ([oldNode canUpdateToNodeModel:nodeModel]) { + // Just wrap the node in a block. The collection element will -setNodeModel: + nodeBlock = ^{ + return oldNode; + }; + } + } + } + if (nodeBlock == nil) { + nodeBlock = [dataSource dataController:self nodeBlockAtIndexPath:indexPath shouldAsyncLayout:&shouldAsyncLayout]; + } + } else { + nodeBlock = [dataSource dataController:self supplementaryNodeBlockOfKind:kind atIndexPath:indexPath shouldAsyncLayout:&shouldAsyncLayout]; + } + + ASSizeRange constrainedSize = ASSizeRangeUnconstrained; + if (shouldFetchSizeRanges) { + constrainedSize = [self constrainedSizeForNodeOfKind:kind atIndexPath:indexPath]; + } + + ASCollectionElement *element = [[ASCollectionElement alloc] initWithNodeModel:nodeModel + nodeBlock:nodeBlock + supplementaryElementKind:isRowKind ? nil : kind + constrainedSize:constrainedSize + owningNode:node + traitCollection:traitCollection]; + [map insertElement:element atIndexPath:indexPath]; + changeSet.countForAsyncLayout += (shouldAsyncLayout ? 1 : 0); + } +} + +- (void)invalidateDataSourceItemCounts +{ + ASDisplayNodeAssertMainThread(); + _itemCountsFromDataSourceAreValid = NO; +} + +- (std::vector)itemCountsFromDataSource +{ + ASDisplayNodeAssertMainThread(); + if (NO == _itemCountsFromDataSourceAreValid) { + id source = self.dataSource; + NSInteger sectionCount = [source numberOfSectionsInDataController:self]; + std::vector newCounts; + newCounts.reserve(sectionCount); + for (NSInteger i = 0; i < sectionCount; i++) { + newCounts.push_back([source dataController:self rowsInSection:i]); + } + _itemCountsFromDataSource = newCounts; + _itemCountsFromDataSourceAreValid = YES; + } + return _itemCountsFromDataSource; +} + +- (NSArray *)supplementaryKindsInSections:(NSIndexSet *)sections +{ + if (_dataSourceFlags.supplementaryNodeKindsInSections) { + return [_dataSource dataController:self supplementaryNodeKindsInSections:sections]; + } + + return @[]; +} + +/** + * Returns constrained size for the node of the given kind and at the given index path. + * NOTE: index path must be in the data-source index space. + */ +- (ASSizeRange)constrainedSizeForNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath +{ + ASDisplayNodeAssertMainThread(); + + id dataSource = _dataSource; + if (dataSource == nil || indexPath == nil) { + return ASSizeRangeZero; + } + + if ([kind isEqualToString:ASDataControllerRowNodeKind]) { + ASDisplayNodeAssert(_dataSourceFlags.constrainedSizeForNodeAtIndexPath, @"-dataController:constrainedSizeForNodeAtIndexPath: must also be implemented"); + return [dataSource dataController:self constrainedSizeForNodeAtIndexPath:indexPath]; + } + + if (_dataSourceFlags.constrainedSizeForSupplementaryNodeOfKindAtIndexPath){ + return [dataSource dataController:self constrainedSizeForSupplementaryNodeOfKind:kind atIndexPath:indexPath]; + } + + ASDisplayNodeAssert(NO, @"Unknown constrained size for node of kind %@ by data source %@", kind, dataSource); + return ASSizeRangeZero; +} + +#pragma mark - Batching (External API) + +- (void)waitUntilAllUpdatesAreProcessed +{ + // Schedule block in main serial queue to wait until all operations are finished that are + // where scheduled while waiting for the _editingTransactionQueue to finish + [self _scheduleBlockOnMainSerialQueue:^{ }]; +} + +- (BOOL)isProcessingUpdates +{ + ASDisplayNodeAssertMainThread(); + return _mainSerialQueue.numberOfScheduledBlocks > 0 || _editingTransactionGroupCount > 0; +} + +- (void)onDidFinishProcessingUpdates:(void (^)())completion +{ + ASDisplayNodeAssertMainThread(); + if (!completion) { + return; + } + if ([self isProcessingUpdates] == NO) { + ASPerformBlockOnMainThread(completion); + } else { + dispatch_async(_editingTransactionQueue, ^{ + // Retry the block. If we're done processing updates, it'll run immediately, otherwise + // wait again for updates to quiesce completely. + // Don't use _mainSerialQueue so that we don't affect -isProcessingUpdates. + dispatch_async(dispatch_get_main_queue(), ^{ + [self onDidFinishProcessingUpdates:completion]; + }); + }); + } +} + +- (BOOL)isSynchronized { + return _synchronized; +} + +- (void)onDidFinishSynchronizing:(void (^)())completion { + ASDisplayNodeAssertMainThread(); + if (!completion) { + return; + } + if ([self isSynchronized]) { + ASPerformBlockOnMainThread(completion); + } else { + // Hang on to the completion block so that it gets called the next time view is synchronized to data. + [_onDidFinishSynchronizingBlocks addObject:[completion copy]]; + } +} + +- (void)updateWithChangeSet:(_ASHierarchyChangeSet *)changeSet +{ + ASDisplayNodeAssertMainThread(); + + _synchronized = NO; + + [changeSet addCompletionHandler:^(BOOL finished) { + self->_synchronized = YES; + [self onDidFinishProcessingUpdates:^{ + if (self->_synchronized) { + for (ASDataControllerSynchronizationBlock block in self->_onDidFinishSynchronizingBlocks) { + block(); + } + [self->_onDidFinishSynchronizingBlocks removeAllObjects]; + } + }]; + }]; + + if (changeSet.includesReloadData) { + if (_initialReloadDataHasBeenCalled) { + os_log_debug(ASCollectionLog(), "reloadData %@", ASViewToDisplayNode(ASDynamicCast(self.dataSource, UIView))); + } else { + os_log_debug(ASCollectionLog(), "Initial reloadData %@", ASViewToDisplayNode(ASDynamicCast(self.dataSource, UIView))); + _initialReloadDataHasBeenCalled = YES; + } + } else { + os_log_debug(ASCollectionLog(), "performBatchUpdates %@ %@", ASViewToDisplayNode(ASDynamicCast(self.dataSource, UIView)), changeSet); + } + + if (!ASActivateExperimentalFeature(ASExperimentalOptimizeDataControllerPipeline)) { + NSTimeInterval transactionQueueFlushDuration = 0.0f; + { + AS::ScopeTimer t(transactionQueueFlushDuration); + dispatch_group_wait(_editingTransactionGroup, DISPATCH_TIME_FOREVER); + } + } + + // If the initial reloadData has not been called, just bail because we don't have our old data source counts. + // See ASUICollectionViewTests.testThatIssuingAnUpdateBeforeInitialReloadIsUnacceptable + // for the issue that UICollectionView has that we're choosing to workaround. + if (!_initialReloadDataHasBeenCalled) { + os_log_debug(ASCollectionLog(), "%@ Skipped update because load hasn't happened.", ASObjectDescriptionMakeTiny(_dataSource)); + [changeSet executeCompletionHandlerWithFinished:YES]; + return; + } + + [self invalidateDataSourceItemCounts]; + + // Attempt to mark the update completed. This is when update validation will occur inside the changeset. + // If an invalid update exception is thrown, we catch it and inject our "validationErrorSource" object, + // which is the table/collection node's data source, into the exception reason to help debugging. + @try { + [changeSet markCompletedWithNewItemCounts:[self itemCountsFromDataSource]]; + } @catch (NSException *e) { + id responsibleDataSource = self.validationErrorSource; + if (e.name == ASCollectionInvalidUpdateException && responsibleDataSource != nil) { + [NSException raise:ASCollectionInvalidUpdateException format:@"%@: %@", [responsibleDataSource class], e.reason]; + } else { + @throw e; + } + } + + BOOL canDelegate = (self.layoutDelegate != nil); + ASElementMap *newMap; + ASCollectionLayoutContext *layoutContext; + { + as_activity_scope(as_activity_create("Latch new data for collection update", changeSet.rootActivity, OS_ACTIVITY_FLAG_DEFAULT)); + + // Step 1: Populate a new map that reflects the data source's state and use it as pendingMap + ASElementMap *previousMap = self.pendingMap; + if (changeSet.isEmpty) { + // If the change set is empty, nothing has changed so we can just reuse the previous map + newMap = previousMap; + } else { + // Mutable copy of current data. + ASMutableElementMap *mutableMap = [previousMap mutableCopy]; + + // Step 1.1: Update the mutable copies to match the data source's state + [self _updateSectionsInMap:mutableMap changeSet:changeSet]; + ASPrimitiveTraitCollection existingTraitCollection = [self.node primitiveTraitCollection]; + [self _updateElementsInMap:mutableMap changeSet:changeSet traitCollection:existingTraitCollection shouldFetchSizeRanges:(! canDelegate) previousMap:previousMap]; + + // Step 1.2: Clone the new data + newMap = [mutableMap copy]; + } + self.pendingMap = newMap; + + // Step 2: Ask layout delegate for contexts + if (canDelegate) { + layoutContext = [self.layoutDelegate layoutContextWithElements:newMap]; + } + } + + os_log_debug(ASCollectionLog(), "New content: %@", newMap.smallDescription); + + Class layoutDelegateClass = [self.layoutDelegate class]; + ++_editingTransactionGroupCount; + dispatch_group_async(_editingTransactionGroup, _editingTransactionQueue, ^{ + __block __unused os_activity_scope_state_s preparationScope = {}; // unused if deployment target < iOS10 + as_activity_scope_enter(as_activity_create("Prepare nodes for collection update", AS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT), &preparationScope); + + // Step 3: Call the layout delegate if possible. Otherwise, allocate and layout all elements + if (canDelegate) { + [layoutDelegateClass calculateLayoutWithContext:layoutContext]; + } else { + const auto elementsToProcess = [[NSMutableArray alloc] init]; + for (ASCollectionElement *element in newMap) { + ASCellNode *nodeIfAllocated = element.nodeIfAllocated; + if (nodeIfAllocated.shouldUseUIKitCell) { + // If the node exists and we know it is a passthrough cell, we know it will never have a .calculatedLayout. + continue; + } else if (nodeIfAllocated.calculatedLayout == nil) { + // If the node hasn't been allocated, or it doesn't have a valid layout, let's process it. + [elementsToProcess addObject:element]; + } + } + [self _allocateNodesFromElements:elementsToProcess]; + } + + // Step 4: Inform the delegate on main thread + [self->_mainSerialQueue performBlockOnMainThread:^{ + as_activity_scope_leave(&preparationScope); + [self->_delegate dataController:self updateWithChangeSet:changeSet updates:^{ + // Step 5: Deploy the new data as "completed" + // + // Note that since the backing collection view might be busy responding to user events (e.g scrolling), + // it will not consume the batch update blocks immediately. + // As a result, in a short intermidate time, the view will still be relying on the old data source state. + // Thus, we can't just swap the new map immediately before step 4, but until this update block is executed. + // (https://github.com/TextureGroup/Texture/issues/378) + self.visibleMap = newMap; + }]; + }]; + --self->_editingTransactionGroupCount; + }); + + // We've now dispatched node allocation and layout to a concurrent background queue. + // In some cases, it's advantageous to prevent the main thread from returning, to ensure the next + // frame displayed to the user has the view updates in place. Doing this does slightly reduce + // total latency, by donating the main thread's priority to the background threads. As such, the + // two cases where it makes sense to block: + // 1. There is very little work to be performed in the background (UIKit passthrough) + // 2. There is a higher priority on display latency than smoothness, e.g. app startup. + if ([_dataSource dataController:self shouldSynchronouslyProcessChangeSet:changeSet]) { + [self waitUntilAllUpdatesAreProcessed]; + } +} + +/** + * Update sections based on the given change set. + */ +- (void)_updateSectionsInMap:(ASMutableElementMap *)map changeSet:(_ASHierarchyChangeSet *)changeSet +{ + ASDisplayNodeAssertMainThread(); + + if (changeSet.includesReloadData) { + [map removeAllSections]; + + NSUInteger sectionCount = [self itemCountsFromDataSource].size(); + NSIndexSet *sectionIndexes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, sectionCount)]; + [self _insertSectionsIntoMap:map indexes:sectionIndexes]; + // Return immediately because reloadData can't be used in conjuntion with other updates. + return; + } + + for (_ASHierarchySectionChange *change in [changeSet sectionChangesOfType:_ASHierarchyChangeTypeDelete]) { + [map removeSectionsAtIndexes:change.indexSet]; + } + + for (_ASHierarchySectionChange *change in [changeSet sectionChangesOfType:_ASHierarchyChangeTypeInsert]) { + [self _insertSectionsIntoMap:map indexes:change.indexSet]; + } +} + +- (void)_insertSectionsIntoMap:(ASMutableElementMap *)map indexes:(NSIndexSet *)sectionIndexes +{ + ASDisplayNodeAssertMainThread(); + + [sectionIndexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL * _Nonnull stop) { + id context; + if (_dataSourceFlags.contextForSection) { + context = [_dataSource dataController:self contextForSection:idx]; + } + ASSection *section = [[ASSection alloc] initWithSectionID:_nextSectionID context:context]; + [map insertSection:section atIndex:idx]; + _nextSectionID++; + }]; +} + +/** + * Update elements based on the given change set. + */ +- (void)_updateElementsInMap:(ASMutableElementMap *)map + changeSet:(_ASHierarchyChangeSet *)changeSet + traitCollection:(ASPrimitiveTraitCollection)traitCollection + shouldFetchSizeRanges:(BOOL)shouldFetchSizeRanges + previousMap:(ASElementMap *)previousMap +{ + ASDisplayNodeAssertMainThread(); + + if (changeSet.includesReloadData) { + [map removeAllElements]; + + NSUInteger sectionCount = [self itemCountsFromDataSource].size(); + if (sectionCount > 0) { + NSIndexSet *sectionIndexes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, sectionCount)]; + [self _insertElementsIntoMap:map sections:sectionIndexes traitCollection:traitCollection shouldFetchSizeRanges:shouldFetchSizeRanges changeSet:changeSet previousMap:previousMap]; + } + // Return immediately because reloadData can't be used in conjuntion with other updates. + return; + } + + // Migrate old supplementary nodes to their new index paths. + [map migrateSupplementaryElementsWithSectionMapping:changeSet.sectionMapping]; + + for (_ASHierarchyItemChange *change in [changeSet itemChangesOfType:_ASHierarchyChangeTypeDelete]) { + [map removeItemsAtIndexPaths:change.indexPaths]; + // Aggressively repopulate supplementary nodes (#1773 & #1629) + [self _repopulateSupplementaryNodesIntoMap:map forSectionsContainingIndexPaths:change.indexPaths + changeSet:changeSet + traitCollection:traitCollection + indexPathsAreNew:NO + shouldFetchSizeRanges:shouldFetchSizeRanges + previousMap:previousMap]; + } + + for (_ASHierarchySectionChange *change in [changeSet sectionChangesOfType:_ASHierarchyChangeTypeDelete]) { + NSIndexSet *sectionIndexes = change.indexSet; + [map removeSectionsOfItems:sectionIndexes]; + } + + for (_ASHierarchySectionChange *change in [changeSet sectionChangesOfType:_ASHierarchyChangeTypeInsert]) { + [self _insertElementsIntoMap:map sections:change.indexSet traitCollection:traitCollection shouldFetchSizeRanges:shouldFetchSizeRanges changeSet:changeSet previousMap:previousMap]; + } + + for (_ASHierarchyItemChange *change in [changeSet itemChangesOfType:_ASHierarchyChangeTypeInsert]) { + [self _insertElementsIntoMap:map kind:ASDataControllerRowNodeKind atIndexPaths:change.indexPaths traitCollection:traitCollection shouldFetchSizeRanges:shouldFetchSizeRanges changeSet:changeSet previousMap:previousMap]; + // Aggressively reload supplementary nodes (#1773 & #1629) + [self _repopulateSupplementaryNodesIntoMap:map forSectionsContainingIndexPaths:change.indexPaths + changeSet:changeSet + traitCollection:traitCollection + indexPathsAreNew:YES + shouldFetchSizeRanges:shouldFetchSizeRanges + previousMap:previousMap]; + } +} + +- (void)_insertElementsIntoMap:(ASMutableElementMap *)map + sections:(NSIndexSet *)sectionIndexes + traitCollection:(ASPrimitiveTraitCollection)traitCollection + shouldFetchSizeRanges:(BOOL)shouldFetchSizeRanges + changeSet:(_ASHierarchyChangeSet *)changeSet + previousMap:(ASElementMap *)previousMap +{ + ASDisplayNodeAssertMainThread(); + + if (sectionIndexes.count == 0 || _dataSource == nil) { + return; + } + + // Items + [map insertEmptySectionsOfItemsAtIndexes:sectionIndexes]; + [self _insertElementsIntoMap:map kind:ASDataControllerRowNodeKind forSections:sectionIndexes traitCollection:traitCollection shouldFetchSizeRanges:shouldFetchSizeRanges changeSet:changeSet previousMap:previousMap]; + + // Supplementaries + for (NSString *kind in [self supplementaryKindsInSections:sectionIndexes]) { + // Step 2: Populate new elements for all sections + [self _insertElementsIntoMap:map kind:kind forSections:sectionIndexes traitCollection:traitCollection shouldFetchSizeRanges:shouldFetchSizeRanges changeSet:changeSet previousMap:previousMap]; + } +} + +#pragma mark - Relayout + +- (void)relayoutNodes:(id)nodes nodesSizeChanged:(NSMutableArray *)nodesSizesChanged +{ + NSParameterAssert(nodes); + NSParameterAssert(nodesSizesChanged); + + ASDisplayNodeAssertMainThread(); + if (!_initialReloadDataHasBeenCalled) { + return; + } + + id dataSource = self.dataSource; + const auto visibleMap = self.visibleMap; + const auto pendingMap = self.pendingMap; + for (ASCellNode *node in nodes) { + const auto element = node.collectionElement; + NSIndexPath *indexPathInPendingMap = [pendingMap indexPathForElement:element]; + // Ensure the element is present in both maps or skip it. If it's not in the visible map, + // then we can't check the presented size. If it's not in the pending map, we can't get the constrained size. + // This will only happen if the element has been deleted, so the specifics of this behavior aren't important. + if (indexPathInPendingMap == nil || [visibleMap indexPathForElement:element] == nil) { + continue; + } + + NSString *kind = element.supplementaryElementKind ?: ASDataControllerRowNodeKind; + ASSizeRange constrainedSize = [self constrainedSizeForNodeOfKind:kind atIndexPath:indexPathInPendingMap]; + [self _layoutNode:node withConstrainedSize:constrainedSize]; + + BOOL matchesSize = [dataSource dataController:self presentedSizeForElement:element matchesSize:node.frame.size]; + if (! matchesSize) { + [nodesSizesChanged addObject:node]; + } + } +} + +- (void)relayoutAllNodesWithInvalidationBlock:(nullable void (^)())invalidationBlock +{ + ASDisplayNodeAssertMainThread(); + if (!_initialReloadDataHasBeenCalled) { + return; + } + + // Can't relayout right away because _visibleMap may not be up-to-date, + // i.e there might be some nodes that were measured using the old constrained size but haven't been added to _visibleMap + LOG(@"Edit Command - relayoutRows"); + [self _scheduleBlockOnMainSerialQueue:^{ + // Because -invalidateLayout doesn't trigger any operations by itself, and we answer queries from UICollectionView using layoutThatFits:, + // we invalidate the layout before we have updated all of the cells. Any cells that the collection needs the size of immediately will get + // -layoutThatFits: with a new constraint, on the main thread, and synchronously calculate them. Meanwhile, relayoutAllNodes will update + // the layout of any remaining nodes on background threads (and fast-return for any nodes that the UICV got to first). + if (invalidationBlock) { + invalidationBlock(); + } + [self _relayoutAllNodes]; + }]; +} + +- (void)_relayoutAllNodes +{ + ASDisplayNodeAssertMainThread(); + // Aggressively repopulate all supplemtary elements + // Assuming this method is run on the main serial queue, _pending and _visible maps are synced and can be manipulated directly. + ASDisplayNodeAssert(_visibleMap == _pendingMap, @"Expected visible and pending maps to be synchronized: %@", self); + + ASMutableElementMap *newMap = [_pendingMap mutableCopy]; + [self _updateSupplementaryNodesIntoMap:newMap + traitCollection:[self.node primitiveTraitCollection] + shouldFetchSizeRanges:YES + previousMap:_pendingMap]; + _pendingMap = [newMap copy]; + _visibleMap = _pendingMap; + + for (ASCollectionElement *element in _visibleMap) { + // Ignore this element if it is no longer in the latest data. It is still recognized in the UIKit world but will be deleted soon. + NSIndexPath *indexPathInPendingMap = [_pendingMap indexPathForElement:element]; + if (indexPathInPendingMap == nil) { + continue; + } + + NSString *kind = element.supplementaryElementKind ?: ASDataControllerRowNodeKind; + ASSizeRange newConstrainedSize = [self constrainedSizeForNodeOfKind:kind atIndexPath:indexPathInPendingMap]; + + if (ASSizeRangeHasSignificantArea(newConstrainedSize)) { + element.constrainedSize = newConstrainedSize; + + // Node may not be allocated yet (e.g node virtualization or same size optimization) + // Call context.nodeIfAllocated here to avoid premature node allocation and layout + ASCellNode *node = element.nodeIfAllocated; + if (node) { + [self _layoutNode:node withConstrainedSize:newConstrainedSize]; + } + } + } +} + +# pragma mark - ASPrimitiveTraitCollection + +- (void)environmentDidChange +{ + ASPerformBlockOnMainThread(^{ + if (!self->_initialReloadDataHasBeenCalled) { + return; + } + + // Can't update the trait collection right away because _visibleMap may not be up-to-date, + // i.e there might be some elements that were allocated using the old trait collection but haven't been added to _visibleMap + [self _scheduleBlockOnMainSerialQueue:^{ + ASPrimitiveTraitCollection newTraitCollection = [self.node primitiveTraitCollection]; + for (ASCollectionElement *element in self->_visibleMap) { + element.traitCollection = newTraitCollection; + } + }]; + }); +} + +- (void)clearData +{ + ASDisplayNodeAssertMainThread(); + if (_initialReloadDataHasBeenCalled) { + [self waitUntilAllUpdatesAreProcessed]; + self.visibleMap = self.pendingMap = [[ASElementMap alloc] init]; + } +} + +# pragma mark - Helper methods + +- (void)_scheduleBlockOnMainSerialQueue:(dispatch_block_t)block +{ + ASDisplayNodeAssertMainThread(); + dispatch_group_wait(_editingTransactionGroup, DISPATCH_TIME_FOREVER); + [_mainSerialQueue performBlockOnMainThread:block]; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASDelegateProxy.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASDelegateProxy.h new file mode 100644 index 0000000..7ed406b --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASDelegateProxy.h @@ -0,0 +1,56 @@ +// +// ASDelegateProxy.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +@class ASDelegateProxy; +@protocol ASDelegateProxyInterceptor +@required +// Called if the target object is discovered to be nil if it had been non-nil at init time. +// This happens if the object is deallocated, because the proxy must maintain a weak reference to avoid cycles. +// Though the target object may become nil, the interceptor must not; it is assumed the interceptor owns the proxy. +- (void)proxyTargetHasDeallocated:(ASDelegateProxy *)proxy; +@end + +/** + * Stand-in for delegates like UITableView or UICollectionView's delegate / dataSource. + * Any selectors flagged by "interceptsSelector" are routed to the interceptor object and are not delivered to the target. + * Everything else leaves AsyncDisplayKit safely and arrives at the original target object. + */ + +@interface ASDelegateProxy : NSProxy + +- (instancetype)initWithTarget:(id)target interceptor:(id )interceptor; + +// This method must be overridden by a subclass. +- (BOOL)interceptsSelector:(SEL)selector; + +@end + +/** + * ASTableView intercepts and/or overrides a few of UITableView's critical data source and delegate methods. + * + * Any selector included in this function *MUST* be implemented by ASTableView. + */ + +@interface ASTableViewProxy : ASDelegateProxy +@end + +/** + * ASCollectionView intercepts and/or overrides a few of UICollectionView's critical data source and delegate methods. + * + * Any selector included in this function *MUST* be implemented by ASCollectionView. + */ + +@interface ASCollectionViewProxy : ASDelegateProxy +@end + +@interface ASPagerNodeProxy : ASDelegateProxy +@end + diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASDelegateProxy.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASDelegateProxy.mm new file mode 100644 index 0000000..844fe63 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASDelegateProxy.mm @@ -0,0 +1,268 @@ +// +// ASDelegateProxy.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import + +// UIKit performs a class check for UIDataSourceModelAssociation protocol conformance rather than an instance check, so +// the implementation of conformsToProtocol: below never gets called. We need to declare the two as conforming to the protocol here, then +// we need to implement dummy methods to get rid of a compiler warning about not conforming to the protocol. +@interface ASTableViewProxy () +@end + +@interface ASCollectionViewProxy () +@end + +@interface ASDelegateProxy (UIDataSourceModelAssociationPrivate) +- (nullable NSString *)_modelIdentifierForElementAtIndexPath:(NSIndexPath *)indexPath inView:(UIView *)view; +- (nullable NSIndexPath *)_indexPathForElementWithModelIdentifier:(NSString *)identifier inView:(UIView *)view; +@end + +@implementation ASTableViewProxy + +- (BOOL)interceptsSelector:(SEL)selector +{ + return ( + // handled by ASTableView node<->cell machinery + selector == @selector(tableView:cellForRowAtIndexPath:) || + selector == @selector(tableView:heightForRowAtIndexPath:) || + + // Selection, highlighting, menu + selector == @selector(tableView:willSelectRowAtIndexPath:) || + selector == @selector(tableView:didSelectRowAtIndexPath:) || + selector == @selector(tableView:willDeselectRowAtIndexPath:) || + selector == @selector(tableView:didDeselectRowAtIndexPath:) || + selector == @selector(tableView:shouldHighlightRowAtIndexPath:) || + selector == @selector(tableView:didHighlightRowAtIndexPath:) || + selector == @selector(tableView:didUnhighlightRowAtIndexPath:) || + selector == @selector(tableView:shouldShowMenuForRowAtIndexPath:) || + selector == @selector(tableView:canPerformAction:forRowAtIndexPath:withSender:) || + selector == @selector(tableView:performAction:forRowAtIndexPath:withSender:) || + + // handled by ASRangeController + selector == @selector(numberOfSectionsInTableView:) || + selector == @selector(tableView:numberOfRowsInSection:) || + + // reordering support + selector == @selector(tableView:canMoveRowAtIndexPath:) || + selector == @selector(tableView:moveRowAtIndexPath:toIndexPath:) || + + // used for ASCellNode visibility + selector == @selector(scrollViewDidScroll:) || + + // used for ASCellNode user interaction + selector == @selector(scrollViewWillBeginDragging:) || + selector == @selector(scrollViewDidEndDragging:willDecelerate:) || + + // used for ASRangeController visibility updates + selector == @selector(tableView:willDisplayCell:forRowAtIndexPath:) || + selector == @selector(tableView:didEndDisplayingCell:forRowAtIndexPath:) || + + // used for batch fetching API + selector == @selector(scrollViewWillEndDragging:withVelocity:targetContentOffset:) || + selector == @selector(scrollViewDidEndDecelerating:) || + + // UIDataSourceModelAssociation + selector == @selector(modelIdentifierForElementAtIndexPath:inView:) || + selector == @selector(indexPathForElementWithModelIdentifier:inView:) + ); +} + +- (nullable NSString *)modelIdentifierForElementAtIndexPath:(NSIndexPath *)indexPath inView:(UIView *)view { + return [self _modelIdentifierForElementAtIndexPath:indexPath inView:view]; +} + +- (nullable NSIndexPath *)indexPathForElementWithModelIdentifier:(NSString *)identifier inView:(UIView *)view { + return [self _indexPathForElementWithModelIdentifier:identifier inView:view]; +} + +@end + +@implementation ASCollectionViewProxy + +- (BOOL)interceptsSelector:(SEL)selector +{ + return ( + // handled by ASCollectionView node<->cell machinery + selector == @selector(collectionView:cellForItemAtIndexPath:) || + selector == @selector(collectionView:layout:sizeForItemAtIndexPath:) || + selector == @selector(collectionView:layout:insetForSectionAtIndex:) || + selector == @selector(collectionView:layout:minimumLineSpacingForSectionAtIndex:) || + selector == @selector(collectionView:layout:minimumInteritemSpacingForSectionAtIndex:) || + selector == @selector(collectionView:layout:referenceSizeForHeaderInSection:) || + selector == @selector(collectionView:layout:referenceSizeForFooterInSection:) || + selector == @selector(collectionView:viewForSupplementaryElementOfKind:atIndexPath:) || + + // Selection, highlighting, menu + selector == @selector(collectionView:shouldSelectItemAtIndexPath:) || + selector == @selector(collectionView:didSelectItemAtIndexPath:) || + selector == @selector(collectionView:shouldDeselectItemAtIndexPath:) || + selector == @selector(collectionView:didDeselectItemAtIndexPath:) || + selector == @selector(collectionView:shouldHighlightItemAtIndexPath:) || + selector == @selector(collectionView:didHighlightItemAtIndexPath:) || + selector == @selector(collectionView:didUnhighlightItemAtIndexPath:) || + selector == @selector(collectionView:shouldShowMenuForItemAtIndexPath:) || + selector == @selector(collectionView:canPerformAction:forItemAtIndexPath:withSender:) || + selector == @selector(collectionView:performAction:forItemAtIndexPath:withSender:) || + + // Item counts + selector == @selector(numberOfSectionsInCollectionView:) || + selector == @selector(collectionView:numberOfItemsInSection:) || + + // Element appearance callbacks + selector == @selector(collectionView:willDisplayCell:forItemAtIndexPath:) || + selector == @selector(collectionView:didEndDisplayingCell:forItemAtIndexPath:) || + selector == @selector(collectionView:willDisplaySupplementaryView:forElementKind:atIndexPath:) || + selector == @selector(collectionView:didEndDisplayingSupplementaryView:forElementOfKind:atIndexPath:) || + + // used for batch fetching API + selector == @selector(scrollViewWillEndDragging:withVelocity:targetContentOffset:) || + selector == @selector(scrollViewDidEndDecelerating:) || + + // used for ASCellNode visibility + selector == @selector(scrollViewDidScroll:) || + + // used for ASCellNode user interaction + selector == @selector(scrollViewWillBeginDragging:) || + selector == @selector(scrollViewDidEndDragging:willDecelerate:) || + + // intercepted due to not being supported by ASCollectionView (prevent bugs caused by usage) + selector == @selector(collectionView:canMoveItemAtIndexPath:) || + selector == @selector(collectionView:moveItemAtIndexPath:toIndexPath:) || + + // UIDataSourceModelAssociation + selector == @selector(modelIdentifierForElementAtIndexPath:inView:) || + selector == @selector(indexPathForElementWithModelIdentifier:inView:) + ); +} + +- (nullable NSString *)modelIdentifierForElementAtIndexPath:(NSIndexPath *)indexPath inView:(UIView *)view { + return [self _modelIdentifierForElementAtIndexPath:indexPath inView:view]; +} + +- (nullable NSIndexPath *)indexPathForElementWithModelIdentifier:(NSString *)identifier inView:(UIView *)view { + return [self _indexPathForElementWithModelIdentifier:identifier inView:view]; +} + +@end + +@implementation ASPagerNodeProxy + +- (BOOL)interceptsSelector:(SEL)selector +{ + return ( + // handled by ASPagerDataSource node<->cell machinery + selector == @selector(collectionNode:nodeForItemAtIndexPath:) || + selector == @selector(collectionNode:nodeBlockForItemAtIndexPath:) || + selector == @selector(collectionNode:numberOfItemsInSection:) || + selector == @selector(collectionNode:constrainedSizeForItemAtIndexPath:) + ); +} + +@end + +@implementation ASDelegateProxy { + id __weak _interceptor; + id __weak _target; +} + +- (instancetype)initWithTarget:(id)target interceptor:(id )interceptor +{ + ASDisplayNodeAssert(interceptor, @"interceptor must not be nil"); + + _target = target ? : [NSNull null]; + _interceptor = interceptor; + + return self; +} + +- (BOOL)conformsToProtocol:(Protocol *)aProtocol +{ + id target = _target; + if (target) { + return [target conformsToProtocol:aProtocol]; + } else { + return [super conformsToProtocol:aProtocol]; + } +} + +- (BOOL)respondsToSelector:(SEL)aSelector +{ + if ([self interceptsSelector:aSelector]) { + return [_interceptor respondsToSelector:aSelector]; + } else { + // Also return NO if _target has become nil due to zeroing weak reference (or placeholder initialization). + return [_target respondsToSelector:aSelector]; + } +} + +- (id)forwardingTargetForSelector:(SEL)aSelector +{ + if ([self interceptsSelector:aSelector]) { + return _interceptor; + } else { + id target = _target; + if (target) { + return [target respondsToSelector:aSelector] ? target : nil; + } else { + // The _interceptor needs to be nilled out in this scenario. For that a strong reference needs to be created + // to be able to nil out the _interceptor but still let it know that the proxy target has deallocated + // We have to hold a strong reference to the interceptor as we have to nil it out and call the proxyTargetHasDeallocated + // The reason that the interceptor needs to be nilled out is that there maybe a change of a infinite loop, for example + // if a method will be called in the proxyTargetHasDeallocated: that again would trigger a whole new forwarding cycle + id interceptor = _interceptor; + _interceptor = nil; + [interceptor proxyTargetHasDeallocated:self]; + + return nil; + } + } +} + +- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector +{ + // Check for a compiled definition for the selector + NSMethodSignature *methodSignature = nil; + if ([self interceptsSelector:aSelector]) { + methodSignature = [[_interceptor class] instanceMethodSignatureForSelector:aSelector]; + } else { + methodSignature = [[_target class] instanceMethodSignatureForSelector:aSelector]; + } + + // Unfortunately, in order to get this object to work properly, the use of a method which creates an NSMethodSignature + // from a C string. -methodSignatureForSelector is called when a compiled definition for the selector cannot be found. + // This is the place where we have to create our own dud NSMethodSignature. This is necessary because if this method + // returns nil, a selector not found exception is raised. The string argument to -signatureWithObjCTypes: outlines + // the return type and arguments to the message. To return a dud NSMethodSignature, pretty much any signature will + // suffice. Since the -forwardInvocation call will do nothing if the delegate does not respond to the selector, + // the dud NSMethodSignature simply gets us around the exception. + return methodSignature ?: [NSMethodSignature signatureWithObjCTypes:"@^v^c"]; +} + +- (void)forwardInvocation:(NSInvocation *)invocation +{ + // If we are down here this means _interceptor and _target where nil. Just don't do anything to prevent a crash +} + +- (BOOL)interceptsSelector:(SEL)selector +{ + ASDisplayNodeAssert(NO, @"This method must be overridden by subclasses."); + return NO; +} + +- (nullable NSString *)_modelIdentifierForElementAtIndexPath:(NSIndexPath *)indexPath inView:(UIView *)view { + return [(id)_interceptor modelIdentifierForElementAtIndexPath:indexPath inView:view]; +} + +- (nullable NSIndexPath *)_indexPathForElementWithModelIdentifier:(NSString *)identifier inView:(UIView *)view { + return [(id)_interceptor indexPathForElementWithModelIdentifier:identifier inView:view]; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASElementMap.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASElementMap.h new file mode 100644 index 0000000..bf5b259 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASElementMap.h @@ -0,0 +1,133 @@ +// +// ASElementMap.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@class ASCollectionElement, ASSection, UICollectionViewLayoutAttributes; +@protocol ASSectionContext; + +/** + * An immutable representation of the state of a collection view's data. + * All items and supplementary elements are represented by ASCollectionElement. + * Fast enumeration is in terms of ASCollectionElement. + */ +AS_SUBCLASSING_RESTRICTED +@interface ASElementMap : NSObject + +/** + * The total number of elements in this map. + */ +@property (readonly) NSUInteger count; + +/** + * The number of sections (of items) in this map. + */ +@property (readonly) NSInteger numberOfSections; + +/** + * The kinds of supplementary elements present in this map. O(1) + */ +@property (copy, readonly) NSArray *supplementaryElementKinds; + +/** + * Returns number of items in the given section. O(1) + */ +- (NSInteger)numberOfItemsInSection:(NSInteger)section; + +/** + * Returns the context object for the given section, if any. O(1) + */ +- (nullable id)contextForSection:(NSInteger)section; + +/** + * All the index paths for all the items in this map. O(N) + * + * This property may be removed in the future, since it doesn't account for supplementary nodes. + */ +@property (copy, readonly) NSArray *itemIndexPaths; + +/** + * All the item elements in this map, in ascending order. O(N) + */ +@property (copy, readonly) NSArray *itemElements; + +/** + * Returns the index path that corresponds to the same element in @c map at the given @c indexPath. + * O(1) for items, fast O(N) for sections. + * + * Note you can pass "section index paths" of length 1 and get a corresponding section index path. + */ +- (nullable NSIndexPath *)convertIndexPath:(NSIndexPath *)indexPath fromMap:(ASElementMap *)map; + +/** + * Returns the section index into the receiver that corresponds to the same element in @c map at @c sectionIndex. Fast O(N). + * + * Returns @c NSNotFound if the section does not exist in the receiver. + */ +- (NSInteger)convertSection:(NSInteger)sectionIndex fromMap:(ASElementMap *)map; + +/** + * Returns the index path for the given element. O(1) + */ +- (nullable NSIndexPath *)indexPathForElement:(ASCollectionElement *)element; + +/** + * Returns the index path for the given element, if it represents a cell. O(1) + */ +- (nullable NSIndexPath *)indexPathForElementIfCell:(ASCollectionElement *)element; + +/** + * Returns the item-element at the given index path. O(1) + */ +- (nullable ASCollectionElement *)elementForItemAtIndexPath:(NSIndexPath *)indexPath; + +/** + * Returns the element for the supplementary element of the given kind at the given index path. O(1) + */ +- (nullable ASCollectionElement *)supplementaryElementOfKind:(NSString *)supplementaryElementKind atIndexPath:(NSIndexPath *)indexPath; + +/** + * Returns the element that corresponds to the given layout attributes, if any. + * + * NOTE: This method only regards the category, kind, and index path of the attributes object. Elements do not + * have any concept of size/position. + */ +- (nullable ASCollectionElement *)elementForLayoutAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes; + +/** + * A very terse description e.g. { itemCounts = [ ] } + */ +@property (readonly) NSString *smallDescription; + +#pragma mark - Initialization -- Only Useful to ASDataController + + +// SectionIndex -> ItemIndex -> Element +typedef NSArray *> ASCollectionElementTwoDimensionalArray; + +// ElementKind -> IndexPath -> Element +typedef NSDictionary *> ASSupplementaryElementDictionary; + +/** + * Create a new element map for this dataset. You probably don't need to use this – ASDataController is the only one who creates these. + * + * @param sections The array of ASSection objects. + * @param items A 2D array of ASCollectionElements, for each item. + * @param supplementaryElements A dictionary of gathered supplementary elements. + */ +- (instancetype)initWithSections:(NSArray *)sections + items:(ASCollectionElementTwoDimensionalArray *)items + supplementaryElements:(ASSupplementaryElementDictionary *)supplementaryElements; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASElementMap.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASElementMap.mm new file mode 100644 index 0000000..6dd0d64 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASElementMap.mm @@ -0,0 +1,275 @@ +// +// ASElementMap.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import +#import +#import +#import +#import + +@interface ASElementMap () + +@property (nonatomic, readonly) NSArray *sections; + +// Element -> IndexPath +@property (nonatomic, readonly) NSMapTable *elementToIndexPathMap; + +// The items, in a 2D array +@property (nonatomic, readonly) ASCollectionElementTwoDimensionalArray *sectionsOfItems; + +@property (nonatomic, readonly) ASSupplementaryElementDictionary *supplementaryElements; + +@end + +@implementation ASElementMap + +- (instancetype)init +{ + return [self initWithSections:@[] items:@[] supplementaryElements:@{}]; +} + +- (instancetype)initWithSections:(NSArray *)sections items:(ASCollectionElementTwoDimensionalArray *)items supplementaryElements:(ASSupplementaryElementDictionary *)supplementaryElements +{ + NSCParameterAssert(items.count == sections.count); + + if (self = [super init]) { + _sections = [sections copy]; + _sectionsOfItems = [[NSArray alloc] initWithArray:items copyItems:YES]; + _supplementaryElements = [[NSDictionary alloc] initWithDictionary:supplementaryElements copyItems:YES]; + + // Setup our index path map + _elementToIndexPathMap = [NSMapTable mapTableWithKeyOptions:(NSMapTableStrongMemory | NSMapTableObjectPointerPersonality) valueOptions:NSMapTableCopyIn]; + NSInteger s = 0; + for (NSArray *section in _sectionsOfItems) { + NSInteger i = 0; + for (ASCollectionElement *element in section) { + NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:s]; + [_elementToIndexPathMap setObject:indexPath forKey:element]; + i++; + } + s++; + } + for (NSDictionary *supplementariesForKind in [_supplementaryElements objectEnumerator]) { + [supplementariesForKind enumerateKeysAndObjectsUsingBlock:^(NSIndexPath *_Nonnull indexPath, ASCollectionElement * _Nonnull element, BOOL * _Nonnull stop) { + [_elementToIndexPathMap setObject:indexPath forKey:element]; + }]; + } + } + return self; +} + +- (NSUInteger)count +{ + return _elementToIndexPathMap.count; +} + +- (NSArray *)itemIndexPaths +{ + return ASIndexPathsForTwoDimensionalArray(_sectionsOfItems); +} + +- (NSArray *)itemElements +{ + return ASElementsInTwoDimensionalArray(_sectionsOfItems); +} + +- (NSInteger)numberOfSections +{ + return _sectionsOfItems.count; +} + +- (NSArray *)supplementaryElementKinds +{ + return _supplementaryElements.allKeys; +} + +- (NSInteger)numberOfItemsInSection:(NSInteger)section +{ + if (![self sectionIndexIsValid:section assert:YES]) { + return 0; + } + + return _sectionsOfItems[section].count; +} + +- (id)contextForSection:(NSInteger)section +{ + if (![self sectionIndexIsValid:section assert:NO]) { + return nil; + } + + return _sections[section].context; +} + +- (nullable NSIndexPath *)indexPathForElement:(ASCollectionElement *)element +{ + return element ? [_elementToIndexPathMap objectForKey:element] : nil; +} + +- (nullable NSIndexPath *)indexPathForElementIfCell:(ASCollectionElement *)element +{ + if (element.supplementaryElementKind == nil) { + return [self indexPathForElement:element]; + } else { + return nil; + } +} + +- (nullable ASCollectionElement *)elementForItemAtIndexPath:(NSIndexPath *)indexPath +{ + NSInteger section, item; + if (![self itemIndexPathIsValid:indexPath assert:NO item:&item section:§ion]) { + return nil; + } + + return _sectionsOfItems[section][item]; +} + +- (nullable ASCollectionElement *)supplementaryElementOfKind:(NSString *)supplementaryElementKind atIndexPath:(NSIndexPath *)indexPath +{ + return _supplementaryElements[supplementaryElementKind][indexPath]; +} + +- (ASCollectionElement *)elementForLayoutAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes +{ + switch (layoutAttributes.representedElementCategory) { + case UICollectionElementCategoryCell: + // Cell + return [self elementForItemAtIndexPath:layoutAttributes.indexPath]; + case UICollectionElementCategorySupplementaryView: + // Supplementary element. + return [self supplementaryElementOfKind:layoutAttributes.representedElementKind atIndexPath:layoutAttributes.indexPath]; + case UICollectionElementCategoryDecorationView: + // No support for decoration views. + return nil; + } +} + +- (NSIndexPath *)convertIndexPath:(NSIndexPath *)indexPath fromMap:(ASElementMap *)map +{ + if (indexPath.item == NSNotFound) { + // Section index path + NSInteger result = [self convertSection:indexPath.section fromMap:map]; + return (result != NSNotFound ? [NSIndexPath indexPathWithIndex:result] : nil); + } else { + // Item index path + ASCollectionElement *element = [map elementForItemAtIndexPath:indexPath]; + return [self indexPathForElement:element]; + } +} + +- (NSInteger)convertSection:(NSInteger)sectionIndex fromMap:(ASElementMap *)map +{ + if (![map sectionIndexIsValid:sectionIndex assert:YES]) { + return NSNotFound; + } + + ASSection *section = map.sections[sectionIndex]; + return [_sections indexOfObjectIdenticalTo:section]; +} + +#pragma mark - NSCopying + +- (id)copyWithZone:(NSZone *)zone +{ + return self; +} + +// NSMutableCopying conformance is declared in ASMutableElementMap.h, so that most consumers of ASElementMap don't bother with it. +#pragma mark - NSMutableCopying + +- (id)mutableCopyWithZone:(NSZone *)zone +{ + return [[ASMutableElementMap alloc] initWithSections:_sections items:_sectionsOfItems supplementaryElements:_supplementaryElements]; +} + +#pragma mark - NSFastEnumeration + +- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id _Nullable unowned [])buffer count:(NSUInteger)len +{ + return [_elementToIndexPathMap countByEnumeratingWithState:state objects:buffer count:len]; +} + +- (NSString *)smallDescription +{ + NSMutableArray *sectionDescriptions = [NSMutableArray array]; + + NSUInteger i = 0; + for (NSArray *section in _sectionsOfItems) { + [sectionDescriptions addObject:[NSString stringWithFormat:@"", i, section.count]]; + i++; + } + return ASObjectDescriptionMakeWithoutObject(@[ @{ @"itemCounts": sectionDescriptions }]); +} + +#pragma mark - ASDescriptionProvider + +- (NSString *)description +{ + return ASObjectDescriptionMake(self, [self propertiesForDescription]); +} + +- (NSMutableArray *)propertiesForDescription +{ + NSMutableArray *result = [NSMutableArray array]; + [result addObject:@{ @"items" : _sectionsOfItems }]; + [result addObject:@{ @"supplementaryElements" : _supplementaryElements }]; + return result; +} + +#pragma mark - Internal + +/** + * Fails assert + return NO if section is out of bounds. + */ +- (BOOL)sectionIndexIsValid:(NSInteger)section assert:(BOOL)assert +{ + NSInteger sectionCount = _sectionsOfItems.count; + if (section >= sectionCount || section < 0) { + if (assert) { + ASDisplayNodeFailAssert(@"Invalid section index %ld when there are only %ld sections!", (long)section, (long)sectionCount); + } + return NO; + } else { + return YES; + } +} + +/** + * If indexPath is nil, just returns NO. + * If indexPath is invalid, fails assertion and returns NO. + * Otherwise returns YES and sets the item & section. + */ +- (BOOL)itemIndexPathIsValid:(NSIndexPath *)indexPath assert:(BOOL)assert item:(out NSInteger *)outItem section:(out NSInteger *)outSection +{ + if (indexPath == nil) { + return NO; + } + + NSInteger section = indexPath.section; + if (![self sectionIndexIsValid:section assert:assert]) { + return NO; + } + + NSInteger itemCount = _sectionsOfItems[section].count; + NSInteger item = indexPath.item; + if (item >= itemCount || item < 0) { + if (assert) { + ASDisplayNodeFailAssert(@"Invalid item index %ld in section %ld which only has %ld items!", (long)item, (long)section, (long)itemCount); + } + return NO; + } + *outItem = item; + *outSection = section; + return YES; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASGraphicsContext.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASGraphicsContext.h new file mode 100644 index 0000000..5bd1537 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASGraphicsContext.h @@ -0,0 +1,65 @@ +// +// ASGraphicsContext.h +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * A wrapper for the UIKit drawing APIs. If you are in ASExperimentalDrawingGlobal, and you have iOS >= 10, we will create + * a UIGraphicsRenderer with an appropriate format. Otherwise, we will use UIGraphicsBeginImageContext et al. + * + * @param size The size of the context. + * @param opaque Whether the context should be opaque or not. + * @param scale The scale of the context. 0 uses main screen scale. + * @param sourceImage If you are planning to render a UIImage into this context, provide it here and we will use its + * preferred renderer format if we are using UIGraphicsImageRenderer. + * @param isCancelled An optional block for canceling the drawing before forming the image. Only takes effect under + * the legacy code path, as UIGraphicsRenderer does not support cancellation. + * @param work A block, wherein the current UIGraphics context is set based on the arguments. + * + * @return The rendered image. You can also render intermediary images using UIGraphicsGetImageFromCurrentImageContext. + */ +ASDK_EXTERN UIImage *ASGraphicsCreateImageWithOptions(CGSize size, BOOL opaque, CGFloat scale, UIImage * _Nullable sourceImage, asdisplaynode_iscancelled_block_t NS_NOESCAPE _Nullable isCancelled, void (NS_NOESCAPE ^work)(void)) ASDISPLAYNODE_DEPRECATED_MSG("Use ASGraphicsCreateImageWithTraitCollectionAndOptions instead"); + +/** +* A wrapper for the UIKit drawing APIs. If you are in ASExperimentalDrawingGlobal, and you have iOS >= 10, we will create +* a UIGraphicsRenderer with an appropriate format. Otherwise, we will use UIGraphicsBeginImageContext et al. +* +* @param traitCollection Trait collection. The `work` block will be executed with this trait collection, so it will affect dynamic colors, etc. +* @param size The size of the context. +* @param opaque Whether the context should be opaque or not. +* @param scale The scale of the context. 0 uses main screen scale. +* @param sourceImage If you are planning to render a UIImage into this context, provide it here and we will use its +* preferred renderer format if we are using UIGraphicsImageRenderer. +* @param isCancelled An optional block for canceling the drawing before forming the image. +* @param work A block, wherein the current UIGraphics context is set based on the arguments. +* +* @return The rendered image. You can also render intermediary images using UIGraphicsGetImageFromCurrentImageContext. +*/ +ASDK_EXTERN UIImage *ASGraphicsCreateImage(ASPrimitiveTraitCollection traitCollection, CGSize size, BOOL opaque, CGFloat scale, UIImage * _Nullable sourceImage, asdisplaynode_iscancelled_block_t _Nullable NS_NOESCAPE isCancelled, void (NS_NOESCAPE ^work)(void)); + +/** +* A wrapper for the UIKit drawing APIs. +* +* @param traitCollection Trait collection. The `work` block will be executed with this trait collection, so it will affect dynamic colors, etc. +* @param size The size of the context. +* @param opaque Whether the context should be opaque or not. +* @param scale The scale of the context. 0 uses main screen scale. +* @param sourceImage If you are planning to render a UIImage into this context, provide it here and we will use its +* preferred renderer format if we are using UIGraphicsImageRenderer. +* @param work A block, wherein the current UIGraphics context is set based on the arguments. +* +* @return The rendered image. You can also render intermediary images using UIGraphicsGetImageFromCurrentImageContext. +*/ +ASDK_EXTERN UIImage *ASGraphicsCreateImageWithTraitCollectionAndOptions(ASPrimitiveTraitCollection traitCollection, CGSize size, BOOL opaque, CGFloat scale, UIImage * _Nullable sourceImage, void (NS_NOESCAPE ^work)(void)) ASDISPLAYNODE_DEPRECATED_MSG("Use ASGraphicsCreateImage instead"); + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASGraphicsContext.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASGraphicsContext.mm new file mode 100644 index 0000000..8f543e0 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASGraphicsContext.mm @@ -0,0 +1,136 @@ +// +// ASGraphicsContext.mm +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import +#import +#import + + +#if AS_AT_LEAST_IOS13 +#define ASPerformBlockWithTraitCollection(work, traitCollection) \ + if (@available(iOS 13.0, tvOS 13.0, *)) { \ + UITraitCollection *uiTraitCollection = ASPrimitiveTraitCollectionToUITraitCollection(traitCollection); \ + [uiTraitCollection performAsCurrentTraitCollection:^{ \ + work(); \ + }];\ + } else { \ + work(); \ + } +#else +#define ASPerformBlockWithTraitCollection(work, traitCollection) work(); +#endif + + +NS_AVAILABLE_IOS(10) +NS_INLINE void ASConfigureExtendedRange(UIGraphicsImageRendererFormat *format) +{ + if (AS_AVAILABLE_IOS_TVOS(12, 12)) { + // nop. We always use automatic range on iOS >= 12. + } else { + // Currently we never do wide color. One day we could pipe this information through from the ASImageNode if it was worth it. + format.prefersExtendedRange = NO; + } +} + +UIImage *ASGraphicsCreateImageWithOptions(CGSize size, BOOL opaque, CGFloat scale, UIImage *sourceImage, + asdisplaynode_iscancelled_block_t NS_NOESCAPE isCancelled, + void (^NS_NOESCAPE work)()) +{ + return ASGraphicsCreateImage(ASPrimitiveTraitCollectionMakeDefault(), size, opaque, scale, sourceImage, isCancelled, work); +} + +UIImage *ASGraphicsCreateImage(ASPrimitiveTraitCollection traitCollection, CGSize size, BOOL opaque, CGFloat scale, UIImage * sourceImage, asdisplaynode_iscancelled_block_t NS_NOESCAPE isCancelled, void (NS_NOESCAPE ^work)()) { + if (AS_AVAILABLE_IOS_TVOS(10, 10)) { + if (ASActivateExperimentalFeature(ASExperimentalDrawingGlobal)) { + // If they used default scale, reuse one of two preferred formats. + static UIGraphicsImageRendererFormat *defaultFormat; + static UIGraphicsImageRendererFormat *opaqueFormat; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + if (AS_AVAILABLE_IOS_TVOS(11, 11)) { + defaultFormat = [UIGraphicsImageRendererFormat preferredFormat]; + opaqueFormat = [UIGraphicsImageRendererFormat preferredFormat]; + } else { + defaultFormat = [UIGraphicsImageRendererFormat defaultFormat]; + opaqueFormat = [UIGraphicsImageRendererFormat defaultFormat]; + } + opaqueFormat.opaque = YES; + ASConfigureExtendedRange(defaultFormat); + ASConfigureExtendedRange(opaqueFormat); + }); + + UIGraphicsImageRendererFormat *format; + if (sourceImage) { + if (sourceImage.renderingMode == UIImageRenderingModeAlwaysTemplate) { + // Template images will be black and transparent, so if we use + // sourceImage.imageRenderFormat it will assume a grayscale color space. + // This is not good because a template image should be able to tint to any color, + // so we'll just use the default here. + if (AS_AVAILABLE_IOS_TVOS(11, 11)) { + format = [UIGraphicsImageRendererFormat preferredFormat]; + } else { + format = [UIGraphicsImageRendererFormat defaultFormat]; + } + } else { + format = sourceImage.imageRendererFormat; + } + // We only want the private bits (color space and bits per component) from the image. + // We have our own ideas about opacity and scale. + format.opaque = opaque; + format.scale = scale; + } else if (scale == 0 || scale == ASScreenScale()) { + format = opaque ? opaqueFormat : defaultFormat; + } else { + if (AS_AVAILABLE_IOS_TVOS(11, 11)) { + format = [UIGraphicsImageRendererFormat preferredFormat]; + } else { + format = [UIGraphicsImageRendererFormat defaultFormat]; + } + if (opaque) format.opaque = YES; + format.scale = scale; + ASConfigureExtendedRange(format); + } + + // Avoid using the imageWithActions: method because it does not support cancellation at the + // last moment i.e. before actually creating the resulting image. + __block UIImage *image; + NSError *error; + [[[UIGraphicsImageRenderer alloc] initWithSize:size format:format] + runDrawingActions:^(UIGraphicsImageRendererContext *rendererContext) { + ASDisplayNodeCAssert(UIGraphicsGetCurrentContext(), @"Should have a context!"); + ASPerformBlockWithTraitCollection(work, traitCollection); + } + completionActions:^(UIGraphicsImageRendererContext *rendererContext) { + if (isCancelled == nil || !isCancelled()) { + image = rendererContext.currentImage; + } + } + error:&error]; + if (error) { + NSCAssert(NO, @"Error drawing: %@", error); + } + return image; + } + } + + // Bad OS or experiment flag. Use UIGraphics* API. + UIGraphicsBeginImageContextWithOptions(size, opaque, scale); + ASPerformBlockWithTraitCollection(work, traitCollection) + UIImage *image = nil; + if (isCancelled == nil || !isCancelled()) { + image = UIGraphicsGetImageFromCurrentImageContext(); + } + UIGraphicsEndImageContext(); + return image; +} + +UIImage *ASGraphicsCreateImageWithTraitCollectionAndOptions(ASPrimitiveTraitCollection traitCollection, CGSize size, BOOL opaque, CGFloat scale, UIImage * sourceImage, void (NS_NOESCAPE ^work)()) { + return ASGraphicsCreateImage(traitCollection, size, opaque, scale, sourceImage, nil, work); +} diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASHashing.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASHashing.h new file mode 100644 index 0000000..42a4a98 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASHashing.h @@ -0,0 +1,40 @@ +// +// ASHashing.h +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * When std::hash is unavailable, this function will hash a bucket o' bits real fast. + * The hashing algorithm is copied from CoreFoundation's private function CFHashBytes. + * https://opensource.apple.com/source/CF/CF-1153.18/CFUtilities.c.auto.html + * + * Simple example: + * CGRect myRect = { ... }; + * ASHashBytes(&myRect, sizeof(myRect)); + * + * Example: + * struct { + * NSUInteger imageHash; + * CGSize size; + * } data = { + * _image.hash, + * _bounds.size + * }; + * return ASHashBytes(&data, sizeof(data)); + * + * @warning: If a struct has padding, any fields that are intiailized in {} + * will have garbage data for their padding, which will break this hash! Either + * use `pragma clang diagnostic warning "-Wpadded"` around your struct definition + * or manually initialize the fields of your struct (`myStruct.x = 7;` etc). + */ +ASDK_EXTERN NSUInteger ASHashBytes(void *bytes, size_t length); + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASHashing.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASHashing.mm new file mode 100644 index 0000000..17bf66b --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASHashing.mm @@ -0,0 +1,38 @@ +// +// ASHashing.mm +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#define ELF_STEP(B) T1 = (H << 4) + B; T2 = T1 & 0xF0000000; if (T2) T1 ^= (T2 >> 24); T1 &= (~T2); H = T1; + +/** + * The hashing algorithm copied from CoreFoundation CFHashBytes function. + * https://opensource.apple.com/source/CF/CF-1153.18/CFUtilities.c.auto.html + */ +NSUInteger ASHashBytes(void *bytesarg, size_t length) { + /* The ELF hash algorithm, used in the ELF object file format */ + uint8_t *bytes = (uint8_t *)bytesarg; + UInt32 H = 0, T1, T2; + SInt32 rem = (SInt32)length; + while (3 < rem) { + ELF_STEP(bytes[length - rem]); + ELF_STEP(bytes[length - rem + 1]); + ELF_STEP(bytes[length - rem + 2]); + ELF_STEP(bytes[length - rem + 3]); + rem -= 4; + } + switch (rem) { + case 3: ELF_STEP(bytes[length - 3]); + case 2: ELF_STEP(bytes[length - 2]); + case 1: ELF_STEP(bytes[length - 1]); + case 0: ; + } + return H; +} + +#undef ELF_STEP diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASHighlightOverlayLayer.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASHighlightOverlayLayer.h new file mode 100644 index 0000000..aff6694 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASHighlightOverlayLayer.h @@ -0,0 +1,51 @@ +// +// ASHighlightOverlayLayer.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +AS_SUBCLASSING_RESTRICTED +@interface ASHighlightOverlayLayer : CALayer + +/** + @summary Initializes with CGRects for the highlighting, in the targetLayer's coordinate space. + + @desc This is the designated initializer. + + @param rects Array containing CGRects wrapped in NSValue. + @param targetLayer The layer that the rects are relative to. The rects will be translated to the receiver's coordinate space when rendering. + */ +- (instancetype)initWithRects:(NSArray *)rects targetLayer:(nullable CALayer *)targetLayer; + +/** + @summary Initializes with CGRects for the highlighting, in the receiver's coordinate space. + + @param rects Array containing CGRects wrapped in NSValue. + */ +- (instancetype)initWithRects:(NSArray *)rects; + +@property (nullable, nonatomic) __attribute__((NSObject)) CGColorRef highlightColor; +@property (nonatomic, weak) CALayer *targetLayer; + +@end + +@interface CALayer (ASHighlightOverlayLayerSupport) + +/** + @summary Set to YES to indicate to a sublayer that this is where highlight overlay layers (for pressed states) should + be added so that the highlight won't be clipped by a neighboring layer. + */ +@property (nonatomic, setter=as_setAllowsHighlightDrawing:) BOOL as_allowsHighlightDrawing; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASHighlightOverlayLayer.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASHighlightOverlayLayer.mm new file mode 100644 index 0000000..03cf468 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASHighlightOverlayLayer.mm @@ -0,0 +1,134 @@ +// +// ASHighlightOverlayLayer.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import +#import + +#import + +static const CGFloat kCornerRadius = 2.5; +static const UIEdgeInsets padding = {2, 4, 1.5, 4}; + +@implementation ASHighlightOverlayLayer +{ + NSArray *_rects; +} + ++ (id)defaultValueForKey:(NSString *)key +{ + if ([key isEqualToString:@"contentsScale"]) { + return @(ASScreenScale()); + } else if ([key isEqualToString:@"highlightColor"]) { + CGFloat components[] = {0, 0, 0, 0.25}; + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + CGColorRef color = CGColorCreate(colorSpace, components); + CGColorSpaceRelease(colorSpace); + return CFBridgingRelease(color); + } else { + return [super defaultValueForKey:key]; + } +} + ++ (BOOL)needsDisplayForKey:(NSString *)key +{ + if ([key isEqualToString:@"bounds"]) { + return YES; + } else { + return [super needsDisplayForKey:key]; + } +} + ++ (id)defaultActionForKey:(NSString *)event +{ + return (id)[NSNull null]; +} + +- (instancetype)initWithRects:(NSArray *)rects +{ + return [self initWithRects:rects targetLayer:nil]; +} + +- (instancetype)initWithRects:(NSArray *)rects targetLayer:(id)targetLayer +{ + if (self = [super init]) { + _rects = [rects copy]; + _targetLayer = targetLayer; + } + return self; +} + +@dynamic highlightColor; + +- (void)drawInContext:(CGContextRef)ctx +{ + [super drawInContext:ctx]; + + CGAffineTransform affine = CGAffineTransformIdentity; + CGMutablePathRef highlightPath = CGPathCreateMutable(); + CALayer *targetLayer = self.targetLayer; + + for (NSValue *value in _rects) { + CGRect rect = [value CGRectValue]; + + // Don't highlight empty rects. + if (CGRectIsEmpty(rect)) { + continue; + } + + if (targetLayer != nil) { + rect = [self convertRect:rect fromLayer:targetLayer]; + } + rect = CGRectMake(std::round(rect.origin.x), std::round(rect.origin.y), std::round(rect.size.width), std::round(rect.size.height)); + + CGFloat minX = rect.origin.x - padding.left; + CGFloat maxX = CGRectGetMaxX(rect) + padding.right; + CGFloat midX = (maxX - minX) / 2 + minX; + CGFloat minY = rect.origin.y - padding.top; + CGFloat maxY = CGRectGetMaxY(rect) + padding.bottom; + CGFloat midY = (maxY - minY) / 2 + minY; + + CGPathMoveToPoint(highlightPath, &affine, minX, midY); + CGPathAddArcToPoint(highlightPath, &affine, minX, maxY, midX, maxY, kCornerRadius); + CGPathAddArcToPoint(highlightPath, &affine, maxX, maxY, maxX, midY, kCornerRadius); + CGPathAddArcToPoint(highlightPath, &affine, maxX, minY, midX, minY, kCornerRadius); + CGPathAddArcToPoint(highlightPath, &affine, minX, minY, minX, midY, kCornerRadius); + CGPathCloseSubpath(highlightPath); + } + + CGContextAddPath(ctx, highlightPath); + CGContextSetFillColorWithColor(ctx, self.highlightColor); + CGContextDrawPath(ctx, kCGPathFill); + CGPathRelease(highlightPath); +} + +- (CALayer *)hitTest:(CGPoint)p +{ + // Don't handle taps + return nil; +} + +@end + +@implementation CALayer (ASHighlightOverlayLayerSupport) + +static NSString *kAllowsHighlightDrawingKey = @"allows_highlight_drawing"; + +- (BOOL)as_allowsHighlightDrawing +{ + return [[self valueForKey:kAllowsHighlightDrawingKey] boolValue]; +} + +- (void)as_setAllowsHighlightDrawing:(BOOL)allowsHighlightDrawing +{ + [self setValue:@(allowsHighlightDrawing) forKey:kAllowsHighlightDrawingKey]; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASImageContainerProtocolCategories.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASImageContainerProtocolCategories.h new file mode 100644 index 0000000..44ddc35 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASImageContainerProtocolCategories.h @@ -0,0 +1,19 @@ +// +// ASImageContainerProtocolCategories.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +@interface UIImage (ASImageContainerProtocol) + +@end + +@interface NSData (ASImageContainerProtocol) + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASImageContainerProtocolCategories.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASImageContainerProtocolCategories.mm new file mode 100644 index 0000000..c9316c3 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASImageContainerProtocolCategories.mm @@ -0,0 +1,38 @@ +// +// ASImageContainerProtocolCategories.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +@implementation UIImage (ASImageContainerProtocol) + +- (UIImage *)asdk_image +{ + return self; +} + +- (NSData *)asdk_animatedImageData +{ + return nil; +} + +@end + +@implementation NSData (ASImageContainerProtocol) + +- (UIImage *)asdk_image +{ + return nil; +} + +- (NSData *)asdk_animatedImageData +{ + return self; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASImageProtocols.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASImageProtocols.h new file mode 100644 index 0000000..d91b8a7 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASImageProtocols.h @@ -0,0 +1,245 @@ +// +// ASImageProtocols.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@protocol ASAnimatedImageProtocol; + +@protocol ASImageContainerProtocol + +- (nullable UIImage *)asdk_image; +- (nullable NSData *)asdk_animatedImageData; + +@end + +typedef NS_ENUM(NSInteger, ASImageCacheType) { + ASImageCacheTypeAsynchronous = 0, + ASImageCacheTypeSynchronous, +}; + +typedef void(^ASImageCacherCompletion)(id _Nullable imageFromCache, ASImageCacheType cacheType); + +@protocol ASImageCacheProtocol + +/** + @abstract Attempts to fetch an image with the given URL from the cache. + @param URL The URL of the image to retrieve from the cache. + @param callbackQueue The queue to call `completion` on. + @param completion The block to be called when the cache has either hit or missed. + @discussion If `URL` is nil, `completion` will be invoked immediately with a nil image. This method should not block + the calling thread as it is likely to be called from the main thread. + */ +- (void)cachedImageWithURL:(NSURL *)URL + callbackQueue:(dispatch_queue_t)callbackQueue + completion:(ASImageCacherCompletion)completion; + +@optional + +/** + @abstract Attempts to fetch an image with the given URL from a memory cache. + @param URL The URL of the image to retrieve from the cache. + @discussion This method exists to support synchronous rendering of nodes. Before the layer is drawn, this method + is called to attempt to get the image out of the cache synchronously. This allows drawing to occur on the main thread + if displaysAsynchronously is set to NO or recursivelyEnsureDisplaySynchronously: has been called. + + This method *should* block the calling thread to fetch the image from a fast memory cache. It is OK to return nil from + this method and instead support only cachedImageWithURL:callbackQueue:completion: however, synchronous rendering will + not be possible. + */ +- (nullable id )synchronouslyFetchedCachedImageWithURL:(NSURL *)URL; + +/** + @abstract Called during clearPreloadedData. Allows the cache to optionally trim items. + @note Depending on your caches implementation you may *not* wish to respond to this method. It is however useful + if you have a memory and disk cache in which case you'll likely want to clear out the memory cache. + */ +- (void)clearFetchedImageFromCacheWithURL:(NSURL *)URL; + +@end + +/** + @param image The image that was downloaded, if the image could be successfully downloaded; nil otherwise. + @param error An error describing why the download of `URL` failed, if the download failed; nil otherwise. + @param downloadIdentifier The identifier for the download task that completed. + @param userInfo Any additional info that your downloader would like to communicate through Texture. + */ +typedef void(^ASImageDownloaderCompletion)(id _Nullable image, NSError * _Nullable error, id _Nullable downloadIdentifier, id _Nullable userInfo); + +/** + @param progress The progress of the download, in the range of (0.0, 1.0), inclusive. + */ +typedef void(^ASImageDownloaderProgress)(CGFloat progress); +typedef void(^ASImageDownloaderProgressImage)(UIImage *progressImage, CGFloat progress, id _Nullable downloadIdentifier); + +typedef NS_ENUM(NSUInteger, ASImageDownloaderPriority) { + ASImageDownloaderPriorityPreload = 0, + ASImageDownloaderPriorityImminent, + ASImageDownloaderPriorityVisible +}; + +@protocol ASImageDownloaderProtocol + +@required + +/** + @abstract Downloads an image with the given URL. + @param URL The URL of the image to download. + @param callbackQueue The queue to call `downloadProgressBlock` and `completion` on. + @param downloadProgress The block to be invoked when the download of `URL` progresses. + @param completion The block to be invoked when the download has completed, or has failed. + @discussion This method is likely to be called on the main thread, so any custom implementations should make sure to background any expensive download operations. + @result An opaque identifier to be used in canceling the download, via `cancelImageDownloadForIdentifier:`. You must + retain the identifier if you wish to use it later. + */ +- (nullable id)downloadImageWithURL:(NSURL *)URL + callbackQueue:(dispatch_queue_t)callbackQueue + downloadProgress:(nullable ASImageDownloaderProgress)downloadProgress + completion:(ASImageDownloaderCompletion)completion; + +/** + @abstract Cancels an image download. + @param downloadIdentifier The opaque download identifier object returned from + `downloadImageWithURL:callbackQueue:downloadProgress:completion:`. + @discussion This method has no effect if `downloadIdentifier` is nil. + */ +- (void)cancelImageDownloadForIdentifier:(id)downloadIdentifier; + +@optional + +/** + @abstract Downloads an image with the given URL. + @param URL The URL of the image to download. + @param priority The priority at which the image should be downloaded. + @param callbackQueue The queue to call `downloadProgressBlock` and `completion` on. + @param downloadProgress The block to be invoked when the download of `URL` progresses. + @param completion The block to be invoked when the download has completed, or has failed. + @discussion This method is likely to be called on the main thread, so any custom implementations should make sure to background any expensive download operations. + @note If this method is implemented, it will be called instead of the required method (`downloadImageWithURL:callbackQueue:downloadProgress:completion:`). + @result An opaque identifier to be used in canceling the download, via `cancelImageDownloadForIdentifier:`. You must + retain the identifier if you wish to use it later. + */ +- (nullable id)downloadImageWithURL:(NSURL *)URL + priority:(ASImageDownloaderPriority)priority + callbackQueue:(dispatch_queue_t)callbackQueue + downloadProgress:(nullable ASImageDownloaderProgress)downloadProgress + completion:(ASImageDownloaderCompletion)completion; + +/** + @abstract Cancels an image download, however indicating resume data should be stored in case of redownload. + @param downloadIdentifier The opaque download identifier object returned from + `downloadImageWithURL:callbackQueue:downloadProgress:completion:`. + @discussion This method has no effect if `downloadIdentifier` is nil. If implemented, this method + may be called instead of `cancelImageDownloadForIdentifier:` in cases where ASDK believes there's a chance + the image download will be resumed (currently when an image exits preload range). You can use this to store + any data that has already been downloaded for use in resuming the download later. + */ +- (void)cancelImageDownloadWithResumePossibilityForIdentifier:(id)downloadIdentifier; + +/** + @abstract Return an object that conforms to ASAnimatedImageProtocol + @param animatedImageData Data that represents an animated image. + */ +- (nullable id )animatedImageWithData:(NSData *)animatedImageData; + + +/** + @abstract Sets block to be called when a progress image is available. + @param progressBlock The block to be invoked when the download has a progressive render of an image available. + @param callbackQueue The queue to call `progressImageBlock` on. + @param downloadIdentifier The opaque download identifier object returned from + `downloadImageWithURL:callbackQueue:downloadProgressBlock:completion:`. + */ +- (void)setProgressImageBlock:(nullable ASImageDownloaderProgressImage)progressBlock + callbackQueue:(dispatch_queue_t)callbackQueue + withDownloadIdentifier:(id)downloadIdentifier; + +/** + @abstract Called to indicate what priority an image should be downloaded at. + @param priority The priority at which the image should be downloaded. + @param downloadIdentifier The opaque download identifier object returned from + `downloadImageWithURL:callbackQueue:downloadProgressBlock:completion:`. + */ +- (void)setPriority:(ASImageDownloaderPriority)priority +withDownloadIdentifier:(id)downloadIdentifier; + +@end + +@protocol ASAnimatedImageProtocol + +@optional + +/** + @abstract A block which receives the cover image. Should be called when the objects cover image is ready. + */ +@property (nonatomic) void (^coverImageReadyCallback)(UIImage *coverImage); + +/** + @abstract Returns whether the supplied data contains a supported animated image format. + @param data the data to check if contains a supported animated image. + */ +- (BOOL)isDataSupported:(NSData *)data; + + +@required + +/** + @abstract Return the objects's cover image. + */ +@property (nonatomic, readonly) UIImage *coverImage; +/** + @abstract Return a boolean to indicate that the cover image is ready. + */ +@property (nonatomic, readonly) BOOL coverImageReady; +/** + @abstract Return the total duration of the animated image's playback. + */ +@property (nonatomic, readonly) CFTimeInterval totalDuration; +/** + @abstract Return the interval at which playback should occur. Will be set to a CADisplayLink's frame interval. + */ +@property (nonatomic, readonly) NSUInteger frameInterval; +/** + @abstract Return the total number of loops the animated image should play or 0 to loop infinitely. + */ +@property (nonatomic, readonly) size_t loopCount; +/** + @abstract Return the total number of frames in the animated image. + */ +@property (nonatomic, readonly) size_t frameCount; +/** + @abstract Return YES when playback is ready to occur. + */ +@property (nonatomic, readonly) BOOL playbackReady; +/** + @abstract Return any error that has occured. Playback will be paused if this returns non-nil. + */ +@property (nonatomic, readonly) NSError *error; +/** + @abstract Should be called when playback is ready. + */ +@property (nonatomic) dispatch_block_t playbackReadyCallback; + +/** + @abstract Return the image at a given index. + */ +- (CGImageRef)imageAtIndex:(NSUInteger)index; +/** + @abstract Return the duration at a given index. + */ +- (CFTimeInterval)durationAtIndex:(NSUInteger)index; +/** + @abstract Clear any cached data. Called when playback is paused. + */ +- (void)clearAnimatedImageCache; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASIntegerMap.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASIntegerMap.h new file mode 100644 index 0000000..ece6f5a --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASIntegerMap.h @@ -0,0 +1,68 @@ +// +// ASIntegerMap.h +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * An objective-C wrapper for unordered_map. + */ +AS_SUBCLASSING_RESTRICTED +@interface ASIntegerMap : NSObject + +/** + * Creates a map based on the specified update to an array. + * + * If oldCount is 0, returns the empty map. + * If deleted and inserted are empty, returns the identity map. + */ ++ (ASIntegerMap *)mapForUpdateWithOldCount:(NSInteger)oldCount + deleted:(nullable NSIndexSet *)deleted + inserted:(nullable NSIndexSet *)inserted NS_RETURNS_RETAINED; + +/** + * A singleton that maps each integer to itself. Its inverse is itself. + * + * Note: You cannot mutate this. + */ +@property (class, readonly) ASIntegerMap *identityMap; ++ (ASIntegerMap *)identityMap NS_RETURNS_RETAINED; + +/** + * A singleton that returns NSNotFound for all keys. Its inverse is itself. + * + * Note: You cannot mutate this. + */ +@property (class, readonly) ASIntegerMap *emptyMap; ++ (ASIntegerMap *)emptyMap NS_RETURNS_RETAINED; + +/** + * Retrieves the integer for a given key, or NSNotFound if the key is not found. + * + * @param key A key to lookup the value for. + */ +- (NSInteger)integerForKey:(NSInteger)key; + +/** + * Sets the value for a given key. + * + * @param value The new value. + * @param key The key to store the value for. + */ +- (void)setInteger:(NSInteger)value forKey:(NSInteger)key; + +/** + * Create and return a map with the inverse mapping. + */ +- (ASIntegerMap *)inverseMap; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASIntegerMap.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASIntegerMap.mm new file mode 100644 index 0000000..fdf0d52 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASIntegerMap.mm @@ -0,0 +1,184 @@ +// +// ASIntegerMap.mm +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import "ASIntegerMap.h" +#import +#import +#import + +/** + * This is just a friendly Objective-C interface to unordered_map + */ +@interface ASIntegerMap () +@end + +@implementation ASIntegerMap { + std::unordered_map _map; + BOOL _isIdentity; + BOOL _isEmpty; + BOOL _immutable; // identity map and empty mape are immutable. +} + +#pragma mark - Singleton + ++ (ASIntegerMap *)identityMap NS_RETURNS_RETAINED +{ + static ASIntegerMap *identityMap; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + identityMap = [[ASIntegerMap alloc] init]; + identityMap->_isIdentity = YES; + identityMap->_immutable = YES; + }); + return identityMap; +} + ++ (ASIntegerMap *)emptyMap NS_RETURNS_RETAINED +{ + static ASIntegerMap *emptyMap; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + emptyMap = [[ASIntegerMap alloc] init]; + emptyMap->_isEmpty = YES; + emptyMap->_immutable = YES; + }); + return emptyMap; +} + ++ (ASIntegerMap *)mapForUpdateWithOldCount:(NSInteger)oldCount deleted:(NSIndexSet *)deletions inserted:(NSIndexSet *)insertions NS_RETURNS_RETAINED +{ + if (oldCount == 0) { + return ASIntegerMap.emptyMap; + } + + if (deletions.count == 0 && insertions.count == 0) { + return ASIntegerMap.identityMap; + } + + ASIntegerMap *result = [[ASIntegerMap alloc] init]; + // Start with the old indexes + NSMutableIndexSet *indexes = [NSMutableIndexSet indexSetWithIndexesInRange:NSMakeRange(0, oldCount)]; + + // Descending order, shift deleted ranges left + [deletions enumerateRangesWithOptions:NSEnumerationReverse usingBlock:^(NSRange range, BOOL * _Nonnull stop) { + [indexes shiftIndexesStartingAtIndex:NSMaxRange(range) by:-range.length]; + }]; + + // Ascending order, shift inserted ranges right + [insertions enumerateRangesUsingBlock:^(NSRange range, BOOL * _Nonnull stop) { + [indexes shiftIndexesStartingAtIndex:range.location by:range.length]; + }]; + + __block NSInteger oldIndex = 0; + [indexes enumerateRangesUsingBlock:^(NSRange range, BOOL * _Nonnull stop) { + // Note we advance oldIndex unconditionally, not newIndex + for (NSInteger newIndex = range.location; newIndex < NSMaxRange(range); oldIndex++) { + if ([deletions containsIndex:oldIndex]) { + // index was deleted, do nothing, just let oldIndex advance. + } else { + // assign the next index for this item. + result->_map[oldIndex] = newIndex++; + } + } + }]; + return result; +} + +- (NSInteger)integerForKey:(NSInteger)key +{ + if (_isIdentity) { + return key; + } else if (_isEmpty) { + return NSNotFound; + } + + const auto result = _map.find(key); + return result != _map.end() ? result->second : NSNotFound; +} + +- (void)setInteger:(NSInteger)value forKey:(NSInteger)key +{ + if (_immutable) { + ASDisplayNodeFailAssert(@"Cannot mutate special integer map: %@", self); + return; + } + + _map[key] = value; +} + +- (ASIntegerMap *)inverseMap +{ + if (_isIdentity || _isEmpty) { + return self; + } + + const auto result = [[ASIntegerMap alloc] init]; + + for (const auto &e : _map) { + result->_map[e.second] = e.first; + } + return result; +} + +#pragma mark - NSCopying + +- (id)copyWithZone:(NSZone *)zone +{ + if (_immutable) { + return self; + } + + const auto newMap = [[ASIntegerMap allocWithZone:zone] init]; + newMap->_map = _map; + return newMap; +} + +#pragma mark - Description + +- (NSMutableArray *)propertiesForDescription +{ + NSMutableArray *result = [NSMutableArray array]; + + if (_isIdentity) { + [result addObject:@{ @"map": @"" }]; + } else if (_isEmpty) { + [result addObject:@{ @"map": @"" }]; + } else { + // { 1->2 3->4 5->6 } + NSMutableString *str = [NSMutableString string]; + for (const auto &e : _map) { + [str appendFormat:@" %ld->%ld", (long)e.first, (long)e.second]; + } + // Remove leading space + if (str.length > 0) { + [str deleteCharactersInRange:NSMakeRange(0, 1)]; + } + [result addObject:@{ @"map": str }]; + } + + return result; +} + +- (NSString *)description +{ + return ASObjectDescriptionMakeWithoutObject([self propertiesForDescription]); +} + +- (BOOL)isEqual:(id)object +{ + if ([super isEqual:object]) { + return YES; + } + + if (ASIntegerMap *otherMap = ASDynamicCast(object, ASIntegerMap)) { + return otherMap->_map == _map; + } + return NO; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASLayoutController.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASLayoutController.h new file mode 100644 index 0000000..d0e2f60 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASLayoutController.h @@ -0,0 +1,40 @@ +// +// ASLayoutController.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@class ASCollectionElement, ASElementMap; + +struct ASDirectionalScreenfulBuffer { + CGFloat positiveDirection; // Positive relative to iOS Core Animation layer coordinate space. + CGFloat negativeDirection; +}; +typedef struct ASDirectionalScreenfulBuffer ASDirectionalScreenfulBuffer; + +@protocol ASLayoutController + +- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType; + +- (ASRangeTuningParameters)tuningParametersForRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType; + +- (NSHashTable *)elementsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType map:(ASElementMap *)map; + +- (void)allElementsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode displaySet:(NSHashTable * _Nullable * _Nullable)displaySet preloadSet:(NSHashTable * _Nullable * _Nullable)preloadSet map:(ASElementMap *)map; + +@optional + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASLayoutRangeType.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASLayoutRangeType.h new file mode 100644 index 0000000..1a60e29 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASLayoutRangeType.h @@ -0,0 +1,68 @@ +// +// ASLayoutRangeType.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +typedef struct { + CGFloat leadingBufferScreenfuls; + CGFloat trailingBufferScreenfuls; +} ASRangeTuningParameters; + +ASDK_EXTERN ASRangeTuningParameters const ASRangeTuningParametersZero; + +ASDK_EXTERN BOOL ASRangeTuningParametersEqualToRangeTuningParameters(ASRangeTuningParameters lhs, ASRangeTuningParameters rhs); + +/** + * Each mode has a complete set of tuning parameters for range types. + * Depending on some conditions (including interface state and direction of the scroll view, state of rendering engine, etc), + * a range controller can choose which mode it should use at a given time. + */ +typedef NS_ENUM(char, ASLayoutRangeMode) { + ASLayoutRangeModeUnspecified = -1, + + /** + * Minimum mode is used when a range controller should limit the amount of work it performs. + * Thus, fewer views/layers are created and less data is fetched, saving system resources. + * Range controller can automatically switch to full mode when conditions change. + */ + ASLayoutRangeModeMinimum = 0, + + /** + * Normal/Full mode that a range controller uses to provide the best experience for end users. + * This mode is usually used for an active scroll view. + * A range controller under this requires more resources compare to minimum mode. + */ + ASLayoutRangeModeFull, + + /** + * Visible Only mode is used when a range controller should set its display and preload regions to only the size of their bounds. + * This causes all additional backing stores & preloaded data to be released, while ensuring a user revisiting the view will + * still be able to see the expected content. This mode is automatically set on all ASRangeControllers when the app suspends, + * allowing the operating system to keep the app alive longer and increase the chance it is still warm when the user returns. + */ + ASLayoutRangeModeVisibleOnly, + + /** + * Low Memory mode is used when a range controller should discard ALL graphics buffers, including for the area that would be visible + * the next time the user views it (bounds). The only range it preserves is Preload, which is limited to the bounds, allowing + * the content to be restored relatively quickly by re-decoding images (the compressed images are ~10% the size of the decoded ones, + * and text is a tiny fraction of its rendered size). + */ + ASLayoutRangeModeLowMemory +}; + +static NSInteger const ASLayoutRangeModeCount = 4; + +typedef NS_ENUM(NSInteger, ASLayoutRangeType) { + ASLayoutRangeTypeDisplay, + ASLayoutRangeTypePreload +}; + +static NSInteger const ASLayoutRangeTypeCount = 2; diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASMainSerialQueue.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASMainSerialQueue.h new file mode 100644 index 0000000..405164f --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASMainSerialQueue.h @@ -0,0 +1,19 @@ +// +// ASMainSerialQueue.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +AS_SUBCLASSING_RESTRICTED +@interface ASMainSerialQueue : NSObject + +@property (nonatomic, readonly) NSUInteger numberOfScheduledBlocks; +- (void)performBlockOnMainThread:(dispatch_block_t)block; + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASMainSerialQueue.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASMainSerialQueue.mm new file mode 100644 index 0000000..ba2c743 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASMainSerialQueue.mm @@ -0,0 +1,76 @@ +// +// ASMainSerialQueue.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import +#import +#import + +@interface ASMainSerialQueue () +{ + AS::Mutex _serialQueueLock; + std::queue _blocks; +} + +@end + +@implementation ASMainSerialQueue + +- (NSUInteger)numberOfScheduledBlocks +{ + AS::MutexLocker l(_serialQueueLock); + return _blocks.size(); +} + +- (void)performBlockOnMainThread:(dispatch_block_t)block +{ + { + AS::MutexLocker l(_serialQueueLock); + _blocks.push(block); + } + + [self runBlocks]; +} + +- (void)runBlocks +{ + dispatch_block_t mainThread = ^{ + AS::UniqueLock l(self->_serialQueueLock); + do { + if (self->_blocks.empty()) { + break; + } + dispatch_block_t block = self->_blocks.front(); + self->_blocks.pop(); + { + l.unlock(); + block(); + l.lock(); + } + } while (true); + }; + + ASPerformBlockOnMainThread(mainThread); +} + +- (NSString *)description +{ + NSString *desc = [super description]; + std::queue blocks = _blocks; + [desc stringByAppendingString:@" Blocks: "]; + while (!blocks.empty()) { + dispatch_block_t block = blocks.front(); + [desc stringByAppendingFormat:@"%@", block]; + blocks.pop(); + } + return desc; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASMutableAttributedStringBuilder.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASMutableAttributedStringBuilder.h new file mode 100644 index 0000000..d531549 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASMutableAttributedStringBuilder.h @@ -0,0 +1,65 @@ +// +// ASMutableAttributedStringBuilder.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +/* + * Use this class to compose new attributed strings. You may use the normal + * attributed string calls on this the same way you would on a normal mutable + * attributed string, but it coalesces your changes into transactions on the + * actual string allowing improvements in performance. + * + * @discussion This is a use-once and throw away class for each string you make. + * Since this class is designed for increasing performance, we actually hand + * back the internally managed mutable attributed string in the + * `composedAttributedString` call. So once you make that call, any more + * changes will actually modify the string that was handed back to you in that + * method. + * + * Combination of multiple calls into single attribution is managed through + * merging of attribute dictionaries over ranges. For best performance, call + * collections of attributions over a single range together. So for instance, + * don't call addAttributes for range1, then range2, then range1 again. Group + * them together so you call addAttributes for both range1 together, and then + * range2. + * + * Also please note that switching between addAttribute and setAttributes in the + * middle of composition is a bad idea for performance because they have + * semantically different meanings, and trigger a commit of the pending + * attributes. + * + * Please note that ALL of the standard NSString methods are left unimplemented. + */ +AS_SUBCLASSING_RESTRICTED +@interface ASMutableAttributedStringBuilder : NSMutableAttributedString + +- (instancetype)initWithString:(NSString *)str attributes:(nullable NSDictionary *)attrs; +- (instancetype)initWithAttributedString:(NSAttributedString *)attrStr; + +- (void)replaceCharactersInRange:(NSRange)range withString:(NSString *)str; +- (void)setAttributes:(nullable NSDictionary *)attrs range:(NSRange)range; + +- (void)addAttribute:(NSString *)name value:(id)value range:(NSRange)range; +- (void)addAttributes:(NSDictionary *)attrs range:(NSRange)range; +- (void)removeAttribute:(NSString *)name range:(NSRange)range; + +- (void)replaceCharactersInRange:(NSRange)range withAttributedString:(NSAttributedString *)attrString; +- (void)insertAttributedString:(NSAttributedString *)attrString atIndex:(NSUInteger)loc; +- (void)appendAttributedString:(NSAttributedString *)attrString; +- (void)deleteCharactersInRange:(NSRange)range; +- (void)setAttributedString:(NSAttributedString *)attrString; + +- (NSMutableAttributedString *)composedAttributedString; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASMutableAttributedStringBuilder.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASMutableAttributedStringBuilder.mm new file mode 100644 index 0000000..b393fe1 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASMutableAttributedStringBuilder.mm @@ -0,0 +1,254 @@ +// +// ASMutableAttributedStringBuilder.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +@implementation ASMutableAttributedStringBuilder { + // Flag for the type of the current transaction (set or add) + BOOL _setRange; + // The range over which the currently pending transaction will occur + NSRange _pendingRange; + // The actual attribute dictionary that is being composed + NSMutableDictionary *_pendingRangeAttributes; + NSMutableAttributedString *_attrStr; + + // We delay initialization of the _attrStr until we need to + NSString *_initString; +} + +- (instancetype)init +{ + if (self = [super init]) { + _attrStr = [[NSMutableAttributedString alloc] init]; + _pendingRange.location = NSNotFound; + } + return self; +} + +- (instancetype)initWithString:(NSString *)str +{ + return [self initWithString:str attributes:@{}]; +} + +- (instancetype)initWithString:(NSString *)str attributes:(NSDictionary *)attrs +{ + if (self = [super init]) { + // We cache this in an ivar that we can lazily construct the attributed + // string with when we get to a forced commit point. + _initString = str; + // Triggers a creation of the _pendingRangeAttributes dictionary which then + // is filled with entries from the given attrs dict. + [[self _pendingRangeAttributes] addEntriesFromDictionary:attrs]; + _setRange = NO; + _pendingRange = NSMakeRange(0, _initString.length); + } + return self; +} + +- (instancetype)initWithAttributedString:(NSAttributedString *)attrStr +{ + if (self = [super init]) { + _attrStr = [[NSMutableAttributedString alloc] initWithAttributedString:attrStr]; + _pendingRange.location = NSNotFound; + } + return self; +} + +- (NSMutableAttributedString *)_attributedString +{ + if (_attrStr == nil && _initString != nil) { + // We can lazily construct the attributed string if it hasn't already been + // created with the existing pending attributes. This is significantly + // faster if more attributes are added after initializing this instance + // and the new attributions are for the entire string anyway. + _attrStr = [[NSMutableAttributedString alloc] initWithString:_initString attributes:_pendingRangeAttributes]; + _pendingRangeAttributes = nil; + _pendingRange.location = NSNotFound; + _initString = nil; + } + + return _attrStr; +} + +#pragma mark - Pending attribution + +- (NSMutableDictionary *)_pendingRangeAttributes +{ + // Lazy dictionary creation. Call this if you want to force initialization, + // otherwise just use the ivar. + if (_pendingRangeAttributes == nil) { + _pendingRangeAttributes = [[NSMutableDictionary alloc] init]; + } + return _pendingRangeAttributes; +} + +- (void)_applyPendingRangeAttributions +{ + if (_attrStr == nil) { + // Trigger its creation if it doesn't exist. + [self _attributedString]; + } + + if (_pendingRangeAttributes.count == 0) { + return; + } + + if (_pendingRange.location == NSNotFound) { + return; + } + + if (_setRange) { + [[self _attributedString] setAttributes:_pendingRangeAttributes range:_pendingRange]; + } else { + [[self _attributedString] addAttributes:_pendingRangeAttributes range:_pendingRange]; + } + _pendingRangeAttributes = nil; + _pendingRange.location = NSNotFound; +} + +#pragma mark - Editing + +- (void)replaceCharactersInRange:(NSRange)range withString:(NSString *)str +{ + [self _applyPendingRangeAttributions]; + [[self _attributedString] replaceCharactersInRange:range withString:str]; +} + +- (void)replaceCharactersInRange:(NSRange)range withAttributedString:(NSAttributedString *)attrString +{ + [self _applyPendingRangeAttributions]; + [[self _attributedString] replaceCharactersInRange:range withAttributedString:attrString]; +} + +- (void)addAttribute:(NSString *)name value:(id)value range:(NSRange)range +{ + if (_setRange) { + [self _applyPendingRangeAttributions]; + _setRange = NO; + } + + if (!NSEqualRanges(_pendingRange, range)) { + [self _applyPendingRangeAttributions]; + _pendingRange = range; + } + + NSMutableDictionary *pendingAttributes = [self _pendingRangeAttributes]; + pendingAttributes[name] = value; +} + +- (void)addAttributes:(NSDictionary *)attrs range:(NSRange)range +{ + if (_setRange) { + [self _applyPendingRangeAttributions]; + _setRange = NO; + } + + if (!NSEqualRanges(_pendingRange, range)) { + [self _applyPendingRangeAttributions]; + _pendingRange = range; + } + + NSMutableDictionary *pendingAttributes = [self _pendingRangeAttributes]; + [pendingAttributes addEntriesFromDictionary:attrs]; +} + +- (void)insertAttributedString:(NSAttributedString *)attrString atIndex:(NSUInteger)loc +{ + [self _applyPendingRangeAttributions]; + [[self _attributedString] insertAttributedString:attrString atIndex:loc]; +} + +- (void)appendAttributedString:(NSAttributedString *)attrString +{ + [self _applyPendingRangeAttributions]; + [[self _attributedString] appendAttributedString:attrString]; +} + +- (void)deleteCharactersInRange:(NSRange)range +{ + [self _applyPendingRangeAttributions]; + [[self _attributedString] deleteCharactersInRange:range]; +} + +- (void)setAttributedString:(NSAttributedString *)attrString +{ + [self _applyPendingRangeAttributions]; + [[self _attributedString] setAttributedString:attrString]; +} + +- (void)setAttributes:(NSDictionary *)attrs range:(NSRange)range +{ + if (!_setRange) { + [self _applyPendingRangeAttributions]; + _setRange = YES; + } + + if (!NSEqualRanges(_pendingRange, range)) { + [self _applyPendingRangeAttributions]; + _pendingRange = range; + } + + NSMutableDictionary *pendingAttributes = [self _pendingRangeAttributes]; + [pendingAttributes addEntriesFromDictionary:attrs]; +} + +- (void)removeAttribute:(NSString *)name range:(NSRange)range +{ + // This call looks like the other set/add functions, but in order for this + // function to perform as advertised we MUST first add the attributes we + // currently have pending. + [self _applyPendingRangeAttributions]; + + [[self _attributedString] removeAttribute:name range:range]; +} + +#pragma mark - Output + +- (NSMutableAttributedString *)composedAttributedString +{ + if (_pendingRangeAttributes.count > 0) { + [self _applyPendingRangeAttributions]; + } + return [self _attributedString]; +} + +#pragma mark - Forwarding + +- (NSUInteger)length +{ + // If we just want a length call, no need to lazily construct the attributed string + return _attrStr ? _attrStr.length : _initString.length; +} + +- (NSDictionary *)attributesAtIndex:(NSUInteger)location effectiveRange:(NSRangePointer)range +{ + return [[self _attributedString] attributesAtIndex:location effectiveRange:range]; +} + +- (NSString *)string +{ + return _attrStr ? _attrStr.string : _initString; +} + +- (NSMutableString *)mutableString +{ + return [[self _attributedString] mutableString]; +} + +- (void)beginEditing +{ + [[self _attributedString] beginEditing]; +} + +- (void)endEditing +{ + [[self _attributedString] endEditing]; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASObjectDescriptionHelpers.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASObjectDescriptionHelpers.h new file mode 100644 index 0000000..beaf889 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASObjectDescriptionHelpers.h @@ -0,0 +1,65 @@ +// +// ASObjectDescriptionHelpers.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@protocol ASDebugNameProvider + +@required +/** + * @abstract Name that is printed by ascii art string and displayed in description. + */ +@property (nullable, nonatomic, copy) NSString *debugName; + +@end + +/** + * Your base class should conform to this and override `-debugDescription` + * to call `[self propertiesForDebugDescription]` and use `ASObjectDescriptionMake` + * to return a string. Subclasses of this base class just need to override + * `propertiesForDebugDescription`, call super, and modify the result as needed. + */ +@protocol ASDebugDescriptionProvider +@required +- (NSMutableArray *)propertiesForDebugDescription; +@end + +/** + * Your base class should conform to this and override `-description` + * to call `[self propertiesForDescription]` and use `ASObjectDescriptionMake` + * to return a string. Subclasses of this base class just need to override + * `propertiesForDescription`, call super, and modify the result as needed. + */ +@protocol ASDescriptionProvider +@required +- (NSMutableArray *)propertiesForDescription; +@end + +ASDK_EXTERN NSString *ASGetDescriptionValueString(id object); + +/// Useful for structs etc. Returns e.g. { position = (0 0); frame = (0 0; 50 50) } +ASDK_EXTERN NSString *ASObjectDescriptionMakeWithoutObject(NSArray * _Nullable propertyGroups); + +/// Returns e.g. +ASDK_EXTERN NSString *ASObjectDescriptionMake(__autoreleasing id object, NSArray * _Nullable propertyGroups); + +/** + * Returns e.g. + * + * Note: `object` param is autoreleasing so that this function is dealloc-safe. + * No, unsafe_unretained isn't acceptable here – the optimizer may deallocate object early. + */ +ASDK_EXTERN NSString *ASObjectDescriptionMakeTiny(__autoreleasing id _Nullable object); + +ASDK_EXTERN NSString * _Nullable ASStringWithQuotesIfMultiword(NSString * _Nullable string); + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASObjectDescriptionHelpers.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASObjectDescriptionHelpers.mm new file mode 100644 index 0000000..e0b7c21 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASObjectDescriptionHelpers.mm @@ -0,0 +1,104 @@ +// +// ASObjectDescriptionHelpers.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import + +#import + +NSString *ASGetDescriptionValueString(id object) +{ + if ([object isKindOfClass:[NSValue class]]) { + // Use shortened NSValue descriptions + NSValue *value = object; + const char *type = value.objCType; + + if (strcmp(type, @encode(CGRect)) == 0) { + CGRect rect = [value CGRectValue]; + return [NSString stringWithFormat:@"(%g %g; %g %g)", rect.origin.x, rect.origin.y, rect.size.width, rect.size.height]; + } else if (strcmp(type, @encode(CGSize)) == 0) { + return NSStringFromCGSize(value.CGSizeValue); + } else if (strcmp(type, @encode(CGPoint)) == 0) { + return NSStringFromCGPoint(value.CGPointValue); + } + + } else if ([object isKindOfClass:[NSIndexSet class]]) { + return [object as_smallDescription]; + } else if ([object isKindOfClass:[NSIndexPath class]]) { + // index paths like (0, 7) + NSIndexPath *indexPath = object; + NSMutableArray *strings = [NSMutableArray array]; + for (NSUInteger i = 0; i < indexPath.length; i++) { + [strings addObject:[NSString stringWithFormat:@"%lu", (unsigned long)[indexPath indexAtPosition:i]]]; + } + return [NSString stringWithFormat:@"(%@)", [strings componentsJoinedByString:@", "]]; + } else if ([object respondsToSelector:@selector(componentsJoinedByString:)]) { + // e.g. "[ ]" + return [NSString stringWithFormat:@"[ %@ ]", [object componentsJoinedByString:@" "]]; + } + return [object description]; +} + +NSString *_ASObjectDescriptionMakePropertyList(NSArray * _Nullable propertyGroups) +{ + NSMutableArray *components = [NSMutableArray array]; + for (NSDictionary *properties in propertyGroups) { + [properties enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) { + NSString *str; + if (key == (id)kCFNull) { + str = ASGetDescriptionValueString(obj); + } else { + str = [NSString stringWithFormat:@"%@ = %@", key, ASGetDescriptionValueString(obj)]; + } + [components addObject:str]; + }]; + } + return [components componentsJoinedByString:@"; "]; +} + +NSString *ASObjectDescriptionMakeWithoutObject(NSArray * _Nullable propertyGroups) +{ + return [NSString stringWithFormat:@"{ %@ }", _ASObjectDescriptionMakePropertyList(propertyGroups)]; +} + +NSString *ASObjectDescriptionMake(__autoreleasing id object, NSArray *propertyGroups) +{ + if (object == nil) { + return @"(null)"; + } + + NSMutableString *str = [NSMutableString stringWithFormat:@"<%s: %p", object_getClassName(object), object]; + + NSString *propList = _ASObjectDescriptionMakePropertyList(propertyGroups); + if (propList.length > 0) { + [str appendFormat:@"; %@", propList]; + } + [str appendString:@">"]; + return str; +} + +NSString *ASObjectDescriptionMakeTiny(__autoreleasing id object) { + static constexpr int kBufSize = 64; + char buf[kBufSize]; + int len = snprintf(buf, kBufSize, "<%s: %p>", object_getClassName(object), object); + return (__bridge_transfer NSString *)CFStringCreateWithBytes(NULL, (const UInt8 *)buf, len, kCFStringEncodingASCII, false); +} + +NSString *ASStringWithQuotesIfMultiword(NSString *string) { + if (string == nil) { + return nil; + } + + if ([string rangeOfCharacterFromSet:[NSCharacterSet whitespaceCharacterSet]].location != NSNotFound) { + return [NSString stringWithFormat:@"\"%@\"", string]; + } else { + return string; + } +} diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASPINRemoteImageDownloader.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASPINRemoteImageDownloader.h new file mode 100644 index 0000000..cbbe857 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASPINRemoteImageDownloader.h @@ -0,0 +1,80 @@ +// +// ASPINRemoteImageDownloader.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#if AS_PIN_REMOTE_IMAGE + +#import + +NS_ASSUME_NONNULL_BEGIN + +@class PINRemoteImageManager; +@protocol PINRemoteImageCaching; + +@interface ASPINRemoteImageDownloader : NSObject + +/** + * A shared image downloader which can be used by @c ASNetworkImageNodes and @c ASMultiplexImageNodes. + * The userInfo provided by this downloader is an instance of `PINRemoteImageManagerResult`. + * + * This is the default downloader used by network backed image nodes if PINRemoteImage and PINCache are + * available. It uses PINRemoteImage's features to provide caching and progressive image downloads. + */ ++ (ASPINRemoteImageDownloader *)sharedDownloader NS_RETURNS_RETAINED; + +/** + * Sets the default NSURLSessionConfiguration that will be used by @c ASNetworkImageNodes and @c ASMultiplexImageNodes + * while loading images off the network. This must be specified early in the application lifecycle before + * `sharedDownloader` is accessed. + * + * @param configuration The session configuration that will be used by `sharedDownloader` + * + */ ++ (void)setSharedImageManagerWithConfiguration:(nullable NSURLSessionConfiguration *)configuration; + +/** + * Sets the default NSURLSessionConfiguration that will be used by @c ASNetworkImageNodes and @c ASMultiplexImageNodes + * while loading images off the network. This must be specified early in the application lifecycle before + * `sharedDownloader` is accessed. + * + * @param configuration The session configuration that will be used by `sharedDownloader` + * @param imageCache The cache to be used by PINRemoteImage - nil will set up a default cache: PINCache + * if it is available, PINRemoteImageBasicCache (NSCache) if not. + * + */ ++ (void)setSharedImageManagerWithConfiguration:(nullable NSURLSessionConfiguration *)configuration + imageCache:(nullable id)imageCache; + +/** + * Sets a custom preconfigured PINRemoteImageManager that will be used by @c ASNetworkImageNodes and @c ASMultiplexImageNodes + * while loading images off the network. This must be specified early in the application lifecycle before + * `sharedDownloader` is accessed. + * + * @param preconfiguredPINRemoteImageManager The preconfigured remote image manager that will be used by `sharedDownloader` + */ ++ (void)setSharedPreconfiguredRemoteImageManager:(PINRemoteImageManager *)preconfiguredPINRemoteImageManager; + +/** + * The shared instance of a @c PINRemoteImageManager used by all @c ASPINRemoteImageDownloaders + * + * @discussion you can use this method to access the shared manager. This is useful to share a cache + * and resources if you need to download images outside of an @c ASNetworkImageNode or + * @c ASMultiplexImageNode. It's also useful to access the memoryCache and diskCache to set limits + * or handle authentication challenges. + * + * @return An instance of a @c PINRemoteImageManager + */ +- (PINRemoteImageManager *)sharedPINRemoteImageManager; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASPINRemoteImageDownloader.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASPINRemoteImageDownloader.mm new file mode 100644 index 0000000..df538f0 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASPINRemoteImageDownloader.mm @@ -0,0 +1,390 @@ +// +// ASPINRemoteImageDownloader.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#if AS_PIN_REMOTE_IMAGE +#import + +#import +#import +#import + +#if __has_include () +#define PIN_ANIMATED_AVAILABLE 1 +#import +#import +#else +#define PIN_ANIMATED_AVAILABLE 0 +#endif + +#if __has_include() +#define PIN_WEBP_AVAILABLE 1 +#else +#define PIN_WEBP_AVAILABLE 0 +#endif + +#import +#import +#import + +static inline PINRemoteImageManagerPriority PINRemoteImageManagerPriorityWithASImageDownloaderPriority(ASImageDownloaderPriority priority) { + switch (priority) { + case ASImageDownloaderPriorityPreload: + return PINRemoteImageManagerPriorityLow; + + case ASImageDownloaderPriorityImminent: + return PINRemoteImageManagerPriorityDefault; + + case ASImageDownloaderPriorityVisible: + return PINRemoteImageManagerPriorityHigh; + } +} + +#if PIN_ANIMATED_AVAILABLE + +@interface ASPINRemoteImageDownloader () +@end + +@interface PINCachedAnimatedImage (ASPINRemoteImageDownloader) +@end + +@implementation PINCachedAnimatedImage (ASPINRemoteImageDownloader) + +- (BOOL)isDataSupported:(NSData *)data +{ + if ([data pin_isGIF]) { + return YES; + } +#if PIN_WEBP_AVAILABLE + else if ([data pin_isAnimatedWebP]) { + return YES; + } +#endif + return NO; +} + +@end +#endif + +// Declare two key methods on PINCache objects, avoiding a direct dependency on PINCache.h +@protocol ASPINCache +- (id)diskCache; +@end + +@protocol ASPINDiskCache +@property NSUInteger byteLimit; +@end + +@interface ASPINRemoteImageManager : PINRemoteImageManager +@end + +@implementation ASPINRemoteImageManager + +//Share image cache with sharedImageManager image cache. ++ (id )defaultImageCache +{ + static dispatch_once_t onceToken; + static id cache = nil; + dispatch_once(&onceToken, ^{ + cache = [[PINRemoteImageManager sharedImageManager] cache]; + if ([cache respondsToSelector:@selector(diskCache)]) { + id diskCache = [(id )cache diskCache]; + if ([diskCache respondsToSelector:@selector(setByteLimit:)]) { + // Set a default byteLimit. PINCache recently implemented a 50MB default (PR #201). + // Ensure that older versions of PINCache also have a byteLimit applied. + // NOTE: Using 20MB limit while large cache initialization is being optimized (Issue #144). + ((id )diskCache).byteLimit = 20 * 1024 * 1024; + } + } + }); + return cache; +} + +@end + +static ASPINRemoteImageDownloader *sharedDownloader = nil; +static PINRemoteImageManager *sharedPINRemoteImageManager = nil; + +@interface ASPINRemoteImageDownloader () +@end + +@implementation ASPINRemoteImageDownloader + ++ (ASPINRemoteImageDownloader *)sharedDownloader NS_RETURNS_RETAINED +{ + static dispatch_once_t onceToken = 0; + dispatch_once(&onceToken, ^{ + sharedDownloader = [[ASPINRemoteImageDownloader alloc] init]; + }); + return sharedDownloader; +} + ++ (void)setSharedImageManagerWithConfiguration:(nullable NSURLSessionConfiguration *)configuration +{ + NSAssert(sharedDownloader == nil, @"Singleton has been created and session can no longer be configured."); + PINRemoteImageManager *sharedManager = [self PINRemoteImageManagerWithConfiguration:configuration imageCache:nil]; + [self setSharedPreconfiguredRemoteImageManager:sharedManager]; +} + ++ (void)setSharedImageManagerWithConfiguration:(nullable NSURLSessionConfiguration *)configuration + imageCache:(nullable id)imageCache +{ + NSAssert(sharedDownloader == nil, @"Singleton has been created and session can no longer be configured."); + PINRemoteImageManager *sharedManager = [self PINRemoteImageManagerWithConfiguration:configuration imageCache:imageCache]; + [self setSharedPreconfiguredRemoteImageManager:sharedManager]; +} + +static dispatch_once_t shared_init_predicate; + ++ (void)setSharedPreconfiguredRemoteImageManager:(PINRemoteImageManager *)preconfiguredPINRemoteImageManager +{ + NSAssert(preconfiguredPINRemoteImageManager != nil, @"setSharedPreconfiguredRemoteImageManager requires a non-nil parameter"); + NSAssert1(sharedPINRemoteImageManager == nil, @"An instance of %@ has been set. Either configuration or preconfigured image manager can be set at a time and only once.", [[sharedPINRemoteImageManager class] description]); + + dispatch_once(&shared_init_predicate, ^{ + sharedPINRemoteImageManager = preconfiguredPINRemoteImageManager; + }); +} + ++ (PINRemoteImageManager *)PINRemoteImageManagerWithConfiguration:(nullable NSURLSessionConfiguration *)configuration imageCache:(nullable id)imageCache +{ + PINRemoteImageManager *manager = nil; +#if DEBUG + // Check that Carthage users have linked both PINRemoteImage & PINCache by testing for one file each + if (!(NSClassFromString(@"PINRemoteImageManager"))) { + NSException *e = [NSException + exceptionWithName:@"FrameworkSetupException" + reason:@"Missing the path to the PINRemoteImage framework." + userInfo:nil]; + @throw e; + } + if (!(NSClassFromString(@"PINCache"))) { + NSException *e = [NSException + exceptionWithName:@"FrameworkSetupException" + reason:@"Missing the path to the PINCache framework." + userInfo:nil]; + @throw e; + } +#endif +#if PIN_ANIMATED_AVAILABLE + manager = [[ASPINRemoteImageManager alloc] initWithSessionConfiguration:configuration + alternativeRepresentationProvider:[self sharedDownloader] + imageCache:imageCache]; +#else + manager = [[ASPINRemoteImageManager alloc] initWithSessionConfiguration:configuration + alternativeRepresentationProvider:nil + imageCache:imageCache]; +#endif + return manager; +} + +- (PINRemoteImageManager *)sharedPINRemoteImageManager +{ + dispatch_once(&shared_init_predicate, ^{ + sharedPINRemoteImageManager = [ASPINRemoteImageDownloader PINRemoteImageManagerWithConfiguration:nil imageCache:nil]; + }); + return sharedPINRemoteImageManager; +} + +- (BOOL)sharedImageManagerSupportsMemoryRemoval +{ + static BOOL sharedImageManagerSupportsMemoryRemoval = NO; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + sharedImageManagerSupportsMemoryRemoval = [[[self sharedPINRemoteImageManager] cache] respondsToSelector:@selector(removeObjectForKeyFromMemory:)]; + }); + return sharedImageManagerSupportsMemoryRemoval; +} + +#pragma mark ASImageProtocols + +#if PIN_ANIMATED_AVAILABLE +- (nullable id )animatedImageWithData:(NSData *)animatedImageData +{ + return [[PINCachedAnimatedImage alloc] initWithAnimatedImageData:animatedImageData]; +} +#endif + +- (id )synchronouslyFetchedCachedImageWithURL:(NSURL *)URL; +{ + PINRemoteImageManager *manager = [self sharedPINRemoteImageManager]; + PINRemoteImageManagerResult *result = [manager synchronousImageFromCacheWithURL:URL processorKey:nil options:PINRemoteImageManagerDownloadOptionsSkipDecode]; + +#if PIN_ANIMATED_AVAILABLE + if (result.alternativeRepresentation) { + return result.alternativeRepresentation; + } +#endif + return result.image; +} + +- (void)cachedImageWithURL:(NSURL *)URL + callbackQueue:(dispatch_queue_t)callbackQueue + completion:(ASImageCacherCompletion)completion +{ + [[self sharedPINRemoteImageManager] imageFromCacheWithURL:URL processorKey:nil options:PINRemoteImageManagerDownloadOptionsSkipDecode completion:^(PINRemoteImageManagerResult * _Nonnull result) { + [ASPINRemoteImageDownloader _performWithCallbackQueue:callbackQueue work:^{ + ASImageCacheType cacheType = (result.resultType == PINRemoteImageResultTypeMemoryCache ? ASImageCacheTypeSynchronous : ASImageCacheTypeAsynchronous); +#if PIN_ANIMATED_AVAILABLE + if (result.alternativeRepresentation) { + completion(result.alternativeRepresentation, cacheType); + } else { + completion(result.image, cacheType); + } +#else + completion(result.image, cacheType); +#endif + }]; + }]; +} + +- (void)clearFetchedImageFromCacheWithURL:(NSURL *)URL +{ + if ([self sharedImageManagerSupportsMemoryRemoval]) { + PINRemoteImageManager *manager = [self sharedPINRemoteImageManager]; + NSString *key = [manager cacheKeyForURL:URL processorKey:nil]; + [[manager cache] removeObjectForKeyFromMemory:key]; + } +} + +- (nullable id)downloadImageWithURL:(NSURL *)URL + callbackQueue:(dispatch_queue_t)callbackQueue + downloadProgress:(ASImageDownloaderProgress)downloadProgress + completion:(ASImageDownloaderCompletion)completion; +{ + return [self downloadImageWithURL:URL + priority:ASImageDownloaderPriorityImminent // maps to default priority + callbackQueue:callbackQueue + downloadProgress:downloadProgress + completion:completion]; +} + +- (nullable id)downloadImageWithURL:(NSURL *)URL + priority:(ASImageDownloaderPriority)priority + callbackQueue:(dispatch_queue_t)callbackQueue + downloadProgress:(ASImageDownloaderProgress)downloadProgress + completion:(ASImageDownloaderCompletion)completion +{ + PINRemoteImageManagerPriority pi_priority = PINRemoteImageManagerPriorityWithASImageDownloaderPriority(priority); + + PINRemoteImageManagerProgressDownload progressDownload = ^(int64_t completedBytes, int64_t totalBytes) { + if (downloadProgress == nil) { return; } + + [ASPINRemoteImageDownloader _performWithCallbackQueue:callbackQueue work:^{ + downloadProgress(completedBytes / (CGFloat)totalBytes); + }]; + }; + + PINRemoteImageManagerImageCompletion imageCompletion = ^(PINRemoteImageManagerResult * _Nonnull result) { + [ASPINRemoteImageDownloader _performWithCallbackQueue:callbackQueue work:^{ +#if PIN_ANIMATED_AVAILABLE + if (result.alternativeRepresentation) { + completion(result.alternativeRepresentation, result.error, result.UUID, result); + } else { + completion(result.image, result.error, result.UUID, result); + } +#else + completion(result.image, result.error, result.UUID, result); +#endif + }]; + }; + + // add "IgnoreCache" option since we have a caching API so we already checked it, not worth checking again. + // PINRemoteImage is responsible for coalescing downloads, and even if it wasn't, the tiny probability of + // extra downloads isn't worth the effort of rechecking caches every single time. In order to provide + // feedback to the consumer about whether images are cached, we can't simply make the cache a no-op and + // check the cache as part of this download. + return [[self sharedPINRemoteImageManager] downloadImageWithURL:URL + options:PINRemoteImageManagerDownloadOptionsSkipDecode | PINRemoteImageManagerDownloadOptionsIgnoreCache + priority:pi_priority + progressImage:nil + progressDownload:progressDownload + completion:imageCompletion]; +} + +- (void)cancelImageDownloadForIdentifier:(id)downloadIdentifier +{ + ASDisplayNodeAssert([downloadIdentifier isKindOfClass:[NSUUID class]], @"downloadIdentifier must be NSUUID"); + [[self sharedPINRemoteImageManager] cancelTaskWithUUID:downloadIdentifier storeResumeData:NO]; +} + +- (void)cancelImageDownloadWithResumePossibilityForIdentifier:(id)downloadIdentifier +{ + ASDisplayNodeAssert([downloadIdentifier isKindOfClass:[NSUUID class]], @"downloadIdentifier must be NSUUID"); + [[self sharedPINRemoteImageManager] cancelTaskWithUUID:downloadIdentifier storeResumeData:YES]; +} + +- (void)setProgressImageBlock:(ASImageDownloaderProgressImage)progressBlock callbackQueue:(dispatch_queue_t)callbackQueue withDownloadIdentifier:(id)downloadIdentifier +{ + ASDisplayNodeAssert([downloadIdentifier isKindOfClass:[NSUUID class]], @"downloadIdentifier must be NSUUID"); + + if (progressBlock) { + [[self sharedPINRemoteImageManager] setProgressImageCallback:^(PINRemoteImageManagerResult * _Nonnull result) { + dispatch_async(callbackQueue, ^{ + progressBlock(result.image, result.renderedImageQuality, result.UUID); + }); + } ofTaskWithUUID:downloadIdentifier]; + } else { + [[self sharedPINRemoteImageManager] setProgressImageCallback:nil ofTaskWithUUID:downloadIdentifier]; + } +} + +- (void)setPriority:(ASImageDownloaderPriority)priority withDownloadIdentifier:(id)downloadIdentifier +{ + ASDisplayNodeAssert([downloadIdentifier isKindOfClass:[NSUUID class]], @"downloadIdentifier must be NSUUID"); + + PINRemoteImageManagerPriority pi_priority = PINRemoteImageManagerPriorityWithASImageDownloaderPriority(priority); + [[self sharedPINRemoteImageManager] setPriority:pi_priority ofTaskWithUUID:downloadIdentifier]; +} + +#pragma mark - PINRemoteImageManagerAlternateRepresentationProvider + +- (id)alternateRepresentationWithData:(NSData *)data options:(PINRemoteImageManagerDownloadOptions)options +{ +#if PIN_ANIMATED_AVAILABLE + if ([data pin_isAnimatedGIF]) { + return data; + } +#if PIN_WEBP_AVAILABLE + else if ([data pin_isAnimatedWebP]) { + return data; + } +#endif + +#endif + return nil; +} + +#pragma mark - Private + +/** + * If on main thread and queue is main, perform now. + * If queue is nil, assert and perform now. + * Otherwise, dispatch async to queue. + */ ++ (void)_performWithCallbackQueue:(dispatch_queue_t)queue work:(void (^)(void))work +{ + if (work == nil) { + // No need to assert here, really. We aren't expecting any feedback from this method. + return; + } + + if (ASDisplayNodeThreadIsMain() && queue == dispatch_get_main_queue()) { + work(); + } else if (queue == nil) { + ASDisplayNodeFailAssert(@"Callback queue should not be nil."); + work(); + } else { + dispatch_async(queue, work); + } +} + +@end +#endif diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASPageTable.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASPageTable.h new file mode 100644 index 0000000..e9cd11a --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASPageTable.h @@ -0,0 +1,120 @@ +// +// ASPageTable.h +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import +#import + +@class ASCollectionElement; + +NS_ASSUME_NONNULL_BEGIN + +/** + * Represents x and y coordinates of a page. + */ +typedef uintptr_t ASPageCoordinate; + +/** + * Returns a page coordinate with the given x and y values. Both of them must be less than 65,535. + */ +ASDK_EXTERN ASPageCoordinate ASPageCoordinateMake(uint16_t x, uint16_t y) AS_WARN_UNUSED_RESULT; + +/** + * Returns coordinate of the page that contains the specified point. + * Similar to CGRectContainsPoint, a point is considered inside a page if its lie inside the page or on the minimum X or minimum Y edge. + * + * @param point The point that the page at the returned should contain. Any negative of the point will be corrected to 0.0 + * + * @param pageSize The size of each page. + */ +ASDK_EXTERN ASPageCoordinate ASPageCoordinateForPageThatContainsPoint(CGPoint point, CGSize pageSize) AS_WARN_UNUSED_RESULT; + +ASDK_EXTERN uint16_t ASPageCoordinateGetX(ASPageCoordinate pageCoordinate) AS_WARN_UNUSED_RESULT; + +ASDK_EXTERN uint16_t ASPageCoordinateGetY(ASPageCoordinate pageCoordinate) AS_WARN_UNUSED_RESULT; + +ASDK_EXTERN CGRect ASPageCoordinateGetPageRect(ASPageCoordinate pageCoordinate, CGSize pageSize) AS_WARN_UNUSED_RESULT; + +/** + * Returns coordinate pointers for pages that intersect the specified rect. For each pointer, use ASPageCoordinateFromPointer() to get the original coordinate. + * The specified rect is restricted to the bounds of a content rect that has an origin of {0, 0} and a size of the given contentSize. + * + * @param rect The rect intersecting the target pages. + * + * @param contentSize The combined size of all pages. + * + * @param pageSize The size of each page. + */ +ASDK_EXTERN NSPointerArray * _Nullable ASPageCoordinatesForPagesThatIntersectRect(CGRect rect, CGSize contentSize, CGSize pageSize) AS_WARN_UNUSED_RESULT; + +/** + * An alias for an NSMapTable created to store objects using ASPageCoordinates as keys. + * + * You should not call -objectForKey:, -setObject:forKey:, or -removeObjectForKey: + * on these objects. + */ +typedef NSMapTable ASPageTable; + +/** + * A page to array of layout attributes table. + */ +typedef ASPageTable *> ASPageToLayoutAttributesTable; + +/** + * A category for creating & using map tables meant for storing objects using ASPage as keys. + */ +@interface NSMapTable (ASPageTableMethods) + +/** + * Creates a new page table with (NSMapTableStrongMemory | NSMapTableObjectPointerPersonality) for values. + */ ++ (ASPageTable *)pageTableForStrongObjectPointers NS_RETURNS_RETAINED; + +/** + * Creates a new page table with (NSMapTableWeakMemory | NSMapTableObjectPointerPersonality) for values. + */ ++ (ASPageTable *)pageTableForWeakObjectPointers NS_RETURNS_RETAINED; + +/** + * Builds a new page to layout attributes from the given layout attributes. + * + * @param layoutAttributesEnumerator The layout attributes to build from + * + * @param contentSize The combined size of all pages. + * + * @param pageSize The size of each page. + */ ++ (ASPageToLayoutAttributesTable *)pageTableWithLayoutAttributes:(id)layoutAttributesEnumerator contentSize:(CGSize)contentSize pageSize:(CGSize)pageSize NS_RETURNS_RETAINED; + +/** + * Retrieves the object for a given page, or nil if the page is not found. + * + * @param page A page to lookup the object for. + */ +- (nullable ObjectType)objectForPage:(ASPageCoordinate)page; + +/** + * Sets the given object for the associated page. + * + * @param object The object to store as value. + * + * @param page The page to use for the rect. + */ +- (void)setObject:(ObjectType)object forPage:(ASPageCoordinate)page; + +/** + * Removes the object for the given page, if one exists. + * + * @param page The page to remove. + */ +- (void)removeObjectForPage:(ASPageCoordinate)page; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASPageTable.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASPageTable.mm new file mode 100644 index 0000000..fde8108 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASPageTable.mm @@ -0,0 +1,147 @@ +// +// ASPageTable.mm +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +ASPageCoordinate ASPageCoordinateMake(uint16_t x, uint16_t y) +{ + // Add 1 to the end result because 0 is not accepted by NSArray and NSMapTable. + // To avoid overflow after adding, x and y can't be UINT16_MAX (0xFFFF) **at the same time**. + // But for API simplification, we enforce the same restriction to both values. + ASDisplayNodeCAssert(x < UINT16_MAX, @"x coordinate must be less than 65,535"); + ASDisplayNodeCAssert(y < UINT16_MAX, @"y coordinate must be less than 65,535"); + return (x << 16) + y + 1; +} + +ASPageCoordinate ASPageCoordinateForPageThatContainsPoint(CGPoint point, CGSize pageSize) +{ + return ASPageCoordinateMake((MAX(0.0, point.x) / pageSize.width), (MAX(0.0, point.y) / pageSize.height)); +} + +uint16_t ASPageCoordinateGetX(ASPageCoordinate pageCoordinate) +{ + return (pageCoordinate - 1) >> 16; +} + +uint16_t ASPageCoordinateGetY(ASPageCoordinate pageCoordinate) +{ + return (pageCoordinate - 1) & ~(0xFFFF<<16); +} + +CGRect ASPageCoordinateGetPageRect(ASPageCoordinate pageCoordinate, CGSize pageSize) +{ + CGFloat pageWidth = pageSize.width; + CGFloat pageHeight = pageSize.height; + return CGRectMake(ASPageCoordinateGetX(pageCoordinate) * pageWidth, ASPageCoordinateGetY(pageCoordinate) * pageHeight, pageWidth, pageHeight); +} + +NSPointerArray *ASPageCoordinatesForPagesThatIntersectRect(CGRect rect, CGSize contentSize, CGSize pageSize) +{ + CGRect contentRect = CGRectMake(0.0, 0.0, contentSize.width, contentSize.height); + // Make sure the specified rect is within contentRect + rect = CGRectIntersection(rect, contentRect); + if (CGRectIsNull(rect) || CGRectIsEmpty(rect)) { + return nil; + } + + NSPointerArray *result = [NSPointerArray pointerArrayWithOptions:(NSPointerFunctionsIntegerPersonality | NSPointerFunctionsOpaqueMemory)]; + + ASPageCoordinate minPage = ASPageCoordinateForPageThatContainsPoint(CGPointMake(CGRectGetMinX(rect), CGRectGetMinY(rect)), pageSize); + ASPageCoordinate maxPage = ASPageCoordinateForPageThatContainsPoint(CGPointMake(CGRectGetMaxX(rect), CGRectGetMaxY(rect)), pageSize); + if (minPage == maxPage) { + [result addPointer:(void *)minPage]; + return result; + } + + NSUInteger minX = ASPageCoordinateGetX(minPage); + NSUInteger minY = ASPageCoordinateGetY(minPage); + NSUInteger maxX = ASPageCoordinateGetX(maxPage); + NSUInteger maxY = ASPageCoordinateGetY(maxPage); + + for (NSUInteger x = minX; x <= maxX; x++) { + for (NSUInteger y = minY; y <= maxY; y++) { + ASPageCoordinate page = ASPageCoordinateMake(x, y); + [result addPointer:(void *)page]; + } + } + + return result; +} + +@implementation NSMapTable (ASPageTableMethods) + ++ (instancetype)pageTableWithValuePointerFunctions:(NSPointerFunctions *)valueFuncs NS_RETURNS_RETAINED +{ + static NSPointerFunctions *pageCoordinatesFuncs; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + pageCoordinatesFuncs = [NSPointerFunctions pointerFunctionsWithOptions:NSPointerFunctionsIntegerPersonality | NSPointerFunctionsOpaqueMemory]; + }); + + return [[NSMapTable alloc] initWithKeyPointerFunctions:pageCoordinatesFuncs valuePointerFunctions:valueFuncs capacity:0]; +} + ++ (ASPageTable *)pageTableForStrongObjectPointers NS_RETURNS_RETAINED +{ + static NSPointerFunctions *strongObjectPointerFuncs; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + strongObjectPointerFuncs = [NSPointerFunctions pointerFunctionsWithOptions:NSPointerFunctionsStrongMemory]; + }); + return [self pageTableWithValuePointerFunctions:strongObjectPointerFuncs]; +} + ++ (ASPageTable *)pageTableForWeakObjectPointers NS_RETURNS_RETAINED +{ + static NSPointerFunctions *weakObjectPointerFuncs; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + weakObjectPointerFuncs = [NSPointerFunctions pointerFunctionsWithOptions:NSPointerFunctionsWeakMemory]; + }); + return [self pageTableWithValuePointerFunctions:weakObjectPointerFuncs]; +} + ++ (ASPageToLayoutAttributesTable *)pageTableWithLayoutAttributes:(id)layoutAttributesEnumerator contentSize:(CGSize)contentSize pageSize:(CGSize)pageSize NS_RETURNS_RETAINED +{ + ASPageToLayoutAttributesTable *result = [ASPageTable pageTableForStrongObjectPointers]; + for (UICollectionViewLayoutAttributes *attrs in layoutAttributesEnumerator) { + // This attrs may span multiple pages. Make sure it's registered to all of them + NSPointerArray *pages = ASPageCoordinatesForPagesThatIntersectRect(attrs.frame, contentSize, pageSize); + + for (id pagePtr in pages) { + ASPageCoordinate page = (ASPageCoordinate)pagePtr; + NSMutableArray *attrsInPage = [result objectForPage:page]; + if (attrsInPage == nil) { + attrsInPage = [[NSMutableArray alloc] init]; + [result setObject:attrsInPage forPage:page]; + } + [attrsInPage addObject:attrs]; + } + } + return result; +} + +- (id)objectForPage:(ASPageCoordinate)page +{ + unowned id key = (__bridge id)(void *)page; + return [self objectForKey:key]; +} + +- (void)setObject:(id)object forPage:(ASPageCoordinate)page +{ + unowned id key = (__bridge id)(void *)page; + [self setObject:object forKey:key]; +} + +- (void)removeObjectForPage:(ASPageCoordinate)page +{ + unowned id key = (__bridge id)(void *)page; + [self removeObjectForKey:key]; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASPhotosFrameworkImageRequest.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASPhotosFrameworkImageRequest.h new file mode 100644 index 0000000..90f04fa --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASPhotosFrameworkImageRequest.h @@ -0,0 +1,73 @@ +// +// ASPhotosFrameworkImageRequest.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#if AS_USE_PHOTOS + +#import +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +ASDK_EXTERN NSString *const ASPhotosURLScheme; + +/** + @abstract Use ASPhotosFrameworkImageRequest to encapsulate all the information needed to request an image from + the Photos framework and store it in a URL. + */ +API_AVAILABLE(ios(8.0), tvos(10.0)) +@interface ASPhotosFrameworkImageRequest : NSObject + +- (instancetype)initWithAssetIdentifier:(NSString *)assetIdentifier NS_DESIGNATED_INITIALIZER; + +/** + @return A new image request deserialized from `url`, or nil if `url` is not a valid photos URL. + */ ++ (nullable ASPhotosFrameworkImageRequest *)requestWithURL:(NSURL *)url; + +/** + @abstract The asset identifier for this image request provided during initialization. + */ +@property (nonatomic, readonly) NSString *assetIdentifier; + +/** + @abstract The target size for this image request. Defaults to `PHImageManagerMaximumSize`. + */ +@property (nonatomic) CGSize targetSize; + +/** + @abstract The content mode for this image request. Defaults to `PHImageContentModeDefault`. + + @see `PHImageManager` + */ +@property (nonatomic) PHImageContentMode contentMode; + +/** + @abstract The options specified for this request. Default value is the result of `[PHImageRequestOptions new]`. + + @discussion Some properties of this object are ignored when converting this request into a URL. + As of iOS SDK 9.0, these properties are `progressHandler` and `synchronous`. + */ +@property (nonatomic) PHImageRequestOptions *options; + +/** + @return A new URL converted from this request. + */ +@property (nonatomic, readonly) NSURL *url; + +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)new NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END + +#endif // AS_USE_PHOTOS diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASPhotosFrameworkImageRequest.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASPhotosFrameworkImageRequest.mm new file mode 100644 index 0000000..ac1d477 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASPhotosFrameworkImageRequest.mm @@ -0,0 +1,157 @@ +// +// ASPhotosFrameworkImageRequest.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#if AS_USE_PHOTOS + +NSString *const ASPhotosURLScheme = @"ph"; + +static NSString *const _ASPhotosURLQueryKeyWidth = @"width"; +static NSString *const _ASPhotosURLQueryKeyHeight = @"height"; + +// value is PHImageContentMode value +static NSString *const _ASPhotosURLQueryKeyContentMode = @"contentmode"; + +// value is PHImageRequestOptionsResizeMode value +static NSString *const _ASPhotosURLQueryKeyResizeMode = @"resizemode"; + +// value is PHImageRequestOptionsDeliveryMode value +static NSString *const _ASPhotosURLQueryKeyDeliveryMode = @"deliverymode"; + +// value is PHImageRequestOptionsVersion value +static NSString *const _ASPhotosURLQueryKeyVersion = @"version"; + +// value is 0 or 1 +static NSString *const _ASPhotosURLQueryKeyAllowNetworkAccess = @"network"; + +static NSString *const _ASPhotosURLQueryKeyCropOriginX = @"crop_x"; +static NSString *const _ASPhotosURLQueryKeyCropOriginY = @"crop_y"; +static NSString *const _ASPhotosURLQueryKeyCropWidth = @"crop_w"; +static NSString *const _ASPhotosURLQueryKeyCropHeight = @"crop_h"; + +@implementation ASPhotosFrameworkImageRequest + +- (instancetype)initWithAssetIdentifier:(NSString *)assetIdentifier +{ + self = [super init]; + if (self) { + _assetIdentifier = assetIdentifier; + _options = [PHImageRequestOptions new]; + _contentMode = PHImageContentModeDefault; + _targetSize = PHImageManagerMaximumSize; + } + return self; +} + +#pragma mark NSCopying + +- (id)copyWithZone:(NSZone *)zone +{ + ASPhotosFrameworkImageRequest *copy = [[ASPhotosFrameworkImageRequest alloc] initWithAssetIdentifier:self.assetIdentifier]; + copy.options = [self.options copy]; + copy.targetSize = self.targetSize; + copy.contentMode = self.contentMode; + return copy; +} + +#pragma mark Converting to URL + +- (NSURL *)url +{ + NSURLComponents *comp = [NSURLComponents new]; + comp.scheme = ASPhotosURLScheme; + comp.host = _assetIdentifier; + NSMutableArray *queryItems = [NSMutableArray arrayWithObjects: + [NSURLQueryItem queryItemWithName:_ASPhotosURLQueryKeyWidth value:@(_targetSize.width).stringValue], + [NSURLQueryItem queryItemWithName:_ASPhotosURLQueryKeyHeight value:@(_targetSize.height).stringValue], + [NSURLQueryItem queryItemWithName:_ASPhotosURLQueryKeyVersion value:@(_options.version).stringValue], + [NSURLQueryItem queryItemWithName:_ASPhotosURLQueryKeyContentMode value:@(_contentMode).stringValue], + [NSURLQueryItem queryItemWithName:_ASPhotosURLQueryKeyAllowNetworkAccess value:@(_options.networkAccessAllowed).stringValue], + [NSURLQueryItem queryItemWithName:_ASPhotosURLQueryKeyResizeMode value:@(_options.resizeMode).stringValue], + [NSURLQueryItem queryItemWithName:_ASPhotosURLQueryKeyDeliveryMode value:@(_options.deliveryMode).stringValue] + , nil]; + + CGRect cropRect = _options.normalizedCropRect; + if (!CGRectIsEmpty(cropRect)) { + [queryItems addObjectsFromArray:@[ + [NSURLQueryItem queryItemWithName:_ASPhotosURLQueryKeyCropOriginX value:@(cropRect.origin.x).stringValue], + [NSURLQueryItem queryItemWithName:_ASPhotosURLQueryKeyCropOriginY value:@(cropRect.origin.y).stringValue], + [NSURLQueryItem queryItemWithName:_ASPhotosURLQueryKeyCropWidth value:@(cropRect.size.width).stringValue], + [NSURLQueryItem queryItemWithName:_ASPhotosURLQueryKeyCropHeight value:@(cropRect.size.height).stringValue] + ]]; + } + comp.queryItems = queryItems; + return comp.URL; +} + +#pragma mark Converting from URL + ++ (ASPhotosFrameworkImageRequest *)requestWithURL:(NSURL *)url +{ + // not a photos URL + if (![url.scheme isEqualToString:ASPhotosURLScheme]) { + return nil; + } + + NSURLComponents *comp = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:NO]; + + ASPhotosFrameworkImageRequest *request = [[ASPhotosFrameworkImageRequest alloc] initWithAssetIdentifier:url.host]; + + CGRect cropRect = CGRectZero; + CGSize targetSize = PHImageManagerMaximumSize; + for (NSURLQueryItem *item in comp.queryItems) { + if ([_ASPhotosURLQueryKeyAllowNetworkAccess isEqualToString:item.name]) { + request.options.networkAccessAllowed = item.value.boolValue; + } else if ([_ASPhotosURLQueryKeyWidth isEqualToString:item.name]) { + targetSize.width = item.value.doubleValue; + } else if ([_ASPhotosURLQueryKeyHeight isEqualToString:item.name]) { + targetSize.height = item.value.doubleValue; + } else if ([_ASPhotosURLQueryKeyContentMode isEqualToString:item.name]) { + request.contentMode = (PHImageContentMode)item.value.integerValue; + } else if ([_ASPhotosURLQueryKeyVersion isEqualToString:item.name]) { + request.options.version = (PHImageRequestOptionsVersion)item.value.integerValue; + } else if ([_ASPhotosURLQueryKeyCropOriginX isEqualToString:item.name]) { + cropRect.origin.x = item.value.doubleValue; + } else if ([_ASPhotosURLQueryKeyCropOriginY isEqualToString:item.name]) { + cropRect.origin.y = item.value.doubleValue; + } else if ([_ASPhotosURLQueryKeyCropWidth isEqualToString:item.name]) { + cropRect.size.width = item.value.doubleValue; + } else if ([_ASPhotosURLQueryKeyCropHeight isEqualToString:item.name]) { + cropRect.size.height = item.value.doubleValue; + } else if ([_ASPhotosURLQueryKeyResizeMode isEqualToString:item.name]) { + request.options.resizeMode = (PHImageRequestOptionsResizeMode)item.value.integerValue; + } else if ([_ASPhotosURLQueryKeyDeliveryMode isEqualToString:item.name]) { + request.options.deliveryMode = (PHImageRequestOptionsDeliveryMode)item.value.integerValue; + } + } + request.targetSize = targetSize; + request.options.normalizedCropRect = cropRect; + return request; +} + +#pragma mark NSObject + +- (BOOL)isEqual:(id)object +{ + if (![object isKindOfClass:ASPhotosFrameworkImageRequest.class]) { + return NO; + } + ASPhotosFrameworkImageRequest *other = object; + return [other.assetIdentifier isEqualToString:self.assetIdentifier] && + other.contentMode == self.contentMode && + CGSizeEqualToSize(other.targetSize, self.targetSize) && + CGRectEqualToRect(other.options.normalizedCropRect, self.options.normalizedCropRect) && + other.options.resizeMode == self.options.resizeMode && + other.options.version == self.options.version; +} + +@end + +#endif // AS_USE_PHOTOS diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASRangeController.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASRangeController.h new file mode 100644 index 0000000..1ce2952 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASRangeController.h @@ -0,0 +1,202 @@ +// +// ASRangeController.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import +#import +#import +#import +#import + +#define ASRangeControllerLoggingEnabled 0 + +NS_ASSUME_NONNULL_BEGIN + +@class _ASHierarchyChangeSet; +@protocol ASRangeControllerDataSource; +@protocol ASRangeControllerDelegate; +@protocol ASLayoutController; + +/** + * Working range controller. + * + * Used internally by ASTableView and ASCollectionView. It is paired with ASDataController. + * It is designed to support custom scrolling containers as well. Observes the visible range, maintains + * "working ranges" to trigger network calls and rendering, and is responsible for driving asynchronous layout of cells. + * This includes cancelling those asynchronous operations as cells fall outside of the working ranges. + */ +AS_SUBCLASSING_RESTRICTED +@interface ASRangeController : NSObject +{ + id _layoutController; + __weak id _dataSource; + __weak id _delegate; +} + +/** + * Notify the range controller that the visible range has been updated. + * This is the primary input call that drives updating the working ranges, and triggering their actions. + * The ranges will be updated in the next turn of the main loop, or when -updateIfNeeded is called. + * + * @see [ASRangeControllerDelegate rangeControllerVisibleNodeIndexPaths:] + */ +- (void)setNeedsUpdate; + +/** + * Update the ranges immediately, if -setNeedsUpdate has been called since the last update. + * This is useful because the ranges must be updated immediately after a cell is added + * into a table/collection to satisfy interface state API guarantees. + */ +- (void)updateIfNeeded; + +/** + * Force update the ranges immediately. + */ +- (void)updateRanges; + +/** + * Add the sized node for `indexPath` as a subview of `contentView`. + * + * @param contentView UIView to add a (sized) node's view to. + * + * @param node The cell node to be added. + */ +- (void)configureContentView:(UIView *)contentView forCellNode:(ASCellNode *)node; + +- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType; + +- (ASRangeTuningParameters)tuningParametersForRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType; + +// These methods call the corresponding method on each node, visiting each one that +// the range controller has set a non-default interface state on. +- (void)clearContents; +- (void)clearPreloadedData; + +/** + * An object that describes the layout behavior of the ranged component (table view, collection view, etc.) + * + * Used primarily for providing the current range of index paths and identifying when the + * range controller should invalidate its range. + */ +@property (nonatomic) id layoutController; + +/** + * The underlying data source for the range controller + */ +@property (nonatomic, weak) id dataSource; + +/** + * Delegate for handling range controller events. Must not be nil. + */ +@property (nonatomic, weak) id delegate; + +/** + * Property that indicates whether the scroll view for this range controller has ever changed its contentOffset. + */ +@property (nonatomic) BOOL contentHasBeenScrolled; + +@end + + +/** + * Data source for ASRangeController. + * + * Allows the range controller to perform external queries on the range. + * Ex. range nodes, visible index paths, and viewport size. + */ +@protocol ASRangeControllerDataSource + +/** + * @param rangeController Sender. + * + * @return an table of elements corresponding to the data currently visible onscreen (i.e., the visible range). + */ +- (nullable NSHashTable *)visibleElementsForRangeController:(ASRangeController *)rangeController; + +/** + * @param rangeController Sender. + * + * @return the current scroll direction of the view using this range controller. + */ +- (ASScrollDirection)scrollDirectionForRangeController:(ASRangeController *)rangeController; + +/** + * @param rangeController Sender. + * + * @return the ASInterfaceState of the node that this controller is powering. This allows nested range controllers + * to collaborate with one another, as an outer controller may set bits in .interfaceState such as Visible. + * If this controller is an orthogonally scrolling element, it waits until it is visible to preload outside the viewport. + */ +- (ASInterfaceState)interfaceStateForRangeController:(ASRangeController *)rangeController; + +- (ASElementMap *)elementMapForRangeController:(ASRangeController *)rangeController; + +- (NSString *)nameForRangeControllerDataSource; + +@end + +/** + * Delegate for ASRangeController. + */ +@protocol ASRangeControllerDelegate + +/** + * Called to update with given change set. + * + * @param changeSet The change set that includes all updates + * + * @param updates The block that performs relevant data updates. + * + * @discussion The updates block must always be executed or the data controller will get into a bad state. + * It should be called at the time the backing view is ready to process the updates, + * i.e inside the updates block of `-[UICollectionView performBatchUpdates:completion:] or after calling `-[UITableView beginUpdates]`. + */ +- (void)rangeController:(ASRangeController *)rangeController updateWithChangeSet:(_ASHierarchyChangeSet *)changeSet updates:(dispatch_block_t)updates; + +- (BOOL)rangeControllerShouldUpdateRanges:(ASRangeController *)rangeController; + +@end + +@interface ASRangeController (ASRangeControllerUpdateRangeProtocol) + +/** + * Update the range mode for a range controller to a explicitly set mode until the node that contains the range + * controller becomes visible again + * + * Logic for the automatic range mode: + * 1. If there are no visible node paths available nothing is to be done and no range update will happen + * 2. The initial range update if the range controller is visible always will be + * ASLayoutRangeModeMinimum as it's the initial fetch + * 3. The range mode set explicitly via updateCurrentRangeWithMode: will last at least one range update. After that it + the range controller will use the explicit set range mode until it becomes visible and a new range update was + triggered or a new range mode via updateCurrentRangeWithMode: is set + * 4. If range mode is not explicitly set the range mode is variying based if the range controller is visible or not + */ +- (void)updateCurrentRangeWithMode:(ASLayoutRangeMode)rangeMode; + +@end + +@interface ASRangeController (DebugInternal) + ++ (void)layoutDebugOverlayIfNeeded; + +- (void)addRangeControllerToRangeDebugOverlay; + +- (void)updateRangeController:(ASRangeController *)controller + withScrollableDirections:(ASScrollDirection)scrollableDirections + scrollDirection:(ASScrollDirection)direction + rangeMode:(ASLayoutRangeMode)mode + displayTuningParameters:(ASRangeTuningParameters)displayTuningParameters + preloadTuningParameters:(ASRangeTuningParameters)preloadTuningParameters + interfaceState:(ASInterfaceState)interfaceState; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASRangeController.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASRangeController.mm new file mode 100644 index 0000000..1dcd062 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASRangeController.mm @@ -0,0 +1,663 @@ +// +// ASRangeController.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import +#import +#import +#import +#import +#import // Required for interfaceState and hierarchyState setter methods. +#import +#import + +#import +#import +#import + +#define AS_RANGECONTROLLER_LOG_UPDATE_FREQ 0 + +#ifndef ASRangeControllerAutomaticLowMemoryHandling +#define ASRangeControllerAutomaticLowMemoryHandling 1 +#endif + +@interface ASRangeController () +{ + BOOL _rangeIsValid; + BOOL _needsRangeUpdate; + NSSet *_allPreviousIndexPaths; + NSHashTable *_visibleNodes; + ASLayoutRangeMode _currentRangeMode; + BOOL _contentHasBeenScrolled; + BOOL _preserveCurrentRangeMode; + BOOL _didRegisterForNodeDisplayNotifications; + CFTimeInterval _pendingDisplayNodesTimestamp; + + // If the user is not currently scrolling, we will keep our ranges + // configured to match their previous scroll direction. Defaults + // to [.right, .down] so that when the user first opens a screen + // the ranges point down into the content. + ASScrollDirection _previousScrollDirection; + +#if AS_RANGECONTROLLER_LOG_UPDATE_FREQ + NSUInteger _updateCountThisFrame; + CADisplayLink *_displayLink; +#endif +} + +@end + +static UIApplicationState __ApplicationState = UIApplicationStateActive; + +@implementation ASRangeController + +#pragma mark - Lifecycle + +- (instancetype)init +{ + if (!(self = [super init])) { + return nil; + } + + _rangeIsValid = YES; + _currentRangeMode = ASLayoutRangeModeUnspecified; + _contentHasBeenScrolled = NO; + _preserveCurrentRangeMode = NO; + _previousScrollDirection = ASScrollDirectionDown | ASScrollDirectionRight; + + [[[self class] allRangeControllersWeakSet] addObject:self]; + +#if AS_RANGECONTROLLER_LOG_UPDATE_FREQ + _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(_updateCountDisplayLinkDidFire)]; + [_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; +#endif + + if (ASDisplayNode.shouldShowRangeDebugOverlay) { + [self addRangeControllerToRangeDebugOverlay]; + } + + return self; +} + +- (void)dealloc +{ +#if AS_RANGECONTROLLER_LOG_UPDATE_FREQ + [_displayLink invalidate]; +#endif + + if (_didRegisterForNodeDisplayNotifications) { + [[NSNotificationCenter defaultCenter] removeObserver:self name:ASRenderingEngineDidDisplayScheduledNodesNotification object:nil]; + } +} + +#pragma mark - Core visible node range management API + ++ (BOOL)isFirstRangeUpdateForRangeMode:(ASLayoutRangeMode)rangeMode +{ + return (rangeMode == ASLayoutRangeModeUnspecified); +} + ++ (ASLayoutRangeMode)rangeModeForInterfaceState:(ASInterfaceState)interfaceState + currentRangeMode:(ASLayoutRangeMode)currentRangeMode +{ + BOOL isVisible = (ASInterfaceStateIncludesVisible(interfaceState)); + BOOL isFirstRangeUpdate = [self isFirstRangeUpdateForRangeMode:currentRangeMode]; + if (!isVisible || isFirstRangeUpdate) { + return ASLayoutRangeModeMinimum; + } + + return ASLayoutRangeModeFull; +} + +- (ASInterfaceState)interfaceState +{ + ASInterfaceState selfInterfaceState = ASInterfaceStateNone; + if (_dataSource) { + selfInterfaceState = [_dataSource interfaceStateForRangeController:self]; + } + if (__ApplicationState == UIApplicationStateBackground) { + // If the app is background, pretend to be invisible so that we inform each cell it is no longer being viewed by the user + selfInterfaceState &= ~(ASInterfaceStateVisible); + } + return selfInterfaceState; +} + +- (void)setNeedsUpdate +{ + if (!_needsRangeUpdate) { + _needsRangeUpdate = YES; + + __weak __typeof__(self) weakSelf = self; + dispatch_async(dispatch_get_main_queue(), ^{ + [weakSelf updateIfNeeded]; + }); + } +} + +- (void)updateIfNeeded +{ + if (_needsRangeUpdate) { + [self updateRanges]; + } +} + +- (void)updateRanges +{ + _needsRangeUpdate = NO; + [self _updateVisibleNodeIndexPaths]; +} + +- (void)updateCurrentRangeWithMode:(ASLayoutRangeMode)rangeMode +{ + _preserveCurrentRangeMode = YES; + if (_currentRangeMode != rangeMode) { + _currentRangeMode = rangeMode; + + [self setNeedsUpdate]; + } +} + +- (void)setLayoutController:(id)layoutController +{ + _layoutController = layoutController; + if (layoutController && _dataSource) { + [self updateIfNeeded]; + } +} + +- (void)setDataSource:(id)dataSource +{ + _dataSource = dataSource; + if (dataSource && _layoutController) { + [self updateIfNeeded]; + } +} + +// Clear the visible bit from any nodes that disappeared since last update. +// Currently we guarantee that nodes will not be marked visible when deallocated, +// but it's OK to be in e.g. the preload range. So for the visible bit specifically, +// we add this extra mechanism to account for e.g. deleted items. +// +// NOTE: There is a minor risk here, if a node is transferred from one range controller +// to another before the first rc updates and clears the node out of this set. It's a pretty +// wild scenario that I doubt happens in practice. +- (void)_setVisibleNodes:(NSHashTable *)newVisibleNodes +{ + for (ASCellNode *node in _visibleNodes) { + if (![newVisibleNodes containsObject:node] && node.isVisible) { + [node exitInterfaceState:ASInterfaceStateVisible]; + } + } + _visibleNodes = newVisibleNodes; +} + +- (void)_updateVisibleNodeIndexPaths +{ + as_activity_scope_verbose(as_activity_create("Update range controller", AS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT)); + as_log_verbose(ASCollectionLog(), "Updating ranges for %@", ASViewToDisplayNode(ASDynamicCast(self.delegate, UIView))); + ASDisplayNodeAssert(_layoutController, @"An ASLayoutController is required by ASRangeController"); + if (!_layoutController || !_dataSource) { + return; + } + + if (![_delegate rangeControllerShouldUpdateRanges:self]) { + return; + } + +#if AS_RANGECONTROLLER_LOG_UPDATE_FREQ + _updateCountThisFrame += 1; +#endif + + ASElementMap *map = [_dataSource elementMapForRangeController:self]; + + // TODO: Consider if we need to use this codepath, or can rely on something more similar to the data & display ranges + // Example: ... = [_layoutController indexPathsForScrolling:scrollDirection rangeType:ASLayoutRangeTypeVisible]; + auto visibleElements = [_dataSource visibleElementsForRangeController:self]; + NSHashTable *newVisibleNodes = [NSHashTable hashTableWithOptions:NSHashTableObjectPointerPersonality]; + + ASSignpostStart(RangeControllerUpdate, _dataSource, "%@", ASObjectDescriptionMakeTiny(_dataSource)); + + // Get the scroll direction. Default to using the previous one, if they're not scrolling. + ASScrollDirection scrollDirection = [_dataSource scrollDirectionForRangeController:self]; + if (scrollDirection == ASScrollDirectionNone) { + scrollDirection = _previousScrollDirection; + } + _previousScrollDirection = scrollDirection; + + if (visibleElements.count == 0) { // if we don't have any visibleNodes currently (scrolled before or after content)... + // Verify the actual state by checking the layout with a "VisibleOnly" range. + // This allows us to avoid thrashing through -didExitVisibleState in the case of -reloadData, since that generates didEndDisplayingCell calls. + // Those didEndDisplayingCell calls result in items being removed from the visibleElements returned by the _dataSource, even though the layout remains correct. + visibleElements = [_layoutController elementsForScrolling:scrollDirection rangeMode:ASLayoutRangeModeVisibleOnly rangeType:ASLayoutRangeTypeDisplay map:map]; + for (ASCollectionElement *element in visibleElements) { + [newVisibleNodes addObject:element.node]; + } + [self _setVisibleNodes:newVisibleNodes]; + ASSignpostEnd(RangeControllerUpdate, _dataSource, ""); + return; // don't do anything for this update, but leave _rangeIsValid == NO to make sure we update it later + } + + ASInterfaceState selfInterfaceState = [self interfaceState]; + ASLayoutRangeMode rangeMode = _currentRangeMode; + BOOL updateRangeMode = (!_preserveCurrentRangeMode && _contentHasBeenScrolled); + + // If we've never scrolled before, we never update the range mode, so it doesn't jump into Full too early. + // This can happen if we have multiple, noisy updates occurring from application code before the user has engaged. + // If the range mode is explicitly set via updateCurrentRangeWithMode:, we'll preserve that for at least one update cycle. + // Once the user has scrolled and the range is visible, we'll always resume managing the range mode automatically. + if ((updateRangeMode && ASInterfaceStateIncludesVisible(selfInterfaceState)) || [[self class] isFirstRangeUpdateForRangeMode:rangeMode]) { + rangeMode = [ASRangeController rangeModeForInterfaceState:selfInterfaceState currentRangeMode:_currentRangeMode]; + } + + ASRangeTuningParameters parametersPreload = [_layoutController tuningParametersForRangeMode:rangeMode + rangeType:ASLayoutRangeTypePreload]; + ASRangeTuningParameters parametersDisplay = [_layoutController tuningParametersForRangeMode:rangeMode + rangeType:ASLayoutRangeTypeDisplay]; + + // Preload can express the ultra-low-memory state with 0, 0 returned for its tuningParameters above, and will match Visible. + // However, in this rangeMode, Display is not supposed to contain *any* paths -- not even the visible bounds. TuningParameters can't express this. + BOOL emptyDisplayRange = (rangeMode == ASLayoutRangeModeLowMemory); + BOOL equalDisplayPreload = ASRangeTuningParametersEqualToRangeTuningParameters(parametersDisplay, parametersPreload); + BOOL equalDisplayVisible = (ASRangeTuningParametersEqualToRangeTuningParameters(parametersDisplay, ASRangeTuningParametersZero) + && emptyDisplayRange == NO); + + // Check if both Display and Preload are unique. If they are, we load them with a single fetch from the layout controller for performance. + BOOL optimizedLoadingOfBothRanges = (equalDisplayPreload == NO && equalDisplayVisible == NO && emptyDisplayRange == NO); + + NSHashTable *displayElements = nil; + NSHashTable *preloadElements = nil; + + if (optimizedLoadingOfBothRanges) { + [_layoutController allElementsForScrolling:scrollDirection rangeMode:rangeMode displaySet:&displayElements preloadSet:&preloadElements map:map]; + } else { + if (emptyDisplayRange == YES) { + displayElements = [NSHashTable hashTableWithOptions:NSHashTableObjectPointerPersonality]; + } else if (equalDisplayVisible == YES) { + displayElements = visibleElements; + } else { + // Calculating only the Display range means the Preload range is either the same as Display or Visible. + displayElements = [_layoutController elementsForScrolling:scrollDirection rangeMode:rangeMode rangeType:ASLayoutRangeTypeDisplay map:map]; + } + + BOOL equalPreloadVisible = ASRangeTuningParametersEqualToRangeTuningParameters(parametersPreload, ASRangeTuningParametersZero); + if (equalDisplayPreload == YES) { + preloadElements = displayElements; + } else if (equalPreloadVisible == YES) { + preloadElements = visibleElements; + } else { + preloadElements = [_layoutController elementsForScrolling:scrollDirection rangeMode:rangeMode rangeType:ASLayoutRangeTypePreload map:map]; + } + } + + // For now we are only interested in items. Filter-map out from element to item-index-path. + NSSet *visibleIndexPaths = ASSetByFlatMapping(visibleElements, ASCollectionElement *element, [map indexPathForElementIfCell:element]); + NSSet *displayIndexPaths = ASSetByFlatMapping(displayElements, ASCollectionElement *element, [map indexPathForElementIfCell:element]); + NSSet *preloadIndexPaths = ASSetByFlatMapping(preloadElements, ASCollectionElement *element, [map indexPathForElementIfCell:element]); + + // Prioritize the order in which we visit each. Visible nodes should be updated first so they are enqueued on + // the network or display queues before preloading (offscreen) nodes are enqueued. + NSMutableOrderedSet *allIndexPaths = [[NSMutableOrderedSet alloc] initWithSet:visibleIndexPaths]; + + // Typically the preloadIndexPaths will be the largest, and be a superset of the others, though it may be disjoint. + // Because allIndexPaths is an NSMutableOrderedSet, this adds the non-duplicate items /after/ the existing items. + // This means that during iteration, we will first visit visible, then display, then preload nodes. + [allIndexPaths unionSet:displayIndexPaths]; + [allIndexPaths unionSet:preloadIndexPaths]; + + // Add anything we had applied interfaceState to in the last update, but is no longer in range, so we can clear any + // range flags it still has enabled. Most of the time, all but a few elements are equal; a large programmatic + // scroll or major main thread stall could cause entirely disjoint sets. In either case we must visit all. + // Calling "-set" on NSMutableOrderedSet just references the underlying mutable data store, so we must copy it. + NSSet *allCurrentIndexPaths = [[allIndexPaths set] copy]; + [allIndexPaths unionSet:_allPreviousIndexPaths]; + _allPreviousIndexPaths = allCurrentIndexPaths; + + _currentRangeMode = rangeMode; + _preserveCurrentRangeMode = NO; + + if (!_rangeIsValid) { + [allIndexPaths addObjectsFromArray:map.itemIndexPaths]; + } + +#if ASRangeControllerLoggingEnabled + ASDisplayNodeAssertTrue([visibleIndexPaths isSubsetOfSet:displayIndexPaths]); + NSMutableArray *modifiedIndexPaths = (ASRangeControllerLoggingEnabled ? [NSMutableArray array] : nil); +#endif + + for (NSIndexPath *indexPath in allIndexPaths) { + // Before a node / indexPath is exposed to ASRangeController, ASDataController should have already measured it. + // For consistency, make sure each node knows that it should measure itself if something changes. + ASInterfaceState interfaceState = ASInterfaceStateMeasureLayout; + + if (ASInterfaceStateIncludesVisible(selfInterfaceState)) { + if ([visibleIndexPaths containsObject:indexPath]) { + interfaceState |= (ASInterfaceStateVisible | ASInterfaceStateDisplay | ASInterfaceStatePreload); + } else { + if ([preloadIndexPaths containsObject:indexPath]) { + interfaceState |= ASInterfaceStatePreload; + } + if ([displayIndexPaths containsObject:indexPath]) { + interfaceState |= ASInterfaceStateDisplay; + } + } + } else { + // If selfInterfaceState isn't visible, then visibleIndexPaths represents either what /will/ be immediately visible at the + // instant we come onscreen, or what /will/ no longer be visible at the instant we come offscreen. + // So, preload and display all of those things, but don't waste resources displaying others. + // + // DO NOT set Visible: even though these elements are in the visible range / "viewport", + // our overall container object is itself not yet, or no longer, visible. + // The moment it becomes visible, we will run the condition above. + if ([visibleIndexPaths containsObject:indexPath]) { + interfaceState |= ASInterfaceStatePreload; + if (rangeMode != ASLayoutRangeModeLowMemory) { + interfaceState |= ASInterfaceStateDisplay; + } + } else if ([displayIndexPaths containsObject:indexPath]) { + interfaceState |= ASInterfaceStatePreload; + } + } + + ASCellNode *node = [map elementForItemAtIndexPath:indexPath].nodeIfAllocated; + if (node != nil) { + ASDisplayNodeAssert(node.hierarchyState & ASHierarchyStateRangeManaged, @"All nodes reaching this point should be range-managed, or interfaceState may be incorrectly reset."); + if (ASInterfaceStateIncludesVisible(interfaceState)) { + [newVisibleNodes addObject:node]; + } + // Skip the many method calls of the recursive operation if the top level cell node already has the right interfaceState. + if (node.pendingInterfaceState != interfaceState) { +#if ASRangeControllerLoggingEnabled + [modifiedIndexPaths addObject:indexPath]; +#endif + + BOOL nodeShouldScheduleDisplay = [node shouldScheduleDisplayWithNewInterfaceState:interfaceState]; + [node recursivelySetInterfaceState:interfaceState]; + + if (nodeShouldScheduleDisplay) { + [self registerForNodeDisplayNotificationsForInterfaceStateIfNeeded:selfInterfaceState]; + if (_didRegisterForNodeDisplayNotifications) { + _pendingDisplayNodesTimestamp = CACurrentMediaTime(); + } + } + } + } + } + + [self _setVisibleNodes:newVisibleNodes]; + + // TODO: This code is for debugging only, but would be great to clean up with a delegate method implementation. + if (ASDisplayNode.shouldShowRangeDebugOverlay) { + ASScrollDirection scrollableDirections = ASScrollDirectionUp | ASScrollDirectionDown; + if ([_dataSource isKindOfClass:NSClassFromString(@"ASCollectionView")]) { + scrollableDirections = ((ASCollectionView *)_dataSource).scrollableDirections; + } + + [self updateRangeController:self + withScrollableDirections:scrollableDirections + scrollDirection:scrollDirection + rangeMode:rangeMode + displayTuningParameters:parametersDisplay + preloadTuningParameters:parametersPreload + interfaceState:selfInterfaceState]; + } + + _rangeIsValid = YES; + +#if ASRangeControllerLoggingEnabled +// NSSet *visibleNodePathsSet = [NSSet setWithArray:visibleNodePaths]; +// BOOL setsAreEqual = [visibleIndexPaths isEqualToSet:visibleNodePathsSet]; +// NSLog(@"visible sets are equal: %d", setsAreEqual); +// if (!setsAreEqual) { +// NSLog(@"standard: %@", visibleIndexPaths); +// NSLog(@"custom: %@", visibleNodePathsSet); +// } + [modifiedIndexPaths sortUsingSelector:@selector(compare:)]; + NSLog(@"Range update complete; modifiedIndexPaths: %@, rangeMode: %d", [self descriptionWithIndexPaths:modifiedIndexPaths], rangeMode); +#endif + + ASSignpostEnd(RangeControllerUpdate, _dataSource, ""); +} + +#pragma mark - Notification observers + +/** + * If we're in a restricted range mode, but we're going to change to a full range mode soon, + * go ahead and schedule the transition as soon as all the currently-scheduled rendering is done #1163. + */ +- (void)registerForNodeDisplayNotificationsForInterfaceStateIfNeeded:(ASInterfaceState)interfaceState +{ + // Do not schedule to listen if we're already in full range mode. + // This avoids updating the range controller during a collection teardown when it is removed + // from the hierarchy and its data source is cleared, causing UIKit to call -reloadData. + if (!_didRegisterForNodeDisplayNotifications && _currentRangeMode != ASLayoutRangeModeFull) { + ASLayoutRangeMode nextRangeMode = [ASRangeController rangeModeForInterfaceState:interfaceState + currentRangeMode:_currentRangeMode]; + if (_currentRangeMode != nextRangeMode) { + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(scheduledNodesDidDisplay:) + name:ASRenderingEngineDidDisplayScheduledNodesNotification + object:nil]; + _didRegisterForNodeDisplayNotifications = YES; + } + } +} + +- (void)scheduledNodesDidDisplay:(NSNotification *)notification +{ + CFAbsoluteTime notificationTimestamp = ((NSNumber *) notification.userInfo[ASRenderingEngineDidDisplayNodesScheduledBeforeTimestamp]).doubleValue; + if (_pendingDisplayNodesTimestamp < notificationTimestamp) { + // The rendering engine has processed all the nodes this range controller scheduled. Let's schedule a range update + [[NSNotificationCenter defaultCenter] removeObserver:self name:ASRenderingEngineDidDisplayScheduledNodesNotification object:nil]; + _didRegisterForNodeDisplayNotifications = NO; + + [self setNeedsUpdate]; + } +} + +#pragma mark - Cell node view handling + +- (void)configureContentView:(UIView *)contentView forCellNode:(ASCellNode *)node +{ + ASDisplayNodeAssertMainThread(); + ASDisplayNodeAssert(node, @"Cannot move a nil node to a view"); + ASDisplayNodeAssert(contentView, @"Cannot move a node to a non-existent view"); + + if (node.shouldUseUIKitCell) { + // When using UIKit cells, the ASCellNode is just a placeholder object with a preferredSize. + // In this case, we should not disrupt the subviews of the contentView. + return; + } + + if (node.view.superview == contentView) { + // this content view is already correctly configured + return; + } + + // clean the content view + for (UIView *view in contentView.subviews) { + [view removeFromSuperview]; + } + + [contentView addSubview:node.view]; +} + +- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType +{ + [_layoutController setTuningParameters:tuningParameters forRangeMode:rangeMode rangeType:rangeType]; +} + +- (ASRangeTuningParameters)tuningParametersForRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType +{ + return [_layoutController tuningParametersForRangeMode:rangeMode rangeType:rangeType]; +} + +#pragma mark - ASDataControllerDelegete + +- (void)dataController:(ASDataController *)dataController updateWithChangeSet:(_ASHierarchyChangeSet *)changeSet updates:(dispatch_block_t)updates +{ + ASDisplayNodeAssertMainThread(); + if (changeSet.includesReloadData) { + [self _setVisibleNodes:nil]; + } + _rangeIsValid = NO; + [_delegate rangeController:self updateWithChangeSet:changeSet updates:updates]; +} + +#pragma mark - Memory Management + +// Skip the many method calls of the recursive operation if the top level cell node already has the right interfaceState. +- (void)clearContents +{ + ASDisplayNodeAssertMainThread(); + for (ASCollectionElement *element in [_dataSource elementMapForRangeController:self]) { + ASCellNode *node = element.nodeIfAllocated; + if (ASInterfaceStateIncludesDisplay(node.interfaceState)) { + [node exitInterfaceState:ASInterfaceStateDisplay]; + } + } +} + +- (void)clearPreloadedData +{ + ASDisplayNodeAssertMainThread(); + for (ASCollectionElement *element in [_dataSource elementMapForRangeController:self]) { + ASCellNode *node = element.nodeIfAllocated; + if (ASInterfaceStateIncludesPreload(node.interfaceState)) { + [node exitInterfaceState:ASInterfaceStatePreload]; + } + } +} + +#pragma mark - Class Methods (Application Notification Handlers) + ++ (ASWeakSet *)allRangeControllersWeakSet +{ + static ASWeakSet *__allRangeControllersWeakSet; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + __allRangeControllersWeakSet = [[ASWeakSet alloc] init]; + [self registerSharedApplicationNotifications]; + }); + return __allRangeControllersWeakSet; +} + ++ (void)registerSharedApplicationNotifications +{ + NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; +#if ASRangeControllerAutomaticLowMemoryHandling + [center addObserver:self selector:@selector(didReceiveMemoryWarning:) name:UIApplicationDidReceiveMemoryWarningNotification object:nil]; +#endif + [center addObserver:self selector:@selector(didEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil]; + [center addObserver:self selector:@selector(willEnterForeground:) name:UIApplicationWillEnterForegroundNotification object:nil]; +} + +static ASLayoutRangeMode __rangeModeForMemoryWarnings = ASLayoutRangeModeLowMemory; ++ (void)setRangeModeForMemoryWarnings:(ASLayoutRangeMode)rangeMode +{ + ASDisplayNodeAssert(rangeMode == ASLayoutRangeModeVisibleOnly || rangeMode == ASLayoutRangeModeLowMemory, @"It is highly inadvisable to engage a larger range mode when a memory warning occurs, as this will almost certainly cause app eviction"); + __rangeModeForMemoryWarnings = rangeMode; +} + ++ (void)didReceiveMemoryWarning:(NSNotification *)notification +{ + NSArray *allRangeControllers = [[self allRangeControllersWeakSet] allObjects]; + for (ASRangeController *rangeController in allRangeControllers) { + BOOL isDisplay = ASInterfaceStateIncludesDisplay([rangeController interfaceState]); + [rangeController updateCurrentRangeWithMode:isDisplay ? ASLayoutRangeModeVisibleOnly : __rangeModeForMemoryWarnings]; + // There's no need to call needs update as updateCurrentRangeWithMode sets this if necessary. + [rangeController updateIfNeeded]; + } + +#if ASRangeControllerLoggingEnabled + NSLog(@"+[ASRangeController didReceiveMemoryWarning] with controllers: %@", allRangeControllers); +#endif +} + ++ (void)didEnterBackground:(NSNotification *)notification +{ + NSArray *allRangeControllers = [[self allRangeControllersWeakSet] allObjects]; + for (ASRangeController *rangeController in allRangeControllers) { + // We do not want to fully collapse the Display ranges of any visible range controllers so that flashes can be avoided when + // the app is resumed. Non-visible controllers can be more aggressively culled to the LowMemory state (see definitions for documentation) + BOOL isVisible = ASInterfaceStateIncludesVisible([rangeController interfaceState]); + [rangeController updateCurrentRangeWithMode:isVisible ? ASLayoutRangeModeVisibleOnly : ASLayoutRangeModeLowMemory]; + } + + // Because -interfaceState checks __ApplicationState and always clears the "visible" bit if Backgrounded, we must set this after updating the range mode. + __ApplicationState = UIApplicationStateBackground; + for (ASRangeController *rangeController in allRangeControllers) { + // Trigger a range update immediately, as we may not be allowed by the system to run the update block scheduled by changing range mode. + // There's no need to call needs update as updateCurrentRangeWithMode sets this if necessary. + [rangeController updateIfNeeded]; + } + +#if ASRangeControllerLoggingEnabled + NSLog(@"+[ASRangeController didEnterBackground] with controllers, after backgrounding: %@", allRangeControllers); +#endif +} + ++ (void)willEnterForeground:(NSNotification *)notification +{ + NSArray *allRangeControllers = [[self allRangeControllersWeakSet] allObjects]; + __ApplicationState = UIApplicationStateActive; + for (ASRangeController *rangeController in allRangeControllers) { + BOOL isVisible = ASInterfaceStateIncludesVisible([rangeController interfaceState]); + [rangeController updateCurrentRangeWithMode:isVisible ? ASLayoutRangeModeMinimum : ASLayoutRangeModeVisibleOnly]; + // There's no need to call needs update as updateCurrentRangeWithMode sets this if necessary. + [rangeController updateIfNeeded]; + } + +#if ASRangeControllerLoggingEnabled + NSLog(@"+[ASRangeController willEnterForeground] with controllers, after foregrounding: %@", allRangeControllers); +#endif +} + +#pragma mark - Debugging + +#if AS_RANGECONTROLLER_LOG_UPDATE_FREQ +- (void)_updateCountDisplayLinkDidFire +{ + if (_updateCountThisFrame > 1) { + NSLog(@"ASRangeController %p updated %lu times this frame.", self, (unsigned long)_updateCountThisFrame); + } + _updateCountThisFrame = 0; +} +#endif + +- (NSString *)descriptionWithIndexPaths:(NSArray *)indexPaths +{ + NSMutableString *description = [NSMutableString stringWithFormat:@"%@ %@", [super description], @" allPreviousIndexPaths:\n"]; + for (NSIndexPath *indexPath in indexPaths) { + ASDisplayNode *node = [[_dataSource elementMapForRangeController:self] elementForItemAtIndexPath:indexPath].nodeIfAllocated; + ASInterfaceState interfaceState = node.interfaceState; + BOOL inVisible = ASInterfaceStateIncludesVisible(interfaceState); + BOOL inDisplay = ASInterfaceStateIncludesDisplay(interfaceState); + BOOL inPreload = ASInterfaceStateIncludesPreload(interfaceState); + [description appendFormat:@"indexPath %@, Visible: %d, Display: %d, Preload: %d\n", indexPath, inVisible, inDisplay, inPreload]; + } + return description; +} + +- (NSString *)description +{ + NSArray *indexPaths = [[_allPreviousIndexPaths allObjects] sortedArrayUsingSelector:@selector(compare:)]; + return [self descriptionWithIndexPaths:indexPaths]; +} + +@end + +@implementation ASDisplayNode (RangeModeConfiguring) + ++ (void)setRangeModeForMemoryWarnings:(ASLayoutRangeMode)rangeMode +{ + [ASRangeController setRangeModeForMemoryWarnings:rangeMode]; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASRangeControllerUpdateRangeProtocol+Beta.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASRangeControllerUpdateRangeProtocol+Beta.h new file mode 100644 index 0000000..4c5b08a --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASRangeControllerUpdateRangeProtocol+Beta.h @@ -0,0 +1,21 @@ +// +// ASRangeControllerUpdateRangeProtocol+Beta.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +@protocol ASRangeControllerUpdateRangeProtocol + +/** + * Updates the current range mode of the range controller for at least the next range update + * and, if the new mode is different from the previous mode, enqueues a range update. + */ +- (void)updateCurrentRangeWithMode:(ASLayoutRangeMode)rangeMode; + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASRecursiveUnfairLock.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASRecursiveUnfairLock.h new file mode 100644 index 0000000..bf99f7e --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASRecursiveUnfairLock.h @@ -0,0 +1,47 @@ +// +// ASRecursiveUnfairLock.h +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import +#import + +// Note: We don't use ATOMIC_VAR_INIT here because C++ compilers don't like it, +// and it literally does absolutely nothing. +#define AS_RECURSIVE_UNFAIR_LOCK_INIT ((ASRecursiveUnfairLock){ OS_UNFAIR_LOCK_INIT, NULL, 0}) + +NS_ASSUME_NONNULL_BEGIN + +OS_UNFAIR_LOCK_AVAILABILITY +typedef struct { + os_unfair_lock _lock OS_UNFAIR_LOCK_AVAILABILITY; + _Atomic(pthread_t) _thread; + int _count; // Protected by lock +} ASRecursiveUnfairLock; + +/** + * Lock, blocking if needed. + */ +ASDK_EXTERN OS_UNFAIR_LOCK_AVAILABILITY +void ASRecursiveUnfairLockLock(ASRecursiveUnfairLock *l); + +/** + * Try to lock without blocking. Returns whether we took the lock. + */ +ASDK_EXTERN OS_UNFAIR_LOCK_AVAILABILITY +BOOL ASRecursiveUnfairLockTryLock(ASRecursiveUnfairLock *l); + +/** + * Unlock. Calling this on a thread that does not own + * the lock will result in an assertion failure, and undefined + * behavior if foundation assertions are disabled. + */ +ASDK_EXTERN OS_UNFAIR_LOCK_AVAILABILITY +void ASRecursiveUnfairLockUnlock(ASRecursiveUnfairLock *l); + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASRecursiveUnfairLock.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASRecursiveUnfairLock.mm new file mode 100644 index 0000000..d6eb188 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASRecursiveUnfairLock.mm @@ -0,0 +1,87 @@ +// +// ASRecursiveUnfairLock.mm +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import "ASRecursiveUnfairLock.h" + +#import + +/** + * Since the lock itself is a memory barrier, we only need memory_order_relaxed for our + * thread atomic. That guarantees we won't have torn writes, but otherwise no ordering + * is required. + */ +#define rul_set_thread(l, t) atomic_store_explicit(&l->_thread, t, memory_order_relaxed) +#define rul_get_thread(l) atomic_load_explicit(&l->_thread, memory_order_relaxed) + +OS_UNFAIR_LOCK_AVAILABILITY +NS_INLINE void ASRecursiveUnfairLockDidAcquire(ASRecursiveUnfairLock *l, pthread_t tid) { + NSCAssert(pthread_equal(rul_get_thread(l), NULL) && l->_count == 0, @"Unfair lock error"); + rul_set_thread(l, tid); +} + +OS_UNFAIR_LOCK_AVAILABILITY +NS_INLINE void ASRecursiveUnfairLockWillRelease(ASRecursiveUnfairLock *l) { + NSCAssert(pthread_equal(rul_get_thread(l), pthread_self()) && l->_count == 0, @"Unfair lock error"); + rul_set_thread(l, NULL); +} + +OS_UNFAIR_LOCK_AVAILABILITY +NS_INLINE void ASRecursiveUnfairLockAssertHeld(ASRecursiveUnfairLock *l) { + NSCAssert(pthread_equal(rul_get_thread(l), pthread_self()) && l->_count > 0, @"Unfair lock error"); +} + +void ASRecursiveUnfairLockLock(ASRecursiveUnfairLock *l) +{ + // Try to lock without blocking. If we fail, check what thread owns it. + // Note that the owning thread CAN CHANGE freely, but if the thread is specifically `self` then we know + // it is a recursive call, because we clear it before unlocking, and only `self` can set it + // to `self`. + + const pthread_t s = pthread_self(); + if (pthread_equal(rul_get_thread(l), s)) { + // Owned by self (recursive lock.) nop. + ASRecursiveUnfairLockAssertHeld(l); + } else { + os_unfair_lock_lock(&l->_lock); + ASRecursiveUnfairLockDidAcquire(l, s); + } + + l->_count++; +} + +BOOL ASRecursiveUnfairLockTryLock(ASRecursiveUnfairLock *l) { + // Same as Lock above. See comments there. + const pthread_t s = pthread_self(); + if (pthread_equal(rul_get_thread(l), s)) { + ASRecursiveUnfairLockAssertHeld(l); + } else if (os_unfair_lock_trylock(&l->_lock)) { + ASRecursiveUnfairLockDidAcquire(l, s); + } else { + // Owned by other thread. Fail. + return NO; + } + + l->_count++; + return YES; +} + +void ASRecursiveUnfairLockUnlock(ASRecursiveUnfairLock *l) +{ + // Ensure we have the lock. This check may miss some pathological cases, + // but it'll catch 99.999999% of this serious programmer error. + NSCAssert(pthread_equal(rul_get_thread(l), pthread_self()), @"Unlocking from a different thread than locked."); + + if (0 == --l->_count) { + // Note that we have to clear this before unlocking because, if another thread + // succeeds in locking above, but hasn't managed to update _thread, and we + // try to re-lock, and fail the -tryLock, and read _thread, then we'll mistakenly + // think that we still own the lock and proceed without blocking. + ASRecursiveUnfairLockWillRelease(l); + os_unfair_lock_unlock(&l->_lock); + } +} diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASScrollDirection.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASScrollDirection.h new file mode 100644 index 0000000..14317ba --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASScrollDirection.h @@ -0,0 +1,37 @@ +// +// ASScrollDirection.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +#import + +NS_ASSUME_NONNULL_BEGIN + +typedef NS_OPTIONS(unsigned char, ASScrollDirection) { + ASScrollDirectionNone = 0, + ASScrollDirectionRight = 1 << 0, + ASScrollDirectionLeft = 1 << 1, + ASScrollDirectionUp = 1 << 2, + ASScrollDirectionDown = 1 << 3 +}; + +ASDK_EXTERN const ASScrollDirection ASScrollDirectionHorizontalDirections; +ASDK_EXTERN const ASScrollDirection ASScrollDirectionVerticalDirections; + +ASDK_EXTERN BOOL ASScrollDirectionContainsVerticalDirection(ASScrollDirection scrollDirection); +ASDK_EXTERN BOOL ASScrollDirectionContainsHorizontalDirection(ASScrollDirection scrollDirection); + +ASDK_EXTERN BOOL ASScrollDirectionContainsRight(ASScrollDirection scrollDirection); +ASDK_EXTERN BOOL ASScrollDirectionContainsLeft(ASScrollDirection scrollDirection); +ASDK_EXTERN BOOL ASScrollDirectionContainsUp(ASScrollDirection scrollDirection); +ASDK_EXTERN BOOL ASScrollDirectionContainsDown(ASScrollDirection scrollDirection); +ASDK_EXTERN ASScrollDirection ASScrollDirectionApplyTransform(ASScrollDirection scrollDirection, CGAffineTransform transform); + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASScrollDirection.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASScrollDirection.mm new file mode 100644 index 0000000..3dff6ba --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASScrollDirection.mm @@ -0,0 +1,64 @@ +// +// ASScrollDirection.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +const ASScrollDirection ASScrollDirectionHorizontalDirections = ASScrollDirectionLeft | ASScrollDirectionRight; +const ASScrollDirection ASScrollDirectionVerticalDirections = ASScrollDirectionUp | ASScrollDirectionDown; + +BOOL ASScrollDirectionContainsVerticalDirection(ASScrollDirection scrollDirection) { + return (scrollDirection & ASScrollDirectionVerticalDirections) != 0; +} + +BOOL ASScrollDirectionContainsHorizontalDirection(ASScrollDirection scrollDirection) { + return (scrollDirection & ASScrollDirectionHorizontalDirections) != 0; +} + +BOOL ASScrollDirectionContainsRight(ASScrollDirection scrollDirection) { + return (scrollDirection & ASScrollDirectionRight) != 0; +} + +BOOL ASScrollDirectionContainsLeft(ASScrollDirection scrollDirection) { + return (scrollDirection & ASScrollDirectionLeft) != 0; +} + +BOOL ASScrollDirectionContainsUp(ASScrollDirection scrollDirection) { + return (scrollDirection & ASScrollDirectionUp) != 0; +} + +BOOL ASScrollDirectionContainsDown(ASScrollDirection scrollDirection) { + return (scrollDirection & ASScrollDirectionDown) != 0; +} + +ASScrollDirection ASScrollDirectionInvertHorizontally(ASScrollDirection scrollDirection) { + if (scrollDirection == ASScrollDirectionRight) { + return ASScrollDirectionLeft; + } else if (scrollDirection == ASScrollDirectionLeft) { + return ASScrollDirectionRight; + } + return scrollDirection; +} + +ASScrollDirection ASScrollDirectionInvertVertically(ASScrollDirection scrollDirection) { + if (scrollDirection == ASScrollDirectionUp) { + return ASScrollDirectionDown; + } else if (scrollDirection == ASScrollDirectionDown) { + return ASScrollDirectionUp; + } + return scrollDirection; +} + +ASScrollDirection ASScrollDirectionApplyTransform(ASScrollDirection scrollDirection, CGAffineTransform transform) { + if ((transform.a < 0) && ASScrollDirectionContainsHorizontalDirection(scrollDirection)) { + return ASScrollDirectionInvertHorizontally(scrollDirection); + } else if ((transform.d < 0) && ASScrollDirectionContainsVerticalDirection(scrollDirection)) { + return ASScrollDirectionInvertVertically(scrollDirection); + } + return scrollDirection; +} diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASSectionContext.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASSectionContext.h new file mode 100644 index 0000000..8112cb2 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASSectionContext.h @@ -0,0 +1,22 @@ +// +// ASSectionContext.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +@class ASCollectionView; + +@protocol ASSectionContext + +/** + * Custom name of this section, for debugging only. + */ +@property (nonatomic, copy, nullable) NSString *sectionName; +@property (nonatomic, weak, nullable) ASCollectionView *collectionView; + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASTableLayoutController.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASTableLayoutController.h new file mode 100644 index 0000000..385a6a4 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASTableLayoutController.h @@ -0,0 +1,29 @@ +// +// ASTableLayoutController.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@class UITableView; + +/** + * A layout controller designed for use with UITableView. + */ +AS_SUBCLASSING_RESTRICTED +@interface ASTableLayoutController : ASAbstractLayoutController + +@property (nonatomic, weak, readonly) UITableView *tableView; + +- (instancetype)initWithTableView:(UITableView *)tableView; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASTableLayoutController.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASTableLayoutController.mm new file mode 100644 index 0000000..46429ca --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASTableLayoutController.mm @@ -0,0 +1,54 @@ +// +// ASTableLayoutController.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import + +#import +#import + +@interface ASTableLayoutController() +@end + +@implementation ASTableLayoutController + +- (instancetype)initWithTableView:(UITableView *)tableView +{ + if (!(self = [super init])) { + return nil; + } + _tableView = tableView; + return self; +} + +#pragma mark - ASLayoutController + +- (NSHashTable *)elementsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType map:(ASElementMap *)map +{ + CGRect bounds = _tableView.bounds; + + ASRangeTuningParameters tuningParameters = [self tuningParametersForRangeMode:rangeMode rangeType:rangeType]; + CGRect rangeBounds = CGRectExpandToRangeWithScrollableDirections(bounds, tuningParameters, ASScrollDirectionVerticalDirections, scrollDirection); + NSArray *array = [_tableView indexPathsForRowsInRect:rangeBounds]; + return ASPointerTableByFlatMapping(array, NSIndexPath *indexPath, [map elementForItemAtIndexPath:indexPath]); +} + +- (void)allElementsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode displaySet:(NSHashTable *__autoreleasing _Nullable *)displaySet preloadSet:(NSHashTable *__autoreleasing _Nullable *)preloadSet map:(ASElementMap *)map +{ + if (displaySet == NULL || preloadSet == NULL) { + return; + } + + *displaySet = [self elementsForScrolling:scrollDirection rangeMode:rangeMode rangeType:ASLayoutRangeTypeDisplay map:map]; + *preloadSet = [self elementsForScrolling:scrollDirection rangeMode:rangeMode rangeType:ASLayoutRangeTypePreload map:map]; + return; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASThread.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASThread.h new file mode 100644 index 0000000..0ef3d32 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASThread.h @@ -0,0 +1,333 @@ +// +// ASThread.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import +#import + +#import +#import +#import +#import +#import +#import +#import + +ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT BOOL ASDisplayNodeThreadIsMain() +{ + return 0 != pthread_main_np(); +} + +/** + * Adds the lock to the current scope. + * + * A C version of the C++ lockers. Pass in any id. + * One benefit this has over C++ lockers is that the lock is retained. We + * had bugs in the past where an object would be deallocated while someone + * had locked its instanceLock, and we'd get a crash. This macro + * retains the locked object until it can be unlocked, which is nice. + */ +#define ASLockScope(nsLocking) \ + id __lockToken __attribute__((cleanup(_ASLockScopeCleanup))) NS_VALID_UNTIL_END_OF_SCOPE = nsLocking; \ + [__lockToken lock]; + +/// Same as ASLockScope(1) but lock isn't retained (be careful). +#define ASLockScopeUnowned(nsLocking) \ + unowned id __lockToken __attribute__((cleanup(_ASLockScopeUnownedCleanup))) = nsLocking; \ + [__lockToken lock]; + +ASDISPLAYNODE_INLINE void _ASLockScopeCleanup(id __strong * const lockPtr) { + [*lockPtr unlock]; +} + +ASDISPLAYNODE_INLINE void _ASLockScopeUnownedCleanup(id unowned * const lockPtr) { + [*lockPtr unlock]; +} + +/** + * Same as ASLockScope(1) but it uses self, so we can skip retain/release. + */ +#define ASLockScopeSelf() ASLockScopeUnowned(self) + +/// One-liner while holding the lock. +#define ASLocked(nsLocking, expr) ({ ASLockScope(nsLocking); expr; }) + +/// Faster self-version. +#define ASLockedSelf(expr) ({ ASLockScopeSelf(); expr; }) + +#define ASLockedSelfCompareAssign(lvalue, newValue) \ + ASLockedSelf(ASCompareAssign(lvalue, newValue)) + +#define ASLockedSelfCompareAssignObjects(lvalue, newValue) \ + ASLockedSelf(ASCompareAssignObjects(lvalue, newValue)) + +#define ASLockedSelfCompareAssignCustom(lvalue, newValue, isequal) \ + ASLockedSelf(ASCompareAssignCustom(lvalue, newValue, isequal)) + +#define ASLockedSelfCompareAssignCopy(lvalue, obj) \ + ASLockedSelf(ASCompareAssignCopy(lvalue, obj)) + +#define ASUnlockScope(nsLocking) \ + id __lockToken __attribute__((cleanup(_ASUnlockScopeCleanup))) NS_VALID_UNTIL_END_OF_SCOPE = nsLocking; \ + [__lockToken unlock]; + +#define ASSynthesizeLockingMethodsWithMutex(mutex) \ +- (void)lock { mutex.lock(); } \ +- (void)unlock { mutex.unlock(); } \ +- (BOOL)tryLock { return (BOOL)mutex.try_lock(); } + +#define ASSynthesizeLockingMethodsWithObject(object) \ +- (void)lock { [object lock]; } \ +- (void)unlock { [object unlock]; } \ +- (BOOL)tryLock { return [object tryLock]; } + +ASDISPLAYNODE_INLINE void _ASUnlockScopeCleanup(id __strong *lockPtr) { + [*lockPtr lock]; +} + +#ifdef __cplusplus + +#include +#include +#include +#include + +// These macros are here for legacy reasons. We may get rid of them later. +#define DISABLED_ASAssertLocked(m) +#define DISABLED_ASAssertUnlocked(m) + +namespace AS { + + // Set once in Mutex constructor. Linker fails if this is a member variable. ?? + static bool gMutex_unfair; + +// Silence unguarded availability warnings in here, because +// perf is critical and we will check availability once +// and not again. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunguarded-availability" + class Mutex + { + public: + /// Constructs a plain mutex (the default). + Mutex () : Mutex (false) {} + + void SetDebugNameWithObject(id object) { +#if ASEnableVerboseLogging && ASDISPLAYNODE_ASSERTIONS_ENABLED + _debug_name = std::string(ASObjectDescriptionMakeTiny(object).UTF8String); +#endif + } + + ~Mutex () { + // Manually destroy since unions can't do it. + switch (_type) { + case Plain: + _plain.~mutex(); + break; + case Recursive: + _recursive.~recursive_mutex(); + break; + case Unfair: + // nop + break; + case RecursiveUnfair: + // nop + break; + } + } + + Mutex (const Mutex&) = delete; + Mutex &operator=(const Mutex&) = delete; + + bool try_lock() { + bool success = false; + switch (_type) { + case Plain: + success = _plain.try_lock(); + break; + case Recursive: + success = _recursive.try_lock(); + break; + case Unfair: + success = os_unfair_lock_trylock(&_unfair); + break; + case RecursiveUnfair: + if (@available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *)) { + success = ASRecursiveUnfairLockTryLock(&_runfair); + } else { + success = _recursive.try_lock(); + } + break; + } + if (success) { + DidLock(); + } + return success; + } + + void lock() { + switch (_type) { + case Plain: + _plain.lock(); + break; + case Recursive: + _recursive.lock(); + break; + case Unfair: + os_unfair_lock_lock(&_unfair); + break; + case RecursiveUnfair: + if (@available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *)) { + ASRecursiveUnfairLockLock(&_runfair); + } else { + _recursive.lock(); + } + break; + } + DidLock(); + } + + void unlock() { + WillUnlock(); + switch (_type) { + case Plain: + _plain.unlock(); + break; + case Recursive: + _recursive.unlock(); + break; + case Unfair: + os_unfair_lock_unlock(&_unfair); + break; + case RecursiveUnfair: + if (@available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *)) { + ASRecursiveUnfairLockUnlock(&_runfair); + } else { + _recursive.unlock(); + } + break; + } + } + + void AssertHeld() { + ASDisplayNodeCAssert(_owner == std::this_thread::get_id(), @"Thread should hold lock"); + } + + void AssertNotHeld() { + ASDisplayNodeCAssert(_owner != std::this_thread::get_id(), @"Thread should not hold lock"); + } + + explicit Mutex (bool recursive) { + + // Check if we can use unfair lock and store in static var. + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + if (AS_AVAILABLE_IOS_TVOS(10, 10)) { + gMutex_unfair = YES; + } + }); + + if (recursive) { + if (gMutex_unfair) { + _type = RecursiveUnfair; + if (@available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *)) { + _runfair = AS_RECURSIVE_UNFAIR_LOCK_INIT; + } else { + new (&_recursive) std::recursive_mutex(); + } + } else { + _type = Recursive; + new (&_recursive) std::recursive_mutex(); + } + } else { + if (gMutex_unfair) { + _type = Unfair; + _unfair = OS_UNFAIR_LOCK_INIT; + } else { + _type = Plain; + new (&_plain) std::mutex(); + } + } + } + + private: + enum Type { + Plain, + Recursive, + Unfair, + RecursiveUnfair + }; + + void WillUnlock() { +#if ASDISPLAYNODE_ASSERTIONS_ENABLED +#if ASEnableVerboseLogging + if (!_debug_name.empty()) { + as_log_verbose(ASLockingLog(), "unlock %s, count is %d", _debug_name.c_str(), (int)(_count - 1)); + } +#endif + if (--_count == 0) { + _owner = std::thread::id(); + } +#endif + } + + void DidLock() { +#if ASDISPLAYNODE_ASSERTIONS_ENABLED +#if ASEnableVerboseLogging + if (!_debug_name.empty()) { + as_log_verbose(ASLockingLog(), "lock %s, count is %d", _debug_name.c_str(), (int)(_count + 1)); + } +#endif + if (++_count == 1) { + // New owner. + _owner = std::this_thread::get_id(); + } +#endif + } + + Type _type; + union { + os_unfair_lock _unfair; + ASRecursiveUnfairLock _runfair; + std::mutex _plain; + std::recursive_mutex _recursive; + }; +#if ASEnableVerboseLogging + std::string _debug_name; +#endif + +#if ASDISPLAYNODE_ASSERTIONS_ENABLED + std::thread::id _owner = std::thread::id(); + int _count = 0; +#endif + }; +#pragma clang diagnostic pop // ignored "-Wunguarded-availability" + + /** + Obj-C doesn't allow you to pass parameters to C++ ivar constructors. + Provide a convenience to change the default from non-recursive to recursive. + + But wait! Recursive mutexes are a bad idea. Think twice before using one: + + http://www.zaval.org/resources/library/butenhof1.html + http://www.fieryrobot.com/blog/2008/10/14/recursive-locks-will-kill-you/ + */ + class RecursiveMutex : public Mutex + { + public: + RecursiveMutex () : Mutex (true) {} + }; + + typedef std::lock_guard MutexLocker; + typedef std::unique_lock UniqueLock; + +} // namespace AS + +#endif /* __cplusplus */ diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASTraitCollection.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASTraitCollection.h new file mode 100644 index 0000000..7adb197 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASTraitCollection.h @@ -0,0 +1,179 @@ +// +// ASTraitCollection.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + + +#import + +#import + +@class ASTraitCollection; +@protocol ASLayoutElement; +@protocol ASTraitEnvironment; + +NS_ASSUME_NONNULL_BEGIN + +#pragma mark - ASPrimitiveTraitCollection + +/** + * @abstract This is an internal struct-representation of ASTraitCollection. + * + * @discussion This struct is for internal use only. Framework users should always use ASTraitCollection. + * + * If you use ASPrimitiveTraitCollection, please do make sure to initialize it with ASPrimitiveTraitCollectionMakeDefault() + * or ASPrimitiveTraitCollectionFromUITraitCollection(UITraitCollection*). + */ +#pragma clang diagnostic push +#pragma clang diagnostic warning "-Wpadded" +typedef struct { + UIUserInterfaceSizeClass horizontalSizeClass; + UIUserInterfaceSizeClass verticalSizeClass; + + CGFloat displayScale; + UIDisplayGamut displayGamut API_AVAILABLE(ios(10.0)); + + UIUserInterfaceIdiom userInterfaceIdiom; + UIForceTouchCapability forceTouchCapability; + UITraitEnvironmentLayoutDirection layoutDirection API_AVAILABLE(ios(10.0)); + UIUserInterfaceStyle userInterfaceStyle API_AVAILABLE(tvos(10.0), ios(12.0)); + + + // NOTE: This must be a constant. We will assert. + unowned UIContentSizeCategory preferredContentSizeCategory API_AVAILABLE(ios(10.0)); + + CGSize containerSize; + +#if TARGET_OS_IOS + UIUserInterfaceLevel userInterfaceLevel API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos); +#endif + UIAccessibilityContrast accessibilityContrast API_AVAILABLE(ios(13.0)); + UILegibilityWeight legibilityWeight API_AVAILABLE(ios(13.0)); +} ASPrimitiveTraitCollection; +#pragma clang diagnostic pop + +/** + * Creates ASPrimitiveTraitCollection with default values. + */ +ASDK_EXTERN ASPrimitiveTraitCollection ASPrimitiveTraitCollectionMakeDefault(void); + +/** + * Creates a ASPrimitiveTraitCollection from a given UITraitCollection. + */ +ASDK_EXTERN ASPrimitiveTraitCollection ASPrimitiveTraitCollectionFromUITraitCollection(UITraitCollection *traitCollection); + +/** + * Creates a UITraitCollection from a given ASPrimitiveTraitCollection. + */ +ASDK_EXTERN UITraitCollection * ASPrimitiveTraitCollectionToUITraitCollection(ASPrimitiveTraitCollection traitCollection); + + +/** + * Compares two ASPrimitiveTraitCollection to determine if they are the same. + */ +ASDK_EXTERN BOOL ASPrimitiveTraitCollectionIsEqualToASPrimitiveTraitCollection(ASPrimitiveTraitCollection lhs, ASPrimitiveTraitCollection rhs); + +/** + * Returns a string representation of a ASPrimitiveTraitCollection. + */ +ASDK_EXTERN NSString *NSStringFromASPrimitiveTraitCollection(ASPrimitiveTraitCollection traits); + +/** + * This function will walk the layout element hierarchy and updates the layout element trait collection for every + * layout element within the hierarchy. + */ +ASDK_EXTERN void ASTraitCollectionPropagateDown(id element, ASPrimitiveTraitCollection traitCollection); + +/** + * Abstraction on top of UITraitCollection for propagation within AsyncDisplayKit-Layout + */ +@protocol ASTraitEnvironment + +/** + * @abstract Returns a struct-representation of the environment's ASEnvironmentDisplayTraits. + * + * @discussion This only exists as an internal convenience method. Users should access the trait collections through + * the NSObject based asyncTraitCollection API + */ +- (ASPrimitiveTraitCollection)primitiveTraitCollection; + +/** + * @abstract Sets a trait collection on this environment state. + * + * @discussion This only exists as an internal convenience method. Users should not override trait collection using it. + * Use [ASDKViewController overrideDisplayTraitsWithTraitCollection] block instead. + */ +- (void)setPrimitiveTraitCollection:(ASPrimitiveTraitCollection)traitCollection; + +/** + * @abstract Returns the thread-safe UITraitCollection equivalent. + */ +- (ASTraitCollection *)asyncTraitCollection; + +@end + +#define ASLayoutElementCollectionTableSetTraitCollection(lock) \ +- (void)setPrimitiveTraitCollection:(ASPrimitiveTraitCollection)traitCollection\ +{\ + AS::MutexLocker l(lock);\ +\ + ASPrimitiveTraitCollection oldTraits = self.primitiveTraitCollection;\ + [super setPrimitiveTraitCollection:traitCollection];\ +\ + /* Extra Trait Collection Handling */\ +\ + /* If the node is not loaded yet don't do anything as otherwise the access of the view will trigger a load */\ + if (! self.isNodeLoaded) { return; }\ +\ + ASPrimitiveTraitCollection currentTraits = self.primitiveTraitCollection;\ + if (ASPrimitiveTraitCollectionIsEqualToASPrimitiveTraitCollection(currentTraits, oldTraits) == NO) {\ + [self.dataController environmentDidChange];\ + }\ +}\ + +#pragma mark - ASTraitCollection + +AS_SUBCLASSING_RESTRICTED +@interface ASTraitCollection : NSObject + +@property (readonly) UIUserInterfaceSizeClass horizontalSizeClass; +@property (readonly) UIUserInterfaceSizeClass verticalSizeClass; + +@property (readonly) CGFloat displayScale; +@property (readonly) UIDisplayGamut displayGamut API_AVAILABLE(ios(10.0)); + +@property (readonly) UIUserInterfaceIdiom userInterfaceIdiom; +@property (readonly) UIForceTouchCapability forceTouchCapability; +@property (readonly) UITraitEnvironmentLayoutDirection layoutDirection API_AVAILABLE(ios(10.0)); +@property (readonly) UIUserInterfaceStyle userInterfaceStyle API_AVAILABLE(tvos(10.0), ios(12.0)); +@property (readonly) UIContentSizeCategory preferredContentSizeCategory API_AVAILABLE(ios(10.0)); + +@property (readonly) CGSize containerSize; + +#if TARGET_OS_IOS +@property (readonly) UIUserInterfaceLevel userInterfaceLevel API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos); +#endif + +@property (readonly) UIAccessibilityContrast accessibilityContrast API_AVAILABLE(ios(13.0)); +@property (readonly) UILegibilityWeight legibilityWeight API_AVAILABLE(ios(13.0)); + +- (BOOL)isEqualToTraitCollection:(ASTraitCollection *)traitCollection; + +@end + +/** + * These are internal helper methods. Should never be called by the framework users. + */ +@interface ASTraitCollection (PrimitiveTraits) + ++ (ASTraitCollection *)traitCollectionWithASPrimitiveTraitCollection:(ASPrimitiveTraitCollection)traits NS_RETURNS_RETAINED; + +- (ASPrimitiveTraitCollection)primitiveTraitCollection; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASTraitCollection.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASTraitCollection.mm new file mode 100644 index 0000000..f26a2be --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASTraitCollection.mm @@ -0,0 +1,366 @@ +// +// ASTraitCollection.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import +#import +#import + +#pragma mark - ASPrimitiveTraitCollection + +void ASTraitCollectionPropagateDown(id element, ASPrimitiveTraitCollection traitCollection) { + if (element) { + element.primitiveTraitCollection = traitCollection; + } + + for (id subelement in element.sublayoutElements) { + ASTraitCollectionPropagateDown(subelement, traitCollection); + } +} + +ASPrimitiveTraitCollection ASPrimitiveTraitCollectionMakeDefault() { + ASPrimitiveTraitCollection tc = {}; + tc.userInterfaceIdiom = UIUserInterfaceIdiomUnspecified; + tc.forceTouchCapability = UIForceTouchCapabilityUnknown; + tc.displayScale = 0.0; + tc.horizontalSizeClass = UIUserInterfaceSizeClassUnspecified; + tc.verticalSizeClass = UIUserInterfaceSizeClassUnspecified; + tc.containerSize = CGSizeZero; + if (AS_AVAILABLE_IOS_TVOS(10, 10)) { + tc.displayGamut = UIDisplayGamutUnspecified; + tc.preferredContentSizeCategory = UIContentSizeCategoryUnspecified; + tc.layoutDirection = UITraitEnvironmentLayoutDirectionUnspecified; + } + if (AS_AVAILABLE_IOS_TVOS(12, 10)) { + tc.userInterfaceStyle = UIUserInterfaceStyleUnspecified; + } + +#if TARGET_OS_IOS + if(AS_AVAILABLE_IOS(13)){ + tc.userInterfaceLevel = UIUserInterfaceLevelUnspecified; + } +#endif + + if (AS_AVAILABLE_IOS_TVOS(13, 13)) { + tc.accessibilityContrast = UIAccessibilityContrastUnspecified; + tc.legibilityWeight = UILegibilityWeightUnspecified; + } + return tc; +} + +ASPrimitiveTraitCollection ASPrimitiveTraitCollectionFromUITraitCollection(UITraitCollection *traitCollection) { + ASPrimitiveTraitCollection environmentTraitCollection = ASPrimitiveTraitCollectionMakeDefault(); + environmentTraitCollection.horizontalSizeClass = traitCollection.horizontalSizeClass; + environmentTraitCollection.verticalSizeClass = traitCollection.verticalSizeClass; + environmentTraitCollection.displayScale = traitCollection.displayScale; + environmentTraitCollection.userInterfaceIdiom = traitCollection.userInterfaceIdiom; + environmentTraitCollection.forceTouchCapability = traitCollection.forceTouchCapability; + if (AS_AVAILABLE_IOS_TVOS(10, 10)) { + environmentTraitCollection.displayGamut = traitCollection.displayGamut; + environmentTraitCollection.layoutDirection = traitCollection.layoutDirection; + + ASDisplayNodeCAssertPermanent(traitCollection.preferredContentSizeCategory); + environmentTraitCollection.preferredContentSizeCategory = traitCollection.preferredContentSizeCategory; + } + if (AS_AVAILABLE_IOS_TVOS(12, 10)) { + environmentTraitCollection.userInterfaceStyle = traitCollection.userInterfaceStyle; + } + +#if TARGET_OS_IOS + if(AS_AVAILABLE_IOS(13)){ + environmentTraitCollection.userInterfaceLevel = traitCollection.userInterfaceLevel; + } +#endif + + if (AS_AVAILABLE_IOS_TVOS(13, 13)) { + environmentTraitCollection.accessibilityContrast = traitCollection.accessibilityContrast; + environmentTraitCollection.legibilityWeight = traitCollection.legibilityWeight; + } + return environmentTraitCollection; +} + +ASDK_EXTERN UITraitCollection * ASPrimitiveTraitCollectionToUITraitCollection(ASPrimitiveTraitCollection traitCollection) { + NSMutableArray *collections = [[NSMutableArray alloc] initWithArray:@[ + [UITraitCollection traitCollectionWithHorizontalSizeClass:traitCollection.horizontalSizeClass], + [UITraitCollection traitCollectionWithVerticalSizeClass:traitCollection.verticalSizeClass], + [UITraitCollection traitCollectionWithDisplayScale:traitCollection.displayScale], + [UITraitCollection traitCollectionWithUserInterfaceIdiom:traitCollection.userInterfaceIdiom], + [UITraitCollection traitCollectionWithForceTouchCapability:traitCollection.forceTouchCapability], + ]]; + + if (AS_AVAILABLE_IOS_TVOS(10, 10)) { + [collections addObject:[UITraitCollection traitCollectionWithDisplayGamut:traitCollection.displayGamut]]; + [collections addObject:[UITraitCollection traitCollectionWithLayoutDirection:traitCollection.layoutDirection]]; + [collections addObject:[UITraitCollection traitCollectionWithPreferredContentSizeCategory:traitCollection.preferredContentSizeCategory]]; + } + if (AS_AVAILABLE_IOS_TVOS(12, 10)) { + [collections addObject:[UITraitCollection traitCollectionWithUserInterfaceStyle:traitCollection.userInterfaceStyle]]; + } + + UITraitCollection *result = [UITraitCollection traitCollectionWithTraitsFromCollections:collections]; + return result; +} + +BOOL ASPrimitiveTraitCollectionIsEqualToASPrimitiveTraitCollection(ASPrimitiveTraitCollection lhs, ASPrimitiveTraitCollection rhs) { + return !memcmp(&lhs, &rhs, sizeof(ASPrimitiveTraitCollection)); +} + +// Named so as not to conflict with a hidden Apple function, in case compiler decides not to inline +ASDISPLAYNODE_INLINE NSString *AS_NSStringFromUIUserInterfaceIdiom(UIUserInterfaceIdiom idiom) { + switch (idiom) { + case UIUserInterfaceIdiomTV: + return @"TV"; + case UIUserInterfaceIdiomPad: + return @"Pad"; + case UIUserInterfaceIdiomPhone: + return @"Phone"; + case UIUserInterfaceIdiomCarPlay: + return @"CarPlay"; + default: + return @"Unspecified"; + } +} + +// Named so as not to conflict with a hidden Apple function, in case compiler decides not to inline +ASDISPLAYNODE_INLINE NSString *AS_NSStringFromUIForceTouchCapability(UIForceTouchCapability capability) { + switch (capability) { + case UIForceTouchCapabilityAvailable: + return @"Available"; + case UIForceTouchCapabilityUnavailable: + return @"Unavailable"; + default: + return @"Unknown"; + } +} + +// Named so as not to conflict with a hidden Apple function, in case compiler decides not to inline +ASDISPLAYNODE_INLINE NSString *AS_NSStringFromUIUserInterfaceSizeClass(UIUserInterfaceSizeClass sizeClass) { + switch (sizeClass) { + case UIUserInterfaceSizeClassCompact: + return @"Compact"; + case UIUserInterfaceSizeClassRegular: + return @"Regular"; + default: + return @"Unspecified"; + } +} + +// Named so as not to conflict with a hidden Apple function, in case compiler decides not to inline +API_AVAILABLE(ios(10)) +ASDISPLAYNODE_INLINE NSString *AS_NSStringFromUIDisplayGamut(UIDisplayGamut displayGamut) { + switch (displayGamut) { + case UIDisplayGamutSRGB: + return @"sRGB"; + case UIDisplayGamutP3: + return @"P3"; + default: + return @"Unspecified"; + } +} + +// Named so as not to conflict with a hidden Apple function, in case compiler decides not to inline +API_AVAILABLE(ios(10)) +ASDISPLAYNODE_INLINE NSString *AS_NSStringFromUITraitEnvironmentLayoutDirection(UITraitEnvironmentLayoutDirection layoutDirection) { + switch (layoutDirection) { + case UITraitEnvironmentLayoutDirectionLeftToRight: + return @"LeftToRight"; + case UITraitEnvironmentLayoutDirectionRightToLeft: + return @"RightToLeft"; + default: + return @"Unspecified"; + } +} + +// Named so as not to conflict with a hidden Apple function, in case compiler decides not to inline +API_AVAILABLE(tvos(10.0), ios(12.0)) +ASDISPLAYNODE_INLINE NSString *AS_NSStringFromUIUserInterfaceStyle(UIUserInterfaceStyle userInterfaceStyle) { + switch (userInterfaceStyle) { + case UIUserInterfaceStyleLight: + return @"Light"; + case UIUserInterfaceStyleDark: + return @"Dark"; + default: + return @"Unspecified"; + } +} + +#if TARGET_OS_IOS +// Named so as not to conflict with a hidden Apple function, in case compiler decides not to inline +API_AVAILABLE(ios(13)) +ASDISPLAYNODE_INLINE NSString *AS_NSStringFromUITraitEnvironmentUserInterfaceLevel(UIUserInterfaceLevel userInterfaceLevel) { + switch (userInterfaceLevel) { + case UIUserInterfaceLevelBase: + return @"Base"; + case UIUserInterfaceLevelElevated: + return @"Elevated"; + default: + return @"Unspecified"; + } +} +#endif + +// Named so as not to conflict with a hidden Apple function, in case compiler decides not to inline +API_AVAILABLE(ios(13)) +ASDISPLAYNODE_INLINE NSString *AS_NSStringFromUITraitEnvironmentAccessibilityContrast(UIAccessibilityContrast accessibilityContrast) { + switch (accessibilityContrast) { + case UIAccessibilityContrastNormal: + return @"Normal"; + case UIAccessibilityContrastHigh: + return @"High"; + default: + return @"Unspecified"; + } +} + +// Named so as not to conflict with a hidden Apple function, in case compiler decides not to inline +API_AVAILABLE(ios(13)) +ASDISPLAYNODE_INLINE NSString *AS_NSStringFromUITraitEnvironmentLegibilityWeight(UILegibilityWeight legibilityWeight) { + switch (legibilityWeight) { + case UILegibilityWeightRegular: + return @"Regular"; + case UILegibilityWeightBold: + return @"Bold"; + default: + return @"Unspecified"; + } +} + + + +NSString *NSStringFromASPrimitiveTraitCollection(ASPrimitiveTraitCollection traits) { + NSMutableArray *props = [NSMutableArray array]; + [props addObject:@{ @"verticalSizeClass": AS_NSStringFromUIUserInterfaceSizeClass(traits.verticalSizeClass) }]; + [props addObject:@{ @"horizontalSizeClass": AS_NSStringFromUIUserInterfaceSizeClass(traits.horizontalSizeClass) }]; + [props addObject:@{ @"displayScale": [NSString stringWithFormat: @"%.0lf", (double)traits.displayScale] }]; + [props addObject:@{ @"userInterfaceIdiom": AS_NSStringFromUIUserInterfaceIdiom(traits.userInterfaceIdiom) }]; + [props addObject:@{ @"forceTouchCapability": AS_NSStringFromUIForceTouchCapability(traits.forceTouchCapability) }]; + if (AS_AVAILABLE_IOS_TVOS(12, 10)) { + [props addObject:@{ @"userInterfaceStyle": AS_NSStringFromUIUserInterfaceStyle(traits.userInterfaceStyle) }]; + } + if (AS_AVAILABLE_IOS_TVOS(10, 10)) { + [props addObject:@{ @"layoutDirection": AS_NSStringFromUITraitEnvironmentLayoutDirection(traits.layoutDirection) }]; + if (traits.preferredContentSizeCategory != nil) { + [props addObject:@{ @"preferredContentSizeCategory": traits.preferredContentSizeCategory }]; + } + [props addObject:@{ @"displayGamut": AS_NSStringFromUIDisplayGamut(traits.displayGamut) }]; + } + +#if TARGET_OS_IOS + if (AS_AVAILABLE_IOS(13)){ + [props addObject:@{ @"userInterfaceLevel": AS_NSStringFromUITraitEnvironmentUserInterfaceLevel(traits.userInterfaceLevel) }]; + } +#endif + + if (AS_AVAILABLE_IOS_TVOS(13, 13)) { + [props addObject:@{ @"accessibilityContrast": AS_NSStringFromUITraitEnvironmentAccessibilityContrast(traits.accessibilityContrast) }]; + [props addObject:@{ @"legibilityWeight": AS_NSStringFromUITraitEnvironmentLegibilityWeight(traits.legibilityWeight) }]; + } + [props addObject:@{ @"containerSize": NSStringFromCGSize(traits.containerSize) }]; + return ASObjectDescriptionMakeWithoutObject(props); +} + +#pragma mark - ASTraitCollection + +@implementation ASTraitCollection { + ASPrimitiveTraitCollection _prim; +} + ++ (ASTraitCollection *)traitCollectionWithASPrimitiveTraitCollection:(ASPrimitiveTraitCollection)traits NS_RETURNS_RETAINED { + ASTraitCollection *tc = [[ASTraitCollection alloc] init]; + if (AS_AVAILABLE_IOS_TVOS(10, 10)) { + ASDisplayNodeCAssertPermanent(traits.preferredContentSizeCategory); + } + tc->_prim = traits; + return tc; +} + +- (ASPrimitiveTraitCollection)primitiveTraitCollection { + return _prim; +} +- (UIUserInterfaceSizeClass)horizontalSizeClass +{ + return _prim.horizontalSizeClass; +} +-(UIUserInterfaceSizeClass)verticalSizeClass +{ + return _prim.verticalSizeClass; +} +- (CGFloat)displayScale +{ + return _prim.displayScale; +} +- (UIDisplayGamut)displayGamut +{ + return _prim.displayGamut; +} +- (UIForceTouchCapability)forceTouchCapability +{ + return _prim.forceTouchCapability; +} +- (UITraitEnvironmentLayoutDirection)layoutDirection +{ + return _prim.layoutDirection; +} +- (CGSize)containerSize +{ + return _prim.containerSize; +} + +- (UIUserInterfaceStyle)userInterfaceStyle +{ + return _prim.userInterfaceStyle; +} + +- (UIContentSizeCategory)preferredContentSizeCategory +{ + return _prim.preferredContentSizeCategory; +} + +#if TARGET_OS_IOS +- (UIUserInterfaceLevel)userInterfaceLevel +{ + return _prim.userInterfaceLevel; +} +#endif + +- (UIAccessibilityContrast)accessibilityContrast +{ + return _prim.accessibilityContrast; +} + +- (UILegibilityWeight)legibilityWeight +{ + return _prim.legibilityWeight; +} + +- (NSUInteger)hash { + return ASHashBytes(&_prim, sizeof(ASPrimitiveTraitCollection)); +} + +- (BOOL)isEqual:(id)object { + if (!object || ![object isKindOfClass:ASTraitCollection.class]) { + return NO; + } + return [self isEqualToTraitCollection:object]; +} + +- (BOOL)isEqualToTraitCollection:(ASTraitCollection *)traitCollection +{ + if (traitCollection == nil) { + return NO; + } + + if (self == traitCollection) { + return YES; + } + return ASPrimitiveTraitCollectionIsEqualToASPrimitiveTraitCollection(_prim, traitCollection->_prim); +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASWeakProxy.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASWeakProxy.h new file mode 100644 index 0000000..7396474 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASWeakProxy.h @@ -0,0 +1,32 @@ +// +// ASWeakProxy.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +AS_SUBCLASSING_RESTRICTED +@interface ASWeakProxy : NSProxy + +/** + * @return target The target which will be forwarded all messages sent to the weak proxy. + */ +@property (nonatomic, weak, readonly) id target; + +/** + * An object which forwards messages to a target which it weakly references + * + * @discussion This class is useful for breaking retain cycles. You can pass this in place + * of the target to something which creates a strong reference. All messages sent to the + * proxy will be passed onto the target. + * + * @return an instance of ASWeakProxy + */ ++ (instancetype)weakProxyWithTarget:(id)target NS_RETURNS_RETAINED; + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASWeakProxy.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASWeakProxy.mm new file mode 100644 index 0000000..4a73408 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASWeakProxy.mm @@ -0,0 +1,72 @@ +// +// ASWeakProxy.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import + +@implementation ASWeakProxy + +- (instancetype)initWithTarget:(id)target +{ + if (self) { + _target = target; + } + return self; +} + ++ (instancetype)weakProxyWithTarget:(id)target NS_RETURNS_RETAINED +{ + return [[ASWeakProxy alloc] initWithTarget:target]; +} + +- (id)forwardingTargetForSelector:(SEL)aSelector +{ + return _target; +} + +- (BOOL)respondsToSelector:(SEL)aSelector +{ + return [_target respondsToSelector:aSelector]; +} + +- (BOOL)conformsToProtocol:(Protocol *)aProtocol +{ + return [_target conformsToProtocol:aProtocol]; +} + +/// Strangely, this method doesn't get forwarded by ObjC. +- (BOOL)isKindOfClass:(Class)aClass +{ + return [_target isKindOfClass:aClass]; +} + +- (NSString *)description +{ + return ASObjectDescriptionMake(self, @[@{ @"target": _target ?: (id)kCFNull }]); +} + +- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel +{ + ASDisplayNodeAssertNil(_target, @"ASWeakProxy got %@ when its target is still alive, which is unexpected.", NSStringFromSelector(_cmd)); + // Unfortunately, in order to get this object to work properly, the use of a method which creates an NSMethodSignature + // from a C string. -methodSignatureForSelector is called when a compiled definition for the selector cannot be found. + // This is the place where we have to create our own dud NSMethodSignature. This is necessary because if this method + // returns nil, a selector not found exception is raised. The string argument to -signatureWithObjCTypes: outlines + // the return type and arguments to the message. To return a dud NSMethodSignature, pretty much any signature will + // suffice. Since the -forwardInvocation call will do nothing if the target does not respond to the selector, + // the dud NSMethodSignature simply gets us around the exception. + return [NSMethodSignature signatureWithObjCTypes:"@^v^c"]; +} +- (void)forwardInvocation:(NSInvocation *)invocation +{ + ASDisplayNodeAssertNil(_target, @"ASWeakProxy got %@ when its target is still alive, which is unexpected.", NSStringFromSelector(_cmd)); +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASWeakSet.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASWeakSet.h new file mode 100644 index 0000000..cc435de --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASWeakSet.h @@ -0,0 +1,50 @@ +// +// ASWeakSet.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * A class similar to NSSet that stores objects weakly. + * Note that this class uses NSPointerFunctionsObjectPointerPersonality – + * that is, it uses shifted pointer for hashing, and identity comparison for equality. + */ +AS_SUBCLASSING_RESTRICTED +@interface ASWeakSet<__covariant ObjectType> : NSObject + +/// Returns YES if the receiver is empty, NO otherwise. +@property (nonatomic, readonly, getter=isEmpty) BOOL empty; + +/// Returns YES if `object` is in the receiver, NO otherwise. +- (BOOL)containsObject:(ObjectType)object AS_WARN_UNUSED_RESULT; + +/// Insets `object` into the set. +- (void)addObject:(ObjectType)object; + +/// Removes object from the set. +- (void)removeObject:(ObjectType)object; + +/// Removes all objects from the set. +- (void)removeAllObjects; + +/// Returns a standard *retained* NSArray of all objects. Not free to generate, but useful for iterating over contents. +- (NSArray *)allObjects AS_WARN_UNUSED_RESULT; + +/** + * How many objects are contained in this set. + + * NOTE: This computed property is O(N). Consider using the `empty` property. + */ +@property (nonatomic, readonly) NSUInteger count; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASWeakSet.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASWeakSet.mm new file mode 100644 index 0000000..a65dbff --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/ASWeakSet.mm @@ -0,0 +1,84 @@ +// +// ASWeakSet.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +@interface ASWeakSet<__covariant ObjectType> () +@property (nonatomic, readonly) NSHashTable *hashTable; +@end + +@implementation ASWeakSet + +- (instancetype)init +{ + self = [super init]; + if (self) { + _hashTable = [NSHashTable hashTableWithOptions:NSHashTableWeakMemory | NSHashTableObjectPointerPersonality]; + } + return self; +} + +- (void)addObject:(id)object +{ + [_hashTable addObject:object]; +} + +- (void)removeObject:(id)object +{ + [_hashTable removeObject:object]; +} + +- (void)removeAllObjects +{ + [_hashTable removeAllObjects]; +} + +- (NSArray *)allObjects +{ + return _hashTable.allObjects; +} + +- (BOOL)containsObject:(id)object +{ + return [_hashTable containsObject:object]; +} + +- (BOOL)isEmpty +{ + return [_hashTable anyObject] == nil; +} + +/** + Note: The `count` property of NSHashTable is unreliable + in the case of weak-memory hash tables because entries + that have been deallocated are not removed immediately. + + In order to get the true count we have to fall back to using + fast enumeration. + */ +- (NSUInteger)count +{ + NSUInteger count = 0; + for (__unused id object in _hashTable) { + count += 1; + } + return count; +} + +- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(unowned id _Nonnull *)buffer count:(NSUInteger)len +{ + return [_hashTable countByEnumeratingWithState:state objects:buffer count:len]; +} + +- (NSString *)description +{ + return [[super description] stringByAppendingFormat:@" count: %tu, contents: %@", self.count, _hashTable]; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/CoreGraphics+ASConvenience.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/CoreGraphics+ASConvenience.h new file mode 100644 index 0000000..a0805f1 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/CoreGraphics+ASConvenience.h @@ -0,0 +1,51 @@ +// +// CoreGraphics+ASConvenience.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import +#import + +#import + + +#ifndef CGFLOAT_EPSILON + #if CGFLOAT_IS_DOUBLE + #define CGFLOAT_EPSILON DBL_EPSILON + #else + #define CGFLOAT_EPSILON FLT_EPSILON + #endif +#endif + +NS_ASSUME_NONNULL_BEGIN + +ASDISPLAYNODE_INLINE CGFloat ASCGFloatFromString(NSString *string) +{ +#if CGFLOAT_IS_DOUBLE + return string.doubleValue; +#else + return string.floatValue; +#endif +} + +ASDISPLAYNODE_INLINE CGFloat ASCGFloatFromNumber(NSNumber *number) +{ +#if CGFLOAT_IS_DOUBLE + return number.doubleValue; +#else + return number.floatValue; +#endif +} + +ASDISPLAYNODE_INLINE BOOL CGSizeEqualToSizeWithIn(CGSize size1, CGSize size2, CGFloat delta) +{ + return fabs(size1.width - size2.width) < delta && fabs(size1.height - size2.height) < delta; +}; + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/NSArray+Diffing.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/NSArray+Diffing.h new file mode 100644 index 0000000..c463903 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/NSArray+Diffing.h @@ -0,0 +1,92 @@ +// +// NSArray+Diffing.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +/** + * These changes can be used to transform `self` to `array` by applying them in (any) order, *without shifting* the + * other elements. This can be done (in an NSMutableArray) by calling `setObject:atIndexedSubscript:` (or just use + * [subscripting] directly) for insertions from `array` into `self` (not the seemingly more apt `insertObject:atIndex`!), + * and using the same method for deletions from `self` (*set* a `[NSNull null]` as opposed to `removeObject:atIndex:`). + * After all inserts/deletes have been applied, there will be no nulls left (except possibly at the end of the array if + * `[array count] < [self count]`) + + * Some examples: + * in: ab c + * out: abdc + * diff: ..+. + * + * in: abcd + * out: dcba + * dif: ---.+++ + * + * in: abcd + * out: ab d + * diff: ..-. + * + * in: a bcd + * out: adbc + * diff: .+..- + * + * If `moves` pointer is passed in, instances where one element moves to another location are detected and reported, + * possibly replacing pairs of delete/insert. The process for transforming an array remains the same, however now it is + * important to apply the moves in order and not overwrite an element that needs to be moved somewhere else. + * + * the same examples, with moves: + * in: ab c + * out: abdc + * diff: ..+. + * + * in: abcd + * out: dcba + * diff: 321. + * + * in: abcd + * out: ab d + * diff: ..-. + * + * in: abcd + * out: adbc + * diff: .312 + * + * Other notes: + * + * No index will be both moved from and deleted. + * Each index 0...[self count] will be either moved from or deleted. If it is moved to the same location, we omit it. + * Each index 0...[array count] will be the destination of ONE move or ONE insert. + * Knowing these things means any two of the three (delete, move, insert) implies the third. + */ + +@interface NSArray (Diffing) + +/** + * @abstract Compares two arrays, providing the insertion and deletion indexes needed to transform into the target array. + * @discussion This compares the equality of each object with `isEqual:`. + * This diffing algorithm uses a bottom-up memoized longest common subsequence solution to identify differences. + * It runs in O(mn) complexity. + */ +- (void)asdk_diffWithArray:(NSArray *)array insertions:(NSIndexSet **)insertions deletions:(NSIndexSet **)deletions; + +/** + * @abstract Compares two arrays, providing the insertion and deletion indexes needed to transform into the target array. + * @discussion The `compareBlock` is used to identify the equality of the objects within the arrays. + * This diffing algorithm uses a bottom-up memoized longest common subsequence solution to identify differences. + * It runs in O(mn) complexity. + */ +- (void)asdk_diffWithArray:(NSArray *)array insertions:(NSIndexSet **)insertions deletions:(NSIndexSet **)deletions compareBlock:(BOOL (^)(id lhs, id rhs))comparison; + +/** + * @abstract Compares two arrays, providing the insertion, deletion, and move indexes needed to transform into the target array. + * @discussion This compares the equality of each object with `isEqual:`. + * This diffing algorithm uses a bottom-up memoized longest common subsequence solution to identify differences. + * It runs in O(mn) complexity. + * The moves are returned in ascending order of their destination index. + */ +- (void)asdk_diffWithArray:(NSArray *)array insertions:(NSIndexSet **)insertions deletions:(NSIndexSet **)deletions moves:(NSArray **)moves; +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/NSArray+Diffing.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/NSArray+Diffing.mm new file mode 100644 index 0000000..83d32fe --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/NSArray+Diffing.mm @@ -0,0 +1,177 @@ +// +// NSArray+Diffing.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import +#import + +@implementation NSArray (Diffing) + +typedef BOOL (^compareBlock)(id _Nonnull lhs, id _Nonnull rhs); + +- (void)asdk_diffWithArray:(NSArray *)array insertions:(NSIndexSet **)insertions deletions:(NSIndexSet **)deletions +{ + [self asdk_diffWithArray:array insertions:insertions deletions:deletions moves:nil compareBlock:[NSArray defaultCompareBlock]]; +} + +- (void)asdk_diffWithArray:(NSArray *)array insertions:(NSIndexSet **)insertions deletions:(NSIndexSet **)deletions + compareBlock:(compareBlock)comparison +{ + [self asdk_diffWithArray:array insertions:insertions deletions:deletions moves:nil compareBlock:comparison]; +} + +- (void)asdk_diffWithArray:(NSArray *)array insertions:(NSIndexSet **)insertions deletions:(NSIndexSet **)deletions + moves:(NSArray **)moves +{ + [self asdk_diffWithArray:array insertions:insertions deletions:deletions moves:moves + compareBlock:[NSArray defaultCompareBlock]]; +} + +- (void)asdk_diffWithArray:(NSArray *)array insertions:(NSIndexSet **)insertions deletions:(NSIndexSet **)deletions + moves:(NSArray **)moves compareBlock:(compareBlock)comparison +{ + struct NSObjectHash + { + std::size_t operator()(id k) const { return (std::size_t) [k hash]; }; + }; + struct NSObjectCompare + { + bool operator()(id lhs, id rhs) const { return (bool) [lhs isEqual:rhs]; }; + }; + std::unordered_multimap potentialMoves; + + NSAssert(comparison != nil, @"Comparison block is required"); + NSAssert(moves == nil || comparison == [NSArray defaultCompareBlock], @"move detection requires isEqual: and hash (no custom compare)"); + NSMutableArray *moveIndexPaths = nil; + NSMutableIndexSet *insertionIndexes = nil, *deletionIndexes = nil; + if (moves) { + moveIndexPaths = [NSMutableArray new]; + } + NSMutableIndexSet *commonIndexes = [self _asdk_commonIndexesWithArray:array compareBlock:comparison]; + + if (deletions || moves) { + deletionIndexes = [NSMutableIndexSet indexSet]; + NSUInteger i = 0; + for (id element in self) { + if (![commonIndexes containsIndex:i]) { + [deletionIndexes addIndex:i]; + } + if (moves) { + potentialMoves.insert(std::pair(element, i)); + } + ++i; + } + } + + if (insertions || moves) { + insertionIndexes = [NSMutableIndexSet indexSet]; + NSArray *commonObjects = [self objectsAtIndexes:commonIndexes]; + for (NSUInteger i = 0, j = 0; j < array.count; j++) { + auto moveFound = potentialMoves.find(array[j]); + NSUInteger movedFrom = NSNotFound; + if (moveFound != potentialMoves.end() && moveFound->second != j) { + movedFrom = moveFound->second; + potentialMoves.erase(moveFound); + [moveIndexPaths addObject:[NSIndexPath indexPathForItem:j inSection:movedFrom]]; + } + if (i < commonObjects.count && j < array.count && comparison(commonObjects[i], array[j])) { + i++; + } else { + if (movedFrom != NSNotFound) { + // moves will coalesce a delete / insert - the insert is just not done, and here we remove the delete: + [deletionIndexes removeIndex:movedFrom]; + // OR a move will have come from the LCS: + if ([commonIndexes containsIndex:movedFrom]) { + [commonIndexes removeIndex:movedFrom]; + commonObjects = [self objectsAtIndexes:commonIndexes]; + } + } else { + [insertionIndexes addIndex:j]; + } + } + } + } + + if (moves) {*moves = moveIndexPaths;} + if (deletions) {*deletions = deletionIndexes;} + if (insertions) {*insertions = insertionIndexes;} +} + +// https://github.com/raywenderlich/swift-algorithm-club/tree/master/Longest%20Common%20Subsequence is not exactly this code (obviously), but +// is a good commentary on the algorithm. +- (NSMutableIndexSet *)_asdk_commonIndexesWithArray:(NSArray *)array compareBlock:(BOOL (^)(id lhs, id rhs))comparison +{ + NSAssert(comparison != nil, @"Comparison block is required"); + + NSInteger selfCount = self.count; + NSInteger arrayCount = array.count; + + // Allocate the diff map in the heap so we don't blow the stack for large arrays. + NSInteger **lengths = NULL; + lengths = (NSInteger **)malloc(sizeof(NSInteger*) * (selfCount+1)); + if (lengths == NULL) { + ASDisplayNodeFailAssert(@"Failed to allocate memory for diffing"); + return nil; + } + // Fill in a LCS length matrix: + for (NSInteger i = 0; i <= selfCount; i++) { + lengths[i] = (NSInteger *)malloc(sizeof(NSInteger) * (arrayCount+1)); + if (lengths[i] == NULL) { + ASDisplayNodeFailAssert(@"Failed to allocate memory for diffing"); + return nil; + } + id selfObj = i > 0 ? self[i-1] : nil; + for (NSInteger j = 0; j <= arrayCount; j++) { + if (i == 0 || j == 0) { + lengths[i][j] = 0; + } else if (comparison(selfObj, array[j-1])) { + lengths[i][j] = 1 + lengths[i-1][j-1]; + } else { + lengths[i][j] = MAX(lengths[i-1][j], lengths[i][j-1]); + } + } + } + // Backtrack to fill in indices based on length matrix: + NSMutableIndexSet *common = [NSMutableIndexSet indexSet]; + NSInteger i = selfCount, j = arrayCount; + while(i > 0 && j > 0) { + if (comparison(self[i-1], array[j-1])) { + [common addIndex:(i-1)]; + i--; j--; + } else if (lengths[i-1][j] > lengths[i][j-1]) { + i--; + } else { + j--; + } + } + + for (NSInteger i = 0; i <= selfCount; i++) { + free(lengths[i]); + } + free(lengths); + return common; +} + +static compareBlock defaultCompare = nil; + ++ (compareBlock)defaultCompareBlock +{ + static dispatch_once_t onceToken; + + dispatch_once(&onceToken, ^{ + defaultCompare = ^BOOL(id lhs, id rhs) { + return [lhs isEqual:rhs]; + }; + }); + + return defaultCompare; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/NSIndexSet+ASHelpers.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/NSIndexSet+ASHelpers.h new file mode 100644 index 0000000..cab7c94 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/NSIndexSet+ASHelpers.h @@ -0,0 +1,29 @@ +// +// NSIndexSet+ASHelpers.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +@interface NSIndexSet (ASHelpers) + +- (NSIndexSet *)as_indexesByMapping:(NSUInteger (^)(NSUInteger idx))block; + +- (NSIndexSet *)as_intersectionWithIndexes:(NSIndexSet *)indexes; + +/// Returns all the item indexes from the given index paths that are in the given section. ++ (NSIndexSet *)as_indexSetFromIndexPaths:(NSArray *)indexPaths inSection:(NSUInteger)section; + +/// If you've got an old index, and you insert items using this index set, this returns the change to get to the new index. +- (NSUInteger)as_indexChangeByInsertingItemsBelowIndex:(NSUInteger)index; + +- (NSString *)as_smallDescription; + +/// Returns all the section indexes contained in the index paths array. ++ (NSIndexSet *)as_sectionsFromIndexPaths:(NSArray *)indexPaths; + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/NSIndexSet+ASHelpers.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/NSIndexSet+ASHelpers.mm new file mode 100644 index 0000000..0eba035 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/NSIndexSet+ASHelpers.mm @@ -0,0 +1,91 @@ +// +// NSIndexSet+ASHelpers.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +// UIKit indexPath helpers +#import + +#import + +@implementation NSIndexSet (ASHelpers) + +- (NSIndexSet *)as_indexesByMapping:(NSUInteger (^)(NSUInteger))block +{ + NSMutableIndexSet *result = [[NSMutableIndexSet alloc] init]; + [self enumerateRangesUsingBlock:^(NSRange range, BOOL * _Nonnull stop) { + for (NSUInteger i = range.location; i < NSMaxRange(range); i++) { + NSUInteger newIndex = block(i); + if (newIndex != NSNotFound) { + [result addIndex:newIndex]; + } + } + }]; + return result; +} + +- (NSIndexSet *)as_intersectionWithIndexes:(NSIndexSet *)indexes +{ + NSMutableIndexSet *result = [[NSMutableIndexSet alloc] init]; + [self enumerateRangesUsingBlock:^(NSRange range, BOOL * _Nonnull stop) { + [indexes enumerateRangesInRange:range options:kNilOptions usingBlock:^(NSRange range, BOOL * _Nonnull stop) { + [result addIndexesInRange:range]; + }]; + }]; + return result; +} + ++ (NSIndexSet *)as_indexSetFromIndexPaths:(NSArray *)indexPaths inSection:(NSUInteger)section +{ + NSMutableIndexSet *result = [[NSMutableIndexSet alloc] init]; + for (NSIndexPath *indexPath in indexPaths) { + if (indexPath.section == section) { + [result addIndex:indexPath.item]; + } + } + return result; +} + +- (NSUInteger)as_indexChangeByInsertingItemsBelowIndex:(NSUInteger)index +{ + __block NSUInteger newIndex = index; + [self enumerateRangesUsingBlock:^(NSRange range, BOOL * _Nonnull stop) { + for (NSUInteger i = range.location; i < NSMaxRange(range); i++) { + if (i <= newIndex) { + newIndex += 1; + } else { + *stop = YES; + } + } + }]; + return newIndex - index; +} + +- (NSString *)as_smallDescription +{ + NSMutableString *result = [NSMutableString stringWithString:@"{ "]; + [self enumerateRangesUsingBlock:^(NSRange range, BOOL * _Nonnull stop) { + if (range.length == 1) { + [result appendFormat:@"%tu ", range.location]; + } else { + [result appendFormat:@"%tu-%tu ", range.location, NSMaxRange(range) - 1]; + } + }]; + [result appendString:@"}"]; + return result; +} + ++ (NSIndexSet *)as_sectionsFromIndexPaths:(NSArray *)indexPaths +{ + NSMutableIndexSet *result = [[NSMutableIndexSet alloc] init]; + for (NSIndexPath *indexPath in indexPaths) { + [result addIndex:indexPath.section]; + } + return result; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/NSMutableAttributedString+TextKitAdditions.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/NSMutableAttributedString+TextKitAdditions.h new file mode 100644 index 0000000..afb9672 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/NSMutableAttributedString+TextKitAdditions.h @@ -0,0 +1,26 @@ +// +// NSMutableAttributedString+TextKitAdditions.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface NSMutableAttributedString (TextKitAdditions) + +- (void)attributeTextInRange:(NSRange)range withTextKitMinimumLineHeight:(CGFloat)minimumLineHeight; + +- (void)attributeTextInRange:(NSRange)range withTextKitMinimumLineHeight:(CGFloat)minimumLineHeight maximumLineHeight:(CGFloat)maximumLineHeight; + +- (void)attributeTextInRange:(NSRange)range withTextKitLineHeight:(CGFloat)lineHeight; + +- (void)attributeTextInRange:(NSRange)range withTextKitParagraphStyle:(NSParagraphStyle *)paragraphStyle; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/NSMutableAttributedString+TextKitAdditions.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/NSMutableAttributedString+TextKitAdditions.mm new file mode 100644 index 0000000..3f2ed9d --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/NSMutableAttributedString+TextKitAdditions.mm @@ -0,0 +1,49 @@ +// +// NSMutableAttributedString+TextKitAdditions.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +@implementation NSMutableAttributedString (TextKitAdditions) + +#pragma mark - Convenience Methods + +- (void)attributeTextInRange:(NSRange)range withTextKitMinimumLineHeight:(CGFloat)minimumLineHeight +{ + if (range.length) { + + NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init]; + [style setMinimumLineHeight:minimumLineHeight]; + [self attributeTextInRange:range withTextKitParagraphStyle:style]; + } +} + +- (void)attributeTextInRange:(NSRange)range withTextKitMinimumLineHeight:(CGFloat)minimumLineHeight maximumLineHeight:(CGFloat)maximumLineHeight +{ + if (range.length) { + + NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init]; + [style setMinimumLineHeight:minimumLineHeight]; + [style setMaximumLineHeight:maximumLineHeight]; + [self attributeTextInRange:range withTextKitParagraphStyle:style]; + } +} + +- (void)attributeTextInRange:(NSRange)range withTextKitLineHeight:(CGFloat)lineHeight +{ + [self attributeTextInRange:range withTextKitMinimumLineHeight:lineHeight maximumLineHeight:lineHeight]; +} + +- (void)attributeTextInRange:(NSRange)range withTextKitParagraphStyle:(NSParagraphStyle *)paragraphStyle +{ + if (range.length) { + [self addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:range]; + } +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/Transactions/_ASAsyncTransaction.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/Transactions/_ASAsyncTransaction.h new file mode 100644 index 0000000..e3d0f21 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/Transactions/_ASAsyncTransaction.h @@ -0,0 +1,119 @@ +// +// _ASAsyncTransaction.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +#define ASDISPLAYNODE_DELAY_DISPLAY 0 + +@class _ASAsyncTransaction; + +typedef void(^asyncdisplaykit_async_transaction_completion_block_t)(_ASAsyncTransaction *completedTransaction, BOOL canceled); +typedef id _Nullable(^asyncdisplaykit_async_transaction_operation_block_t)(void); +typedef void(^asyncdisplaykit_async_transaction_operation_completion_block_t)(id _Nullable value, BOOL canceled); + +/** + State is initially ASAsyncTransactionStateOpen. + Every transaction MUST be committed. It is an error to fail to commit a transaction. + A committed transaction MAY be canceled. You cannot cancel an open (uncommitted) transaction. + */ +typedef NS_ENUM(NSUInteger, ASAsyncTransactionState) { + ASAsyncTransactionStateOpen = 0, + ASAsyncTransactionStateCommitted, + ASAsyncTransactionStateCanceled, + ASAsyncTransactionStateComplete +}; + +ASDK_EXTERN NSInteger const ASDefaultTransactionPriority; + +/** + @summary ASAsyncTransaction provides lightweight transaction semantics for asynchronous operations. + + @desc ASAsyncTransaction provides the following properties: + + - Transactions group an arbitrary number of operations, each consisting of an execution block and a completion block. + - The execution block returns a single object that will be passed to the completion block. + - Execution blocks added to a transaction will run in parallel on the global background dispatch queues; + the completion blocks are dispatched to the callback queue. + - Every operation completion block is guaranteed to execute, regardless of cancelation. + However, execution blocks may be skipped if the transaction is canceled. + - Operation completion blocks are always executed in the order they were added to the transaction, assuming the + callback queue is serial of course. + */ +@interface _ASAsyncTransaction : NSObject + +/** + @summary Initialize a transaction that can start collecting async operations. + + @param completionBlock A block that is called when the transaction is completed. + */ +- (instancetype)initWithCompletionBlock:(nullable asyncdisplaykit_async_transaction_completion_block_t)completionBlock; + +/** + @summary Block the main thread until the transaction is complete, including callbacks. + + @desc This must be called on the main thread. + */ +- (void)waitUntilComplete; + +/** + A block that is called when the transaction is completed. + */ +@property (nullable, readonly) asyncdisplaykit_async_transaction_completion_block_t completionBlock; + +/** + The state of the transaction. + @see ASAsyncTransactionState + */ +@property (readonly) ASAsyncTransactionState state; + +/** + @summary Adds a synchronous operation to the transaction. The execution block will be executed immediately. + + @desc The block will be executed on the specified queue and is expected to complete synchronously. The async + transaction will wait for all operations to execute on their appropriate queues, so the blocks may still be executing + async if they are running on a concurrent queue, even though the work for this block is synchronous. + + @param block The execution block that will be executed on a background queue. This is where the expensive work goes. + @param priority Execution priority; Tasks with higher priority will be executed sooner + @param queue The dispatch queue on which to execute the block. + @param completion The completion block that will be executed with the output of the execution block when all of the + operations in the transaction are completed. Executed and released on callbackQueue. + */ +- (void)addOperationWithBlock:(asyncdisplaykit_async_transaction_operation_block_t)block + priority:(NSInteger)priority + queue:(dispatch_queue_t)queue + completion:(nullable asyncdisplaykit_async_transaction_operation_completion_block_t)completion; + +/** + @summary Cancels all operations in the transaction. + + @desc You can only cancel a committed transaction. + + All completion blocks are always called, regardless of cancelation. Execution blocks may be skipped if canceled. + */ +- (void)cancel; + +/** + @summary Marks the end of adding operations to the transaction. + + @desc You MUST commit every transaction you create. It is an error to create a transaction that is never committed. + + When all of the operations that have been added have completed the transaction will execute their completion + blocks. + + If no operations were added to this transaction, invoking commit will execute the transaction's completion block synchronously. + */ +- (void)commit; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/Transactions/_ASAsyncTransaction.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/Transactions/_ASAsyncTransaction.mm new file mode 100644 index 0000000..35d9a43 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/Transactions/_ASAsyncTransaction.mm @@ -0,0 +1,461 @@ +// +// _ASAsyncTransaction.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + + +#import +#import +#import +#import +#import +#import + +#ifndef __STRICT_ANSI__ + #warning "Texture must be compiled with std=c++11 to prevent layout issues. gnu++ is not supported. This is hopefully temporary." +#endif + +ASDK_EXTERN NSRunLoopMode const UITrackingRunLoopMode; + +NSInteger const ASDefaultTransactionPriority = 0; + +@interface ASAsyncTransactionOperation : NSObject +- (instancetype)initWithOperationCompletionBlock:(asyncdisplaykit_async_transaction_operation_completion_block_t)operationCompletionBlock; +@property (nonatomic) asyncdisplaykit_async_transaction_operation_completion_block_t operationCompletionBlock; +@property id value; // set on bg queue by the operation block +@end + +@implementation ASAsyncTransactionOperation + +- (instancetype)initWithOperationCompletionBlock:(asyncdisplaykit_async_transaction_operation_completion_block_t)operationCompletionBlock +{ + if ((self = [super init])) { + _operationCompletionBlock = operationCompletionBlock; + } + return self; +} + +- (void)dealloc +{ + NSAssert(_operationCompletionBlock == nil, @"Should have been called and released before -dealloc"); +} + +- (void)callAndReleaseCompletionBlock:(BOOL)canceled; +{ + ASDisplayNodeAssertMainThread(); + if (_operationCompletionBlock) { + _operationCompletionBlock(self.value, canceled); + // Guarantee that _operationCompletionBlock is released on main thread + _operationCompletionBlock = nil; + } +} + +- (NSString *)description +{ + return [NSString stringWithFormat:@"", self, self.value]; +} + +@end + +// Lightweight operation queue for _ASAsyncTransaction that limits number of spawned threads +class ASAsyncTransactionQueue +{ +public: + + // Similar to dispatch_group_t + class Group + { + public: + // call when group is no longer needed; after last scheduled operation the group will delete itself + virtual void release() = 0; + + // schedule block on given queue + virtual void schedule(NSInteger priority, dispatch_queue_t queue, dispatch_block_t block) = 0; + + // dispatch block on given queue when all previously scheduled blocks finished executing + virtual void notify(dispatch_queue_t queue, dispatch_block_t block) = 0; + + // used when manually executing blocks + virtual void enter() = 0; + virtual void leave() = 0; + + // wait until all scheduled blocks finished executing + virtual void wait() = 0; + + protected: + virtual ~Group() { }; // call release() instead + }; + + // Create new group + Group *createGroup(); + + static ASAsyncTransactionQueue &instance(); + +private: + + struct GroupNotify + { + dispatch_block_t _block; + dispatch_queue_t _queue; + }; + + class GroupImpl : public Group + { + public: + GroupImpl(ASAsyncTransactionQueue &queue) + : _pendingOperations(0) + , _releaseCalled(false) + , _queue(queue) + { + } + + virtual void release(); + virtual void schedule(NSInteger priority, dispatch_queue_t queue, dispatch_block_t block); + virtual void notify(dispatch_queue_t queue, dispatch_block_t block); + virtual void enter(); + virtual void leave(); + virtual void wait(); + + int _pendingOperations; + std::list _notifyList; + std::condition_variable _condition; + BOOL _releaseCalled; + ASAsyncTransactionQueue &_queue; + }; + + struct Operation + { + dispatch_block_t _block; + GroupImpl *_group; + NSInteger _priority; + }; + + struct DispatchEntry // entry for each dispatch queue + { + typedef std::list OperationQueue; + typedef std::list OperationIteratorList; // each item points to operation queue + typedef std::map OperationPriorityMap; // sorted by priority + + OperationQueue _operationQueue; + OperationPriorityMap _operationPriorityMap; + int _threadCount; + + Operation popNextOperation(bool respectPriority); // assumes locked mutex + void pushOperation(Operation operation); // assumes locked mutex + }; + + std::map _entries; + std::mutex _mutex; +}; + +ASAsyncTransactionQueue::Group* ASAsyncTransactionQueue::createGroup() +{ + Group *res = new GroupImpl(*this); + return res; +} + +void ASAsyncTransactionQueue::GroupImpl::release() +{ + std::lock_guard l(_queue._mutex); + + if (_pendingOperations == 0) { + delete this; + } else { + _releaseCalled = YES; + } +} + +ASAsyncTransactionQueue::Operation ASAsyncTransactionQueue::DispatchEntry::popNextOperation(bool respectPriority) +{ + NSCAssert(!_operationQueue.empty() && !_operationPriorityMap.empty(), @"No scheduled operations available"); + + OperationQueue::iterator queueIterator; + OperationPriorityMap::iterator mapIterator; + + if (respectPriority) { + mapIterator = --_operationPriorityMap.end(); // highest priority "bucket" + queueIterator = *mapIterator->second.begin(); + } else { + queueIterator = _operationQueue.begin(); + mapIterator = _operationPriorityMap.find(queueIterator->_priority); + } + + // no matter what, first item in "bucket" must match item in queue + NSCAssert(mapIterator->second.front() == queueIterator, @"Queue inconsistency"); + + Operation res = *queueIterator; + _operationQueue.erase(queueIterator); + + mapIterator->second.pop_front(); + if (mapIterator->second.empty()) { + _operationPriorityMap.erase(mapIterator); + } + + return res; +} + +void ASAsyncTransactionQueue::DispatchEntry::pushOperation(ASAsyncTransactionQueue::Operation operation) +{ + _operationQueue.push_back(operation); + + OperationIteratorList &list = _operationPriorityMap[operation._priority]; + list.push_back(--_operationQueue.end()); +} + +void ASAsyncTransactionQueue::GroupImpl::schedule(NSInteger priority, dispatch_queue_t queue, dispatch_block_t block) +{ + ASAsyncTransactionQueue &q = _queue; + std::lock_guard l(q._mutex); + + DispatchEntry &entry = q._entries[queue]; + + Operation operation; + operation._block = block; + operation._group = this; + operation._priority = priority; + entry.pushOperation(operation); + + ++_pendingOperations; // enter group + +#if ASDISPLAYNODE_DELAY_DISPLAY + NSUInteger maxThreads = 1; +#else + NSUInteger maxThreads = [NSProcessInfo processInfo].activeProcessorCount * 2; + + // Bit questionable maybe - we can give main thread more CPU time during tracking. + if ([[NSRunLoop mainRunLoop].currentMode isEqualToString:UITrackingRunLoopMode]) + --maxThreads; +#endif + + if (entry._threadCount < maxThreads) { // we need to spawn another thread + + // first thread will take operations in queue order (regardless of priority), other threads will respect priority + bool respectPriority = entry._threadCount > 0; + ++entry._threadCount; + + dispatch_async(queue, ^{ + std::unique_lock lock(q._mutex); + + // go until there are no more pending operations + while (!entry._operationQueue.empty()) { + Operation operation = entry.popNextOperation(respectPriority); + lock.unlock(); + if (operation._block) { + operation._block(); + } + operation._group->leave(); + operation._block = nil; // the block must be freed while mutex is unlocked + lock.lock(); + } + --entry._threadCount; + + if (entry._threadCount == 0) { + NSCAssert(entry._operationQueue.empty() || entry._operationPriorityMap.empty(), @"No working threads but operations are still scheduled"); // this shouldn't happen + q._entries.erase(queue); + } + }); + } +} + +void ASAsyncTransactionQueue::GroupImpl::notify(dispatch_queue_t queue, dispatch_block_t block) +{ + std::lock_guard l(_queue._mutex); + + if (_pendingOperations == 0) { + dispatch_async(queue, block); + } else { + _notifyList.push_back({block, queue}); + } +} + +void ASAsyncTransactionQueue::GroupImpl::enter() +{ + std::lock_guard l(_queue._mutex); + ++_pendingOperations; +} + +void ASAsyncTransactionQueue::GroupImpl::leave() +{ + std::lock_guard l(_queue._mutex); + --_pendingOperations; + + if (_pendingOperations == 0) { + std::list notifyList; + _notifyList.swap(notifyList); + + for (GroupNotify & notify : notifyList) { + dispatch_async(notify._queue, notify._block); + } + + _condition.notify_one(); + + // there was attempt to release the group before, but we still + // had operations scheduled so now is good time + if (_releaseCalled) { + delete this; + } + } +} + +void ASAsyncTransactionQueue::GroupImpl::wait() +{ + std::unique_lock lock(_queue._mutex); + while (_pendingOperations > 0) { + _condition.wait(lock); + } +} + +ASAsyncTransactionQueue & ASAsyncTransactionQueue::instance() +{ + static ASAsyncTransactionQueue *instance = new ASAsyncTransactionQueue(); + return *instance; +} + +@interface _ASAsyncTransaction () +@property ASAsyncTransactionState state; +@end + + +@implementation _ASAsyncTransaction +{ + ASAsyncTransactionQueue::Group *_group; + NSMutableArray *_operations; +} + +#pragma mark - Lifecycle + +- (instancetype)initWithCompletionBlock:(void(^)(_ASAsyncTransaction *, BOOL))completionBlock +{ + if ((self = [super init])) { + _completionBlock = completionBlock; + self.state = ASAsyncTransactionStateOpen; + } + return self; +} + +- (void)dealloc +{ + // Uncommitted transactions break our guarantees about releasing completion blocks on callbackQueue. + NSAssert(self.state != ASAsyncTransactionStateOpen, @"Uncommitted ASAsyncTransactions are not allowed"); + if (_group) { + _group->release(); + } +} + +#pragma mark - Transaction Management + +- (void)addOperationWithBlock:(asyncdisplaykit_async_transaction_operation_block_t)block + priority:(NSInteger)priority + queue:(dispatch_queue_t)queue + completion:(asyncdisplaykit_async_transaction_operation_completion_block_t)completion +{ + ASDisplayNodeAssertMainThread(); + NSAssert(self.state == ASAsyncTransactionStateOpen, @"You can only add operations to open transactions"); + + [self _ensureTransactionData]; + + ASAsyncTransactionOperation *operation = [[ASAsyncTransactionOperation alloc] initWithOperationCompletionBlock:completion]; + [_operations addObject:operation]; + _group->schedule(priority, queue, ^{ + @autoreleasepool { + if (self.state != ASAsyncTransactionStateCanceled) { + operation.value = block(); + } + } + }); +} + +- (void)cancel +{ + ASDisplayNodeAssertMainThread(); + NSAssert(self.state != ASAsyncTransactionStateOpen, @"You can only cancel a committed or already-canceled transaction"); + self.state = ASAsyncTransactionStateCanceled; +} + +- (void)commit +{ + ASDisplayNodeAssertMainThread(); + NSAssert(self.state == ASAsyncTransactionStateOpen, @"You cannot double-commit a transaction"); + self.state = ASAsyncTransactionStateCommitted; + + if ([_operations count] == 0) { + // Fast path: if a transaction was opened, but no operations were added, execute completion block synchronously. + if (_completionBlock) { + _completionBlock(self, NO); + } + } else { + NSAssert(_group != NULL, @"If there are operations, dispatch group should have been created"); + + _group->notify(dispatch_get_main_queue(), ^{ + [self completeTransaction]; + }); + } +} + +- (void)completeTransaction +{ + ASDisplayNodeAssertMainThread(); + ASAsyncTransactionState state = self.state; + if (state != ASAsyncTransactionStateComplete) { + BOOL isCanceled = (state == ASAsyncTransactionStateCanceled); + for (ASAsyncTransactionOperation *operation in _operations) { + [operation callAndReleaseCompletionBlock:isCanceled]; + } + + // Always set state to Complete, even if we were cancelled, to block any extraneous + // calls to this method that may have been scheduled for the next runloop + // (e.g. if we needed to force one in this runloop with -waitUntilComplete, but another was already scheduled) + self.state = ASAsyncTransactionStateComplete; + + if (_completionBlock) { + _completionBlock(self, isCanceled); + } + } +} + +- (void)waitUntilComplete +{ + ASDisplayNodeAssertMainThread(); + if (self.state != ASAsyncTransactionStateComplete) { + if (_group) { + _group->wait(); + + // At this point, the asynchronous operation may have completed, but the runloop + // observer has not committed the batch of transactions we belong to. It's important to + // commit ourselves via the group to avoid double-committing the transaction. + // This is only necessary when forcing display work to complete before allowing the runloop + // to continue, e.g. in the implementation of -[ASDisplayNode recursivelyEnsureDisplay]. + if (self.state == ASAsyncTransactionStateOpen) { + [_ASAsyncTransactionGroup.mainTransactionGroup commit]; + NSAssert(self.state != ASAsyncTransactionStateOpen, @"Transaction should not be open after committing group"); + } + // If we needed to commit the group above, -completeTransaction may have already been run. + // It is designed to accommodate this by checking _state to ensure it is not complete. + [self completeTransaction]; + } + } +} + +#pragma mark - Helper Methods + +- (void)_ensureTransactionData +{ + // Lazily initialize _group and _operations to avoid overhead in the case where no operations are added to the transaction + if (_group == NULL) { + _group = ASAsyncTransactionQueue::instance().createGroup(); + } + if (_operations == nil) { + _operations = [[NSMutableArray alloc] init]; + } +} + +- (NSString *)description +{ + return [NSString stringWithFormat:@"<_ASAsyncTransaction: %p - _state = %lu, _group = %p, _operations = %@>", self, (unsigned long)self.state, _group, _operations]; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/Transactions/_ASAsyncTransactionContainer+Private.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/Transactions/_ASAsyncTransactionContainer+Private.h new file mode 100644 index 0000000..cd4b414 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/Transactions/_ASAsyncTransactionContainer+Private.h @@ -0,0 +1,24 @@ +// +// _ASAsyncTransactionContainer+Private.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@class _ASAsyncTransaction; + +@interface CALayer (ASAsyncTransactionContainerTransactions) +@property (nonatomic, nullable, setter=asyncdisplaykit_setAsyncLayerTransactions:) NSMutableSet<_ASAsyncTransaction *> *asyncdisplaykit_asyncLayerTransactions; + +- (void)asyncdisplaykit_asyncTransactionContainerWillBeginTransaction:(_ASAsyncTransaction *)transaction; +- (void)asyncdisplaykit_asyncTransactionContainerDidCompleteTransaction:(_ASAsyncTransaction *)transaction; +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/Transactions/_ASAsyncTransactionContainer.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/Transactions/_ASAsyncTransactionContainer.h new file mode 100644 index 0000000..53fe40f --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/Transactions/_ASAsyncTransactionContainer.h @@ -0,0 +1,81 @@ +// +// _ASAsyncTransactionContainer.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#pragma once + +#import + +NS_ASSUME_NONNULL_BEGIN + +@class _ASAsyncTransaction; + +typedef NS_ENUM(NSUInteger, ASAsyncTransactionContainerState) { + /** + The async container has no outstanding transactions. + Whatever it is displaying is up-to-date. + */ + ASAsyncTransactionContainerStateNoTransactions = 0, + /** + The async container has one or more outstanding async transactions. + Its contents may be out of date or showing a placeholder, depending on the configuration of the contained ASDisplayLayers. + */ + ASAsyncTransactionContainerStatePendingTransactions, +}; + +@protocol ASAsyncTransactionContainer + +/** + @summary If YES, the receiver is marked as a container for async transactions, grouping all of the transactions + in the container hierarchy below the receiver together in a single ASAsyncTransaction. + + @default NO + */ +@property (nonatomic, getter=asyncdisplaykit_isAsyncTransactionContainer, setter=asyncdisplaykit_setAsyncTransactionContainer:) BOOL asyncdisplaykit_asyncTransactionContainer; + +/** + @summary The current state of the receiver; indicates if it is currently performing asynchronous operations or if all operations have finished/canceled. + */ +@property (nonatomic, readonly) ASAsyncTransactionContainerState asyncdisplaykit_asyncTransactionContainerState; + +/** + @summary Cancels all async transactions on the receiver. + */ +- (void)asyncdisplaykit_cancelAsyncTransactions; + +@property (nullable, nonatomic, setter=asyncdisplaykit_setCurrentAsyncTransaction:) _ASAsyncTransaction *asyncdisplaykit_currentAsyncTransaction; + +@end + +@interface CALayer (ASAsyncTransactionContainer) +/** + @summary Returns the current async transaction for this layer. A new transaction is created if one + did not already exist. This method will always return an open, uncommitted transaction. + @desc asyncdisplaykit_asyncTransactionContainer does not need to be YES for this to return a transaction. + Defaults to nil. + */ +@property (nullable, nonatomic, readonly) _ASAsyncTransaction *asyncdisplaykit_asyncTransaction; + +/** + @summary Goes up the superlayer chain until it finds the first layer with asyncdisplaykit_asyncTransactionContainer=YES (including the receiver) and returns it. + Returns nil if no parent container is found. + */ +@property (nullable, nonatomic, readonly) CALayer *asyncdisplaykit_parentTransactionContainer; + +/** + @summary Whether or not this layer should serve as a transaction container. + Defaults to NO. + */ +@property (nonatomic, getter=asyncdisplaykit_isAsyncTransactionContainer, setter = asyncdisplaykit_setAsyncTransactionContainer:) BOOL asyncdisplaykit_asyncTransactionContainer; + +@end + +@interface UIView (ASAsyncTransactionContainer) +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/Transactions/_ASAsyncTransactionContainer.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/Transactions/_ASAsyncTransactionContainer.mm new file mode 100644 index 0000000..669a083 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/Transactions/_ASAsyncTransactionContainer.mm @@ -0,0 +1,126 @@ +// +// _ASAsyncTransactionContainer.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +#import +#import +#import + +@implementation CALayer (ASAsyncTransactionContainerTransactions) +@dynamic asyncdisplaykit_asyncLayerTransactions; + +// No-ops in the base class. Mostly exposed for testing. +- (void)asyncdisplaykit_asyncTransactionContainerWillBeginTransaction:(_ASAsyncTransaction *)transaction {} +- (void)asyncdisplaykit_asyncTransactionContainerDidCompleteTransaction:(_ASAsyncTransaction *)transaction {} +@end + +@implementation CALayer (ASAsyncTransactionContainer) +@dynamic asyncdisplaykit_currentAsyncTransaction; +@dynamic asyncdisplaykit_asyncTransactionContainer; + +- (ASAsyncTransactionContainerState)asyncdisplaykit_asyncTransactionContainerState +{ + return ([self.asyncdisplaykit_asyncLayerTransactions count] == 0) ? ASAsyncTransactionContainerStateNoTransactions : ASAsyncTransactionContainerStatePendingTransactions; +} + +- (void)asyncdisplaykit_cancelAsyncTransactions +{ + // If there was an open transaction, commit and clear the current transaction. Otherwise: + // (1) The run loop observer will try to commit a canceled transaction which is not allowed + // (2) We leave the canceled transaction attached to the layer, dooming future operations + _ASAsyncTransaction *currentTransaction = self.asyncdisplaykit_currentAsyncTransaction; + [currentTransaction commit]; + self.asyncdisplaykit_currentAsyncTransaction = nil; + + for (_ASAsyncTransaction *transaction in [self.asyncdisplaykit_asyncLayerTransactions copy]) { + [transaction cancel]; + } +} + +- (_ASAsyncTransaction *)asyncdisplaykit_asyncTransaction +{ + _ASAsyncTransaction *transaction = self.asyncdisplaykit_currentAsyncTransaction; + if (transaction == nil) { + NSMutableSet<_ASAsyncTransaction *> *transactions = self.asyncdisplaykit_asyncLayerTransactions; + if (transactions == nil) { + transactions = ASCreatePointerBasedMutableSet(); + self.asyncdisplaykit_asyncLayerTransactions = transactions; + } + __weak CALayer *weakSelf = self; + transaction = [[_ASAsyncTransaction alloc] initWithCompletionBlock:^(_ASAsyncTransaction *completedTransaction, BOOL cancelled) { + __strong CALayer *self = weakSelf; + if (self == nil) { + return; + } + [self.asyncdisplaykit_asyncLayerTransactions removeObject:completedTransaction]; + if (self.asyncdisplaykit_asyncLayerTransactions.count == 0) { + // Reclaim object memory. + self.asyncdisplaykit_asyncLayerTransactions = nil; + } + [self asyncdisplaykit_asyncTransactionContainerDidCompleteTransaction:completedTransaction]; + }]; + [transactions addObject:transaction]; + self.asyncdisplaykit_currentAsyncTransaction = transaction; + [self asyncdisplaykit_asyncTransactionContainerWillBeginTransaction:transaction]; + } + [_ASAsyncTransactionGroup.mainTransactionGroup addTransactionContainer:self]; + return transaction; +} + +- (CALayer *)asyncdisplaykit_parentTransactionContainer +{ + CALayer *containerLayer = self; + while (containerLayer && !containerLayer.asyncdisplaykit_isAsyncTransactionContainer) { + containerLayer = containerLayer.superlayer; + } + return containerLayer; +} + +@end + +@implementation UIView (ASAsyncTransactionContainer) + +- (BOOL)asyncdisplaykit_isAsyncTransactionContainer +{ + return self.layer.asyncdisplaykit_isAsyncTransactionContainer; +} + +- (void)asyncdisplaykit_setAsyncTransactionContainer:(BOOL)asyncTransactionContainer +{ + self.layer.asyncdisplaykit_asyncTransactionContainer = asyncTransactionContainer; +} + +- (ASAsyncTransactionContainerState)asyncdisplaykit_asyncTransactionContainerState +{ + return self.layer.asyncdisplaykit_asyncTransactionContainerState; +} + +- (void)asyncdisplaykit_cancelAsyncTransactions +{ + [self.layer asyncdisplaykit_cancelAsyncTransactions]; +} + +- (void)asyncdisplaykit_asyncTransactionContainerStateDidChange +{ + // No-op in the base class. +} + +- (void)asyncdisplaykit_setCurrentAsyncTransaction:(_ASAsyncTransaction *)transaction +{ + self.layer.asyncdisplaykit_currentAsyncTransaction = transaction; +} + +- (_ASAsyncTransaction *)asyncdisplaykit_currentAsyncTransaction +{ + return self.layer.asyncdisplaykit_currentAsyncTransaction; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/Transactions/_ASAsyncTransactionGroup.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/Transactions/_ASAsyncTransactionGroup.h new file mode 100644 index 0000000..cd0b216 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/Transactions/_ASAsyncTransactionGroup.h @@ -0,0 +1,36 @@ +// +// _ASAsyncTransactionGroup.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@protocol ASAsyncTransactionContainer; + +/// A group of transaction containers, for which the current transactions are committed together at the end of the next runloop tick. +AS_SUBCLASSING_RESTRICTED +@interface _ASAsyncTransactionGroup : NSObject + +/// The main transaction group is scheduled to commit on every tick of the main runloop. +/// Access from the main thread only. +@property (class, nonatomic, readonly) _ASAsyncTransactionGroup *mainTransactionGroup; + +- (void)commit; + +/// Add a transaction container to be committed. +- (void)addTransactionContainer:(id)container; + +/// Use the main group. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)new NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/Transactions/_ASAsyncTransactionGroup.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/Transactions/_ASAsyncTransactionGroup.mm new file mode 100644 index 0000000..62cf4c7 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/Transactions/_ASAsyncTransactionGroup.mm @@ -0,0 +1,87 @@ +// +// _ASAsyncTransactionGroup.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import +#import +#import + +@implementation _ASAsyncTransactionGroup { + NSHashTable> *_containers; +} + ++ (_ASAsyncTransactionGroup *)mainTransactionGroup +{ + ASDisplayNodeAssertMainThread(); + static _ASAsyncTransactionGroup *mainTransactionGroup; + + if (mainTransactionGroup == nil) { + mainTransactionGroup = [[_ASAsyncTransactionGroup alloc] _init]; + [mainTransactionGroup registerAsMainRunloopObserver]; + } + return mainTransactionGroup; +} + +- (void)registerAsMainRunloopObserver +{ + ASDisplayNodeAssertMainThread(); + static CFRunLoopObserverRef observer; + ASDisplayNodeAssert(observer == NULL, @"A _ASAsyncTransactionGroup should not be registered on the main runloop twice"); + // defer the commit of the transaction so we can add more during the current runloop iteration + CFRunLoopRef runLoop = CFRunLoopGetCurrent(); + CFOptionFlags activities = (kCFRunLoopBeforeWaiting | // before the run loop starts sleeping + kCFRunLoopExit); // before exiting a runloop run + + observer = CFRunLoopObserverCreateWithHandler(NULL, // allocator + activities, // activities + YES, // repeats + INT_MAX, // order after CA transaction commits + ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) { + ASDisplayNodeCAssertMainThread(); + [self commit]; + }); + CFRunLoopAddObserver(runLoop, observer, kCFRunLoopCommonModes); + CFRelease(observer); +} + +- (instancetype)_init +{ + if ((self = [super init])) { + _containers = [NSHashTable hashTableWithOptions:NSHashTableObjectPointerPersonality]; + } + return self; +} + +- (void)addTransactionContainer:(id)container +{ + ASDisplayNodeAssertMainThread(); + ASDisplayNodeAssert(container != nil, @"No container"); + [_containers addObject:container]; +} + +- (void)commit +{ + ASDisplayNodeAssertMainThread(); + + if ([_containers count]) { + NSHashTable *containersToCommit = _containers; + _containers = [NSHashTable hashTableWithOptions:NSHashTableObjectPointerPersonality]; + + for (id container in containersToCommit) { + // Note that the act of committing a transaction may open a new transaction, + // so we must nil out the transaction we're committing first. + _ASAsyncTransaction *transaction = container.asyncdisplaykit_currentAsyncTransaction; + container.asyncdisplaykit_currentAsyncTransaction = nil; + [transaction commit]; + } + } +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/UICollectionViewLayout+ASConvenience.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/UICollectionViewLayout+ASConvenience.h new file mode 100644 index 0000000..78e55a1 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/UICollectionViewLayout+ASConvenience.h @@ -0,0 +1,28 @@ +// +// UICollectionViewLayout+ASConvenience.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +@protocol ASCollectionViewLayoutInspecting; + +NS_ASSUME_NONNULL_BEGIN + +@interface UICollectionViewLayout (ASLayoutInspectorProviding) + +/** + * You can override this method on your @c UICollectionViewLayout subclass to + * return a layout inspector tailored to your layout. + * + * It's fine to return @c self. You must not return @c nil. + */ +- (id)asdk_layoutInspector; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/UICollectionViewLayout+ASConvenience.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/UICollectionViewLayout+ASConvenience.mm new file mode 100644 index 0000000..b7564d0 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/UICollectionViewLayout+ASConvenience.mm @@ -0,0 +1,28 @@ +// +// UICollectionViewLayout+ASConvenience.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import + +#import + +@implementation UICollectionViewLayout (ASLayoutInspectorProviding) + +- (id)asdk_layoutInspector +{ + UICollectionViewFlowLayout *flow = ASDynamicCast(self, UICollectionViewFlowLayout); + if (flow != nil) { + return [[ASCollectionViewFlowLayoutInspector alloc] initWithFlowLayout:flow]; + } else { + return [[ASCollectionViewLayoutInspector alloc] init]; + } +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/UIView+ASConvenience.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/UIView+ASConvenience.h new file mode 100644 index 0000000..870793b --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/UIView+ASConvenience.h @@ -0,0 +1,101 @@ +// +// UIView+ASConvenience.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +NS_ASSUME_NONNULL_BEGIN + + +/** + These are the properties we support from CALayer (implemented in the pending state) + */ + +@protocol ASDisplayProperties + +@property (nonatomic) CGPoint position; +@property (nonatomic) CGFloat zPosition; +@property (nonatomic) CGPoint anchorPoint; +@property (nonatomic) CGFloat cornerRadius; +@property (nonatomic) CACornerMask maskedCorners API_AVAILABLE(ios(11), tvos(11)); +@property (nullable, nonatomic) id contents; +@property (nonatomic, copy) NSString *contentsGravity; +@property (nonatomic) CGRect contentsRect; +@property (nonatomic) CGRect contentsCenter; +@property (nonatomic) CGFloat contentsScale; +@property (nonatomic) CGFloat rasterizationScale; +@property (nonatomic) CATransform3D transform; +@property (nonatomic) CATransform3D sublayerTransform; +@property (nonatomic) BOOL needsDisplayOnBoundsChange; +@property (nonatomic) __attribute__((NSObject)) CGColorRef shadowColor; +@property (nonatomic) CGFloat shadowOpacity; +@property (nonatomic) CGSize shadowOffset; +@property (nonatomic) CGFloat shadowRadius; +@property (nonatomic) CGFloat borderWidth; +@property (nonatomic, getter = isOpaque) BOOL opaque; +@property (nonatomic) __attribute__((NSObject)) CGColorRef borderColor; +@property (nonatomic) UIColor *backgroundColor; +@property (nonatomic) BOOL allowsGroupOpacity; +@property (nonatomic) BOOL allowsEdgeAntialiasing; +@property (nonatomic) CAEdgeAntialiasingMask edgeAntialiasingMask; +@property (nonatomic, nullable, copy) NSDictionary> *actions; + +- (void)setNeedsDisplay; +- (void)setNeedsLayout; +- (void)layoutIfNeeded; + +@end + +/** + These are all of the "good" properties of the UIView API that we support in pendingViewState or view of an ASDisplayNode. + */ +@protocol ASDisplayNodeViewProperties + +@property (nonatomic) BOOL clipsToBounds; +@property (nonatomic, getter=isHidden) BOOL hidden; +@property (nonatomic) BOOL autoresizesSubviews; +@property (nonatomic) UIViewAutoresizing autoresizingMask; +@property (nonatomic, null_resettable) UIColor *tintColor; +@property (nonatomic) CGFloat alpha; +@property (nonatomic) CGRect bounds; +@property (nonatomic) CGRect frame; // Only for use with nodes wrapping synchronous views +@property (nonatomic) UIViewContentMode contentMode; +@property (nonatomic) UISemanticContentAttribute semanticContentAttribute API_AVAILABLE(ios(9.0), tvos(9.0)); +@property (nonatomic, getter=isUserInteractionEnabled) BOOL userInteractionEnabled; +@property (nonatomic, getter=isExclusiveTouch) BOOL exclusiveTouch; +@property (nonatomic, getter=asyncdisplaykit_isAsyncTransactionContainer, setter = asyncdisplaykit_setAsyncTransactionContainer:) BOOL asyncdisplaykit_asyncTransactionContainer; +@property (nonatomic) UIEdgeInsets layoutMargins; +@property (nonatomic) BOOL preservesSuperviewLayoutMargins; +@property (nonatomic) BOOL insetsLayoutMarginsFromSafeArea; + +/** + Following properties of the UIAccessibility informal protocol are supported as well. + We don't declare them here, so _ASPendingState does not complain about them being not implemented, + as they are already on NSObject + + @property (nonatomic) BOOL isAccessibilityElement; + @property (nonatomic, copy, nullable) NSString *accessibilityLabel; + @property (nonatomic, copy, nullable) NSAttributedString *accessibilityAttributedLabel API_AVAILABLE(ios(11.0),tvos(11.0)); + @property (nonatomic, copy, nullable) NSString *accessibilityHint; + @property (nonatomic, copy, nullable) NSAttributedString *accessibilityAttributedHint API_AVAILABLE(ios(11.0),tvos(11.0)); + @property (nonatomic, copy, nullable) NSString *accessibilityValue; + @property (nonatomic, copy, nullable) NSAttributedString *accessibilityAttributedValue API_AVAILABLE(ios(11.0),tvos(11.0)); + @property (nonatomic) UIAccessibilityTraits accessibilityTraits; + @property (nonatomic) CGRect accessibilityFrame; + @property (nonatomic, nullable) NSString *accessibilityLanguage; + @property (nonatomic) BOOL accessibilityElementsHidden; + @property (nonatomic) BOOL accessibilityViewIsModal; + @property (nonatomic) BOOL shouldGroupAccessibilityChildren; + */ + +// Accessibility identification support +@property (nullable, nonatomic, copy) NSString *accessibilityIdentifier; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/_ASCollectionReusableView.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/_ASCollectionReusableView.h new file mode 100644 index 0000000..fdef359 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/_ASCollectionReusableView.h @@ -0,0 +1,26 @@ +// +// _ASCollectionReusableView.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +@class ASCellNode, ASCollectionElement; + +NS_ASSUME_NONNULL_BEGIN + +AS_SUBCLASSING_RESTRICTED // Note: ASDynamicCastStrict is used on instances of this class based on this restriction. +@interface _ASCollectionReusableView : UICollectionReusableView + +@property (nullable, nonatomic, readonly) ASCellNode *node; +@property (nullable, nonatomic) ASCollectionElement *element; +@property (nullable, nonatomic) UICollectionViewLayoutAttributes *layoutAttributes; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/_ASCollectionReusableView.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/_ASCollectionReusableView.mm new file mode 100644 index 0000000..0ca4b81 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/_ASCollectionReusableView.mm @@ -0,0 +1,89 @@ +// +// _ASCollectionReusableView.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import +#import + +@implementation _ASCollectionReusableView + +- (ASCellNode *)node +{ + return self.element.node; +} + +- (void)setElement:(ASCollectionElement *)element +{ + ASDisplayNodeAssertMainThread(); + element.node.layoutAttributes = _layoutAttributes; + _element = element; +} + +- (void)setLayoutAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes +{ + _layoutAttributes = layoutAttributes; + self.node.layoutAttributes = layoutAttributes; +} + +- (void)prepareForReuse +{ + self.layoutAttributes = nil; + + // Need to clear element before UIKit calls setSelected:NO / setHighlighted:NO on its cells + self.element = nil; + [super prepareForReuse]; +} + +/** + * In the initial case, this is called by UICollectionView during cell dequeueing, before + * we get a chance to assign a node to it, so we must be sure to set these layout attributes + * on our node when one is next assigned to us in @c setNode: . Since there may be cases when we _do_ already + * have our node assigned e.g. during a layout update for existing cells, we also attempt + * to update it now. + */ +- (void)applyLayoutAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes +{ + self.layoutAttributes = layoutAttributes; +} + +/** + * Keep our node filling our content view. + */ +- (void)layoutSubviews +{ + [super layoutSubviews]; + self.node.frame = self.bounds; +} + +@end + +/** + * A category that makes _ASCollectionReusableView conform to IGListBindable. + * + * We don't need to do anything to bind the view model – the cell node + * serves the same purpose. + */ +#if __has_include() + +#import + +@interface _ASCollectionReusableView (IGListBindable) +@end + +@implementation _ASCollectionReusableView (IGListBindable) + +- (void)bindViewModel:(id)viewModel +{ + // nop +} + +@end + +#endif diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/_ASCollectionViewCell.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/_ASCollectionViewCell.h new file mode 100644 index 0000000..94672a9 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/_ASCollectionViewCell.h @@ -0,0 +1,35 @@ +// +// _ASCollectionViewCell.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import + +@class ASCollectionElement; + +NS_ASSUME_NONNULL_BEGIN + +AS_SUBCLASSING_RESTRICTED // Note: ASDynamicCastStrict is used on instances of this class based on this restriction. +@interface _ASCollectionViewCell : UICollectionViewCell + +@property (nonatomic, nullable) ASCollectionElement *element; +@property (nullable, nonatomic, readonly) ASCellNode *node; +@property (nonatomic, nullable) UICollectionViewLayoutAttributes *layoutAttributes; + +/** + * Whether or not this cell is interested in cell node visibility events. + * -cellNodeVisibilityEvent:inScrollView: should be called only if this property is YES. + */ +@property (nonatomic, readonly) BOOL consumesCellNodeVisibilityEvents; + +- (void)cellNodeVisibilityEvent:(ASCellNodeVisibilityEvent)event inScrollView:(UIScrollView *)scrollView; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/_ASCollectionViewCell.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/_ASCollectionViewCell.mm new file mode 100644 index 0000000..28d8cec --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/_ASCollectionViewCell.mm @@ -0,0 +1,147 @@ +// +// _ASCollectionViewCell.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +#import +#import +#import + +@implementation _ASCollectionViewCell + +- (ASCellNode *)node +{ + return self.element.node; +} + +- (void)setElement:(ASCollectionElement *)element +{ + ASDisplayNodeAssertMainThread(); + ASCellNode *node = element.node; + node.layoutAttributes = _layoutAttributes; + _element = element; + + [node __setSelectedFromUIKit:self.selected]; + [node __setHighlightedFromUIKit:self.highlighted]; +} + +- (BOOL)consumesCellNodeVisibilityEvents +{ + ASCellNode *node = self.node; + if (node == nil) { + return NO; + } + return ASSubclassOverridesSelector([ASCellNode class], [node class], @selector(cellNodeVisibilityEvent:inScrollView:withCellFrame:)); +} + +- (void)cellNodeVisibilityEvent:(ASCellNodeVisibilityEvent)event inScrollView:(UIScrollView *)scrollView +{ + [self.node cellNodeVisibilityEvent:event inScrollView:scrollView withCellFrame:self.frame]; +} + +- (void)setSelected:(BOOL)selected +{ + [super setSelected:selected]; + [self.node __setSelectedFromUIKit:selected]; +} + +- (void)setHighlighted:(BOOL)highlighted +{ + [super setHighlighted:highlighted]; + [self.node __setHighlightedFromUIKit:highlighted]; +} + +- (void)setLayoutAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes +{ + _layoutAttributes = layoutAttributes; + self.node.layoutAttributes = layoutAttributes; +} + +- (void)prepareForReuse +{ + self.layoutAttributes = nil; + + // Need to clear element before UIKit calls setSelected:NO / setHighlighted:NO on its cells + self.element = nil; + [super prepareForReuse]; +} + +/** + * In the initial case, this is called by UICollectionView during cell dequeueing, before + * we get a chance to assign a node to it, so we must be sure to set these layout attributes + * on our node when one is next assigned to us in @c setNode: . Since there may be cases when we _do_ already + * have our node assigned e.g. during a layout update for existing cells, we also attempt + * to update it now. + */ +- (void)applyLayoutAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes +{ + [super applyLayoutAttributes:layoutAttributes]; + self.layoutAttributes = layoutAttributes; +} + +/** + * Keep our node filling our content view. + */ +- (void)layoutSubviews +{ + [super layoutSubviews]; + self.node.frame = self.contentView.bounds; +} + +- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event +{ + /** + * The documentation for hitTest:withEvent: on an UIView explicitly states the fact that: + * it ignores view objects that are hidden, that have disabled user interactions, or have an + * alpha level less than 0.01. + * To be able to determine if the collection view cell should skip going further down the tree + * based on the states above we use a valid point within the cells bounds and check the + * superclass hitTest:withEvent: implementation. If this returns a valid value we can go on with + * checking the node as it's expected to not be in one of these states. + */ + if (![super hitTest:self.bounds.origin withEvent:event]) { + return nil; + } + + CGPoint pointOnNode = [self.node.view convertPoint:point fromView:self]; + return [self.node hitTest:pointOnNode withEvent:event]; +} + +- (BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent *)event +{ + CGPoint pointOnNode = [self.node.view convertPoint:point fromView:self]; + return [self.node pointInside:pointOnNode withEvent:event]; +} + +@end + +/** + * A category that makes _ASCollectionViewCell conform to IGListBindable. + * + * We don't need to do anything to bind the view model – the cell node + * serves the same purpose. + */ +#if __has_include() + +#import + +@interface _ASCollectionViewCell (IGListBindable) +@end + +@implementation _ASCollectionViewCell (IGListBindable) + +- (void)bindViewModel:(id)viewModel +{ + // nop +} + +@end + +#endif diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/_ASDisplayLayer.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/_ASDisplayLayer.h new file mode 100644 index 0000000..1066a36 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/_ASDisplayLayer.h @@ -0,0 +1,149 @@ +// +// _ASDisplayLayer.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@class ASDisplayNode; +@protocol _ASDisplayLayerDelegate; + +@interface _ASDisplayLayer : CALayer + +/** + @discussion This property overrides the CALayer category method which implements this via associated objects. + This should result in much better performance for _ASDisplayLayers. + */ +@property (nullable, nonatomic, weak) ASDisplayNode *asyncdisplaykit_node; + +/** + @summary Set to YES to enable asynchronous display for the receiver. + + @default YES (note that this might change for subclasses) + */ +@property (nonatomic) BOOL displaysAsynchronously; + +/** + @summary Cancels any pending async display. + + @desc If the receiver has had display called and is waiting for the dispatched async display to be executed, this will + cancel that dispatched async display. This method is useful to call when removing the receiver from the window. + */ +- (void)cancelAsyncDisplay; + +/** + @summary The dispatch queue used for async display. + + @desc This is exposed here for tests only. + */ ++ (dispatch_queue_t)displayQueue; + +/** + @summary Delegate for asynchronous display of the layer. This should be the node (default) unless you REALLY know what you're doing. + + @desc The asyncDelegate will have the opportunity to override the methods related to async display. + */ +@property (nullable, weak) id<_ASDisplayLayerDelegate> asyncDelegate; + +/** + @summary Suspends both asynchronous and synchronous display of the receiver if YES. + + @desc This can be used to suspend all display calls while the receiver is still in the view hierarchy. If you + want to just cancel pending async display, use cancelAsyncDisplay instead. + + @default NO + */ +@property (nonatomic, getter=isDisplaySuspended) BOOL displaySuspended; + +/** + @summary Bypasses asynchronous rendering and performs a blocking display immediately on the current thread. + + @desc Used by ASDisplayNode to display the layer synchronously on-demand (must be called on the main thread). + */ +- (void)displayImmediately; + +@end + +/** + * Optional methods that the view associated with an _ASDisplayLayer can implement. + * This is distinguished from _ASDisplayLayerDelegate in that it points to the _view_ + * not the node. Unfortunately this is required by ASCollectionView, since we currently + * can't guarantee that an ASCollectionNode exists for it. + */ +@protocol ASCALayerExtendedDelegate + +@optional + +- (void)layer:(CALayer *)layer didChangeBoundsWithOldValue:(CGRect)oldBounds newValue:(CGRect)newBounds; + +@end + +/** + Implement one of +displayAsyncLayer:parameters:isCancelled: or +drawRect:withParameters:isCancelled: to provide drawing for your node. + Use -drawParametersForAsyncLayer: to copy any properties that are involved in drawing into an immutable object for use on the display queue. + display/drawRect implementations MUST be thread-safe, as they can be called on the displayQueue (async) or the main thread (sync/displayImmediately) + */ +@protocol _ASDisplayLayerDelegate + +@optional + +// Called on the display queue and/or main queue (MUST BE THREAD SAFE) + +/** + @summary Delegate method to draw layer contents into a CGBitmapContext. The current UIGraphics context will be set to an appropriate context. + @param parameters An object describing all of the properties you need to draw. Return this from -drawParametersForAsyncLayer: + @param isCancelledBlock Execute this block to check whether the current drawing operation has been cancelled to avoid unnecessary work. A return value of YES means cancel drawing and return. + @param isRasterizing YES if the layer is being rasterized into another layer, in which case drawRect: probably wants to avoid doing things like filling its bounds with a zero-alpha color to clear the backing store. + */ ++ (void)drawRect:(CGRect)bounds + withParameters:(nullable id)parameters + isCancelled:(AS_NOESCAPE asdisplaynode_iscancelled_block_t)isCancelledBlock + isRasterizing:(BOOL)isRasterizing; + +/** + @summary Delegate override to provide new layer contents as a UIImage. + @param parameters An object describing all of the properties you need to draw. Return this from -drawParametersForAsyncLayer: + @param isCancelledBlock Execute this block to check whether the current drawing operation has been cancelled to avoid unnecessary work. A return value of YES means cancel drawing and return. + @return A UIImage (backed by a CGImage) with contents that are ready to display on the main thread. Make sure that the image is already decoded before returning it here. + */ ++ (UIImage *)displayWithParameters:(nullable id)parameters + isCancelled:(AS_NOESCAPE asdisplaynode_iscancelled_block_t)isCancelledBlock; + +// Called on the main thread only + +/** + @summary Delegate override for drawParameters + */ +- (NSObject *)drawParametersForAsyncLayer:(_ASDisplayLayer *)layer; + +/** + @summary Delegate override for willDisplay + */ +- (void)willDisplayAsyncLayer:(_ASDisplayLayer *)layer asynchronously:(BOOL)asynchronously; + +/** + @summary Delegate override for didDisplay + */ +- (void)didDisplayAsyncLayer:(_ASDisplayLayer *)layer; + +/** + @summary Delegate callback to display a layer, synchronously or asynchronously. 'asyncLayer' does not necessarily need to exist (can be nil). Typically, a delegate will display/draw its own contents and then set .contents on the layer when finished. + */ +- (void)displayAsyncLayer:(_ASDisplayLayer *)asyncLayer asynchronously:(BOOL)asynchronously; + +/** + @summary Delegate callback to handle a layer which requests its asynchronous display be cancelled. + */ +- (void)cancelDisplayAsyncLayer:(_ASDisplayLayer *)asyncLayer; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/_ASDisplayLayer.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/_ASDisplayLayer.mm new file mode 100644 index 0000000..4e8569d --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/_ASDisplayLayer.mm @@ -0,0 +1,197 @@ +// +// _ASDisplayLayer.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import +#import +#import +#import + +@implementation _ASDisplayLayer +{ + BOOL _attemptedDisplayWhileZeroSized; +} + +@dynamic displaysAsynchronously; + +#pragma mark - Properties + +- (void)setDisplaySuspended:(BOOL)displaySuspended +{ + ASDisplayNodeAssertMainThread(); + if (_displaySuspended != displaySuspended) { + _displaySuspended = displaySuspended; + if (!displaySuspended) { + // If resuming display, trigger a display now. + [self setNeedsDisplay]; + } else { + // If suspending display, cancel any current async display so that we don't have contents set on us when it's finished. + [self cancelAsyncDisplay]; + } + } +} + +- (void)setBounds:(CGRect)bounds +{ + BOOL valid = ASDisplayNodeAssertNonFatal(ASIsCGRectValidForLayout(bounds), @"Caught attempt to set invalid bounds %@ on %@.", NSStringFromCGRect(bounds), self); + if (!valid) { + return; + } + if ([self.delegate respondsToSelector:@selector(layer:didChangeBoundsWithOldValue:newValue:)]) { + CGRect oldBounds = self.bounds; + [super setBounds:bounds]; + self.asyncdisplaykit_node.threadSafeBounds = bounds; + [(id)self.delegate layer:self didChangeBoundsWithOldValue:oldBounds newValue:bounds]; + } else { + [super setBounds:bounds]; + self.asyncdisplaykit_node.threadSafeBounds = bounds; + } + + if (_attemptedDisplayWhileZeroSized && CGRectIsEmpty(bounds) == NO && self.needsDisplayOnBoundsChange == NO) { + _attemptedDisplayWhileZeroSized = NO; + [self setNeedsDisplay]; + } +} + +#if DEBUG // These override is strictly to help detect application-level threading errors. Avoid method overhead in release. +- (void)setContents:(id)contents +{ + ASDisplayNodeAssertMainThread(); + [super setContents:contents]; +} + +- (void)setNeedsLayout +{ + ASDisplayNodeAssertMainThread(); + as_log_verbose(ASNodeLog(), "%s on %@", sel_getName(_cmd), self); + [super setNeedsLayout]; +} +#endif + +- (void)layoutSublayers +{ + ASDisplayNodeAssertMainThread(); + [super layoutSublayers]; + + [self.asyncdisplaykit_node __layout]; +} + +- (void)setNeedsDisplay +{ + ASDisplayNodeAssertMainThread(); + + // FIXME: Reconsider whether we should cancel a display in progress. + // We should definitely cancel a display that is scheduled, but unstarted display. + [self cancelAsyncDisplay]; + + // Short circuit if display is suspended. When resumed, we will setNeedsDisplay at that time. + if (!_displaySuspended) { + [super setNeedsDisplay]; + } +} + +#pragma mark - + ++ (id)defaultActionForKey:(NSString *)event +{ + // We never want to run one of CA's root default actions. So if we return nil from actionForLayer:forKey:, and let CA + // dig into the actions dictionary, and it doesn't find it there, it will check here and we need to stop the search. + return (id)kCFNull; +} + ++ (dispatch_queue_t)displayQueue +{ + static dispatch_queue_t displayQueue = NULL; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + displayQueue = dispatch_queue_create("org.AsyncDisplayKit.ASDisplayLayer.displayQueue", DISPATCH_QUEUE_CONCURRENT); + // we use the highpri queue to prioritize UI rendering over other async operations + dispatch_set_target_queue(displayQueue, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)); + }); + + return displayQueue; +} + ++ (id)defaultValueForKey:(NSString *)key +{ + if ([key isEqualToString:@"displaysAsynchronously"]) { + return @YES; + } else if ([key isEqualToString:@"opaque"]) { + return @YES; + } else { + return [super defaultValueForKey:key]; + } +} + +#pragma mark - Display + +- (void)displayImmediately +{ + // This method is a low-level bypass that avoids touching CA, including any reset of the + // needsDisplay flag, until the .contents property is set with the result. + // It is designed to be able to block the thread of any caller and fully execute the display. + + ASDisplayNodeAssertMainThread(); + [self display:NO]; +} + +- (void)_hackResetNeedsDisplay +{ + ASDisplayNodeAssertMainThread(); + // Don't listen to our subclasses crazy ideas about setContents by going through super + super.contents = super.contents; +} + +- (void)display +{ + ASDisplayNodeAssertMainThread(); + [self _hackResetNeedsDisplay]; + + if (self.displaySuspended) { + return; + } + + [self display:self.displaysAsynchronously]; +} + +- (void)display:(BOOL)asynchronously +{ + if (CGRectIsEmpty(self.bounds)) { + _attemptedDisplayWhileZeroSized = YES; + } + + [self.asyncDelegate displayAsyncLayer:self asynchronously:asynchronously]; +} + +- (void)cancelAsyncDisplay +{ + ASDisplayNodeAssertMainThread(); + + [self.asyncDelegate cancelDisplayAsyncLayer:self]; +} + +// e.g. > +- (NSString *)description +{ + NSMutableString *description = [[super description] mutableCopy]; + ASDisplayNode *node = self.asyncdisplaykit_node; + if (node != nil) { + NSString *classString = [NSString stringWithFormat:@"%s-", object_getClassName(node)]; + [description replaceOccurrencesOfString:@"_ASDisplay" withString:classString options:kNilOptions range:NSMakeRange(0, description.length)]; + NSUInteger insertionIndex = [description rangeOfString:@">"].location; + if (insertionIndex != NSNotFound) { + NSString *nodeString = [NSString stringWithFormat:@"; node = %@", node]; + [description insertString:nodeString atIndex:insertionIndex]; + } + } + return description; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/_ASDisplayView.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/_ASDisplayView.h new file mode 100644 index 0000000..7e3fe35 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/_ASDisplayView.h @@ -0,0 +1,36 @@ +// +// _ASDisplayView.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +// This class is only for use by ASDisplayNode and should never be subclassed or used directly. +// Note that the "node" property is added to UIView directly via a category in ASDisplayNode. + +@class ASDisplayNode; + +@interface _ASDisplayView : UIView + +/** + @discussion This property overrides the UIView category method which implements this via associated objects. + This should result in much better performance for _ASDisplayView. + */ +@property (nullable, nonatomic, weak) ASDisplayNode *asyncdisplaykit_node; + +// These methods expose a way for ASDisplayNode touch events to let the view call super touch events +// Some UIKit mechanisms, like UITableView and UICollectionView selection handling, require this to work +- (void)__forwardTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event; +- (void)__forwardTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event; +- (void)__forwardTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event; +- (void)__forwardTouchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/_ASDisplayView.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/_ASDisplayView.mm new file mode 100644 index 0000000..7741e44 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/_ASDisplayView.mm @@ -0,0 +1,532 @@ +// +// _ASDisplayView.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import +#import +#import +#import +#import +#import +#import + +#pragma mark - _ASDisplayView + +@interface _ASDisplayView () + +// Keep the node alive while its view is active. If you create a view, add its layer to a layer hierarchy, then release +// the view, the layer retains the view to prevent a crash. This replicates this behaviour for the node abstraction. +@property (nonatomic) ASDisplayNode *keepalive_node; +@end + +@implementation _ASDisplayView +{ + struct _ASDisplayViewInternalFlags { + unsigned inHitTest:1; + unsigned inPointInside:1; + + unsigned inCanBecomeFirstResponder:1; + unsigned inBecomeFirstResponder:1; + unsigned inCanResignFirstResponder:1; + unsigned inResignFirstResponder:1; + unsigned inIsFirstResponder:1; + } _internalFlags; + + NSArray *_accessibilityElements; + CGRect _lastAccessibilityElementsFrame; +} + +#pragma mark - Class + ++ (Class)layerClass +{ + return [_ASDisplayLayer class]; +} + +// e.g. ; frame = ...> +- (NSString *)description +{ + NSMutableString *description = [[super description] mutableCopy]; + + ASDisplayNode *node = _asyncdisplaykit_node; + + if (node != nil) { + NSString *classString = [NSString stringWithFormat:@"%s-", object_getClassName(node)]; + [description replaceOccurrencesOfString:@"_ASDisplay" withString:classString options:kNilOptions range:NSMakeRange(0, description.length)]; + NSUInteger semicolon = [description rangeOfString:@";"].location; + if (semicolon != NSNotFound) { + NSString *nodeString = [NSString stringWithFormat:@"; node = %@", node]; + [description insertString:nodeString atIndex:semicolon]; + } + // Remove layer description – it never contains valuable info and it duplicates the node info. Noisy. + NSRange layerDescriptionRange = [description rangeOfString:@"; layer = <.*>" options:NSRegularExpressionSearch]; + if (layerDescriptionRange.location != NSNotFound) { + [description replaceCharactersInRange:layerDescriptionRange withString:@""]; + // Our regex will grab the closing angle bracket and I'm not clever enough to come up with a better one, so re-add it if needed. + if ([description hasSuffix:@">"] == NO) { + [description appendString:@">"]; + } + } + } + return description; +} + +#pragma mark - UIView Overrides + +- (id)actionForLayer:(CALayer *)layer forKey:(NSString *)event +{ + id uikitAction = [super actionForLayer:layer forKey:event]; + + // Even though the UIKit action will take precedence, we still unconditionally forward to the node so that it can + // track events like kCAOnOrderIn. + id nodeAction = [_asyncdisplaykit_node actionForLayer:layer forKey:event]; + + // If UIKit specifies an action, that takes precedence. That's an animation block so it's explicit. + if (uikitAction && uikitAction != (id)kCFNull) { + return uikitAction; + } + return nodeAction; +} + +- (void)willMoveToWindow:(UIWindow *)newWindow +{ + ASDisplayNode *node = _asyncdisplaykit_node; // Create strong reference to weak ivar. + BOOL visible = (newWindow != nil); + if (visible && !node.inHierarchy) { + [node __enterHierarchy]; + } +} + +- (void)didMoveToWindow +{ + ASDisplayNode *node = _asyncdisplaykit_node; // Create strong reference to weak ivar. + BOOL visible = (self.window != nil); + if (!visible && node.inHierarchy) { + [node __exitHierarchy]; + } +} + +- (void)willMoveToSuperview:(UIView *)newSuperview +{ + // Keep the node alive while the view is in a view hierarchy. This helps ensure that async-drawing views can always + // display their contents as long as they are visible somewhere, and aids in lifecycle management because the + // lifecycle of the node can be treated as the same as the lifecycle of the view (let the view hierarchy own the + // view). + ASDisplayNode *node = _asyncdisplaykit_node; // Create strong reference to weak ivar. + UIView *currentSuperview = self.superview; + if (!currentSuperview && newSuperview) { + self.keepalive_node = node; + } + + if (newSuperview) { + ASDisplayNode *supernode = node.supernode; + BOOL supernodeLoaded = supernode.nodeLoaded; + ASDisplayNodeAssert(!supernode.isLayerBacked, @"Shouldn't be possible for _ASDisplayView's supernode to be layer-backed."); + + BOOL needsSupernodeUpdate = NO; + + if (supernode) { + if (supernodeLoaded) { + if (supernode.layerBacked) { + // See comment in -didMoveToSuperview. This case should be avoided, but is possible with app-level coding errors. + needsSupernodeUpdate = (supernode.layer != newSuperview.layer); + } else { + // If we have a supernode, compensate for users directly messing with views by hitching up to any new supernode. + needsSupernodeUpdate = (supernode.view != newSuperview); + } + } else { + needsSupernodeUpdate = YES; + } + } else { + // If we have no supernode and we are now in a view hierarchy, check to see if we can hook up to a supernode. + needsSupernodeUpdate = (newSuperview != nil); + } + + if (needsSupernodeUpdate) { + // -removeFromSupernode is called by -addSubnode:, if it is needed. + // FIXME: Needs rethinking if automaticallyManagesSubnodes=YES + [newSuperview.asyncdisplaykit_node addSubnode:node]; + } + } +} + +- (void)didMoveToSuperview +{ + ASDisplayNode *node = _asyncdisplaykit_node; // Create strong reference to weak ivar. + UIView *superview = self.superview; + if (superview == nil) { + // Clearing keepalive_node may cause deallocation of the node. In this case, __exitHierarchy may not have an opportunity (e.g. _node will be cleared + // by the time -didMoveToWindow occurs after this) to clear the Visible interfaceState, which we need to do before deallocation to meet an API guarantee. + if (node.inHierarchy) { + [node __exitHierarchy]; + } + self.keepalive_node = nil; + } + +#if DEBUG + // This is only to help detect issues when a root-of-view-controller node is reused separately from its view controller. + // Avoid overhead in release. + if (superview && node.viewControllerRoot) { + UIViewController *vc = [node closestViewController]; + + ASDisplayNodeAssert(vc != nil && [vc isKindOfClass:[ASDKViewController class]] && ((ASDKViewController*)vc).node == node, @"This node was once used as a view controller's node. You should not reuse it without its view controller."); + } +#endif + + ASDisplayNode *supernode = node.supernode; + ASDisplayNodeAssert(!supernode.isLayerBacked, @"Shouldn't be possible for superview's node to be layer-backed."); + + if (supernode) { + ASDisplayNodeAssertTrue(node.nodeLoaded); + BOOL supernodeLoaded = supernode.nodeLoaded; + BOOL needsSupernodeRemoval = NO; + + if (superview) { + // If our new superview is not the same as the supernode's view, or the supernode has no view, disconnect. + if (supernodeLoaded) { + if (supernode.layerBacked) { + // As asserted at the top, this shouldn't be possible, but in production with assertions disabled it can happen. + // We try to make such code behave as well as feasible because it's not that hard of an error to make if some deep + // child node of a layer-backed node happens to be view-backed, but it is not supported and should be avoided. + needsSupernodeRemoval = (supernode.layer != superview.layer); + } else { + needsSupernodeRemoval = (supernode.view != superview); + } + } else { + needsSupernodeRemoval = YES; + } + } else { + // If supernode is loaded but our superview is nil, the user likely manually removed us, so disconnect supernode. + // The unlikely alternative: we are in __unloadNode, with shouldRasterizeSubnodes just having been turned on. + // In the latter case, we don't want to disassemble the node hierarchy because all views are intentionally being destroyed. + BOOL nodeIsRasterized = ((node.hierarchyState & ASHierarchyStateRasterized) == ASHierarchyStateRasterized); + needsSupernodeRemoval = (supernodeLoaded && !nodeIsRasterized); + } + + if (needsSupernodeRemoval) { + // The node will only disconnect from its supernode, not removeFromSuperview, in this condition. + // FIXME: Needs rethinking if automaticallyManagesSubnodes=YES + [node removeFromSupernode]; + } + } +} + +- (void)insertSubview:(UIView *)view atIndex:(NSInteger)index { + [super insertSubview:view atIndex:index]; + +#ifndef ASDK_ACCESSIBILITY_DISABLE + self.accessibilityElements = nil; +#endif +} + +- (void)addSubview:(UIView *)view +{ + [super addSubview:view]; + +#ifndef ASDK_ACCESSIBILITY_DISABLE + self.accessibilityElements = nil; +#endif +} + +- (void)willRemoveSubview:(UIView *)subview +{ + [super willRemoveSubview:subview]; + +#ifndef ASDK_ACCESSIBILITY_DISABLE + self.accessibilityElements = nil; +#endif +} + +- (CGSize)sizeThatFits:(CGSize)size +{ + ASDisplayNode *node = _asyncdisplaykit_node; // Create strong reference to weak ivar. + return node ? [node layoutThatFits:ASSizeRangeMake(size)].size : [super sizeThatFits:size]; +} + +- (void)setNeedsDisplay +{ + ASDisplayNodeAssertMainThread(); + // Standard implementation does not actually get to the layer, at least for views that don't implement drawRect:. + [self.layer setNeedsDisplay]; +} + +- (UIViewContentMode)contentMode +{ + return ASDisplayNodeUIContentModeFromCAContentsGravity(self.layer.contentsGravity); +} + +- (void)setContentMode:(UIViewContentMode)contentMode +{ + ASDisplayNodeAssert(contentMode != UIViewContentModeRedraw, @"Don't do this. Use needsDisplayOnBoundsChange instead."); + + // Do our own mapping so as not to call super and muck up needsDisplayOnBoundsChange. If we're in a production build, fall back to resize if we see redraw + self.layer.contentsGravity = (contentMode != UIViewContentModeRedraw) ? ASDisplayNodeCAContentsGravityFromUIContentMode(contentMode) : kCAGravityResize; +} + +- (void)setBounds:(CGRect)bounds +{ + ASDisplayNode *node = _asyncdisplaykit_node; // Create strong reference to weak ivar. + [super setBounds:bounds]; + node.threadSafeBounds = bounds; +} + +- (void)addGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer +{ + [super addGestureRecognizer:gestureRecognizer]; + [_asyncdisplaykit_node nodeViewDidAddGestureRecognizer]; +} + +#pragma mark - Event Handling + UIResponder Overrides +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event +{ + ASDisplayNode *node = _asyncdisplaykit_node; // Create strong reference to weak ivar. + if (node.methodOverrides & ASDisplayNodeMethodOverrideTouchesBegan) { + [node touchesBegan:touches withEvent:event]; + } else { + [super touchesBegan:touches withEvent:event]; + } +} + +- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event +{ + ASDisplayNode *node = _asyncdisplaykit_node; // Create strong reference to weak ivar. + if (node.methodOverrides & ASDisplayNodeMethodOverrideTouchesMoved) { + [node touchesMoved:touches withEvent:event]; + } else { + [super touchesMoved:touches withEvent:event]; + } +} + +- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event +{ + ASDisplayNode *node = _asyncdisplaykit_node; // Create strong reference to weak ivar. + if (node.methodOverrides & ASDisplayNodeMethodOverrideTouchesEnded) { + [node touchesEnded:touches withEvent:event]; + } else { + [super touchesEnded:touches withEvent:event]; + } +} + +- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event +{ + ASDisplayNode *node = _asyncdisplaykit_node; // Create strong reference to weak ivar. + if (node.methodOverrides & ASDisplayNodeMethodOverrideTouchesCancelled) { + [node touchesCancelled:touches withEvent:event]; + } else { + [super touchesCancelled:touches withEvent:event]; + } +} + +- (void)__forwardTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event +{ + [super touchesBegan:touches withEvent:event]; +} + +- (void)__forwardTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event +{ + [super touchesMoved:touches withEvent:event]; +} + +- (void)__forwardTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event +{ + [super touchesEnded:touches withEvent:event]; +} + +- (void)__forwardTouchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event +{ + [super touchesCancelled:touches withEvent:event]; +} + +- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event +{ + // REVIEW: We should optimize these types of messages by setting a boolean in the associated ASDisplayNode subclass if + // they actually override the method. Same goes for -pointInside:withEvent: below. Many UIKit classes use that + // pattern for meaningful reductions of message send overhead in hot code (especially event handling). + + // Set boolean so this method can be re-entrant. If the node subclass wants to default to / make use of UIView + // hitTest:, it will call it on the view, which is _ASDisplayView. After calling into the node, any additional calls + // should use the UIView implementation of hitTest: + ASDisplayNode *node = _asyncdisplaykit_node; // Create strong reference to weak ivar. + if (!_internalFlags.inHitTest) { + _internalFlags.inHitTest = YES; + UIView *hitView = [node hitTest:point withEvent:event]; + _internalFlags.inHitTest = NO; + return hitView; + } else { + return [super hitTest:point withEvent:event]; + } +} + +- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event +{ + // See comments in -hitTest:withEvent: for the strategy here. + ASDisplayNode *node = _asyncdisplaykit_node; // Create strong reference to weak ivar. + if (!_internalFlags.inPointInside) { + _internalFlags.inPointInside = YES; + BOOL result = [node pointInside:point withEvent:event]; + _internalFlags.inPointInside = NO; + return result; + } else { + return [super pointInside:point withEvent:event]; + } +} + +- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer +{ + ASDisplayNode *node = _asyncdisplaykit_node; // Create strong reference to weak ivar. + return [node gestureRecognizerShouldBegin:gestureRecognizer]; +} + +- (void)tintColorDidChange +{ + ASDisplayNode *node = _asyncdisplaykit_node; // Create strong reference to weak ivar. + [super tintColorDidChange]; + + [node tintColorDidChange]; +} + +#pragma mark UIResponder Handling + +- (BOOL)canBecomeFirstResponder +{ + ASDisplayNode *node = _asyncdisplaykit_node; // Create strong reference to weak ivar. + if (!_internalFlags.inCanBecomeFirstResponder) { + _internalFlags.inCanBecomeFirstResponder = YES; + BOOL result = [node canBecomeFirstResponder]; + _internalFlags.inCanBecomeFirstResponder = NO; + return result; + } else { + return [super canBecomeFirstResponder]; + } +} + +- (BOOL)becomeFirstResponder +{ + ASDisplayNode *node = _asyncdisplaykit_node; // Create strong reference to weak ivar. + if (!_internalFlags.inBecomeFirstResponder) { + _internalFlags.inBecomeFirstResponder = YES; + BOOL result = [node becomeFirstResponder]; + _internalFlags.inBecomeFirstResponder = NO; + return result; + } else { + return [super becomeFirstResponder]; + } +} + +- (BOOL)canResignFirstResponder +{ + ASDisplayNode *node = _asyncdisplaykit_node; // Create strong reference to weak ivar. + if (!_internalFlags.inCanResignFirstResponder) { + _internalFlags.inCanResignFirstResponder = YES; + BOOL result = [node canResignFirstResponder]; + _internalFlags.inCanResignFirstResponder = NO; + return result; + } else { + return [super canResignFirstResponder]; + } +} + +- (BOOL)resignFirstResponder +{ + ASDisplayNode *node = _asyncdisplaykit_node; // Create strong reference to weak ivar. + if (!_internalFlags.inResignFirstResponder) { + _internalFlags.inResignFirstResponder = YES; + BOOL result = [node resignFirstResponder]; + _internalFlags.inResignFirstResponder = NO; + return result; + } else { + return [super resignFirstResponder]; + } +} + +- (BOOL)isFirstResponder +{ + ASDisplayNode *node = _asyncdisplaykit_node; // Create strong reference to weak ivar. + if (!_internalFlags.inIsFirstResponder) { + _internalFlags.inIsFirstResponder = YES; + BOOL result = [node isFirstResponder]; + _internalFlags.inIsFirstResponder = NO; + return result; + } else { + return [super isFirstResponder]; + } +} + +- (BOOL)canPerformAction:(SEL)action withSender:(id)sender +{ + // We forward responder-chain actions to our node if we can't handle them ourselves. See -targetForAction:withSender:. + ASDisplayNode *node = _asyncdisplaykit_node; // Create strong reference to weak ivar. + return ([super canPerformAction:action withSender:sender] || [node respondsToSelector:action]); +} + +- (void)layoutMarginsDidChange +{ + ASDisplayNode *node = _asyncdisplaykit_node; // Create strong reference to weak ivar. + [super layoutMarginsDidChange]; + + [node layoutMarginsDidChange]; +} + +- (void)safeAreaInsetsDidChange +{ + ASDisplayNode *node = _asyncdisplaykit_node; // Create strong reference to weak ivar. + [super safeAreaInsetsDidChange]; + + [node safeAreaInsetsDidChange]; +} + +- (id)forwardingTargetForSelector:(SEL)aSelector +{ + // Ideally, we would implement -targetForAction:withSender: and simply return the node where we don't respond personally. + // Unfortunately UIResponder's default implementation of -targetForAction:withSender: doesn't follow its own documentation. It doesn't call -targetForAction:withSender: up the responder chain when -canPerformAction:withSender: fails, but instead merely calls -canPerformAction:withSender: on itself and then up the chain. rdar://20111500. + // Consequently, to forward responder-chain actions to our node, we override -canPerformAction:withSender: (used by the chain) to indicate support for responder chain-driven actions that our node supports, and then provide the node as a forwarding target here. + ASDisplayNode *node = _asyncdisplaykit_node; // Create strong reference to weak ivar. + return node; +} + +#if TARGET_OS_TV +#pragma mark - tvOS +- (BOOL)canBecomeFocused +{ + ASDisplayNode *node = _asyncdisplaykit_node; // Create strong reference to weak ivar. + return [node canBecomeFocused]; +} + +- (void)didUpdateFocusInContext:(UIFocusUpdateContext *)context withAnimationCoordinator:(UIFocusAnimationCoordinator *)coordinator +{ + ASDisplayNode *node = _asyncdisplaykit_node; // Create strong reference to weak ivar. + return [node didUpdateFocusInContext:context withAnimationCoordinator:coordinator]; +} + +- (void)setNeedsFocusUpdate +{ + ASDisplayNode *node = _asyncdisplaykit_node; // Create strong reference to weak ivar. + return [node setNeedsFocusUpdate]; +} + +- (void)updateFocusIfNeeded +{ + ASDisplayNode *node = _asyncdisplaykit_node; // Create strong reference to weak ivar. + return [node updateFocusIfNeeded]; +} + +- (BOOL)shouldUpdateFocusInContext:(UIFocusUpdateContext *)context +{ + ASDisplayNode *node = _asyncdisplaykit_node; // Create strong reference to weak ivar. + return [node shouldUpdateFocusInContext:context]; +} + +- (UIView *)preferredFocusedView +{ + ASDisplayNode *node = _asyncdisplaykit_node; // Create strong reference to weak ivar. + return [node preferredFocusedView]; +} +#endif +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/_ASDisplayViewAccessiblity.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/_ASDisplayViewAccessiblity.h new file mode 100644 index 0000000..7f159d1 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/_ASDisplayViewAccessiblity.h @@ -0,0 +1,30 @@ +// +// _ASDisplayViewAccessiblity.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +// WARNING: When dealing with accessibility elements, please use the `accessibilityElements` +// property instead of the older methods e.g. `accessibilityElementCount()`. While the older methods +// should still work as long as accessibility is enabled, this framework provides no guarantees on +// their correctness. For details, see +// https://developer.apple.com/documentation/objectivec/nsobject/1615147-accessibilityelements + +// After recusively collecting all of the accessibility elements of a node, they get sorted. This sort determines +// the order that a screen reader will traverse the elements. By default, we sort these elements based on their +// origin: lower y origin comes first, then lower x origin. If 2 nodes have an equal origin, the node with the smaller +// height is placed before the node with the smaller width. If two nodes have the exact same rect, we throw up our hands +// and return NSOrderedSame. +// +// In general this seems to work fairly well. However, if you want to provide a custom sort you can do so via +// setUserDefinedAccessibilitySortComparator(). The two elements you are comparing are NSObjects, which conforms to the +// informal UIAccessibility protocol, so you can safely compare properties like accessibilityFrame. +typedef NSComparisonResult (^ASSortAccessibilityElementsComparator)(NSObject *, NSObject *); + +// Use this method to supply your own custom sort comparator used to determine the order of the accessibility elements +void setUserDefinedAccessibilitySortComparator(ASSortAccessibilityElementsComparator userDefinedComparator); diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/_ASDisplayViewAccessiblity.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/_ASDisplayViewAccessiblity.mm new file mode 100644 index 0000000..7fb79ea --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Details/_ASDisplayViewAccessiblity.mm @@ -0,0 +1,405 @@ +// +// _ASDisplayViewAccessiblity.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#ifndef ASDK_ACCESSIBILITY_DISABLE + +#import +#import +#import +#import +#import +#import +#import +#import + +#import + +#pragma mark - UIAccessibilityElement + +static ASSortAccessibilityElementsComparator currentAccessibilityComparator = nil; +static ASSortAccessibilityElementsComparator defaultAccessibilityComparator = nil; + +void setUserDefinedAccessibilitySortComparator(ASSortAccessibilityElementsComparator userDefinedComparator) { + currentAccessibilityComparator = userDefinedComparator ?: defaultAccessibilityComparator; +} + +/// Sort accessiblity elements first by y and than by x origin. +void SortAccessibilityElements(NSMutableArray *elements) +{ + ASDisplayNodeCAssertNotNil(elements, @"Should pass in a NSMutableArray"); + + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + defaultAccessibilityComparator = ^NSComparisonResult(NSObject *a, NSObject *b) { + CGPoint originA = a.accessibilityFrame.origin; + CGPoint originB = b.accessibilityFrame.origin; + if (originA.y == originB.y) { + if (originA.x == originB.x) { + // if we have the same origin, favor shorter items. If heights are the same, favor thinner items. If size is the same ¯\_(ツ)_/¯ + CGSize sizeA = a.accessibilityFrame.size; + CGSize sizeB = b.accessibilityFrame.size; + if (sizeA.height == sizeB.height) { + if (sizeA.width == sizeB.width) { + return NSOrderedSame; + } + return (sizeA.width < sizeB.width) ? NSOrderedAscending : NSOrderedDescending; + } + return (sizeA.height < sizeB.height) ? NSOrderedAscending : NSOrderedDescending; + } + return (originA.x < originB.x) ? NSOrderedAscending : NSOrderedDescending; + } + return (originA.y < originB.y) ? NSOrderedAscending : NSOrderedDescending; + }; + + if (!currentAccessibilityComparator) { + currentAccessibilityComparator = defaultAccessibilityComparator; + } + }); + + [elements sortUsingComparator:currentAccessibilityComparator]; +} + +static CGRect ASAccessibilityFrameForNode(ASDisplayNode *node) { + CALayer *layer = node.layer; + return [layer convertRect:node.bounds toLayer:ASFindWindowOfLayer(layer).layer]; +} + +@interface ASAccessibilityElement : UIAccessibilityElement + +@property (nonatomic) ASDisplayNode *node; + ++ (ASAccessibilityElement *)accessibilityElementWithContainer:(UIView *)container node:(ASDisplayNode *)node; + +@end + +@implementation ASAccessibilityElement + ++ (ASAccessibilityElement *)accessibilityElementWithContainer:(UIView *)container node:(ASDisplayNode *)node +{ + ASAccessibilityElement *accessibilityElement = [[ASAccessibilityElement alloc] initWithAccessibilityContainer:container]; + accessibilityElement.node = node; + accessibilityElement.accessibilityIdentifier = node.accessibilityIdentifier; + accessibilityElement.accessibilityLabel = node.accessibilityLabel; + accessibilityElement.accessibilityHint = node.accessibilityHint; + accessibilityElement.accessibilityValue = node.accessibilityValue; + accessibilityElement.accessibilityTraits = node.accessibilityTraits; + accessibilityElement.accessibilityElementsHidden = node.accessibilityElementsHidden; + if (AS_AVAILABLE_IOS_TVOS(11, 11)) { + accessibilityElement.accessibilityAttributedLabel = node.accessibilityAttributedLabel; + accessibilityElement.accessibilityAttributedHint = node.accessibilityAttributedHint; + accessibilityElement.accessibilityAttributedValue = node.accessibilityAttributedValue; + } + return accessibilityElement; +} + +- (CGRect)accessibilityFrame +{ + return ASAccessibilityFrameForNode(self.node); +} + +@end + +#pragma mark - _ASDisplayView / UIAccessibilityContainer + +@interface ASAccessibilityCustomAction : UIAccessibilityCustomAction + +@property (nonatomic) ASDisplayNode *node; + +@end + +@implementation ASAccessibilityCustomAction + +- (CGRect)accessibilityFrame +{ + return ASAccessibilityFrameForNode(self.node); +} + +@end + +/// Collect all subnodes for the given node by walking down the subnode tree and calculates the screen coordinates based on the containerNode and container +static void CollectUIAccessibilityElementsForNode(ASDisplayNode *node, ASDisplayNode *containerNode, id container, NSMutableArray *elements) +{ + ASDisplayNodeCAssertNotNil(elements, @"Should pass in a NSMutableArray"); + + ASDisplayNodePerformBlockOnEveryNodeBFS(node, ^(ASDisplayNode * _Nonnull currentNode) { + // For every subnode that is layer backed or it's supernode has subtree rasterization enabled + // we have to create a UIAccessibilityElement as no view for this node exists + if (currentNode != containerNode && currentNode.isAccessibilityElement) { + UIAccessibilityElement *accessibilityElement = [ASAccessibilityElement accessibilityElementWithContainer:container node:currentNode]; + [elements addObject:accessibilityElement]; + } + }); +} + +static void CollectAccessibilityElementsForContainer(ASDisplayNode *container, UIView *view, + NSMutableArray *elements) { + ASDisplayNodeCAssertNotNil(view, @"Passed in view should not be nil"); + if (view == nil) { + return; + } + UIAccessibilityElement *accessiblityElement = + [ASAccessibilityElement accessibilityElementWithContainer:view + node:container]; + + NSMutableArray *labeledNodes = [[NSMutableArray alloc] init]; + NSMutableArray *actions = [[NSMutableArray alloc] init]; + std::queue queue; + queue.push(container); + + // If the container does not have an accessibility label set, or if the label is meant for custom + // actions only, then aggregate its subnodes' labels. Otherwise, treat the label as an overriden + // value and do not perform the aggregation. + BOOL shouldAggregateSubnodeLabels = + (container.accessibilityLabel.length == 0) || + (container.accessibilityTraits & ASInteractiveAccessibilityTraitsMask()); + + ASDisplayNode *node = nil; + while (!queue.empty()) { + node = queue.front(); + queue.pop(); + + if (node != container && node.isAccessibilityContainer) { + UIView *containerView = node.isLayerBacked ? view : node.view; + CollectAccessibilityElementsForContainer(node, containerView, elements); + continue; + } + + if (node.accessibilityLabel.length > 0) { + if (node.accessibilityTraits & ASInteractiveAccessibilityTraitsMask()) { + ASAccessibilityCustomAction *action = [[ASAccessibilityCustomAction alloc] initWithName:node.accessibilityLabel target:node selector:@selector(performAccessibilityCustomAction:)]; + action.node = node; + [actions addObject:action]; + + node.accessibilityCustomAction = action; + } else if (node == container || shouldAggregateSubnodeLabels) { + ASAccessibilityElement *nonInteractiveElement = [ASAccessibilityElement accessibilityElementWithContainer:view node:node]; + [labeledNodes addObject:nonInteractiveElement]; + } + } + + for (ASDisplayNode *subnode in node.subnodes) { + queue.push(subnode); + } + } + + SortAccessibilityElements(labeledNodes); + + if (AS_AVAILABLE_IOS_TVOS(11, 11)) { + NSArray *attributedLabels = [labeledNodes valueForKey:@"accessibilityAttributedLabel"]; + NSMutableAttributedString *attributedLabel = [NSMutableAttributedString new]; + [attributedLabels enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + if (idx != 0) { + [attributedLabel appendAttributedString:[[NSAttributedString alloc] initWithString:@", "]]; + } + [attributedLabel appendAttributedString:(NSAttributedString *)obj]; + }]; + accessiblityElement.accessibilityAttributedLabel = attributedLabel; + } else { + NSArray *labels = [labeledNodes valueForKey:@"accessibilityLabel"]; + accessiblityElement.accessibilityLabel = [labels componentsJoinedByString:@", "]; + } + + SortAccessibilityElements(actions); + accessiblityElement.accessibilityCustomActions = actions; + + [elements addObject:accessiblityElement]; +} + +/// Check if a view is a subviews of an UIScrollView. This is used to determine whether to enforce that +/// accessibility elements must be on screen +static BOOL recusivelyCheckSuperviewsForScrollView(UIView *view) { + if (!view) { + return NO; + } else if ([view isKindOfClass:[UIScrollView class]]) { + return YES; + } + return recusivelyCheckSuperviewsForScrollView(view.superview); +} + +/// returns YES if this node should be considered "hidden" from the screen reader. +static BOOL nodeIsHiddenFromAcessibility(ASDisplayNode *node) { + return node.isHidden || node.alpha == 0.0 || node.accessibilityElementsHidden; +} + +/// Collect all accessibliity elements for a given view and view node +static void CollectAccessibilityElements(ASDisplayNode *node, NSMutableArray *elements) +{ + ASDisplayNodeCAssertNotNil(elements, @"Should pass in a NSMutableArray"); + ASDisplayNodeCAssertFalse(node.isLayerBacked); + if (node.isLayerBacked) { + return; + } + + BOOL anySubNodeIsCollection = (nil != ASDisplayNodeFindFirstNode(node, + ^BOOL(ASDisplayNode *nodeToCheck) { + return ASDynamicCast(nodeToCheck, ASCollectionNode) != nil || + ASDynamicCast(nodeToCheck, ASTableNode) != nil; + })); + + UIView *view = node.view; + + // If we don't have a window, let's just bail out + if (!view.window) { + return; + } + + if (node.isAccessibilityContainer && !anySubNodeIsCollection) { + CollectAccessibilityElementsForContainer(node, view, elements); + return; + } + + // Handle rasterize case + if (node.rasterizesSubtree) { + CollectUIAccessibilityElementsForNode(node, node, view, elements); + return; + } + + if (nodeIsHiddenFromAcessibility(node)) { + return; + } + + // see if one of the subnodes is modal. If it is, then we only need to collect accessibilityElements from that + // node. If more than one subnode is modal, UIKit uses the last view in subviews as the modal view (it appears to + // be based on the index in the subviews array, not the location on screen). Let's do the same. + ASDisplayNode *modalSubnode = nil; + for (ASDisplayNode *subnode in node.subnodes.reverseObjectEnumerator) { + if (subnode.accessibilityViewIsModal) { + modalSubnode = subnode; + break; + } + } + + // If we have a modal subnode, just use that. Otherwise, use all subnodes + NSArray *subnodes = modalSubnode ? @[ modalSubnode ] : node.subnodes; + + for (ASDisplayNode *subnode in subnodes) { + // If a node is hidden or has an alpha of 0.0 we should not include it + if (nodeIsHiddenFromAcessibility(subnode)) { + continue; + } + + // If a subnode is outside of the view's window, exclude it UNLESS it is a subview of an UIScrollView. + // In this case UIKit will return the element even if it is outside of the window or the scrollView's visible rect (contentOffset + contentSize) + CGRect nodeInWindowCoords = [node convertRect:subnode.frame toNode:nil]; + if (!CGRectIntersectsRect(view.window.frame, nodeInWindowCoords) && !recusivelyCheckSuperviewsForScrollView(view)) { + continue; + } + + if (subnode.isAccessibilityElement) { + // An accessiblityElement can either be a UIView or a UIAccessibilityElement + if (subnode.isLayerBacked) { + // No view for layer backed nodes exist. It's necessary to create a UIAccessibilityElement that represents this node + UIAccessibilityElement *accessiblityElement = [ASAccessibilityElement accessibilityElementWithContainer:view node:subnode]; + [elements addObject:accessiblityElement]; + } else { + // Accessiblity element is not layer backed just add the view as accessibility element + [elements addObject:subnode.view]; + } + } else if (subnode.isLayerBacked) { + // Go down the hierarchy of the layer backed subnode and collect all of the UIAccessibilityElement + CollectUIAccessibilityElementsForNode(subnode, node, view, elements); + } else if (subnode.accessibilityElementCount > 0) { + // UIView is itself a UIAccessibilityContainer just add it + [elements addObject:subnode.view]; + } + } +} + +@interface _ASDisplayView () { + NSArray *_accessibilityElements; +} + +@end + +@implementation _ASDisplayView (UIAccessibilityContainer) + +#pragma mark - UIAccessibility + +- (void)setAccessibilityElements:(NSArray *)accessibilityElements +{ + ASDisplayNodeAssertMainThread(); + // While it looks very strange to ignore the accessibilyElements param and set _accessibilityElements to nil, it is actually on purpose. + // _ASDisplayView's accessibilityElements method will always defer to the node for accessibilityElements when _accessibilityElements is + // nil. Calling setAccessibilityElements on _ASDisplayView is basically clearing the cache and forcing _ASDisplayView to ask the node + // for its accessibilityElements the next time they are requested. + _accessibilityElements = nil; +} + +- (NSArray *)accessibilityElements +{ + ASDisplayNodeAssertMainThread(); + + ASDisplayNode *viewNode = self.asyncdisplaykit_node; + if (viewNode == nil) { + return @[]; + } + + // when items become hidden/visible we have to manually clear the _accessibilityElements in order to get an updated version + // Instead, let's try computing the elements every time and see how badly it affects performance. + if (_accessibilityElements == nil || ASActivateExperimentalFeature(ASExperimentalDoNotCacheAccessibilityElements)) { + _accessibilityElements = [viewNode accessibilityElements]; + } + return _accessibilityElements; +} + +@end + +@implementation ASDisplayNode (AccessibilityInternal) + +- (NSArray *)accessibilityElements +{ + // NSObject implements the informal accessibility protocol. This means that all ASDisplayNodes already have an accessibilityElements + // property. If an ASDisplayNode subclass has explicitly set the property, let's use that instead of traversing the node tree to try + // to create the elements automatically + NSArray *elements = [super accessibilityElements]; + if (elements.count) { + return elements; + } + + if (!self.isNodeLoaded) { + ASDisplayNodeFailAssert(@"Cannot access accessibilityElements since node is not loaded"); + return @[]; + } + NSMutableArray *accessibilityElements = [[NSMutableArray alloc] init]; + CollectAccessibilityElements(self, accessibilityElements); + SortAccessibilityElements(accessibilityElements); + return accessibilityElements; +} + +@end + +@implementation _ASDisplayView (UIAccessibilityAction) + +- (BOOL)accessibilityActivate { + return [self.asyncdisplaykit_node accessibilityActivate]; +} + +- (void)accessibilityIncrement { + [self.asyncdisplaykit_node accessibilityIncrement]; +} + +- (void)accessibilityDecrement { + [self.asyncdisplaykit_node accessibilityDecrement]; +} + +- (BOOL)accessibilityScroll:(UIAccessibilityScrollDirection)direction { + return [self.asyncdisplaykit_node accessibilityScroll:direction]; +} + +- (BOOL)accessibilityPerformEscape { + return [self.asyncdisplaykit_node accessibilityPerformEscape]; +} + +- (BOOL)accessibilityPerformMagicTap { + return [self.asyncdisplaykit_node accessibilityPerformMagicTap]; +} + +@end + +#endif diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/IGListAdapter+AsyncDisplayKit.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/IGListAdapter+AsyncDisplayKit.h new file mode 100644 index 0000000..76b53c1 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/IGListAdapter+AsyncDisplayKit.h @@ -0,0 +1,37 @@ +// +// IGListAdapter+AsyncDisplayKit.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#if AS_IG_LIST_KIT + +#import + +NS_ASSUME_NONNULL_BEGIN + +@class ASCollectionNode; + +@interface IGListAdapter (AsyncDisplayKit) + +/** + * Connect this list adapter to the given collection node. + * + * @param collectionNode The collection node to drive with this list adapter. + * + * @note This method may only be called once per list adapter, + * and it must be called on the main thread. -[UIViewController init] + * is a good place to call it. This method does not retain the collection node. + */ +- (void)setASDKCollectionNode:(ASCollectionNode *)collectionNode; + +@end + +NS_ASSUME_NONNULL_END + +#endif // AS_IG_LIST_KIT diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/IGListAdapter+AsyncDisplayKit.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/IGListAdapter+AsyncDisplayKit.mm new file mode 100644 index 0000000..bcfb246 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/IGListAdapter+AsyncDisplayKit.mm @@ -0,0 +1,51 @@ +// +// IGListAdapter+AsyncDisplayKit.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#if AS_IG_LIST_KIT + +#import +#import + +@implementation IGListAdapter (AsyncDisplayKit) + +- (void)setASDKCollectionNode:(ASCollectionNode *)collectionNode +{ + ASDisplayNodeAssertMainThread(); + + // Attempt to retrieve previous data source. + ASIGListAdapterBasedDataSource *dataSource = objc_getAssociatedObject(self, _cmd); + // Bomb if we already made one. + if (dataSource != nil) { + ASDisplayNodeFailAssert(@"Attempt to call %@ multiple times on the same list adapter. Not currently allowed!", NSStringFromSelector(_cmd)); + return; + } + + // Make a data source and retain it. + dataSource = [[ASIGListAdapterBasedDataSource alloc] initWithListAdapter:self collectionDelegate:collectionNode.delegate]; + objc_setAssociatedObject(self, _cmd, dataSource, OBJC_ASSOCIATION_RETAIN_NONATOMIC); + + // Attach the data source to the collection node. + collectionNode.dataSource = dataSource; + collectionNode.delegate = dataSource; + __weak IGListAdapter *weakSelf = self; + [collectionNode onDidLoad:^(__kindof ASCollectionNode * _Nonnull collectionNode) { +#if IG_LIST_COLLECTION_VIEW + // We manually set the superclass of ASCollectionView to IGListCollectionView at runtime if needed. + weakSelf.collectionView = (IGListCollectionView *)collectionNode.view; +#else + weakSelf.collectionView = collectionNode.view; +#endif + }]; +} + +@end + +#endif // AS_IG_LIST_KIT diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASAbsoluteLayoutElement.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASAbsoluteLayoutElement.h new file mode 100644 index 0000000..109816d --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASAbsoluteLayoutElement.h @@ -0,0 +1,26 @@ +// +// ASAbsoluteLayoutElement.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * Layout options that can be defined for an ASLayoutElement being added to a ASAbsoluteLayoutSpec. + */ +@protocol ASAbsoluteLayoutElement + +/** + * @abstract The position of this object within its parent spec. + */ +@property (nonatomic) CGPoint layoutPosition; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASAbsoluteLayoutSpec.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASAbsoluteLayoutSpec.h new file mode 100644 index 0000000..23d27b6 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASAbsoluteLayoutSpec.h @@ -0,0 +1,45 @@ +// +// ASAbsoluteLayoutSpec.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +/** How much space the spec will take up. */ +typedef NS_ENUM(NSInteger, ASAbsoluteLayoutSpecSizing) { + /** The spec will take up the maximum size possible. */ + ASAbsoluteLayoutSpecSizingDefault, + /** Computes a size for the spec that is the union of all children's frames. */ + ASAbsoluteLayoutSpecSizingSizeToFit, +}; + +NS_ASSUME_NONNULL_BEGIN + +/** + A layout spec that positions children at fixed positions. + */ +@interface ASAbsoluteLayoutSpec : ASLayoutSpec + +/** + How much space will the spec taken up + */ +@property (nonatomic) ASAbsoluteLayoutSpecSizing sizing; + +/** + @param sizing How much space the spec will take up + @param children Children to be positioned at fixed positions + */ ++ (instancetype)absoluteLayoutSpecWithSizing:(ASAbsoluteLayoutSpecSizing)sizing children:(NSArray> *)children NS_RETURNS_RETAINED AS_WARN_UNUSED_RESULT; + +/** + @param children Children to be positioned at fixed positions + */ ++ (instancetype)absoluteLayoutSpecWithChildren:(NSArray> *)children NS_RETURNS_RETAINED AS_WARN_UNUSED_RESULT; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASAbsoluteLayoutSpec.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASAbsoluteLayoutSpec.mm new file mode 100644 index 0000000..02b14df --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASAbsoluteLayoutSpec.mm @@ -0,0 +1,103 @@ +// +// ASAbsoluteLayoutSpec.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import +#import +#import +#import + +#pragma mark - ASAbsoluteLayoutSpec + +@implementation ASAbsoluteLayoutSpec + +#pragma mark - Class + ++ (instancetype)absoluteLayoutSpecWithChildren:(NSArray *)children NS_RETURNS_RETAINED +{ + return [[self alloc] initWithChildren:children]; +} + ++ (instancetype)absoluteLayoutSpecWithSizing:(ASAbsoluteLayoutSpecSizing)sizing children:(NSArray> *)children NS_RETURNS_RETAINED +{ + return [[self alloc] initWithSizing:sizing children:children]; +} + +#pragma mark - Lifecycle + +- (instancetype)init +{ + return [self initWithChildren:nil]; +} + +- (instancetype)initWithChildren:(NSArray *)children +{ + return [self initWithSizing:ASAbsoluteLayoutSpecSizingDefault children:children]; +} + +- (instancetype)initWithSizing:(ASAbsoluteLayoutSpecSizing)sizing children:(NSArray> *)children +{ + if (!(self = [super init])) { + return nil; + } + + _sizing = sizing; + self.children = children; + + return self; +} + +#pragma mark - ASLayoutSpec + +- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize +{ + CGSize size = { + ASPointsValidForSize(constrainedSize.max.width) == NO ? ASLayoutElementParentDimensionUndefined : constrainedSize.max.width, + ASPointsValidForSize(constrainedSize.max.height) == NO ? ASLayoutElementParentDimensionUndefined : constrainedSize.max.height + }; + + NSArray *children = self.children; + ASLayout *rawSublayouts[children.count]; + int i = 0; + + for (id child in children) { + CGPoint layoutPosition = child.style.layoutPosition; + CGSize autoMaxSize = { + constrainedSize.max.width - layoutPosition.x, + constrainedSize.max.height - layoutPosition.y + }; + + const ASSizeRange childConstraint = ASLayoutElementSizeResolveAutoSize(child.style.size, size, {{0,0}, autoMaxSize}); + + ASLayout *sublayout = [child layoutThatFits:childConstraint parentSize:size]; + sublayout.position = layoutPosition; + rawSublayouts[i++] = sublayout; + } + const auto sublayouts = [NSArray arrayByTransferring:rawSublayouts count:i]; + + if (_sizing == ASAbsoluteLayoutSpecSizingSizeToFit || isnan(size.width)) { + size.width = constrainedSize.min.width; + for (ASLayout *sublayout in sublayouts) { + size.width = MAX(size.width, sublayout.position.x + sublayout.size.width); + } + } + + if (_sizing == ASAbsoluteLayoutSpecSizingSizeToFit || isnan(size.height)) { + size.height = constrainedSize.min.height; + for (ASLayout *sublayout in sublayouts) { + size.height = MAX(size.height, sublayout.position.y + sublayout.size.height); + } + } + + return [ASLayout layoutWithLayoutElement:self size:ASSizeRangeClamp(constrainedSize, size) sublayouts:sublayouts]; +} + +@end + diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASAsciiArtBoxCreator.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASAsciiArtBoxCreator.h new file mode 100644 index 0000000..83c7bec --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASAsciiArtBoxCreator.h @@ -0,0 +1,60 @@ +// +// ASAsciiArtBoxCreator.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@protocol ASLayoutElementAsciiArtProtocol +/** + * Returns an ascii-art representation of this object and its children. + * For example, an ASInsetSpec may return something like this: + * + * --ASInsetLayoutSpec-- + * | ASTextNode | + * --------------------- + */ +- (NSString *)asciiArtString; + +/** + * returns the name of this object that will display in the ascii art. Usually this can + * simply be NSStringFromClass([self class]). + */ +- (NSString *)asciiArtName; + +@end + +/** + * A that takes a parent and its children and renders as ascii art box. + */ +@interface ASAsciiArtBoxCreator : NSObject + +/** + * Renders an ascii art box with the children aligned horizontally + * Example: + * ------------ASStackLayoutSpec----------- + * | ASTextNode ASTextNode ASTextNode | + * ---------------------------------------- + */ ++ (NSString *)horizontalBoxStringForChildren:(NSArray *)children parent:(NSString *)parent; + +/** + * Renders an ascii art box with the children aligned vertically. + * Example: + * --ASStackLayoutSpec-- + * | ASTextNode | + * | ASTextNode | + * | ASTextNode | + * --------------------- + */ ++ (NSString *)verticalBoxStringForChildren:(NSArray *)children parent:(NSString *)parent; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASAsciiArtBoxCreator.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASAsciiArtBoxCreator.mm new file mode 100644 index 0000000..998267b --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASAsciiArtBoxCreator.mm @@ -0,0 +1,185 @@ +// +// ASAsciiArtBoxCreator.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import + +static const NSUInteger kDebugBoxPadding = 2; + +typedef NS_ENUM(NSUInteger, PIDebugBoxPaddingLocation) +{ + PIDebugBoxPaddingLocationFront, + PIDebugBoxPaddingLocationEnd, + PIDebugBoxPaddingLocationBoth +}; + +@interface NSString(PIDebugBox) + +@end + +@implementation NSString(PIDebugBox) + ++ (instancetype)debugbox_stringWithString:(NSString *)stringToRepeat repeatedCount:(NSUInteger)repeatCount NS_RETURNS_RETAINED +{ + NSMutableString *string = [[NSMutableString alloc] initWithCapacity:[stringToRepeat length] * repeatCount]; + for (NSUInteger index = 0; index < repeatCount; index++) { + [string appendString:stringToRepeat]; + } + return [string copy]; +} + +- (NSString *)debugbox_stringByAddingPadding:(NSString *)padding count:(NSUInteger)count location:(PIDebugBoxPaddingLocation)location +{ + NSString *paddingString = [NSString debugbox_stringWithString:padding repeatedCount:count]; + switch (location) { + case PIDebugBoxPaddingLocationFront: + return [NSString stringWithFormat:@"%@%@", paddingString, self]; + case PIDebugBoxPaddingLocationEnd: + return [NSString stringWithFormat:@"%@%@", self, paddingString]; + case PIDebugBoxPaddingLocationBoth: + return [NSString stringWithFormat:@"%@%@%@", paddingString, self, paddingString]; + } + return [self copy]; +} + +@end + +@implementation ASAsciiArtBoxCreator + ++ (NSString *)horizontalBoxStringForChildren:(NSArray *)children parent:(NSString *)parent +{ + if ([children count] == 0) { + return parent; + } + + NSMutableArray *childrenLines = [NSMutableArray array]; + + // split the children into lines + NSUInteger lineCountPerChild = 0; + for (NSString *child in children) { + NSArray *lines = [child componentsSeparatedByString:@"\n"]; + lineCountPerChild = MAX(lineCountPerChild, [lines count]); + } + + for (NSString *child in children) { + NSMutableArray *lines = [[child componentsSeparatedByString:@"\n"] mutableCopy]; + NSUInteger topPadding = ceil((CGFloat)(lineCountPerChild - [lines count])/2.0); + NSUInteger bottomPadding = (lineCountPerChild - [lines count])/2.0; + NSUInteger lineLength = [lines[0] length]; + + for (NSUInteger index = 0; index < topPadding; index++) { + [lines insertObject:[NSString debugbox_stringWithString:@" " repeatedCount:lineLength] atIndex:0]; + } + for (NSUInteger index = 0; index < bottomPadding; index++) { + [lines addObject:[NSString debugbox_stringWithString:@" " repeatedCount:lineLength]]; + } + [childrenLines addObject:lines]; + } + + NSMutableArray *concatenatedLines = [NSMutableArray array]; + NSString *padding = [NSString debugbox_stringWithString:@" " repeatedCount:kDebugBoxPadding]; + for (NSUInteger index = 0; index < lineCountPerChild; index++) { + NSMutableString *line = [[NSMutableString alloc] init]; + [line appendFormat:@"|%@",padding]; + for (NSArray *childLines in childrenLines) { + [line appendFormat:@"%@%@", childLines[index], padding]; + } + [line appendString:@"|"]; + [concatenatedLines addObject:line]; + } + + // surround the lines in a box + NSUInteger totalLineLength = [concatenatedLines[0] length]; + if (totalLineLength < [parent length]) { + NSUInteger difference = [parent length] + (2 * kDebugBoxPadding) - totalLineLength; + NSUInteger leftPadding = ceil((CGFloat)difference/2.0); + NSUInteger rightPadding = difference/2; + + NSString *leftString = [@"|" debugbox_stringByAddingPadding:@" " count:leftPadding location:PIDebugBoxPaddingLocationEnd]; + NSString *rightString = [@"|" debugbox_stringByAddingPadding:@" " count:rightPadding location:PIDebugBoxPaddingLocationFront]; + + NSMutableArray *paddedLines = [NSMutableArray array]; + for (NSString *line in concatenatedLines) { + NSString *paddedLine = [line stringByReplacingOccurrencesOfString:@"|" withString:leftString options:NSCaseInsensitiveSearch range:NSMakeRange(0, 1)]; + paddedLine = [paddedLine stringByReplacingOccurrencesOfString:@"|" withString:rightString options:NSCaseInsensitiveSearch range:NSMakeRange([paddedLine length] - 1, 1)]; + [paddedLines addObject:paddedLine]; + } + concatenatedLines = paddedLines; + // totalLineLength += difference; + } + concatenatedLines = [self appendTopAndBottomToBoxString:concatenatedLines parent:parent]; + return [concatenatedLines componentsJoinedByString:@"\n"]; + +} + ++ (NSString *)verticalBoxStringForChildren:(NSArray *)children parent:(NSString *)parent +{ + if ([children count] == 0) { + return parent; + } + + NSMutableArray *childrenLines = [NSMutableArray array]; + + NSUInteger maxChildLength = 0; + for (NSString *child in children) { + NSArray *lines = [child componentsSeparatedByString:@"\n"]; + maxChildLength = MAX(maxChildLength, [lines[0] length]); + } + + NSUInteger rightPadding = 0; + NSUInteger leftPadding = 0; + + if (maxChildLength < [parent length]) { + NSUInteger difference = [parent length] + (2 * kDebugBoxPadding) - maxChildLength; + leftPadding = ceil((CGFloat)difference/2.0); + rightPadding = difference/2; + } + + NSString *rightPaddingString = [NSString debugbox_stringWithString:@" " repeatedCount:rightPadding + kDebugBoxPadding]; + NSString *leftPaddingString = [NSString debugbox_stringWithString:@" " repeatedCount:leftPadding + kDebugBoxPadding]; + + for (NSString *child in children) { + NSMutableArray *lines = [[child componentsSeparatedByString:@"\n"] mutableCopy]; + + NSUInteger leftLinePadding = ceil((CGFloat)(maxChildLength - [lines[0] length])/2.0); + NSUInteger rightLinePadding = (maxChildLength - [lines[0] length])/2.0; + + for (NSString *line in lines) { + NSString *rightLinePaddingString = [NSString debugbox_stringWithString:@" " repeatedCount:rightLinePadding]; + rightLinePaddingString = [NSString stringWithFormat:@"%@%@|", rightLinePaddingString, rightPaddingString]; + + NSString *leftLinePaddingString = [NSString debugbox_stringWithString:@" " repeatedCount:leftLinePadding]; + leftLinePaddingString = [NSString stringWithFormat:@"|%@%@", leftLinePaddingString, leftPaddingString]; + + NSString *paddingLine = [NSString stringWithFormat:@"%@%@%@", leftLinePaddingString, line, rightLinePaddingString]; + [childrenLines addObject:paddingLine]; + } + } + + childrenLines = [self appendTopAndBottomToBoxString:childrenLines parent:parent]; + return [childrenLines componentsJoinedByString:@"\n"]; +} + ++ (NSMutableArray *)appendTopAndBottomToBoxString:(NSMutableArray *)boxStrings parent:(NSString *)parent +{ + NSUInteger totalLineLength = [boxStrings[0] length]; + [boxStrings addObject:[NSString debugbox_stringWithString:@"-" repeatedCount:totalLineLength]]; + + NSUInteger leftPadding = ceil(((CGFloat)(totalLineLength - [parent length]))/2.0); + NSUInteger rightPadding = (totalLineLength - [parent length])/2; + + NSString *topLine = [parent debugbox_stringByAddingPadding:@"-" count:leftPadding location:PIDebugBoxPaddingLocationFront]; + topLine = [topLine debugbox_stringByAddingPadding:@"-" count:rightPadding location:PIDebugBoxPaddingLocationEnd]; + [boxStrings insertObject:topLine atIndex:0]; + + return boxStrings; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASBackgroundLayoutSpec.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASBackgroundLayoutSpec.h new file mode 100644 index 0000000..032a240 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASBackgroundLayoutSpec.h @@ -0,0 +1,34 @@ +// +// ASBackgroundLayoutSpec.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + Lays out a single layoutElement child, then lays out a background layoutElement instance behind it stretched to its size. + */ +@interface ASBackgroundLayoutSpec : ASLayoutSpec + +/** + * Background layoutElement for this layout spec + */ +@property (nonatomic) id background; + +/** + * Creates and returns an ASBackgroundLayoutSpec object + * + * @param child A child that is laid out to determine the size of this spec. + * @param background A layoutElement object that is laid out behind the child. + */ ++ (instancetype)backgroundLayoutSpecWithChild:(id)child background:(id)background NS_RETURNS_RETAINED AS_WARN_UNUSED_RESULT; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASBackgroundLayoutSpec.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASBackgroundLayoutSpec.mm new file mode 100644 index 0000000..2de9f49 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASBackgroundLayoutSpec.mm @@ -0,0 +1,91 @@ +// +// ASBackgroundLayoutSpec.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import + +#import + +static NSUInteger const kForegroundChildIndex = 0; +static NSUInteger const kBackgroundChildIndex = 1; + +@implementation ASBackgroundLayoutSpec + +#pragma mark - Class + ++ (instancetype)backgroundLayoutSpecWithChild:(id)child background:(id)background NS_RETURNS_RETAINED +{ + return [[self alloc] initWithChild:child background:background]; +} + +#pragma mark - Lifecycle + +- (instancetype)initWithChild:(id)child background:(id)background +{ + if (!(self = [super init])) { + return nil; + } + self.child = child; + self.background = background; + return self; +} + +#pragma mark - ASLayoutSpec + +/** + * First layout the contents, then fit the background image. + */ +- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize + restrictedToSize:(ASLayoutElementSize)size + relativeToParentSize:(CGSize)parentSize +{ + ASLayout *contentsLayout = [self.child layoutThatFits:constrainedSize parentSize:parentSize]; + + ASLayout *rawSublayouts[2]; + int i = 0; + if (self.background) { + // Size background to exactly the same size. + ASLayout *backgroundLayout = [self.background layoutThatFits:ASSizeRangeMake(contentsLayout.size) + parentSize:parentSize]; + backgroundLayout.position = CGPointZero; + rawSublayouts[i++] = backgroundLayout; + } + contentsLayout.position = CGPointZero; + rawSublayouts[i++] = contentsLayout; + + const auto sublayouts = [NSArray arrayByTransferring:rawSublayouts count:i]; + return [ASLayout layoutWithLayoutElement:self size:contentsLayout.size sublayouts:sublayouts]; +} + +#pragma mark - Background + +- (void)setChild:(id)child +{ + ASDisplayNodeAssertNotNil(child, @"Child cannot be nil"); + [super setChild:child atIndex:kForegroundChildIndex]; +} + +- (id)child +{ + return [super childAtIndex:kForegroundChildIndex]; +} + +- (void)setBackground:(id)background +{ + ASDisplayNodeAssertNotNil(background, @"Background cannot be nil"); + [super setChild:background atIndex:kBackgroundChildIndex]; +} + +- (id)background +{ + return [super childAtIndex:kBackgroundChildIndex]; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASCenterLayoutSpec.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASCenterLayoutSpec.h new file mode 100644 index 0000000..dd1f99a --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASCenterLayoutSpec.h @@ -0,0 +1,70 @@ +// +// ASCenterLayoutSpec.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +/** + * How the child is centered within the spec. + * + * The default option will position the child at {0,0} relatively to the layout bound. + * Swift: use [] for the default behavior. + */ +typedef NS_OPTIONS(NSUInteger, ASCenterLayoutSpecCenteringOptions) { + /** The child is positioned in {0,0} relatively to the layout bounds */ + ASCenterLayoutSpecCenteringNone = 0, + /** The child is centered along the X axis */ + ASCenterLayoutSpecCenteringX = 1 << 0, + /** The child is centered along the Y axis */ + ASCenterLayoutSpecCenteringY = 1 << 1, + /** Convenience option to center both along the X and Y axis */ + ASCenterLayoutSpecCenteringXY = ASCenterLayoutSpecCenteringX | ASCenterLayoutSpecCenteringY +}; + +/** + * How much space the spec will take up. + * + * The default option will allow the spec to take up the maximum size possible. + * Swift: use [] for the default behavior. + */ +typedef NS_OPTIONS(NSUInteger, ASCenterLayoutSpecSizingOptions) { + /** The spec will take up the maximum size possible */ + ASCenterLayoutSpecSizingOptionDefault = ASRelativeLayoutSpecSizingOptionDefault, + /** The spec will take up the minimum size possible along the X axis */ + ASCenterLayoutSpecSizingOptionMinimumX = ASRelativeLayoutSpecSizingOptionMinimumWidth, + /** The spec will take up the minimum size possible along the Y axis */ + ASCenterLayoutSpecSizingOptionMinimumY = ASRelativeLayoutSpecSizingOptionMinimumHeight, + /** Convenience option to take up the minimum size along both the X and Y axis */ + ASCenterLayoutSpecSizingOptionMinimumXY = ASRelativeLayoutSpecSizingOptionMinimumSize +}; + +NS_ASSUME_NONNULL_BEGIN + +/** Lays out a single layoutElement child and position it so that it is centered into the layout bounds. + * NOTE: ASRelativeLayoutSpec offers all of the capabilities of Center, and more. + * Check it out if you would like to be able to position the child at any corner or the middle of an edge. + */ +@interface ASCenterLayoutSpec : ASRelativeLayoutSpec + +@property (nonatomic) ASCenterLayoutSpecCenteringOptions centeringOptions; +@property (nonatomic) ASCenterLayoutSpecSizingOptions sizingOptions; + +/** + * Initializer. + * + * @param centeringOptions How the child is centered. + * @param sizingOptions How much space will be taken up. + * @param child The child to center. + */ ++ (instancetype)centerLayoutSpecWithCenteringOptions:(ASCenterLayoutSpecCenteringOptions)centeringOptions + sizingOptions:(ASCenterLayoutSpecSizingOptions)sizingOptions + child:(id)child NS_RETURNS_RETAINED AS_WARN_UNUSED_RESULT; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASCenterLayoutSpec.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASCenterLayoutSpec.mm new file mode 100644 index 0000000..9bf29ea --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASCenterLayoutSpec.mm @@ -0,0 +1,74 @@ +// +// ASCenterLayoutSpec.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +@implementation ASCenterLayoutSpec +{ + ASCenterLayoutSpecCenteringOptions _centeringOptions; + ASCenterLayoutSpecSizingOptions _sizingOptions; +} + +- (instancetype)initWithCenteringOptions:(ASCenterLayoutSpecCenteringOptions)centeringOptions + sizingOptions:(ASCenterLayoutSpecSizingOptions)sizingOptions + child:(id)child; +{ + ASRelativeLayoutSpecPosition verticalPosition = [self verticalPositionFromCenteringOptions:centeringOptions]; + ASRelativeLayoutSpecPosition horizontalPosition = [self horizontalPositionFromCenteringOptions:centeringOptions]; + + if (!(self = [super initWithHorizontalPosition:horizontalPosition verticalPosition:verticalPosition sizingOption:sizingOptions child:child])) { + return nil; + } + _centeringOptions = centeringOptions; + _sizingOptions = sizingOptions; + return self; +} + ++ (instancetype)centerLayoutSpecWithCenteringOptions:(ASCenterLayoutSpecCenteringOptions)centeringOptions + sizingOptions:(ASCenterLayoutSpecSizingOptions)sizingOptions + child:(id)child NS_RETURNS_RETAINED +{ + return [[self alloc] initWithCenteringOptions:centeringOptions sizingOptions:sizingOptions child:child]; +} + +- (void)setCenteringOptions:(ASCenterLayoutSpecCenteringOptions)centeringOptions +{ + ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); + _centeringOptions = centeringOptions; + + [self setHorizontalPosition:[self horizontalPositionFromCenteringOptions:centeringOptions]]; + [self setVerticalPosition:[self verticalPositionFromCenteringOptions:centeringOptions]]; +} + +- (void)setSizingOptions:(ASCenterLayoutSpecSizingOptions)sizingOptions +{ + ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); + _sizingOptions = sizingOptions; + [self setSizingOption:sizingOptions]; +} + +- (ASRelativeLayoutSpecPosition)horizontalPositionFromCenteringOptions:(ASCenterLayoutSpecCenteringOptions)centeringOptions +{ + if ((centeringOptions & ASCenterLayoutSpecCenteringX) != 0) { + return ASRelativeLayoutSpecPositionCenter; + } else { + return ASRelativeLayoutSpecPositionNone; + } +} + +- (ASRelativeLayoutSpecPosition)verticalPositionFromCenteringOptions:(ASCenterLayoutSpecCenteringOptions)centeringOptions +{ + if ((centeringOptions & ASCenterLayoutSpecCenteringY) != 0) { + return ASRelativeLayoutSpecPositionCenter; + } else { + return ASRelativeLayoutSpecPositionNone; + } +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASCornerLayoutSpec.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASCornerLayoutSpec.h new file mode 100644 index 0000000..799c9c2 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASCornerLayoutSpec.h @@ -0,0 +1,75 @@ +// +// ASCornerLayoutSpec.h +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +/** + The corner location for positioning corner element. + */ +typedef NS_ENUM(NSInteger, ASCornerLayoutLocation) { + ASCornerLayoutLocationTopLeft, + ASCornerLayoutLocationTopRight, + ASCornerLayoutLocationBottomLeft, + ASCornerLayoutLocationBottomRight, +}; + +NS_ASSUME_NONNULL_BEGIN + +/** + A layout spec that positions a corner element which relatives to the child element. + + @warning Both child element and corner element must have valid preferredSize for layout calculation. + */ +@interface ASCornerLayoutSpec : ASLayoutSpec + +/** + A layout spec that positions a corner element which relatives to the child element. + + @param child A child that is laid out to determine the size of this spec. + @param corner A layoutElement object that is laid out to a corner on the child. + @param location The corner position option. + @return An ASCornerLayoutSpec object with a given child and an layoutElement that act as corner. + */ +- (instancetype)initWithChild:(id )child corner:(id )corner location:(ASCornerLayoutLocation)location AS_WARN_UNUSED_RESULT; + +/** + A layout spec that positions a corner element which relatives to the child element. + + @param child A child that is laid out to determine the size of this spec. + @param corner A layoutElement object that is laid out to a corner on the child. + @param location The corner position option. + @return An ASCornerLayoutSpec object with a given child and an layoutElement that act as corner. + */ ++ (instancetype)cornerLayoutSpecWithChild:(id )child corner:(id )corner location:(ASCornerLayoutLocation)location NS_RETURNS_RETAINED AS_WARN_UNUSED_RESULT; + +/** + A layoutElement object that is laid out to a corner on the child. + */ +@property (nonatomic) id corner; + +/** + The corner position option. + */ +@property (nonatomic) ASCornerLayoutLocation cornerLocation; + +/** + The point which offsets from the corner location. Use this property to make delta + distance from the default corner location. Default is CGPointZero. + */ +@property (nonatomic) CGPoint offset; + +/** + Whether should include corner element into layout size calculation. If included, + the layout size will be the union size of both child and corner; If not included, + the layout size will be only child's size. Default is NO. + */ +@property (nonatomic) BOOL wrapsCorner; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASCornerLayoutSpec.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASCornerLayoutSpec.mm new file mode 100644 index 0000000..91a1ac8 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASCornerLayoutSpec.mm @@ -0,0 +1,164 @@ +// +// ASCornerLayoutSpec.mm +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import + +CGPoint as_calculatedCornerOriginIn(CGRect baseFrame, CGSize cornerSize, ASCornerLayoutLocation cornerLocation, CGPoint offset) +{ + CGPoint cornerOrigin = CGPointZero; + CGPoint baseOrigin = baseFrame.origin; + CGSize baseSize = baseFrame.size; + + switch (cornerLocation) { + case ASCornerLayoutLocationTopLeft: + cornerOrigin.x = baseOrigin.x - cornerSize.width / 2; + cornerOrigin.y = baseOrigin.y - cornerSize.height / 2; + break; + case ASCornerLayoutLocationTopRight: + cornerOrigin.x = baseOrigin.x + baseSize.width - cornerSize.width / 2; + cornerOrigin.y = baseOrigin.y - cornerSize.height / 2; + break; + case ASCornerLayoutLocationBottomLeft: + cornerOrigin.x = baseOrigin.x - cornerSize.width / 2; + cornerOrigin.y = baseOrigin.y + baseSize.height - cornerSize.height / 2; + break; + case ASCornerLayoutLocationBottomRight: + cornerOrigin.x = baseOrigin.x + baseSize.width - cornerSize.width / 2; + cornerOrigin.y = baseOrigin.y + baseSize.height - cornerSize.height / 2; + break; + } + + cornerOrigin.x += offset.x; + cornerOrigin.y += offset.y; + + return cornerOrigin; +} + +static NSUInteger const kBaseChildIndex = 0; +static NSUInteger const kCornerChildIndex = 1; + +@interface ASCornerLayoutSpec() +@end + +@implementation ASCornerLayoutSpec + +- (instancetype)initWithChild:(id )child corner:(id )corner location:(ASCornerLayoutLocation)location +{ + self = [super init]; + if (self) { + self.child = child; + self.corner = corner; + self.cornerLocation = location; + } + return self; +} + ++ (instancetype)cornerLayoutSpecWithChild:(id )child corner:(id )corner location:(ASCornerLayoutLocation)location NS_RETURNS_RETAINED +{ + return [[self alloc] initWithChild:child corner:corner location:location]; +} + +#pragma mark - Children + +- (void)setChild:(id)child +{ + ASDisplayNodeAssertNotNil(child, @"Child shouldn't be nil."); + [super setChild:child atIndex:kBaseChildIndex]; +} + +- (id)child +{ + return [super childAtIndex:kBaseChildIndex]; +} + +- (void)setCorner:(id)corner +{ + ASDisplayNodeAssertNotNil(corner, @"Corner element cannot be nil."); + [super setChild:corner atIndex:kCornerChildIndex]; +} + +- (id)corner +{ + return [super childAtIndex:kCornerChildIndex]; +} + +#pragma mark - Calculation + +- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize +{ + CGSize size = { + ASPointsValidForSize(constrainedSize.max.width) == NO ? ASLayoutElementParentDimensionUndefined : constrainedSize.max.width, + ASPointsValidForSize(constrainedSize.max.height) == NO ? ASLayoutElementParentDimensionUndefined : constrainedSize.max.height + }; + + id child = self.child; + id corner = self.corner; + + // Element validation + [self _validateElement:child]; + [self _validateElement:corner]; + + CGRect childFrame = CGRectZero; + CGRect cornerFrame = CGRectZero; + + // Layout child + ASLayout *childLayout = [child layoutThatFits:constrainedSize parentSize:size]; + childFrame.size = childLayout.size; + + // Layout corner + ASLayout *cornerLayout = [corner layoutThatFits:constrainedSize parentSize:size]; + cornerFrame.size = cornerLayout.size; + + // Calculate corner's position + CGPoint relativePosition = as_calculatedCornerOriginIn(childFrame, cornerFrame.size, _cornerLocation, _offset); + + // Update corner's position + cornerFrame.origin = relativePosition; + + // Calculate size + CGRect frame = childFrame; + if (_wrapsCorner) { + frame = CGRectUnion(childFrame, cornerFrame); + frame.size = ASSizeRangeClamp(constrainedSize, frame.size); + } + + // Shift sublayouts' positions if they are off the bounds. + if (frame.origin.x != 0) { + CGFloat deltaX = frame.origin.x; + childFrame.origin.x -= deltaX; + cornerFrame.origin.x -= deltaX; + } + + if (frame.origin.y != 0) { + CGFloat deltaY = frame.origin.y; + childFrame.origin.y -= deltaY; + cornerFrame.origin.y -= deltaY; + } + + childLayout.position = childFrame.origin; + cornerLayout.position = cornerFrame.origin; + + return [ASLayout layoutWithLayoutElement:self size:frame.size sublayouts:@[childLayout, cornerLayout]]; +} + +- (void)_validateElement:(id )element +{ + // Validate non-nil element + if (element == nil) { + ASDisplayNodeAssertNotNil(element, @"[%@]: Must have a non-nil child/corner for layout calculation.", self.class); + } + // Validate preferredSize if needed + CGSize size = element.style.preferredSize; + if (!CGSizeEqualToSize(size, CGSizeZero) && !ASIsCGSizeValidForSize(size) && (size.width < 0 || (size.height < 0))) { + ASDisplayNodeFailAssert(@"[%@]: Should give a valid preferredSize value for %@ before corner's position calculation.", self.class, element); + } +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASDimension.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASDimension.h new file mode 100644 index 0000000..ab0717b --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASDimension.h @@ -0,0 +1,315 @@ +// +// ASDimension.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#pragma once +#import +#import +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +#pragma mark - + +ASDISPLAYNODE_INLINE BOOL AS_WARN_UNUSED_RESULT ASPointsValidForLayout(CGFloat points) +{ + return ((isnormal(points) || points == 0.0) && points >= 0.0 && points < (CGFLOAT_MAX / 2.0)); +} + +ASDISPLAYNODE_INLINE BOOL AS_WARN_UNUSED_RESULT ASIsCGSizeValidForLayout(CGSize size) +{ + return (ASPointsValidForLayout(size.width) && ASPointsValidForLayout(size.height)); +} + +// Note we want YGUndefined (10E20) to be considered invalid, so we have picked a smaller number than CGFLOAT_MAX/2.0 +ASDISPLAYNODE_INLINE BOOL AS_WARN_UNUSED_RESULT ASPointsValidForSize(CGFloat points) +{ + return ((isnormal(points) || points == 0.0) && points >= 0.0 && points < 10000000.0); +} + +ASDISPLAYNODE_INLINE BOOL AS_WARN_UNUSED_RESULT ASIsCGSizeValidForSize(CGSize size) +{ + return (ASPointsValidForSize(size.width) && ASPointsValidForSize(size.height)); +} + +// Note we want YGUndefined (10E20) to be considered invalid, so we have picked a smaller number than CGFLOAT_MAX/2.0 +ASDISPLAYNODE_INLINE BOOL ASIsCGPositionPointsValidForLayout(CGFloat points) +{ + return ((isnormal(points) || points == 0.0) && points < 10000000.0); +} + +ASDISPLAYNODE_INLINE BOOL ASIsCGPositionValidForLayout(CGPoint point) +{ + return (ASIsCGPositionPointsValidForLayout(point.x) && ASIsCGPositionPointsValidForLayout(point.y)); +} + +ASDISPLAYNODE_INLINE BOOL ASIsCGRectValidForLayout(CGRect rect) +{ + return (ASIsCGPositionValidForLayout(rect.origin) && ASIsCGSizeValidForLayout(rect.size)); +} + +#pragma mark - ASDimension + +/** + * A dimension relative to constraints to be provided in the future. + * A ASDimension can be one of three types: + * + * "Auto" - This indicated "I have no opinion" and may be resolved in whatever way makes most sense given the circumstances. + * + * "Points" - Just a number. It will always resolve to exactly this amount. + * + * "Percent" - Multiplied to a provided parent amount to resolve a final amount. + */ +typedef NS_ENUM(NSInteger, ASDimensionUnit) { + /** This indicates "I have no opinion" and may be resolved in whatever way makes most sense given the circumstances. */ + ASDimensionUnitAuto, + /** Just a number. It will always resolve to exactly this amount. This is the default type. */ + ASDimensionUnitPoints, + /** Multiplied to a provided parent amount to resolve a final amount. */ + ASDimensionUnitFraction, +}; + +typedef struct { + ASDimensionUnit unit; + CGFloat value; +} ASDimension; + +/** + * Represents auto as ASDimension + */ +ASDK_EXTERN ASDimension const ASDimensionAuto; + +/** + * Returns a dimension with the specified type and value. + */ +ASOVERLOADABLE ASDISPLAYNODE_INLINE ASDimension ASDimensionMake(ASDimensionUnit unit, CGFloat value) +{ + if (unit == ASDimensionUnitAuto ) { + ASDisplayNodeCAssert(value == 0, @"ASDimension auto value must be 0."); + } else if (unit == ASDimensionUnitPoints) { + ASDisplayNodeCAssertPositiveReal(@"Points", value); + } else if (unit == ASDimensionUnitFraction) { + ASDisplayNodeCAssert( 0 <= value && value <= 1.0, @"ASDimension fraction value (%f) must be between 0 and 1.", value); + } + ASDimension dimension; + dimension.unit = unit; + dimension.value = value; + return dimension; +} + +/** + * Returns a dimension with the specified points value. + */ +ASOVERLOADABLE ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT ASDimension ASDimensionMake(CGFloat points) +{ + return ASDimensionMake(ASDimensionUnitPoints, points); +} + +/** + * Returns a dimension by parsing the specified dimension string. + * Examples: ASDimensionMake(@"50%") = ASDimensionMake(ASDimensionUnitFraction, 0.5) + * ASDimensionMake(@"0.5pt") = ASDimensionMake(ASDimensionUnitPoints, 0.5) + */ +ASOVERLOADABLE AS_WARN_UNUSED_RESULT ASDK_EXTERN ASDimension ASDimensionMake(NSString *dimension); + +/** + * Returns a dimension with the specified points value. + */ +ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT ASDimension ASDimensionMakeWithPoints(CGFloat points) +{ + ASDisplayNodeCAssertPositiveReal(@"Points", points); + return ASDimensionMake(ASDimensionUnitPoints, points); +} + +/** + * Returns a dimension with the specified fraction value. + */ +ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT ASDimension ASDimensionMakeWithFraction(CGFloat fraction) +{ + ASDisplayNodeCAssert( 0 <= fraction && fraction <= 1.0, @"ASDimension fraction value (%f) must be between 0 and 1.", fraction); + return ASDimensionMake(ASDimensionUnitFraction, fraction); +} + +/** + * Returns whether two dimensions are equal. + */ +ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT BOOL ASDimensionEqualToDimension(ASDimension lhs, ASDimension rhs) +{ + return (lhs.unit == rhs.unit && lhs.value == rhs.value); +} + +/** + * Returns a NSString representation of a dimension. + */ +ASDK_EXTERN AS_WARN_UNUSED_RESULT NSString *NSStringFromASDimension(ASDimension dimension); + +/** + * Resolve this dimension to a parent size. + */ +ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT CGFloat ASDimensionResolve(ASDimension dimension, CGFloat parentSize, CGFloat autoSize) +{ + switch (dimension.unit) { + case ASDimensionUnitAuto: + return autoSize; + case ASDimensionUnitPoints: + return dimension.value; + case ASDimensionUnitFraction: + return dimension.value * parentSize; + } +} + +#pragma mark - ASLayoutSize + +/** + * Expresses a size with relative dimensions. Only used for calculations internally in ASDimension.h + */ +typedef struct { + ASDimension width; + ASDimension height; +} ASLayoutSize; + +ASDK_EXTERN ASLayoutSize const ASLayoutSizeAuto; + +/* + * Creates an ASLayoutSize with provided min and max dimensions. + */ +ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT ASLayoutSize ASLayoutSizeMake(ASDimension width, ASDimension height) +{ + ASLayoutSize size; + size.width = width; + size.height = height; + return size; +} + +/** + * Resolve this relative size relative to a parent size. + */ +ASDISPLAYNODE_INLINE CGSize ASLayoutSizeResolveSize(ASLayoutSize layoutSize, CGSize parentSize, CGSize autoSize) +{ + return CGSizeMake(ASDimensionResolve(layoutSize.width, parentSize.width, autoSize.width), + ASDimensionResolve(layoutSize.height, parentSize.height, autoSize.height)); +} + +/* + * Returns a string representation of a relative size. + */ +ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT NSString *NSStringFromASLayoutSize(ASLayoutSize size) +{ + return [NSString stringWithFormat:@"{%@, %@}", + NSStringFromASDimension(size.width), + NSStringFromASDimension(size.height)]; +} + +#pragma mark - ASSizeRange + +/** + * Expresses an inclusive range of sizes. Used to provide a simple constraint to layout. + */ +typedef struct { + CGSize min; + CGSize max; +} ASSizeRange; + +/** + * A size range with all dimensions zero. + */ +ASDK_EXTERN ASSizeRange const ASSizeRangeZero; + +/** + * A size range from zero to infinity in both directions. + */ +ASDK_EXTERN ASSizeRange const ASSizeRangeUnconstrained; + +/** + * Returns whether a size range has > 0.1 max width and max height. + */ +ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT BOOL ASSizeRangeHasSignificantArea(ASSizeRange sizeRange) +{ + static CGFloat const limit = 0.1f; + return (sizeRange.max.width > limit && sizeRange.max.height > limit); +} + +/** + * Creates an ASSizeRange with provided min and max size. + */ +ASOVERLOADABLE ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT ASSizeRange ASSizeRangeMake(CGSize min, CGSize max) +{ + ASDisplayNodeCAssertPositiveReal(@"Range min width", min.width); + ASDisplayNodeCAssertPositiveReal(@"Range min height", min.height); + ASDisplayNodeCAssertInfOrPositiveReal(@"Range max width", max.width); + ASDisplayNodeCAssertInfOrPositiveReal(@"Range max height", max.height); + ASDisplayNodeCAssert(min.width <= max.width, + @"Range min width (%f) must not be larger than max width (%f).", min.width, max.width); + ASDisplayNodeCAssert(min.height <= max.height, + @"Range min height (%f) must not be larger than max height (%f).", min.height, max.height); + ASSizeRange sizeRange; + sizeRange.min = min; + sizeRange.max = max; + return sizeRange; +} + +/** + * Creates an ASSizeRange with provided size as both min and max. + */ +ASOVERLOADABLE ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT ASSizeRange ASSizeRangeMake(CGSize exactSize) +{ + return ASSizeRangeMake(exactSize, exactSize); +} + +/** + * Clamps the provided CGSize between the [min, max] bounds of this ASSizeRange. + */ +ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT CGSize ASSizeRangeClamp(ASSizeRange sizeRange, CGSize size) +{ + return CGSizeMake(MAX(sizeRange.min.width, MIN(sizeRange.max.width, size.width)), + MAX(sizeRange.min.height, MIN(sizeRange.max.height, size.height))); +} + +/** + * Intersects another size range. If the other size range does not overlap in either dimension, this size range + * "wins" by returning a single point within its own range that is closest to the non-overlapping range. + */ +ASDK_EXTERN AS_WARN_UNUSED_RESULT ASSizeRange ASSizeRangeIntersect(ASSizeRange sizeRange, ASSizeRange otherSizeRange); + +/** + * Returns whether two size ranges are equal in min and max size. + */ +ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT BOOL ASSizeRangeEqualToSizeRange(ASSizeRange lhs, ASSizeRange rhs) +{ + return CGSizeEqualToSize(lhs.min, rhs.min) && CGSizeEqualToSize(lhs.max, rhs.max); +} + +/** + * Returns a string representation of a size range + */ +ASDK_EXTERN AS_WARN_UNUSED_RESULT NSString *NSStringFromASSizeRange(ASSizeRange sizeRange); + +#if YOGA + +#pragma mark - ASEdgeInsets + +typedef struct { + ASDimension top; + ASDimension left; + ASDimension bottom; + ASDimension right; + ASDimension start; + ASDimension end; + ASDimension horizontal; + ASDimension vertical; + ASDimension all; +} ASEdgeInsets; + +ASDK_EXTERN ASEdgeInsets const ASEdgeInsetsZero; + +ASDK_EXTERN ASEdgeInsets ASEdgeInsetsMake(UIEdgeInsets edgeInsets); + +#endif + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASDimension.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASDimension.mm new file mode 100644 index 0000000..10af17c --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASDimension.mm @@ -0,0 +1,123 @@ +// +// ASDimension.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import + +#pragma mark - ASDimension + +ASDimension const ASDimensionAuto = {ASDimensionUnitAuto, 0}; + +ASOVERLOADABLE ASDimension ASDimensionMake(NSString *dimension) +{ + if (dimension.length > 0) { + + // Handle points + if ([dimension hasSuffix:@"pt"]) { + return ASDimensionMake(ASDimensionUnitPoints, ASCGFloatFromString(dimension)); + } + + // Handle auto + if ([dimension isEqualToString:@"auto"]) { + return ASDimensionAuto; + } + + // Handle percent + if ([dimension hasSuffix:@"%"]) { + return ASDimensionMake(ASDimensionUnitFraction, (ASCGFloatFromString(dimension) / 100.0)); + } + } + + return ASDimensionAuto; +} + +NSString *NSStringFromASDimension(ASDimension dimension) +{ + switch (dimension.unit) { + case ASDimensionUnitPoints: + return [NSString stringWithFormat:@"%.0fpt", dimension.value]; + case ASDimensionUnitFraction: + return [NSString stringWithFormat:@"%.0f%%", dimension.value * 100.0]; + case ASDimensionUnitAuto: + return @"Auto"; + } +} + +#pragma mark - ASLayoutSize + +ASLayoutSize const ASLayoutSizeAuto = {ASDimensionAuto, ASDimensionAuto}; + +#pragma mark - ASSizeRange + +ASSizeRange const ASSizeRangeZero = {}; + +ASSizeRange const ASSizeRangeUnconstrained = { {0, 0}, { INFINITY, INFINITY }}; + +struct _Range { + CGFloat min; + CGFloat max; + + /** + Intersects another dimension range. If the other range does not overlap, this size range "wins" by returning a + single point within its own range that is closest to the non-overlapping range. + */ + _Range intersect(const _Range &other) const + { + CGFloat newMin = MAX(min, other.min); + CGFloat newMax = MIN(max, other.max); + if (newMin <= newMax) { + return {newMin, newMax}; + } else { + // No intersection. If we're before the other range, return our max; otherwise our min. + if (min < other.min) { + return {max, max}; + } else { + return {min, min}; + } + } + } +}; + +ASSizeRange ASSizeRangeIntersect(ASSizeRange sizeRange, ASSizeRange otherSizeRange) +{ + const auto w = _Range({sizeRange.min.width, sizeRange.max.width}).intersect({otherSizeRange.min.width, otherSizeRange.max.width}); + const auto h = _Range({sizeRange.min.height, sizeRange.max.height}).intersect({otherSizeRange.min.height, otherSizeRange.max.height}); + return {{w.min, h.min}, {w.max, h.max}}; +} + +NSString *NSStringFromASSizeRange(ASSizeRange sizeRange) +{ + // 17 field length copied from iOS 10.3 impl of NSStringFromCGSize. + if (CGSizeEqualToSize(sizeRange.min, sizeRange.max)) { + return [NSString stringWithFormat:@"{{%.*g, %.*g}}", + 17, sizeRange.min.width, + 17, sizeRange.min.height]; + } + return [NSString stringWithFormat:@"{{%.*g, %.*g}, {%.*g, %.*g}}", + 17, sizeRange.min.width, + 17, sizeRange.min.height, + 17, sizeRange.max.width, + 17, sizeRange.max.height]; +} + +#if YOGA +#pragma mark - Yoga - ASEdgeInsets +ASEdgeInsets const ASEdgeInsetsZero = {}; + +ASEdgeInsets ASEdgeInsetsMake(UIEdgeInsets edgeInsets) +{ + ASEdgeInsets asEdgeInsets = ASEdgeInsetsZero; + asEdgeInsets.top = ASDimensionMake(edgeInsets.top); + asEdgeInsets.left = ASDimensionMake(edgeInsets.left); + asEdgeInsets.bottom = ASDimensionMake(edgeInsets.bottom); + asEdgeInsets.right = ASDimensionMake(edgeInsets.right); + return asEdgeInsets; +} +#endif diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASDimensionInternal.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASDimensionInternal.h new file mode 100644 index 0000000..dced2ff --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASDimensionInternal.h @@ -0,0 +1,101 @@ +// +// ASDimensionInternal.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +#pragma mark - ASLayoutElementSize + +/** + * A struct specifying a ASLayoutElement's size. Example: + * + * ASLayoutElementSize size = (ASLayoutElementSize){ + * .width = ASDimensionMakeWithFraction(0.25), + * .maxWidth = ASDimensionMakeWithPoints(200), + * .minHeight = ASDimensionMakeWithFraction(0.50) + * }; + * + * Description: + * + */ +typedef struct { + ASDimension width; + ASDimension height; + ASDimension minWidth; + ASDimension maxWidth; + ASDimension minHeight; + ASDimension maxHeight; +} ASLayoutElementSize; + +/** + * Returns an ASLayoutElementSize with default values. + */ +ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT ASLayoutElementSize ASLayoutElementSizeMake() +{ + return (ASLayoutElementSize){ + .width = ASDimensionAuto, + .height = ASDimensionAuto, + .minWidth = ASDimensionAuto, + .maxWidth = ASDimensionAuto, + .minHeight = ASDimensionAuto, + .maxHeight = ASDimensionAuto + }; +} + +/** + * Returns an ASLayoutElementSize with the specified CGSize values as width and height. + */ +ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT ASLayoutElementSize ASLayoutElementSizeMakeFromCGSize(CGSize size) +{ + ASLayoutElementSize s = ASLayoutElementSizeMake(); + s.width = ASDimensionMakeWithPoints(size.width); + s.height = ASDimensionMakeWithPoints(size.height); + return s; +} + +/** + * Returns whether two sizes are equal. + */ +ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT BOOL ASLayoutElementSizeEqualToLayoutElementSize(ASLayoutElementSize lhs, ASLayoutElementSize rhs) +{ + return (ASDimensionEqualToDimension(lhs.width, rhs.width) + && ASDimensionEqualToDimension(lhs.height, rhs.height) + && ASDimensionEqualToDimension(lhs.minWidth, rhs.minWidth) + && ASDimensionEqualToDimension(lhs.maxWidth, rhs.maxWidth) + && ASDimensionEqualToDimension(lhs.minHeight, rhs.minHeight) + && ASDimensionEqualToDimension(lhs.maxHeight, rhs.maxHeight)); +} + +/** + * Returns a string formatted to contain the data from an ASLayoutElementSize. + */ +ASDK_EXTERN AS_WARN_UNUSED_RESULT NSString *NSStringFromASLayoutElementSize(ASLayoutElementSize size); + +/** + * Resolve the given size relative to a parent size and an auto size. + * From the given size uses width, height to resolve the exact size constraint, uses the minHeight and minWidth to + * resolve the min size constraint and the maxHeight and maxWidth to resolve the max size constraint. For every + * dimension with unit ASDimensionUnitAuto the given autoASSizeRange value will be used. + * Based on the calculated exact, min and max size constraints the final size range will be calculated. + */ +ASDK_EXTERN AS_WARN_UNUSED_RESULT ASSizeRange ASLayoutElementSizeResolveAutoSize(ASLayoutElementSize size, const CGSize parentSize, ASSizeRange autoASSizeRange); + +/** + * Resolve the given size to a parent size. Uses internally ASLayoutElementSizeResolveAutoSize with {INFINITY, INFINITY} as + * as autoASSizeRange. For more information look at ASLayoutElementSizeResolveAutoSize. + */ +ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT ASSizeRange ASLayoutElementSizeResolve(ASLayoutElementSize size, const CGSize parentSize) +{ + return ASLayoutElementSizeResolveAutoSize(size, parentSize, ASSizeRangeMake(CGSizeZero, CGSizeMake(INFINITY, INFINITY))); +} + + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASDimensionInternal.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASDimensionInternal.mm new file mode 100644 index 0000000..8af3555 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASDimensionInternal.mm @@ -0,0 +1,65 @@ +// +// ASDimensionInternal.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#pragma mark - ASLayoutElementSize + +NSString *NSStringFromASLayoutElementSize(ASLayoutElementSize size) +{ + return [NSString stringWithFormat: + @"", + NSStringFromASLayoutSize(ASLayoutSizeMake(size.width, size.height)), + NSStringFromASLayoutSize(ASLayoutSizeMake(size.minWidth, size.minHeight)), + NSStringFromASLayoutSize(ASLayoutSizeMake(size.maxWidth, size.maxHeight))]; +} + +ASDISPLAYNODE_INLINE void ASLayoutElementSizeConstrain(CGFloat minVal, CGFloat exactVal, CGFloat maxVal, CGFloat *outMin, CGFloat *outMax) +{ + NSCAssert(!isnan(minVal), @"minVal must not be NaN"); + NSCAssert(!isnan(maxVal), @"maxVal must not be NaN"); + // Avoid use of min/max primitives since they're harder to reason + // about in the presence of NaN (in exactVal) + // Follow CSS: min overrides max overrides exact. + + // Begin with the min/max range + *outMin = minVal; + *outMax = maxVal; + if (maxVal <= minVal) { + // min overrides max and exactVal is irrelevant + *outMax = minVal; + return; + } + if (isnan(exactVal)) { + // no exact value, so leave as a min/max range + return; + } + if (exactVal > maxVal) { + // clip to max value + *outMin = maxVal; + } else if (exactVal < minVal) { + // clip to min value + *outMax = minVal; + } else { + // use exact value + *outMin = *outMax = exactVal; + } +} + +ASSizeRange ASLayoutElementSizeResolveAutoSize(ASLayoutElementSize size, const CGSize parentSize, ASSizeRange autoASSizeRange) +{ + CGSize resolvedExact = ASLayoutSizeResolveSize(ASLayoutSizeMake(size.width, size.height), parentSize, {NAN, NAN}); + CGSize resolvedMin = ASLayoutSizeResolveSize(ASLayoutSizeMake(size.minWidth, size.minHeight), parentSize, autoASSizeRange.min); + CGSize resolvedMax = ASLayoutSizeResolveSize(ASLayoutSizeMake(size.maxWidth, size.maxHeight), parentSize, autoASSizeRange.max); + + CGSize rangeMin, rangeMax; + ASLayoutElementSizeConstrain(resolvedMin.width, resolvedExact.width, resolvedMax.width, &rangeMin.width, &rangeMax.width); + ASLayoutElementSizeConstrain(resolvedMin.height, resolvedExact.height, resolvedMax.height, &rangeMin.height, &rangeMax.height); + return {rangeMin, rangeMax}; +} diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASInsetLayoutSpec.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASInsetLayoutSpec.h new file mode 100644 index 0000000..8e22fe9 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASInsetLayoutSpec.h @@ -0,0 +1,43 @@ +// +// ASInsetLayoutSpec.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + A layout spec that wraps another layoutElement child, applying insets around it. + + If the child has a size specified as a fraction, the fraction is resolved against this spec's parent + size **after** applying insets. + + @example ASOuterLayoutSpec contains an ASInsetLayoutSpec with an ASInnerLayoutSpec. Suppose that: + - ASOuterLayoutSpec is 200pt wide. + - ASInnerLayoutSpec specifies its width as 100%. + - The ASInsetLayoutSpec has insets of 10pt on every side. + ASInnerLayoutSpec will have size 180pt, not 200pt, because it receives a parent size that has been adjusted for insets. + + If you're familiar with CSS: ASInsetLayoutSpec's child behaves similarly to "box-sizing: border-box". + + An infinite inset is resolved as an inset equal to all remaining space after applying the other insets and child size. + @example An ASInsetLayoutSpec with an infinite left inset and 10px for all other edges will position it's child 10px from the right edge. + */ +@interface ASInsetLayoutSpec : ASLayoutSpec + +@property (nonatomic) UIEdgeInsets insets; + +/** + @param insets The amount of space to inset on each side. + @param child The wrapped child to inset. + */ ++ (instancetype)insetLayoutSpecWithInsets:(UIEdgeInsets)insets child:(id)child NS_RETURNS_RETAINED AS_WARN_UNUSED_RESULT; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASInsetLayoutSpec.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASInsetLayoutSpec.mm new file mode 100644 index 0000000..93fd6ee --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASInsetLayoutSpec.mm @@ -0,0 +1,122 @@ +// +// ASInsetLayoutSpec.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import + +#import + +@interface ASInsetLayoutSpec () +{ + UIEdgeInsets _insets; +} +@end + +/* Returns f if f is finite, substitute otherwise */ +static CGFloat finite(CGFloat f, CGFloat substitute) +{ + return isinf(f) ? substitute : f; +} + +/* Returns f if f is finite, 0 otherwise */ +static CGFloat finiteOrZero(CGFloat f) +{ + return finite(f, 0); +} + +/* Returns the inset required to center 'inner' in 'outer' */ +static CGFloat centerInset(CGFloat outer, CGFloat inner) +{ + return ASRoundPixelValue((outer - inner) / 2); +} + +@implementation ASInsetLayoutSpec + +- (instancetype)initWithInsets:(UIEdgeInsets)insets child:(id)child; +{ + if (!(self = [super init])) { + return nil; + } + ASDisplayNodeAssertNotNil(child, @"Child cannot be nil"); + _insets = insets; + [self setChild:child]; + return self; +} + ++ (instancetype)insetLayoutSpecWithInsets:(UIEdgeInsets)insets child:(id)child NS_RETURNS_RETAINED +{ + return [[self alloc] initWithInsets:insets child:child]; +} + +- (void)setInsets:(UIEdgeInsets)insets +{ + ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); + _insets = insets; +} + +/** + Inset will compute a new constrained size for it's child after applying insets and re-positioning + the child to respect the inset. + */ +- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize + restrictedToSize:(ASLayoutElementSize)size + relativeToParentSize:(CGSize)parentSize +{ + if (self.child == nil) { + ASDisplayNodeAssert(NO, @"Inset spec measured without a child. The spec will do nothing."); + return [ASLayout layoutWithLayoutElement:self size:CGSizeZero]; + } + + const CGFloat insetsX = (finiteOrZero(_insets.left) + finiteOrZero(_insets.right)); + const CGFloat insetsY = (finiteOrZero(_insets.top) + finiteOrZero(_insets.bottom)); + + // if either x-axis inset is infinite, let child be intrinsic width + const CGFloat minWidth = (isinf(_insets.left) || isinf(_insets.right)) ? 0 : constrainedSize.min.width; + // if either y-axis inset is infinite, let child be intrinsic height + const CGFloat minHeight = (isinf(_insets.top) || isinf(_insets.bottom)) ? 0 : constrainedSize.min.height; + + const ASSizeRange insetConstrainedSize = { + { + MAX(0, minWidth - insetsX), + MAX(0, minHeight - insetsY), + }, + { + MAX(0, constrainedSize.max.width - insetsX), + MAX(0, constrainedSize.max.height - insetsY), + } + }; + + const CGSize insetParentSize = { + MAX(0, parentSize.width - insetsX), + MAX(0, parentSize.height - insetsY) + }; + + ASLayout *sublayout = [self.child layoutThatFits:insetConstrainedSize parentSize:insetParentSize]; + + const CGSize computedSize = ASSizeRangeClamp(constrainedSize, { + finite(sublayout.size.width + _insets.left + _insets.right, constrainedSize.max.width), + finite(sublayout.size.height + _insets.top + _insets.bottom, constrainedSize.max.height), + }); + + const CGFloat x = finite(_insets.left, constrainedSize.max.width - + (finite(_insets.right, + centerInset(constrainedSize.max.width, sublayout.size.width)) + sublayout.size.width)); + + const CGFloat y = finite(_insets.top, + constrainedSize.max.height - + (finite(_insets.bottom, + centerInset(constrainedSize.max.height, sublayout.size.height)) + sublayout.size.height)); + + sublayout.position = CGPointMake(x, y); + + return [ASLayout layoutWithLayoutElement:self size:computedSize sublayouts:@[sublayout]]; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASLayout+IGListDiffKit.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASLayout+IGListDiffKit.h new file mode 100644 index 0000000..34636ef --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASLayout+IGListDiffKit.h @@ -0,0 +1,15 @@ +// +// ASLayout+IGListDiffKit.h +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#if AS_IG_LIST_DIFF_KIT +#import +#import + +@interface ASLayout(IGListDiffKit) +@end +#endif // AS_IG_LIST_DIFF_KIT diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASLayout+IGListDiffKit.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASLayout+IGListDiffKit.mm new file mode 100644 index 0000000..d6e57b2 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASLayout+IGListDiffKit.mm @@ -0,0 +1,30 @@ +// +// ASLayout+IGListDiffKit.mm +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// +#import +#if AS_IG_LIST_DIFF_KIT +#import "ASLayout+IGListDiffKit.h" + +@interface ASLayout() { +@public + id _layoutElement; +} +@end + +@implementation ASLayout(IGListDiffKit) + +- (id )diffIdentifier +{ + return self->_layoutElement; +} + +- (BOOL)isEqualToDiffableObject:(id )other +{ + return [self isEqual:other]; +} +@end +#endif // AS_IG_LIST_DIFF_KIT diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASLayout.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASLayout.h new file mode 100644 index 0000000..44b7de0 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASLayout.h @@ -0,0 +1,157 @@ +// +// ASLayout.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#pragma once +#import +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +ASDK_EXTERN CGPoint const ASPointNull; // {NAN, NAN} + +ASDK_EXTERN BOOL ASPointIsNull(CGPoint point); + +ASDK_EXTERN NSString *const ASThreadDictMaxConstraintSizeKey; + +/** + * Safely calculates the layout of the given root layoutElement by guarding against nil nodes. + * @param rootLayoutElement The root node to calculate the layout for. + * @param sizeRange The size range to calculate the root layout within. + */ +ASDK_EXTERN ASLayout *ASCalculateRootLayout(id rootLayoutElement, const ASSizeRange sizeRange); + +/** + * Safely computes the layout of the given node by guarding against nil nodes. + * @param layoutElement The layout element to calculate the layout for. + * @param sizeRange The size range to calculate the node layout within. + * @param parentSize The parent size of the node to calculate the layout for. + */ +ASDK_EXTERN ASLayout *ASCalculateLayout(idlayoutElement, const ASSizeRange sizeRange, const CGSize parentSize); + +/** + * A node in the layout tree that represents the size and position of the object that created it (ASLayoutElement). + */ +@interface ASLayout : NSObject + +/** + * The underlying object described by this layout + */ +@property (nonatomic, weak, readonly) id layoutElement; + +/** + * The type of ASLayoutElement that created this layout + */ +@property (nonatomic, readonly) ASLayoutElementType type; + +/** + * Size of the current layout + */ +@property (nonatomic, readonly) CGSize size; + +/** + * Position in parent. Default to ASPointNull. + * + * @discussion When being used as a sublayout, this property must not equal ASPointNull. + */ +@property (nonatomic, readonly) CGPoint position; + +/** + * Array of ASLayouts. Each must have a valid non-null position. + */ +@property (nonatomic, copy, readonly) NSArray *sublayouts; + +/** + * The frame for the given element, or CGRectNull if + * the element is not a direct descendent of this layout. + */ +- (CGRect)frameForElement:(id)layoutElement; + +/** + * @abstract Returns a valid frame for the current layout computed with the size and position. + * @discussion Clamps the layout's origin or position to 0 if any of the calculated values are infinite. + */ +@property (nonatomic, readonly) CGRect frame; + +/** + * Designated initializer + */ +- (instancetype)initWithLayoutElement:(id)layoutElement + size:(CGSize)size + position:(CGPoint)position + sublayouts:(nullable NSArray *)sublayouts NS_DESIGNATED_INITIALIZER; + +/** + * Convenience class initializer for layout construction. + * + * @param layoutElement The backing ASLayoutElement object. + * @param size The size of this layout. + * @param position The position of this layout within its parent (if available). + * @param sublayouts Sublayouts belong to the new layout. + */ ++ (instancetype)layoutWithLayoutElement:(id)layoutElement + size:(CGSize)size + position:(CGPoint)position + sublayouts:(nullable NSArray *)sublayouts NS_RETURNS_RETAINED AS_WARN_UNUSED_RESULT; + +/** + * Convenience initializer that has CGPointNull position. + * Best used by ASDisplayNode subclasses that are manually creating a layout for -calculateLayoutThatFits:, + * or for ASLayoutSpec subclasses that are referencing the "self" level in the layout tree, + * or for creating a sublayout of which the position is yet to be determined. + * + * @param layoutElement The backing ASLayoutElement object. + * @param size The size of this layout. + * @param sublayouts Sublayouts belong to the new layout. + */ ++ (instancetype)layoutWithLayoutElement:(id)layoutElement + size:(CGSize)size + sublayouts:(nullable NSArray *)sublayouts NS_RETURNS_RETAINED AS_WARN_UNUSED_RESULT; + +/** + * Convenience that has CGPointNull position and no sublayouts. + * Best used for creating a layout that has no sublayouts, and is either a root one + * or a sublayout of which the position is yet to be determined. + * + * @param layoutElement The backing ASLayoutElement object. + * @param size The size of this layout. + */ ++ (instancetype)layoutWithLayoutElement:(id)layoutElement + size:(CGSize)size NS_RETURNS_RETAINED AS_WARN_UNUSED_RESULT; + +/** + * Traverses the existing layout tree and generates a new tree that represents only ASDisplayNode layouts + */ +- (ASLayout *)filteredNodeLayoutTree NS_RETURNS_RETAINED AS_WARN_UNUSED_RESULT; + +- (instancetype)init NS_UNAVAILABLE; +- (instancetype)new NS_UNAVAILABLE; + +@end + +#pragma mark - Debugging + +@interface ASLayout (Debugging) + +/** + * Set to YES to tell all ASLayout instances to retain their sublayout elements. Defaults to NO. + * See `-retainSublayoutLayoutElements` to control this per-instance. + * + * Note: Weaver relies on this API. + */ +@property (class) BOOL shouldRetainSublayoutLayoutElements; + +/** + * Recrusively output the description of the layout tree. + */ +- (NSString *)recursiveDescription; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASLayout.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASLayout.mm new file mode 100644 index 0000000..3979c84 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASLayout.mm @@ -0,0 +1,376 @@ +// +// ASLayout.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import +#import + +#import +#import +#import + +#import +#import + +NSString *const ASThreadDictMaxConstraintSizeKey = @"kASThreadDictMaxConstraintSizeKey"; + +CGPoint const ASPointNull = {NAN, NAN}; + +BOOL ASPointIsNull(CGPoint point) +{ + return isnan(point.x) && isnan(point.y); +} + +/** + * Creates an defined number of " |" indent blocks for the recursive description. + */ +ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT NSString * descriptionIndents(NSUInteger indents) +{ + NSMutableString *description = [NSMutableString string]; + for (NSUInteger i = 0; i < indents; i++) { + [description appendString:@" |"]; + } + if (indents > 0) { + [description appendString:@" "]; + } + return description; +} + +ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT BOOL ASLayoutIsDisplayNodeType(ASLayout *layout) +{ + return layout.type == ASLayoutElementTypeDisplayNode; +} + +@interface ASLayout () +{ + ASLayoutElementType _layoutElementType; + std::atomic_bool _retainSublayoutElements; +} +@end + +@implementation ASLayout + +@dynamic frame, type; + +static std::atomic_bool static_retainsSublayoutLayoutElements = ATOMIC_VAR_INIT(NO); + ++ (void)setShouldRetainSublayoutLayoutElements:(BOOL)shouldRetain +{ + static_retainsSublayoutLayoutElements.store(shouldRetain); +} + ++ (BOOL)shouldRetainSublayoutLayoutElements +{ + return static_retainsSublayoutLayoutElements.load(); +} + +- (instancetype)initWithLayoutElement:(id)layoutElement + size:(CGSize)size + position:(CGPoint)position + sublayouts:(nullable NSArray *)sublayouts +{ + NSParameterAssert(layoutElement); + + self = [super init]; + if (self) { +#if ASDISPLAYNODE_ASSERTIONS_ENABLED + for (ASLayout *sublayout in sublayouts) { + ASDisplayNodeAssert(ASPointIsNull(sublayout.position) == NO, @"Invalid position is not allowed in sublayout."); + } +#endif + + _layoutElement = layoutElement; + + // Read this now to avoid @c weak overhead later. + _layoutElementType = layoutElement.layoutElementType; + + if (!ASIsCGSizeValidForSize(size)) { + ASDisplayNodeFailAssert(@"layoutSize is invalid and unsafe to provide to Core Animation! Release configurations will force to 0, 0. Size = %@, node = %@", NSStringFromCGSize(size), layoutElement); + size = CGSizeZero; + } else { + size = CGSizeMake(ASCeilPixelValue(size.width), ASCeilPixelValue(size.height)); + } + _size = size; + + if (ASPointIsNull(position) == NO) { + _position = ASCeilPointValues(position); + } else { + _position = position; + } + + _sublayouts = [sublayouts copy] ?: @[]; + + if ([ASLayout shouldRetainSublayoutLayoutElements]) { + [self retainSublayoutElements]; + } + } + + return self; +} + +#pragma mark - Class Constructors + ++ (instancetype)layoutWithLayoutElement:(id)layoutElement + size:(CGSize)size + position:(CGPoint)position + sublayouts:(nullable NSArray *)sublayouts NS_RETURNS_RETAINED +{ + return [[self alloc] initWithLayoutElement:layoutElement + size:size + position:position + sublayouts:sublayouts]; +} + ++ (instancetype)layoutWithLayoutElement:(id)layoutElement + size:(CGSize)size + sublayouts:(nullable NSArray *)sublayouts NS_RETURNS_RETAINED +{ + return [self layoutWithLayoutElement:layoutElement + size:size + position:ASPointNull + sublayouts:sublayouts]; +} + ++ (instancetype)layoutWithLayoutElement:(id)layoutElement size:(CGSize)size NS_RETURNS_RETAINED +{ + return [self layoutWithLayoutElement:layoutElement + size:size + position:ASPointNull + sublayouts:nil]; +} + +- (void)dealloc +{ + if (_retainSublayoutElements.load()) { + for (ASLayout *sublayout in _sublayouts) { + // We retained this, so there's no risk of it deallocating on us. + if (CFTypeRef cfElement = (__bridge CFTypeRef)sublayout->_layoutElement) { + CFRelease(cfElement); + } + } + } +} + +#pragma mark - Sublayout Elements Caching + +- (void)retainSublayoutElements +{ + if (_retainSublayoutElements.exchange(true)) { + return; + } + + for (ASLayout *sublayout in _sublayouts) { + // CFBridgingRetain atomically casts and retains. We need the atomicity. + CFBridgingRetain(sublayout->_layoutElement); + } +} + +#pragma mark - Layout Flattening + +- (BOOL)isFlattened +{ + // A layout is flattened if its position is null, and all of its sublayouts are of type displaynode with no sublayouts. + if (!ASPointIsNull(_position)) { + return NO; + } + + for (ASLayout *sublayout in _sublayouts) { + if (ASLayoutIsDisplayNodeType(sublayout) == NO || sublayout->_sublayouts.count > 0) { + return NO; + } + } + + return YES; +} + +- (ASLayout *)filteredNodeLayoutTree NS_RETURNS_RETAINED +{ + if ([self isFlattened]) { + // All flattened layouts must retain sublayout elements until they are applied. + [self retainSublayoutElements]; + return self; + } + + struct Context { + unowned ASLayout *layout; + CGPoint absolutePosition; + }; + + // Queue used to keep track of sublayouts while traversing this layout in a DFS fashion. + std::deque queue; + for (ASLayout *sublayout in _sublayouts) { + queue.push_back({sublayout, sublayout.position}); + } + + std::vector flattenedSublayouts; + + while (!queue.empty()) { + const Context context = std::move(queue.front()); + queue.pop_front(); + + unowned ASLayout *layout = context.layout; + // Direct ivar access to avoid retain/release, use existing +1. + const NSUInteger sublayoutsCount = layout->_sublayouts.count; + const CGPoint absolutePosition = context.absolutePosition; + + if (ASLayoutIsDisplayNodeType(layout)) { + if (sublayoutsCount > 0 || CGPointEqualToPoint(ASCeilPointValues(absolutePosition), layout.position) == NO) { + // Only create a new layout if the existing one can't be reused, which means it has either some sublayouts or an invalid absolute position. + const auto newLayout = [ASLayout layoutWithLayoutElement:layout->_layoutElement + size:layout.size + position:absolutePosition + sublayouts:@[]]; + flattenedSublayouts.push_back(newLayout); + } else { + flattenedSublayouts.push_back(layout); + } + } else if (sublayoutsCount > 0) { + // Fast-reverse-enumerate the sublayouts array by copying it into a C-array and push_front'ing each into the queue. + unowned ASLayout *rawSublayouts[sublayoutsCount]; + [layout->_sublayouts getObjects:rawSublayouts range:NSMakeRange(0, sublayoutsCount)]; + for (NSInteger i = sublayoutsCount - 1; i >= 0; i--) { + queue.push_front({rawSublayouts[i], absolutePosition + rawSublayouts[i].position}); + } + } + } + + NSArray *array = [NSArray arrayByTransferring:flattenedSublayouts.data() count:flattenedSublayouts.size()]; + // flattenedSublayouts is now all nils. + + ASLayout *layout = [ASLayout layoutWithLayoutElement:_layoutElement size:_size sublayouts:array]; + // All flattened layouts must retain sublayout elements until they are applied. + [layout retainSublayoutElements]; + return layout; +} + +#pragma mark - Equality Checking + +- (BOOL)isEqual:(id)object +{ + if (self == object) return YES; + + ASLayout *layout = ASDynamicCast(object, ASLayout); + if (layout == nil) { + return NO; + } + + if (!CGSizeEqualToSize(_size, layout.size)) return NO; + + if (!((ASPointIsNull(self.position) && ASPointIsNull(layout.position)) + || CGPointEqualToPoint(self.position, layout.position))) return NO; + if (_layoutElement != layout.layoutElement) return NO; + + if (!ASObjectIsEqual(_sublayouts, layout.sublayouts)) { + return NO; + } + + return YES; +} + +#pragma mark - Accessors + +- (ASLayoutElementType)type +{ + return _layoutElementType; +} + +- (CGRect)frameForElement:(id)layoutElement +{ + for (ASLayout *l in _sublayouts) { + if (l->_layoutElement == layoutElement) { + return l.frame; + } + } + return CGRectNull; +} + +- (CGRect)frame +{ + CGRect subnodeFrame = CGRectZero; + CGPoint adjustedOrigin = _position; + if (isfinite(adjustedOrigin.x) == NO) { + ASDisplayNodeAssert(0, @"Layout has an invalid position"); + adjustedOrigin.x = 0; + } + if (isfinite(adjustedOrigin.y) == NO) { + ASDisplayNodeAssert(0, @"Layout has an invalid position"); + adjustedOrigin.y = 0; + } + subnodeFrame.origin = adjustedOrigin; + + CGSize adjustedSize = _size; + if (isfinite(adjustedSize.width) == NO) { + ASDisplayNodeAssert(0, @"Layout has an invalid size"); + adjustedSize.width = 0; + } + if (isfinite(adjustedSize.height) == NO) { + ASDisplayNodeAssert(0, @"Layout has an invalid position"); + adjustedSize.height = 0; + } + subnodeFrame.size = adjustedSize; + + return subnodeFrame; +} + +#pragma mark - Description + +- (NSMutableArray *)propertiesForDescription +{ + NSMutableArray *result = [NSMutableArray array]; + [result addObject:@{ @"size" : [NSValue valueWithCGSize:self.size] }]; + + if (id layoutElement = self.layoutElement) { + [result addObject:@{ @"layoutElement" : layoutElement }]; + } + + const auto pos = self.position; + if (!ASPointIsNull(pos)) { + [result addObject:@{ @"position" : [NSValue valueWithCGPoint:pos] }]; + } + return result; +} + +- (NSString *)description +{ + return ASObjectDescriptionMake(self, [self propertiesForDescription]); +} + +- (NSString *)recursiveDescription +{ + return [self _recursiveDescriptionForLayout:self level:0]; +} + +- (NSString *)_recursiveDescriptionForLayout:(ASLayout *)layout level:(NSUInteger)level +{ + NSMutableString *description = [NSMutableString string]; + [description appendString:descriptionIndents(level)]; + [description appendString:[layout description]]; + for (ASLayout *sublayout in layout.sublayouts) { + [description appendString:@"\n"]; + [description appendString:[self _recursiveDescriptionForLayout:sublayout level:level + 1]]; + } + return description; +} + +@end + +ASLayout *ASCalculateLayout(id layoutElement, const ASSizeRange sizeRange, const CGSize parentSize) +{ + NSCParameterAssert(layoutElement != nil); + + return [layoutElement layoutThatFits:sizeRange parentSize:parentSize]; +} + +ASLayout *ASCalculateRootLayout(id rootLayoutElement, const ASSizeRange sizeRange) +{ + ASLayout *layout = ASCalculateLayout(rootLayoutElement, sizeRange, sizeRange.max); + // Here could specific verfication happen + return layout; +} diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASLayoutElement.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASLayoutElement.h new file mode 100644 index 0000000..000f965 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASLayoutElement.h @@ -0,0 +1,306 @@ +// +// ASLayoutElement.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import +#import +#import +#import +#import +#import + +@class ASLayout; +@class ASLayoutSpec; +@protocol ASLayoutElementStylability; + +@protocol ASTraitEnvironment; + +NS_ASSUME_NONNULL_BEGIN + +/** A constant that indicates that the parent's size is not yet determined in a given dimension. */ +ASDK_EXTERN CGFloat const ASLayoutElementParentDimensionUndefined; + +/** A constant that indicates that the parent's size is not yet determined in either dimension. */ +ASDK_EXTERN CGSize const ASLayoutElementParentSizeUndefined; + +/** Type of ASLayoutElement */ +typedef NS_ENUM(unsigned char, ASLayoutElementType) { + ASLayoutElementTypeLayoutSpec, + ASLayoutElementTypeDisplayNode +}; + +#pragma mark - ASLayoutElement + +/** + * The ASLayoutElement protocol declares a method for measuring the layout of an object. A layout + * is defined by an ASLayout return value, and must specify 1) the size (but not position) of the + * layoutElement object, and 2) the size and position of all of its immediate child objects. The tree + * recursion is driven by parents requesting layouts from their children in order to determine their + * size, followed by the parents setting the position of the children once the size is known + * + * The protocol also implements a "family" of LayoutElement protocols. These protocols contain layout + * options that can be used for specific layout specs. For example, ASStackLayoutSpec has options + * defining how a layoutElement should shrink or grow based upon available space. + * + * These layout options are all stored in an ASLayoutOptions class (that is defined in ASLayoutElementPrivate). + * Generally you needn't worry about the layout options class, as the layoutElement protocols allow all direct + * access to the options via convenience properties. If you are creating custom layout spec, then you can + * extend the backing layout options class to accommodate any new layout options. + */ +@protocol ASLayoutElement + +#pragma mark - Getter + +/** + * @abstract Returns type of layoutElement + */ +@property (nonatomic, readonly) ASLayoutElementType layoutElementType; + +/** + * @abstract A size constraint that should apply to this ASLayoutElement. + */ +@property (nonatomic, readonly) ASLayoutElementStyle *style; + +/** + * @abstract Returns all children of an object which class conforms to the ASLayoutElement protocol + */ +- (nullable NSArray> *)sublayoutElements; + +#pragma mark - Calculate layout + +/** + * @abstract Asks the node to return a layout based on given size range. + * + * @param constrainedSize The minimum and maximum sizes the receiver should fit in. + * + * @return An ASLayout instance defining the layout of the receiver (and its children, if the box layout model is used). + * + * @discussion Though this method does not set the bounds of the view, it does have side effects--caching both the + * constraint and the result. + * + * @warning Subclasses must not override this; it caches results from -calculateLayoutThatFits:. Calling this method may + * be expensive if result is not cached. + * + * @see [ASDisplayNode(Subclassing) calculateLayoutThatFits:] + */ +- (ASLayout *)layoutThatFits:(ASSizeRange)constrainedSize; + +/** + * Call this on children layoutElements to compute their layouts within your implementation of -calculateLayoutThatFits:. + * + * @warning You may not override this method. Override -calculateLayoutThatFits: instead. + * @warning In almost all cases, prefer the use of ASCalculateLayout in ASLayout + * + * @param constrainedSize Specifies a minimum and maximum size. The receiver must choose a size that is in this range. + * @param parentSize The parent node's size. If the parent component does not have a final size in a given dimension, + * then it should be passed as ASLayoutElementParentDimensionUndefined (for example, if the parent's width + * depends on the child's size). + * + * @discussion Though this method does not set the bounds of the view, it does have side effects--caching both the + * constraint and the result. + * + * @return An ASLayout instance defining the layout of the receiver (and its children, if the box layout model is used). + */ +- (ASLayout *)layoutThatFits:(ASSizeRange)constrainedSize parentSize:(CGSize)parentSize; + +/** + * Override this method to compute your layoutElement's layout. + * + * @discussion Why do you need to override -calculateLayoutThatFits: instead of -layoutThatFits:parentSize:? + * The base implementation of -layoutThatFits:parentSize: does the following for you: + * 1. First, it uses the parentSize parameter to resolve the nodes's size (the one assigned to the size property). + * 2. Then, it intersects the resolved size with the constrainedSize parameter. If the two don't intersect, + * constrainedSize wins. This allows a component to always override its children's sizes when computing its layout. + * (The analogy for UIView: you might return a certain size from -sizeThatFits:, but a parent view can always override + * that size and set your frame to any size.) + * 3. It caches it result for reuse + * + * @param constrainedSize A min and max size. This is computed as described in the description. The ASLayout you + * return MUST have a size between these two sizes. + */ +- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize; + +/** + * In certain advanced cases, you may want to override this method. Overriding this method allows you to receive the + * layoutElement's size, parentSize, and constrained size. With these values you could calculate the final constrained size + * and call -calculateLayoutThatFits: with the result. + * + * @warning Overriding this method should be done VERY rarely. + */ +- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize + restrictedToSize:(ASLayoutElementSize)size + relativeToParentSize:(CGSize)parentSize; + +- (BOOL)implementsLayoutMethod; + +@end + +#pragma mark - ASLayoutElementStyle + +ASDK_EXTERN NSString * const ASLayoutElementStyleWidthProperty; +ASDK_EXTERN NSString * const ASLayoutElementStyleMinWidthProperty; +ASDK_EXTERN NSString * const ASLayoutElementStyleMaxWidthProperty; + +ASDK_EXTERN NSString * const ASLayoutElementStyleHeightProperty; +ASDK_EXTERN NSString * const ASLayoutElementStyleMinHeightProperty; +ASDK_EXTERN NSString * const ASLayoutElementStyleMaxHeightProperty; + +ASDK_EXTERN NSString * const ASLayoutElementStyleSpacingBeforeProperty; +ASDK_EXTERN NSString * const ASLayoutElementStyleSpacingAfterProperty; +ASDK_EXTERN NSString * const ASLayoutElementStyleFlexGrowProperty; +ASDK_EXTERN NSString * const ASLayoutElementStyleFlexShrinkProperty; +ASDK_EXTERN NSString * const ASLayoutElementStyleFlexBasisProperty; +ASDK_EXTERN NSString * const ASLayoutElementStyleAlignSelfProperty; +ASDK_EXTERN NSString * const ASLayoutElementStyleAscenderProperty; +ASDK_EXTERN NSString * const ASLayoutElementStyleDescenderProperty; + +ASDK_EXTERN NSString * const ASLayoutElementStyleLayoutPositionProperty; + +@protocol ASLayoutElementStyleDelegate +- (void)style:(__kindof ASLayoutElementStyle *)style propertyDidChange:(NSString *)propertyName; +@end + +@interface ASLayoutElementStyle : NSObject + +/** + * @abstract Initializes the layoutElement style with a specified delegate + */ +- (instancetype)initWithDelegate:(id)delegate; + +/** + * @abstract The object that acts as the delegate of the style. + * + * @discussion The delegate must adopt the ASLayoutElementStyleDelegate protocol. The delegate is not retained. + */ +@property (nullable, nonatomic, weak, readonly) id delegate; + + +#pragma mark - Sizing + +/** + * @abstract The width property specifies the width of the content area of an ASLayoutElement. + * The minWidth and maxWidth properties override width. + * Defaults to ASDimensionAuto + */ +@property (nonatomic) ASDimension width; + +/** + * @abstract The height property specifies the height of the content area of an ASLayoutElement + * The minHeight and maxHeight properties override height. + * Defaults to ASDimensionAuto + */ +@property (nonatomic) ASDimension height; + +/** + * @abstract The minHeight property is used to set the minimum height of a given element. It prevents the used value + * of the height property from becoming smaller than the value specified for minHeight. + * The value of minHeight overrides both maxHeight and height. + * Defaults to ASDimensionAuto + */ +@property (nonatomic) ASDimension minHeight; + +/** + * @abstract The maxHeight property is used to set the maximum height of an element. It prevents the used value of the + * height property from becoming larger than the value specified for maxHeight. + * The value of maxHeight overrides height, but minHeight overrides maxHeight. + * Defaults to ASDimensionAuto + */ +@property (nonatomic) ASDimension maxHeight; + +/** + * @abstract The minWidth property is used to set the minimum width of a given element. It prevents the used value of + * the width property from becoming smaller than the value specified for minWidth. + * The value of minWidth overrides both maxWidth and width. + * Defaults to ASDimensionAuto + */ +@property (nonatomic) ASDimension minWidth; + +/** + * @abstract The maxWidth property is used to set the maximum width of a given element. It prevents the used value of + * the width property from becoming larger than the value specified for maxWidth. + * The value of maxWidth overrides width, but minWidth overrides maxWidth. + * Defaults to ASDimensionAuto + */ +@property (nonatomic) ASDimension maxWidth; + +#pragma mark - ASLayoutElementStyleSizeHelpers + +/** + * @abstract Provides a suggested size for a layout element. If the optional minSize or maxSize are provided, + * and the preferredSize exceeds these, the minSize or maxSize will be enforced. If this optional value is not + * provided, the layout element’s size will default to it’s intrinsic content size provided calculateSizeThatFits: + * + * @discussion This method is optional, but one of either preferredSize or preferredLayoutSize is required + * for nodes that either have no intrinsic content size or + * should be laid out at a different size than its intrinsic content size. For example, this property could be + * set on an ASImageNode to display at a size different from the underlying image size. + * + * @warning Calling the getter when the size's width or height are relative will cause an assert. + */ +@property (nonatomic) CGSize preferredSize; + + /** + * @abstract An optional property that provides a minimum size bound for a layout element. If provided, this restriction will + * always be enforced. If a parent layout element’s minimum size is smaller than its child’s minimum size, the child’s + * minimum size will be enforced and its size will extend out of the layout spec’s. + * + * @discussion For example, if you set a preferred relative width of 50% and a minimum width of 200 points on an + * element in a full screen container, this would result in a width of 160 points on an iPhone screen. However, + * since 160 pts is lower than the minimum width of 200 pts, the minimum width would be used. + */ +@property (nonatomic) CGSize minSize; +- (CGSize)minSize UNAVAILABLE_ATTRIBUTE; + +/** + * @abstract An optional property that provides a maximum size bound for a layout element. If provided, this restriction will + * always be enforced. If a child layout element’s maximum size is smaller than its parent, the child’s maximum size will + * be enforced and its size will extend out of the layout spec’s. + * + * @discussion For example, if you set a preferred relative width of 50% and a maximum width of 120 points on an + * element in a full screen container, this would result in a width of 160 points on an iPhone screen. However, + * since 160 pts is higher than the maximum width of 120 pts, the maximum width would be used. + */ +@property (nonatomic) CGSize maxSize; +- (CGSize)maxSize UNAVAILABLE_ATTRIBUTE; + +/** + * @abstract Provides a suggested RELATIVE size for a layout element. An ASLayoutSize uses percentages rather + * than points to specify layout. E.g. width should be 50% of the parent’s width. If the optional minLayoutSize or + * maxLayoutSize are provided, and the preferredLayoutSize exceeds these, the minLayoutSize or maxLayoutSize + * will be enforced. If this optional value is not provided, the layout element’s size will default to its intrinsic content size + * provided calculateSizeThatFits: + */ +@property (nonatomic) ASLayoutSize preferredLayoutSize; + +/** + * @abstract An optional property that provides a minimum RELATIVE size bound for a layout element. If provided, this + * restriction will always be enforced. If a parent layout element’s minimum relative size is smaller than its child’s minimum + * relative size, the child’s minimum relative size will be enforced and its size will extend out of the layout spec’s. + */ +@property (nonatomic) ASLayoutSize minLayoutSize; + +/** + * @abstract An optional property that provides a maximum RELATIVE size bound for a layout element. If provided, this + * restriction will always be enforced. If a parent layout element’s maximum relative size is smaller than its child’s maximum + * relative size, the child’s maximum relative size will be enforced and its size will extend out of the layout spec’s. + */ +@property (nonatomic) ASLayoutSize maxLayoutSize; + +@end + +#pragma mark - ASLayoutElementStylability + +@protocol ASLayoutElementStylability + +- (instancetype)styledWithBlock:(AS_NOESCAPE void (^)(__kindof ASLayoutElementStyle *style))styleBlock; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASLayoutElement.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASLayoutElement.mm new file mode 100644 index 0000000..38d50d6 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASLayoutElement.mm @@ -0,0 +1,884 @@ +// +// ASLayoutElement.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import + +using AS::MutexLocker; + +#if YOGA + #import YOGA_HEADER_PATH + #import +#endif + +#pragma mark - ASLayoutElementContext + +@implementation ASLayoutElementContext + +- (instancetype)init +{ + if (self = [super init]) { + _transitionID = ASLayoutElementContextDefaultTransitionID; + } + return self; +} + +@end + +CGFloat const ASLayoutElementParentDimensionUndefined = NAN; +CGSize const ASLayoutElementParentSizeUndefined = {ASLayoutElementParentDimensionUndefined, ASLayoutElementParentDimensionUndefined}; + +int32_t const ASLayoutElementContextInvalidTransitionID = 0; +int32_t const ASLayoutElementContextDefaultTransitionID = ASLayoutElementContextInvalidTransitionID + 1; + +#if AS_TLS_AVAILABLE + +static _Thread_local unowned ASLayoutElementContext *tls_context; + +void ASLayoutElementPushContext(ASLayoutElementContext *context) +{ + // NOTE: It would be easy to support nested contexts – just use an NSMutableArray here. + ASDisplayNodeCAssertNil(tls_context, @"Nested ASLayoutElementContexts aren't supported."); + + tls_context = (__bridge ASLayoutElementContext *)(__bridge_retained CFTypeRef)context; +} + +ASLayoutElementContext *ASLayoutElementGetCurrentContext() +{ + // Don't retain here. Caller will retain if it wants to! + return tls_context; +} + +void ASLayoutElementPopContext() +{ + ASDisplayNodeCAssertNotNil(tls_context, @"Attempt to pop context when there wasn't a context!"); + CFRelease((__bridge CFTypeRef)tls_context); + tls_context = nil; +} + +#else + +static pthread_key_t ASLayoutElementContextKey() { + static pthread_key_t k; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + pthread_key_create(&k, NULL); + }); + return k; +} +void ASLayoutElementPushContext(ASLayoutElementContext *context) +{ + // NOTE: It would be easy to support nested contexts – just use an NSMutableArray here. + ASDisplayNodeCAssertNil(pthread_getspecific(ASLayoutElementContextKey()), @"Nested ASLayoutElementContexts aren't supported."); + + const auto cfCtx = (__bridge_retained CFTypeRef)context; + pthread_setspecific(ASLayoutElementContextKey(), cfCtx); +} + +ASLayoutElementContext *ASLayoutElementGetCurrentContext() +{ + // Don't retain here. Caller will retain if it wants to! + const auto ctxPtr = pthread_getspecific(ASLayoutElementContextKey()); + return (__bridge ASLayoutElementContext *)ctxPtr; +} + +void ASLayoutElementPopContext() +{ + const auto ctx = (CFTypeRef)pthread_getspecific(ASLayoutElementContextKey()); + ASDisplayNodeCAssertNotNil(ctx, @"Attempt to pop context when there wasn't a context!"); + CFRelease(ctx); + pthread_setspecific(ASLayoutElementContextKey(), NULL); +} + +#endif // AS_TLS_AVAILABLE + +#pragma mark - ASLayoutElementStyle + +NSString * const ASLayoutElementStyleWidthProperty = @"ASLayoutElementStyleWidthProperty"; +NSString * const ASLayoutElementStyleMinWidthProperty = @"ASLayoutElementStyleMinWidthProperty"; +NSString * const ASLayoutElementStyleMaxWidthProperty = @"ASLayoutElementStyleMaxWidthProperty"; + +NSString * const ASLayoutElementStyleHeightProperty = @"ASLayoutElementStyleHeightProperty"; +NSString * const ASLayoutElementStyleMinHeightProperty = @"ASLayoutElementStyleMinHeightProperty"; +NSString * const ASLayoutElementStyleMaxHeightProperty = @"ASLayoutElementStyleMaxHeightProperty"; + +NSString * const ASLayoutElementStyleSpacingBeforeProperty = @"ASLayoutElementStyleSpacingBeforeProperty"; +NSString * const ASLayoutElementStyleSpacingAfterProperty = @"ASLayoutElementStyleSpacingAfterProperty"; +NSString * const ASLayoutElementStyleFlexGrowProperty = @"ASLayoutElementStyleFlexGrowProperty"; +NSString * const ASLayoutElementStyleFlexShrinkProperty = @"ASLayoutElementStyleFlexShrinkProperty"; +NSString * const ASLayoutElementStyleFlexBasisProperty = @"ASLayoutElementStyleFlexBasisProperty"; +NSString * const ASLayoutElementStyleAlignSelfProperty = @"ASLayoutElementStyleAlignSelfProperty"; +NSString * const ASLayoutElementStyleAscenderProperty = @"ASLayoutElementStyleAscenderProperty"; +NSString * const ASLayoutElementStyleDescenderProperty = @"ASLayoutElementStyleDescenderProperty"; + +NSString * const ASLayoutElementStyleLayoutPositionProperty = @"ASLayoutElementStyleLayoutPositionProperty"; + +#if YOGA +NSString * const ASYogaFlexWrapProperty = @"ASLayoutElementStyleLayoutFlexWrapProperty"; +NSString * const ASYogaFlexDirectionProperty = @"ASYogaFlexDirectionProperty"; +NSString * const ASYogaDirectionProperty = @"ASYogaDirectionProperty"; +NSString * const ASYogaSpacingProperty = @"ASYogaSpacingProperty"; +NSString * const ASYogaJustifyContentProperty = @"ASYogaJustifyContentProperty"; +NSString * const ASYogaAlignItemsProperty = @"ASYogaAlignItemsProperty"; +NSString * const ASYogaPositionTypeProperty = @"ASYogaPositionTypeProperty"; +NSString * const ASYogaPositionProperty = @"ASYogaPositionProperty"; +NSString * const ASYogaMarginProperty = @"ASYogaMarginProperty"; +NSString * const ASYogaPaddingProperty = @"ASYogaPaddingProperty"; +NSString * const ASYogaBorderProperty = @"ASYogaBorderProperty"; +NSString * const ASYogaAspectRatioProperty = @"ASYogaAspectRatioProperty"; +#endif + +#define ASLayoutElementStyleSetSizeWithScope(x) \ + ({ \ + __instanceLock__.lock(); \ + const ASLayoutElementSize oldSize = _size.load(); \ + ASLayoutElementSize newSize = oldSize; \ + {x}; \ + BOOL changed = !ASLayoutElementSizeEqualToLayoutElementSize(oldSize, newSize); \ + if (changed) { \ + _size.store(newSize); \ + } \ + __instanceLock__.unlock(); \ + changed; \ + }) + +#define ASLayoutElementStyleCallDelegate(propertyName)\ +do {\ + [self propertyDidChange:propertyName];\ + [_delegate style:self propertyDidChange:propertyName];\ +} while(0) + +@implementation ASLayoutElementStyle { + AS::RecursiveMutex __instanceLock__; + ASLayoutElementStyleExtensions _extensions; + + std::atomic _size; + std::atomic _spacingBefore; + std::atomic _spacingAfter; + std::atomic _flexGrow; + std::atomic _flexShrink; + std::atomic _flexBasis; + std::atomic _alignSelf; + std::atomic _ascender; + std::atomic _descender; + std::atomic _layoutPosition; + +#if YOGA + YGNodeRef _yogaNode; + std::atomic _flexWrap; + std::atomic _flexDirection; + std::atomic _direction; + std::atomic _justifyContent; + std::atomic _alignItems; + std::atomic _positionType; + std::atomic _position; + std::atomic _margin; + std::atomic _padding; + std::atomic _border; + std::atomic _aspectRatio; + ASStackLayoutAlignItems _parentAlignStyle; +#endif +} + +@dynamic width, height, minWidth, maxWidth, minHeight, maxHeight; +@dynamic preferredSize, minSize, maxSize, preferredLayoutSize, minLayoutSize, maxLayoutSize; + +#pragma mark - Lifecycle + +- (instancetype)initWithDelegate:(id)delegate +{ + self = [self init]; + if (self) { + _delegate = delegate; + } + return self; +} + +- (instancetype)init +{ + self = [super init]; + if (self) { + std::atomic_init(&_size, ASLayoutElementSizeMake()); + std::atomic_init(&_flexBasis, ASDimensionAuto); +#if YOGA + _parentAlignStyle = ASStackLayoutAlignItemsNotSet; + std::atomic_init(&_flexDirection, ASStackLayoutDirectionVertical); + std::atomic_init(&_alignItems, ASStackLayoutAlignItemsStretch); + std::atomic_init(&_aspectRatio, static_cast(YGUndefined)); +#endif + } + return self; +} + +ASSynthesizeLockingMethodsWithMutex(__instanceLock__) + +#pragma mark - ASLayoutElementStyleSize + +- (ASLayoutElementSize)size +{ + return _size.load(); +} + +- (void)setSize:(ASLayoutElementSize)size +{ + ASLayoutElementStyleSetSizeWithScope({ + newSize = size; + }); + // No CallDelegate method as ASLayoutElementSize is currently internal. +} + +#pragma mark - ASLayoutElementStyleSizeForwarding + +- (ASDimension)width +{ + return _size.load().width; +} + +- (void)setWidth:(ASDimension)width +{ + BOOL changed = ASLayoutElementStyleSetSizeWithScope({ newSize.width = width; }); + if (changed) { + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleWidthProperty); + } +} + +- (ASDimension)height +{ + return _size.load().height; +} + +- (void)setHeight:(ASDimension)height +{ + BOOL changed = ASLayoutElementStyleSetSizeWithScope({ newSize.height = height; }); + if (changed) { + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleHeightProperty); + } +} + +- (ASDimension)minWidth +{ + return _size.load().minWidth; +} + +- (void)setMinWidth:(ASDimension)minWidth +{ + BOOL changed = ASLayoutElementStyleSetSizeWithScope({ newSize.minWidth = minWidth; }); + if (changed) { + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMinWidthProperty); + } +} + +- (ASDimension)maxWidth +{ + return _size.load().maxWidth; +} + +- (void)setMaxWidth:(ASDimension)maxWidth +{ + BOOL changed = ASLayoutElementStyleSetSizeWithScope({ newSize.maxWidth = maxWidth; }); + if (changed) { + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMaxWidthProperty); + } +} + +- (ASDimension)minHeight +{ + return _size.load().minHeight; +} + +- (void)setMinHeight:(ASDimension)minHeight +{ + BOOL changed = ASLayoutElementStyleSetSizeWithScope({ newSize.minHeight = minHeight; }); + if (changed) { + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMinHeightProperty); + } +} + +- (ASDimension)maxHeight +{ + return _size.load().maxHeight; +} + +- (void)setMaxHeight:(ASDimension)maxHeight +{ + BOOL changed = ASLayoutElementStyleSetSizeWithScope({ newSize.maxHeight = maxHeight; }); + if (changed) { + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMaxHeightProperty); + } +} + + +#pragma mark - ASLayoutElementStyleSizeHelpers + +- (void)setPreferredSize:(CGSize)preferredSize +{ + BOOL changed = ASLayoutElementStyleSetSizeWithScope({ + newSize.width = ASDimensionMakeWithPoints(preferredSize.width); + newSize.height = ASDimensionMakeWithPoints(preferredSize.height); + }); + if (changed) { + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleWidthProperty); + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleHeightProperty); + } +} + +- (CGSize)preferredSize +{ + ASLayoutElementSize size = _size.load(); + if (size.width.unit == ASDimensionUnitFraction) { + NSCAssert(NO, @"Cannot get preferredSize of element with fractional width. Width: %@.", NSStringFromASDimension(size.width)); + return CGSizeZero; + } + + if (size.height.unit == ASDimensionUnitFraction) { + NSCAssert(NO, @"Cannot get preferredSize of element with fractional height. Height: %@.", NSStringFromASDimension(size.height)); + return CGSizeZero; + } + + return CGSizeMake(size.width.value, size.height.value); +} + +- (void)setMinSize:(CGSize)minSize +{ + BOOL changed = ASLayoutElementStyleSetSizeWithScope({ + newSize.minWidth = ASDimensionMakeWithPoints(minSize.width); + newSize.minHeight = ASDimensionMakeWithPoints(minSize.height); + }); + if (changed) { + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMinWidthProperty); + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMinHeightProperty); + } +} + +- (void)setMaxSize:(CGSize)maxSize +{ + BOOL changed = ASLayoutElementStyleSetSizeWithScope({ + newSize.maxWidth = ASDimensionMakeWithPoints(maxSize.width); + newSize.maxHeight = ASDimensionMakeWithPoints(maxSize.height); + }); + if (changed) { + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMaxWidthProperty); + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMaxHeightProperty); + } +} + +- (ASLayoutSize)preferredLayoutSize +{ + ASLayoutElementSize size = _size.load(); + return ASLayoutSizeMake(size.width, size.height); +} + +- (void)setPreferredLayoutSize:(ASLayoutSize)preferredLayoutSize +{ + BOOL changed = ASLayoutElementStyleSetSizeWithScope({ + newSize.width = preferredLayoutSize.width; + newSize.height = preferredLayoutSize.height; + }); + if (changed) { + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleWidthProperty); + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleHeightProperty); + } +} + +- (ASLayoutSize)minLayoutSize +{ + ASLayoutElementSize size = _size.load(); + return ASLayoutSizeMake(size.minWidth, size.minHeight); +} + +- (void)setMinLayoutSize:(ASLayoutSize)minLayoutSize +{ + BOOL changed = ASLayoutElementStyleSetSizeWithScope({ + newSize.minWidth = minLayoutSize.width; + newSize.minHeight = minLayoutSize.height; + }); + if (changed) { + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMinWidthProperty); + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMinHeightProperty); + } +} + +- (ASLayoutSize)maxLayoutSize +{ + ASLayoutElementSize size = _size.load(); + return ASLayoutSizeMake(size.maxWidth, size.maxHeight); +} + +- (void)setMaxLayoutSize:(ASLayoutSize)maxLayoutSize +{ + BOOL changed = ASLayoutElementStyleSetSizeWithScope({ + newSize.maxWidth = maxLayoutSize.width; + newSize.maxHeight = maxLayoutSize.height; + }); + if (changed) { + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMaxWidthProperty); + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMaxHeightProperty); + } +} + +#pragma mark - ASStackLayoutElement + +- (void)setSpacingBefore:(CGFloat)spacingBefore +{ + if (_spacingBefore.exchange(spacingBefore) != spacingBefore) { + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleSpacingBeforeProperty); + } +} + +- (CGFloat)spacingBefore +{ + return _spacingBefore.load(); +} + +- (void)setSpacingAfter:(CGFloat)spacingAfter +{ + if (_spacingAfter.exchange(spacingAfter) != spacingAfter) { + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleSpacingAfterProperty); + } +} + +- (CGFloat)spacingAfter +{ + return _spacingAfter.load(); +} + +- (void)setFlexGrow:(CGFloat)flexGrow +{ + if (_flexGrow.exchange(flexGrow) != flexGrow) { + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleFlexGrowProperty); + } +} + +- (CGFloat)flexGrow +{ + return _flexGrow.load(); +} + +- (void)setFlexShrink:(CGFloat)flexShrink +{ + if (_flexShrink.exchange(flexShrink) != flexShrink) { + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleFlexShrinkProperty); + } +} + +- (CGFloat)flexShrink +{ + return _flexShrink.load(); +} + +- (void)setFlexBasis:(ASDimension)flexBasis +{ + if (!ASDimensionEqualToDimension(_flexBasis.exchange(flexBasis), flexBasis)) { + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleFlexBasisProperty); + } +} + +- (ASDimension)flexBasis +{ + return _flexBasis.load(); +} + +- (void)setAlignSelf:(ASStackLayoutAlignSelf)alignSelf +{ + if (_alignSelf.exchange(alignSelf) != alignSelf) { + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleAlignSelfProperty); + } +} + +- (ASStackLayoutAlignSelf)alignSelf +{ + return _alignSelf.load(); +} + +- (void)setAscender:(CGFloat)ascender +{ + if (_ascender.exchange(ascender) != ascender) { + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleAscenderProperty); + } +} + +- (CGFloat)ascender +{ + return _ascender.load(); +} + +- (void)setDescender:(CGFloat)descender +{ + if (_descender.exchange(descender) != descender) { + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleDescenderProperty); + } +} + +- (CGFloat)descender +{ + return _descender.load(); +} + +#pragma mark - ASAbsoluteLayoutElement + +- (void)setLayoutPosition:(CGPoint)layoutPosition +{ + if (!CGPointEqualToPoint(_layoutPosition.exchange(layoutPosition), layoutPosition)) { + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleLayoutPositionProperty); + } +} + +- (CGPoint)layoutPosition +{ + return _layoutPosition.load(); +} + +#pragma mark - Extensions + +- (void)setLayoutOptionExtensionBool:(BOOL)value atIndex:(int)idx +{ + NSCAssert(idx < kMaxLayoutElementBoolExtensions, @"Setting index outside of max bool extensions space"); + + MutexLocker l(__instanceLock__); + _extensions.boolExtensions[idx] = value; +} + +- (BOOL)layoutOptionExtensionBoolAtIndex:(int)idx\ +{ + NSCAssert(idx < kMaxLayoutElementBoolExtensions, @"Accessing index outside of max bool extensions space"); + + MutexLocker l(__instanceLock__); + return _extensions.boolExtensions[idx]; +} + +- (void)setLayoutOptionExtensionInteger:(NSInteger)value atIndex:(int)idx +{ + NSCAssert(idx < kMaxLayoutElementStateIntegerExtensions, @"Setting index outside of max integer extensions space"); + + MutexLocker l(__instanceLock__); + _extensions.integerExtensions[idx] = value; +} + +- (NSInteger)layoutOptionExtensionIntegerAtIndex:(int)idx +{ + NSCAssert(idx < kMaxLayoutElementStateIntegerExtensions, @"Accessing index outside of max integer extensions space"); + + MutexLocker l(__instanceLock__); + return _extensions.integerExtensions[idx]; +} + +- (void)setLayoutOptionExtensionEdgeInsets:(UIEdgeInsets)value atIndex:(int)idx +{ + NSCAssert(idx < kMaxLayoutElementStateEdgeInsetExtensions, @"Setting index outside of max edge insets extensions space"); + + MutexLocker l(__instanceLock__); + _extensions.edgeInsetsExtensions[idx] = value; +} + +- (UIEdgeInsets)layoutOptionExtensionEdgeInsetsAtIndex:(int)idx +{ + NSCAssert(idx < kMaxLayoutElementStateEdgeInsetExtensions, @"Accessing index outside of max edge insets extensions space"); + + MutexLocker l(__instanceLock__); + return _extensions.edgeInsetsExtensions[idx]; +} + +#pragma mark - Debugging + +- (NSString *)description +{ + return ASObjectDescriptionMake(self, [self propertiesForDescription]); +} + +- (NSMutableArray *)propertiesForDescription +{ + NSMutableArray *result = [NSMutableArray array]; + + if ((self.minLayoutSize.width.unit != ASDimensionUnitAuto || + self.minLayoutSize.height.unit != ASDimensionUnitAuto)) { + [result addObject:@{ @"minLayoutSize" : NSStringFromASLayoutSize(self.minLayoutSize) }]; + } + + if ((self.preferredLayoutSize.width.unit != ASDimensionUnitAuto || + self.preferredLayoutSize.height.unit != ASDimensionUnitAuto)) { + [result addObject:@{ @"preferredSize" : NSStringFromASLayoutSize(self.preferredLayoutSize) }]; + } + + if ((self.maxLayoutSize.width.unit != ASDimensionUnitAuto || + self.maxLayoutSize.height.unit != ASDimensionUnitAuto)) { + [result addObject:@{ @"maxLayoutSize" : NSStringFromASLayoutSize(self.maxLayoutSize) }]; + } + + if (self.alignSelf != ASStackLayoutAlignSelfAuto) { + [result addObject:@{ @"alignSelf" : [@[@"ASStackLayoutAlignSelfAuto", + @"ASStackLayoutAlignSelfStart", + @"ASStackLayoutAlignSelfEnd", + @"ASStackLayoutAlignSelfCenter", + @"ASStackLayoutAlignSelfStretch"] objectAtIndex:self.alignSelf] }]; + } + + if (self.ascender != 0) { + [result addObject:@{ @"ascender" : @(self.ascender) }]; + } + + if (self.descender != 0) { + [result addObject:@{ @"descender" : @(self.descender) }]; + } + + if (ASDimensionEqualToDimension(self.flexBasis, ASDimensionAuto) == NO) { + [result addObject:@{ @"flexBasis" : NSStringFromASDimension(self.flexBasis) }]; + } + + if (self.flexGrow != 0) { + [result addObject:@{ @"flexGrow" : @(self.flexGrow) }]; + } + + if (self.flexShrink != 0) { + [result addObject:@{ @"flexShrink" : @(self.flexShrink) }]; + } + + if (self.spacingAfter != 0) { + [result addObject:@{ @"spacingAfter" : @(self.spacingAfter) }]; + } + + if (self.spacingBefore != 0) { + [result addObject:@{ @"spacingBefore" : @(self.spacingBefore) }]; + } + + if (CGPointEqualToPoint(self.layoutPosition, CGPointZero) == NO) { + [result addObject:@{ @"layoutPosition" : [NSValue valueWithCGPoint:self.layoutPosition] }]; + } + + return result; +} + +- (void)propertyDidChange:(NSString *)propertyName +{ +#if YOGA + /* TODO(appleguy): STYLE SETTER METHODS LEFT TO IMPLEMENT + void YGNodeStyleSetOverflow(YGNodeRef node, YGOverflow overflow); + void YGNodeStyleSetFlex(YGNodeRef node, float flex); + */ + + if (_yogaNode == NULL) { + return; + } + // Because the NSStrings used to identify each property are const, use efficient pointer comparison. + if (propertyName == ASLayoutElementStyleWidthProperty) { + YGNODE_STYLE_SET_DIMENSION(_yogaNode, Width, self.width); + } + else if (propertyName == ASLayoutElementStyleMinWidthProperty) { + YGNODE_STYLE_SET_DIMENSION(_yogaNode, MinWidth, self.minWidth); + } + else if (propertyName == ASLayoutElementStyleMaxWidthProperty) { + YGNODE_STYLE_SET_DIMENSION(_yogaNode, MaxWidth, self.maxWidth); + } + else if (propertyName == ASLayoutElementStyleHeightProperty) { + YGNODE_STYLE_SET_DIMENSION(_yogaNode, Height, self.height); + } + else if (propertyName == ASLayoutElementStyleMinHeightProperty) { + YGNODE_STYLE_SET_DIMENSION(_yogaNode, MinHeight, self.minHeight); + } + else if (propertyName == ASLayoutElementStyleMaxHeightProperty) { + YGNODE_STYLE_SET_DIMENSION(_yogaNode, MaxHeight, self.maxHeight); + } + else if (propertyName == ASLayoutElementStyleFlexGrowProperty) { + YGNodeStyleSetFlexGrow(_yogaNode, self.flexGrow); + } + else if (propertyName == ASLayoutElementStyleFlexShrinkProperty) { + YGNodeStyleSetFlexShrink(_yogaNode, self.flexShrink); + } + else if (propertyName == ASLayoutElementStyleFlexBasisProperty) { + YGNODE_STYLE_SET_DIMENSION(_yogaNode, FlexBasis, self.flexBasis); + } + else if (propertyName == ASLayoutElementStyleAlignSelfProperty) { + YGNodeStyleSetAlignSelf(_yogaNode, yogaAlignSelf(self.alignSelf)); + } + else if (propertyName == ASYogaFlexWrapProperty) { + YGNodeStyleSetFlexWrap(_yogaNode, self.flexWrap); + } + else if (propertyName == ASYogaFlexDirectionProperty) { + YGNodeStyleSetFlexDirection(_yogaNode, yogaFlexDirection(self.flexDirection)); + } + else if (propertyName == ASYogaDirectionProperty) { + YGNodeStyleSetDirection(_yogaNode, self.direction); + } + else if (propertyName == ASYogaJustifyContentProperty) { + YGNodeStyleSetJustifyContent(_yogaNode, yogaJustifyContent(self.justifyContent)); + } + else if (propertyName == ASYogaAlignItemsProperty) { + ASStackLayoutAlignItems alignItems = self.alignItems; + if (alignItems != ASStackLayoutAlignItemsNotSet) { + YGNodeStyleSetAlignItems(_yogaNode, yogaAlignItems(alignItems)); + } + } + else if (propertyName == ASYogaPositionTypeProperty) { + YGNodeStyleSetPositionType(_yogaNode, self.positionType); + } + else if (propertyName == ASYogaPositionProperty) { + ASEdgeInsets position = self.position; + YGEdge edge = YGEdgeLeft; + for (int i = 0; i < YGEdgeAll + 1; ++i) { + YGNODE_STYLE_SET_DIMENSION_WITH_EDGE(_yogaNode, Position, dimensionForEdgeWithEdgeInsets(edge, position), edge); + edge = (YGEdge)(edge + 1); + } + } + else if (propertyName == ASYogaMarginProperty) { + ASEdgeInsets margin = self.margin; + YGEdge edge = YGEdgeLeft; + for (int i = 0; i < YGEdgeAll + 1; ++i) { + YGNODE_STYLE_SET_DIMENSION_WITH_EDGE(_yogaNode, Margin, dimensionForEdgeWithEdgeInsets(edge, margin), edge); + edge = (YGEdge)(edge + 1); + } + } + else if (propertyName == ASYogaPaddingProperty) { + ASEdgeInsets padding = self.padding; + YGEdge edge = YGEdgeLeft; + for (int i = 0; i < YGEdgeAll + 1; ++i) { + YGNODE_STYLE_SET_DIMENSION_WITH_EDGE(_yogaNode, Padding, dimensionForEdgeWithEdgeInsets(edge, padding), edge); + edge = (YGEdge)(edge + 1); + } + } + else if (propertyName == ASYogaBorderProperty) { + ASEdgeInsets border = self.border; + YGEdge edge = YGEdgeLeft; + for (int i = 0; i < YGEdgeAll + 1; ++i) { + YGNODE_STYLE_SET_FLOAT_WITH_EDGE(_yogaNode, Border, dimensionForEdgeWithEdgeInsets(edge, border), edge); + edge = (YGEdge)(edge + 1); + } + } + else if (propertyName == ASYogaAspectRatioProperty) { + CGFloat aspectRatio = self.aspectRatio; + if (aspectRatio > FLT_EPSILON && aspectRatio < CGFLOAT_MAX / 2.0) { + YGNodeStyleSetAspectRatio(_yogaNode, aspectRatio); + } + } +#endif +} + +#pragma mark - Yoga Flexbox Properties + +#if YOGA + ++ (void)initialize +{ + [super initialize]; + YGConfigSetPointScaleFactor(YGConfigGetDefault(), ASScreenScale()); + // Yoga recommends using Web Defaults for all new projects. This will be enabled for Texture very soon. + //YGConfigSetUseWebDefaults(YGConfigGetDefault(), true); +} + +- (YGNodeRef)yogaNode +{ + return _yogaNode; +} + +- (YGNodeRef)yogaNodeCreateIfNeeded +{ + if (_yogaNode == NULL) { + _yogaNode = YGNodeNew(); + } + return _yogaNode; +} + +- (void)destroyYogaNode +{ + if (_yogaNode != NULL) { + // Release the __bridge_retained Context object. + ASLayoutElementYogaUpdateMeasureFunc(_yogaNode, nil); + YGNodeFree(_yogaNode); + _yogaNode = NULL; + } +} + +- (void)dealloc +{ + [self destroyYogaNode]; +} + +- (YGWrap)flexWrap { return _flexWrap.load(); } +- (ASStackLayoutDirection)flexDirection { return _flexDirection.load(); } +- (YGDirection)direction { return _direction.load(); } +- (ASStackLayoutJustifyContent)justifyContent { return _justifyContent.load(); } +- (ASStackLayoutAlignItems)alignItems { return _alignItems.load(); } +- (YGPositionType)positionType { return _positionType.load(); } +- (ASEdgeInsets)position { return _position.load(); } +- (ASEdgeInsets)margin { return _margin.load(); } +- (ASEdgeInsets)padding { return _padding.load(); } +- (ASEdgeInsets)border { return _border.load(); } +- (CGFloat)aspectRatio { return _aspectRatio.load(); } +// private (ASLayoutElementStylePrivate.h) +- (ASStackLayoutAlignItems)parentAlignStyle { + return _parentAlignStyle; +} + +- (void)setFlexWrap:(YGWrap)flexWrap { + if (_flexWrap.exchange(flexWrap) != flexWrap) { + ASLayoutElementStyleCallDelegate(ASYogaFlexWrapProperty); + } +} +- (void)setFlexDirection:(ASStackLayoutDirection)flexDirection { + if (_flexDirection.exchange(flexDirection) != flexDirection) { + ASLayoutElementStyleCallDelegate(ASYogaFlexDirectionProperty); + } +} +- (void)setDirection:(YGDirection)direction { + if (_direction.exchange(direction) != direction) { + ASLayoutElementStyleCallDelegate(ASYogaDirectionProperty); + } +} +- (void)setJustifyContent:(ASStackLayoutJustifyContent)justify { + if (_justifyContent.exchange(justify) != justify) { + ASLayoutElementStyleCallDelegate(ASYogaJustifyContentProperty); + } +} +- (void)setAlignItems:(ASStackLayoutAlignItems)alignItems { + if (_alignItems.exchange(alignItems) != alignItems) { + ASLayoutElementStyleCallDelegate(ASYogaAlignItemsProperty); + } +} +- (void)setPositionType:(YGPositionType)positionType { + if (_positionType.exchange(positionType) != positionType) { + ASLayoutElementStyleCallDelegate(ASYogaPositionTypeProperty); + } +} +/// TODO: smart compare ASEdgeInsets instead of memory compare. +- (void)setPosition:(ASEdgeInsets)position { + ASEdgeInsets oldValue = _position.exchange(position); + if (0 != memcmp(&position, &oldValue, sizeof(ASEdgeInsets))) { + ASLayoutElementStyleCallDelegate(ASYogaPositionProperty); + } +} +- (void)setMargin:(ASEdgeInsets)margin { + ASEdgeInsets oldValue = _margin.exchange(margin); + if (0 != memcmp(&margin, &oldValue, sizeof(ASEdgeInsets))) { + ASLayoutElementStyleCallDelegate(ASYogaMarginProperty); + } +} +- (void)setPadding:(ASEdgeInsets)padding { + ASEdgeInsets oldValue = _padding.exchange(padding); + if (0 != memcmp(&padding, &oldValue, sizeof(ASEdgeInsets))) { + ASLayoutElementStyleCallDelegate(ASYogaPaddingProperty); + } +} +- (void)setBorder:(ASEdgeInsets)border { + ASEdgeInsets oldValue = _border.exchange(border); + if (0 != memcmp(&border, &oldValue, sizeof(ASEdgeInsets))) { + ASLayoutElementStyleCallDelegate(ASYogaBorderProperty); + } +} +- (void)setAspectRatio:(CGFloat)aspectRatio { + if (_aspectRatio.exchange(aspectRatio) != aspectRatio) { + ASLayoutElementStyleCallDelegate(ASYogaAspectRatioProperty); + } +} +// private (ASLayoutElementStylePrivate.h) +- (void)setParentAlignStyle:(ASStackLayoutAlignItems)style { + _parentAlignStyle = style; +} + +#endif /* YOGA */ + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASLayoutElementExtensibility.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASLayoutElementExtensibility.h new file mode 100644 index 0000000..f46b63e --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASLayoutElementExtensibility.h @@ -0,0 +1,106 @@ +// +// ASLayoutElementExtensibility.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import + +#pragma mark - ASLayoutElementExtensibility + +@protocol ASLayoutElementExtensibility + +// The maximum number of extended values per type are defined in ASEnvironment.h above the ASEnvironmentStateExtensions +// struct definition. If you try to set a value at an index after the maximum it will throw an assertion. + +- (void)setLayoutOptionExtensionBool:(BOOL)value atIndex:(int)idx; +- (BOOL)layoutOptionExtensionBoolAtIndex:(int)idx; + +- (void)setLayoutOptionExtensionInteger:(NSInteger)value atIndex:(int)idx; +- (NSInteger)layoutOptionExtensionIntegerAtIndex:(int)idx; + +- (void)setLayoutOptionExtensionEdgeInsets:(UIEdgeInsets)value atIndex:(int)idx; +- (UIEdgeInsets)layoutOptionExtensionEdgeInsetsAtIndex:(int)idx; + +@end + +#pragma mark - Dynamic Properties + +/** + * Unbox NSNumber based on the type + */ +#define ASDK_UNBOX_NUMBER(NUMBER, PROPERTY_TYPE) \ +const char *objCType = [NUMBER objCType]; \ +if (strcmp(objCType, @encode(BOOL)) == 0) { \ + return (PROPERTY_TYPE)[obj boolValue]; \ +} else if (strcmp(objCType, @encode(int)) == 0) { \ + return (PROPERTY_TYPE)[obj intValue]; \ +} else if (strcmp(objCType, @encode(NSInteger)) == 0) { \ + return (PROPERTY_TYPE)[obj integerValue]; \ +} else if (strcmp(objCType, @encode(NSUInteger)) == 0) { \ + return (PROPERTY_TYPE)[obj unsignedIntegerValue]; \ +} else if (strcmp(objCType, @encode(CGFloat)) == 0) { \ + return (PROPERTY_TYPE)[obj floatValue]; \ +} else { \ + NSAssert(NO, @"Data type not supported"); \ +} \ + +/** + * Define a NSObject property + */ +#define ASDK_STYLE_PROP_OBJ(PROPERTY_TYPE, PROPERTY_NAME, SETTER_NAME) \ +@dynamic PROPERTY_NAME; \ +- (PROPERTY_TYPE)PROPERTY_NAME \ +{ \ + return (PROPERTY_TYPE)objc_getAssociatedObject(self, @selector(PROPERTY_NAME)); \ +} \ +\ +- (void)SETTER_NAME:(PROPERTY_TYPE)PROPERTY_NAME \ +{ \ + objc_setAssociatedObject(self, @selector(PROPERTY_NAME), PROPERTY_NAME, OBJC_ASSOCIATION_RETAIN); \ +} \ + +/** + * Define an primitive property + */ +#define ASDK_STYLE_PROP_PRIM(PROPERTY_TYPE, PROPERTY_NAME, SETTER_NAME, DEFAULT_VALUE) \ +@dynamic PROPERTY_NAME; \ +- (PROPERTY_TYPE)PROPERTY_NAME \ +{ \ + id obj = objc_getAssociatedObject(self, @selector(PROPERTY_NAME)); \ + \ + if (obj != nil) { \ + ASDK_UNBOX_NUMBER(obj, PROPERTY_TYPE); \ + } \ + \ + return DEFAULT_VALUE;\ +} \ +\ +- (void)SETTER_NAME:(PROPERTY_TYPE)PROPERTY_NAME \ +{ \ + objc_setAssociatedObject(self, @selector(PROPERTY_NAME), @(PROPERTY_NAME), OBJC_ASSOCIATION_RETAIN); \ +} \ + +/** + * Define an structure property + */ +#define ASDK_STYLE_PROP_STR(PROPERTY_TYPE, PROPERTY_NAME, SETTER_NAME, DEFAULT_STRUCT) \ +@dynamic PROPERTY_NAME; \ +- (PROPERTY_TYPE)PROPERTY_NAME \ +{ \ + id obj = objc_getAssociatedObject(self, @selector(PROPERTY_NAME)); \ + if (obj == nil) { \ + return DEFAULT_STRUCT; \ + } \ + PROPERTY_TYPE PROPERTY_NAME; [obj getValue:&PROPERTY_NAME]; return PROPERTY_NAME; \ +} \ +\ +- (void)SETTER_NAME:(PROPERTY_TYPE)PROPERTY_NAME \ +{ \ + objc_setAssociatedObject(self, @selector(PROPERTY_NAME), [NSValue value:&PROPERTY_NAME withObjCType:@encode(PROPERTY_TYPE)], OBJC_ASSOCIATION_RETAIN_NONATOMIC);\ +} \ diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASLayoutElementPrivate.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASLayoutElementPrivate.h new file mode 100644 index 0000000..bf8c05b --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASLayoutElementPrivate.h @@ -0,0 +1,106 @@ +// +// ASLayoutElementPrivate.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +@protocol ASLayoutElement; +@class ASLayoutElementStyle; + +#pragma mark - ASLayoutElementContext + +NS_ASSUME_NONNULL_BEGIN + +AS_SUBCLASSING_RESTRICTED +@interface ASLayoutElementContext : NSObject +@property (nonatomic) int32_t transitionID; +@end + +ASDK_EXTERN int32_t const ASLayoutElementContextInvalidTransitionID; + +ASDK_EXTERN int32_t const ASLayoutElementContextDefaultTransitionID; + +// Does not currently support nesting – there must be no current context. +ASDK_EXTERN void ASLayoutElementPushContext(ASLayoutElementContext * context); + +ASDK_EXTERN ASLayoutElementContext * _Nullable ASLayoutElementGetCurrentContext(void); + +ASDK_EXTERN void ASLayoutElementPopContext(void); + +NS_ASSUME_NONNULL_END + +#pragma mark - ASLayoutElementLayoutDefaults + +#define ASLayoutElementLayoutCalculationDefaults \ +- (ASLayout *)layoutThatFits:(ASSizeRange)constrainedSize\ +{\ + return [self layoutThatFits:constrainedSize parentSize:constrainedSize.max];\ +}\ +\ +- (ASLayout *)layoutThatFits:(ASSizeRange)constrainedSize parentSize:(CGSize)parentSize\ +{\ + return [self calculateLayoutThatFits:constrainedSize restrictedToSize:self.style.size relativeToParentSize:parentSize];\ +}\ +\ +- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize\ + restrictedToSize:(ASLayoutElementSize)size\ + relativeToParentSize:(CGSize)parentSize\ +{\ + const ASSizeRange resolvedRange = ASSizeRangeIntersect(constrainedSize, ASLayoutElementSizeResolve(self.style.size, parentSize));\ + return [self calculateLayoutThatFits:resolvedRange];\ +}\ + + +#pragma mark - ASLayoutElementExtensibility + +// Provides extension points for elments that comply to ASLayoutElement like ASLayoutSpec to add additional +// properties besides the default one provided in ASLayoutElementStyle + +static const int kMaxLayoutElementBoolExtensions = 1; +static const int kMaxLayoutElementStateIntegerExtensions = 4; +static const int kMaxLayoutElementStateEdgeInsetExtensions = 1; + +typedef struct ASLayoutElementStyleExtensions { + // Values to store extensions + BOOL boolExtensions[kMaxLayoutElementBoolExtensions]; + NSInteger integerExtensions[kMaxLayoutElementStateIntegerExtensions]; + UIEdgeInsets edgeInsetsExtensions[kMaxLayoutElementStateEdgeInsetExtensions]; +} ASLayoutElementStyleExtensions; + +#define ASLayoutElementStyleExtensibilityForwarding \ +- (void)setLayoutOptionExtensionBool:(BOOL)value atIndex:(int)idx\ +{\ + [self.style setLayoutOptionExtensionBool:value atIndex:idx];\ +}\ +\ +- (BOOL)layoutOptionExtensionBoolAtIndex:(int)idx\ +{\ + return [self.style layoutOptionExtensionBoolAtIndex:idx];\ +}\ +\ +- (void)setLayoutOptionExtensionInteger:(NSInteger)value atIndex:(int)idx\ +{\ + [self.style setLayoutOptionExtensionInteger:value atIndex:idx];\ +}\ +\ +- (NSInteger)layoutOptionExtensionIntegerAtIndex:(int)idx\ +{\ + return [self.style layoutOptionExtensionIntegerAtIndex:idx];\ +}\ +\ +- (void)setLayoutOptionExtensionEdgeInsets:(UIEdgeInsets)value atIndex:(int)idx\ +{\ + [self.style setLayoutOptionExtensionEdgeInsets:value atIndex:idx];\ +}\ +\ +- (UIEdgeInsets)layoutOptionExtensionEdgeInsetsAtIndex:(int)idx\ +{\ + return [self.style layoutOptionExtensionEdgeInsetsAtIndex:idx];\ +}\ + diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASLayoutSpec+Subclasses.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASLayoutSpec+Subclasses.h new file mode 100644 index 0000000..34bf6e0 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASLayoutSpec+Subclasses.h @@ -0,0 +1,59 @@ +// +// ASLayoutSpec+Subclasses.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@protocol ASLayoutElement; + +@interface ASLayoutSpec (Subclassing) + +/** + * Adds a child with the given identifier to this layout spec. + * + * @param child A child to be added. + * + * @param index An index associated with the child. + * + * @discussion Every ASLayoutSpec must act on at least one child. The ASLayoutSpec base class takes the + * responsibility of holding on to the spec children. Some layout specs, like ASInsetLayoutSpec, + * only require a single child. + * + * For layout specs that require a known number of children (ASBackgroundLayoutSpec, for example) + * a subclass can use the setChild method to set the "primary" child. It should then use this method + * to set any other required children. Ideally a subclass would hide this from the user, and use the + * setChild:forIndex: internally. For example, ASBackgroundLayoutSpec exposes a backgroundChild + * property that behind the scenes is calling setChild:forIndex:. + */ +- (void)setChild:(id)child atIndex:(NSUInteger)index; + +/** + * Returns the child added to this layout spec using the given index. + * + * @param index An identifier associated with the the child. + */ +- (nullable id)childAtIndex:(NSUInteger)index; + +@end + +@interface ASLayout () + +/** + * Position in parent. Default to CGPointNull. + * + * @discussion When being used as a sublayout, this property must not equal CGPointNull. + */ +@property (nonatomic) CGPoint position; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASLayoutSpec+Subclasses.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASLayoutSpec+Subclasses.mm new file mode 100644 index 0000000..fb9daf3 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASLayoutSpec+Subclasses.mm @@ -0,0 +1,86 @@ +// +// ASLayoutSpec+Subclasses.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import + +#pragma mark - ASNullLayoutSpec + +@interface ASNullLayoutSpec : ASLayoutSpec +- (instancetype)init NS_UNAVAILABLE; ++ (ASNullLayoutSpec *)null; +@end + +@implementation ASNullLayoutSpec : ASLayoutSpec + ++ (ASNullLayoutSpec *)null +{ + static ASNullLayoutSpec *sharedNullLayoutSpec = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + sharedNullLayoutSpec = [[self alloc] init]; + }); + return sharedNullLayoutSpec; +} + +- (BOOL)isMutable +{ + return NO; +} + +- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize +{ + return [ASLayout layoutWithLayoutElement:self size:CGSizeZero]; +} + +@end + + +#pragma mark - ASLayoutSpec (Subclassing) + +@implementation ASLayoutSpec (Subclassing) + +#pragma mark - Child with index + +- (void)setChild:(id)child atIndex:(NSUInteger)index +{ + ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); + + id layoutElement = child ?: [ASNullLayoutSpec null]; + + if (child) { + if (_childrenArray.count < index) { + // Fill up the array with null objects until the index + NSInteger i = _childrenArray.count; + while (i < index) { + _childrenArray[i] = [ASNullLayoutSpec null]; + i++; + } + } + } + + // Replace object at the given index with the layoutElement + _childrenArray[index] = layoutElement; +} + +- (id)childAtIndex:(NSUInteger)index +{ + id layoutElement = nil; + if (index < _childrenArray.count) { + layoutElement = _childrenArray[index]; + } + + // Null layoutElement should not be accessed + ASDisplayNodeAssert(layoutElement != [ASNullLayoutSpec null], @"Access child at index without set a child at that index"); + + return layoutElement; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASLayoutSpec.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASLayoutSpec.h new file mode 100644 index 0000000..c7aa01c --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASLayoutSpec.h @@ -0,0 +1,100 @@ +// +// ASLayoutSpec.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * A layout spec is an immutable object that describes a layout, loosely inspired by React. + */ +@interface ASLayoutSpec : NSObject + +/** + * Creation of a layout spec should only happen by a user in layoutSpecThatFits:. During that method, a + * layout spec can be created and mutated. Once it is passed back to ASDK, the isMutable flag will be + * set to NO and any further mutations will cause an assert. + */ +@property (nonatomic) BOOL isMutable; + +/** + * First child within the children's array. + * + * @discussion Every ASLayoutSpec must act on at least one child. The ASLayoutSpec base class takes the + * responsibility of holding on to the spec children. Some layout specs, like ASInsetLayoutSpec, + * only require a single child. + * + * For layout specs that require a known number of children (ASBackgroundLayoutSpec, for example) + * a subclass should use this method to set the "primary" child. It can then use setChild:atIndex: + * to set any other required children. Ideally a subclass would hide this from the user, and use the + * setChild:atIndex: internally. For example, ASBackgroundLayoutSpec exposes a "background" + * property that behind the scenes is calling setChild:atIndex:. + */ +@property (nullable, nonatomic) id child; + +/** + * An array of ASLayoutElement children + * + * @discussion Every ASLayoutSpec must act on at least one child. The ASLayoutSpec base class takes the + * reponsibility of holding on to the spec children. Some layout specs, like ASStackLayoutSpec, + * can take an unknown number of children. In this case, the this method should be used. + * For good measure, in these layout specs it probably makes sense to define + * setChild: and setChild:forIdentifier: methods to do something appropriate or to assert. + */ +@property (nullable, nonatomic) NSArray> *children; + +@end + +/** + * An ASLayoutSpec subclass that can wrap one or more ASLayoutElement and calculates the layout based on the + * sizes of the children. If multiple children are provided the size of the biggest child will be used to for + * size of this layout spec. + */ +@interface ASWrapperLayoutSpec : ASLayoutSpec + +/* + * Returns an ASWrapperLayoutSpec object with the given layoutElement as child. + */ ++ (instancetype)wrapperWithLayoutElement:(id)layoutElement NS_RETURNS_RETAINED AS_WARN_UNUSED_RESULT; + +/* + * Returns an ASWrapperLayoutSpec object with the given layoutElements as children. + */ ++ (instancetype)wrapperWithLayoutElements:(NSArray> *)layoutElements NS_RETURNS_RETAINED AS_WARN_UNUSED_RESULT; + +/* + * Returns an ASWrapperLayoutSpec object initialized with the given layoutElement as child. + */ +- (instancetype)initWithLayoutElement:(id)layoutElement AS_WARN_UNUSED_RESULT; + +/* + * Returns an ASWrapperLayoutSpec object initialized with the given layoutElements as children. + */ +- (instancetype)initWithLayoutElements:(NSArray> *)layoutElements AS_WARN_UNUSED_RESULT; + +/* + * Init not available for ASWrapperLayoutSpec + */ +- (instancetype)init NS_UNAVAILABLE; + +@end + +@interface ASLayoutSpec (Debugging) +/** + * Used by other layout specs to create ascii art debug strings + */ ++ (NSString *)asciiArtStringForChildren:(NSArray *)children parentName:(NSString *)parentName direction:(ASStackLayoutDirection)direction; ++ (NSString *)asciiArtStringForChildren:(NSArray *)children parentName:(NSString *)parentName; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASLayoutSpec.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASLayoutSpec.mm new file mode 100644 index 0000000..6d0ba62 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASLayoutSpec.mm @@ -0,0 +1,342 @@ +// +// ASLayoutSpec.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +#import + +#import +#import +#import + +@implementation ASLayoutSpec + +// Dynamic properties for ASLayoutElements +@dynamic layoutElementType; +@synthesize debugName = _debugName; + +#pragma mark - Lifecycle + +- (instancetype)init +{ + if (!(self = [super init])) { + return nil; + } + + _isMutable = YES; + _primitiveTraitCollection = ASPrimitiveTraitCollectionMakeDefault(); + _childrenArray = [[NSMutableArray alloc] init]; + + return self; +} + +- (ASLayoutElementType)layoutElementType +{ + return ASLayoutElementTypeLayoutSpec; +} + +- (BOOL)canLayoutAsynchronous +{ + return YES; +} + +- (BOOL)implementsLayoutMethod +{ + return YES; +} + +#pragma mark - Style + +- (ASLayoutElementStyle *)style +{ + AS::MutexLocker l(__instanceLock__); + if (_style == nil) { + _style = [[ASLayoutElementStyle alloc] init]; + } + return _style; +} + +- (instancetype)styledWithBlock:(AS_NOESCAPE void (^)(__kindof ASLayoutElementStyle *style))styleBlock +{ + styleBlock(self.style); + return self; +} + +#pragma mark - Layout + +ASLayoutElementLayoutCalculationDefaults + +- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize +{ + return [ASLayout layoutWithLayoutElement:self size:constrainedSize.min]; +} + +#pragma mark - Child + +- (void)setChild:(id)child +{ + ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); + ASDisplayNodeAssert(_childrenArray.count < 2, @"This layout spec does not support more than one child. Use the setChildren: or the setChild:AtIndex: API"); + + if (child) { + _childrenArray[0] = child; + } else { + if (_childrenArray.count) { + [_childrenArray removeObjectAtIndex:0]; + } + } +} + +- (id)child +{ + ASDisplayNodeAssert(_childrenArray.count < 2, @"This layout spec does not support more than one child. Use the setChildren: or the setChild:AtIndex: API"); + + return _childrenArray.firstObject; +} + +#pragma mark - Children + +- (void)setChildren:(NSArray> *)children +{ + ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); + +#if ASDISPLAYNODE_ASSERTIONS_ENABLED + for (id child in children) { + ASDisplayNodeAssert([child conformsToProtocol:NSProtocolFromString(@"ASLayoutElement")], @"Child %@ of spec %@ is not an ASLayoutElement!", child, self); + } +#endif + [_childrenArray setArray:children]; +} + +- (nullable NSArray> *)children +{ + return [_childrenArray copy]; +} + +- (NSArray> *)sublayoutElements +{ + return [_childrenArray copy]; +} + +#pragma mark - NSFastEnumeration + +- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id unowned _Nullable [_Nonnull])buffer count:(NSUInteger)len +{ + return [_childrenArray countByEnumeratingWithState:state objects:buffer count:len]; +} + +#pragma mark - ASTraitEnvironment + +- (ASTraitCollection *)asyncTraitCollection +{ + AS::MutexLocker l(__instanceLock__); + return [ASTraitCollection traitCollectionWithASPrimitiveTraitCollection:self.primitiveTraitCollection]; +} + +- (ASPrimitiveTraitCollection)primitiveTraitCollection +{ + AS::MutexLocker l(__instanceLock__); + return _primitiveTraitCollection; +} + +- (void)setPrimitiveTraitCollection:(ASPrimitiveTraitCollection)traitCollection +{ + AS::MutexLocker l(__instanceLock__); + _primitiveTraitCollection = traitCollection; +} + +#pragma mark - ASLayoutElementStyleExtensibility + +ASLayoutElementStyleExtensibilityForwarding + +#pragma mark - ASDescriptionProvider + +- (NSMutableArray *)propertiesForDescription +{ + const auto result = [NSMutableArray array]; + if (NSArray *children = self.children) { + // Use tiny descriptions because these trees can get nested very deep. + const auto tinyDescriptions = ASArrayByFlatMapping(children, id object, ASObjectDescriptionMakeTiny(object)); + [result addObject:@{ @"children": tinyDescriptions }]; + } + return result; +} + +- (NSString *)description +{ + return ASObjectDescriptionMake(self, [self propertiesForDescription]); +} + +#pragma mark - Framework Private + +#if AS_DEDUPE_LAYOUT_SPEC_TREE +- (nullable NSHashTable> *)findDuplicatedElementsInSubtree +{ + NSHashTable *result = nil; + NSUInteger count = 0; + [self _findDuplicatedElementsInSubtreeWithWorkingSet:[NSHashTable hashTableWithOptions:NSHashTableObjectPointerPersonality] workingCount:&count result:&result]; + return result; +} + +/** + * This method is extremely performance-sensitive, so we do some strange things. + * + * @param workingSet A working set of elements for use in the recursion. + * @param workingCount The current count of the set for use in the recursion. + * @param result The set into which to put the result. This initially points to @c nil to save time if no duplicates exist. + */ +- (void)_findDuplicatedElementsInSubtreeWithWorkingSet:(NSHashTable> *)workingSet workingCount:(NSUInteger *)workingCount result:(NSHashTable> * _Nullable *)result +{ + Class layoutSpecClass = [ASLayoutSpec class]; + + for (id child in self) { + // Add the object into the set. + [workingSet addObject:child]; + + // Check that addObject: caused the count to increase. + // This is faster than using containsObject. + NSUInteger oldCount = *workingCount; + NSUInteger newCount = workingSet.count; + BOOL objectAlreadyExisted = (newCount != oldCount + 1); + if (objectAlreadyExisted) { + if (*result == nil) { + *result = [NSHashTable hashTableWithOptions:NSHashTableObjectPointerPersonality]; + } + [*result addObject:child]; + } else { + *workingCount = newCount; + // If child is a layout spec we haven't visited, recurse its children. + if ([child isKindOfClass:layoutSpecClass]) { + [(ASLayoutSpec *)child _findDuplicatedElementsInSubtreeWithWorkingSet:workingSet workingCount:workingCount result:result]; + } + } + } +} +#endif + +#pragma mark - Debugging + +- (NSString *)debugName +{ + AS::MutexLocker l(__instanceLock__); + return _debugName; +} + +- (void)setDebugName:(NSString *)debugName +{ + AS::MutexLocker l(__instanceLock__); + if (!ASObjectIsEqual(_debugName, debugName)) { + _debugName = [debugName copy]; + } +} + +#pragma mark - ASLayoutElementAsciiArtProtocol + +- (NSString *)asciiArtString +{ + NSArray *children = self.children.count < 2 && self.child ? @[self.child] : self.children; + return [ASLayoutSpec asciiArtStringForChildren:children parentName:[self asciiArtName]]; +} + +- (NSString *)asciiArtName +{ + NSMutableString *result = [NSMutableString stringWithCString:object_getClassName(self) encoding:NSASCIIStringEncoding]; + if (_debugName) { + [result appendFormat:@" (%@)", _debugName]; + } + return result; +} + +ASSynthesizeLockingMethodsWithMutex(__instanceLock__) + +@end + +#pragma mark - ASWrapperLayoutSpec + +@implementation ASWrapperLayoutSpec + ++ (instancetype)wrapperWithLayoutElement:(id)layoutElement NS_RETURNS_RETAINED +{ + return [[self alloc] initWithLayoutElement:layoutElement]; +} + +- (instancetype)initWithLayoutElement:(id)layoutElement +{ + self = [super init]; + if (self) { + self.child = layoutElement; + } + return self; +} + ++ (instancetype)wrapperWithLayoutElements:(NSArray> *)layoutElements NS_RETURNS_RETAINED +{ + return [[self alloc] initWithLayoutElements:layoutElements]; +} + +- (instancetype)initWithLayoutElements:(NSArray> *)layoutElements +{ + self = [super init]; + if (self) { + self.children = layoutElements; + } + return self; +} + +- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize +{ + NSArray *children = self.children; + const auto count = children.count; + ASLayout *rawSublayouts[count]; + int i = 0; + + CGSize size = constrainedSize.min; + for (id child in children) { + ASLayout *sublayout = [child layoutThatFits:constrainedSize parentSize:constrainedSize.max]; + sublayout.position = CGPointZero; + + size.width = MAX(size.width, sublayout.size.width); + size.height = MAX(size.height, sublayout.size.height); + + rawSublayouts[i++] = sublayout; + } + const auto sublayouts = [NSArray arrayByTransferring:rawSublayouts count:i]; + return [ASLayout layoutWithLayoutElement:self size:size sublayouts:sublayouts]; +} + +@end + +#pragma mark - ASLayoutSpec (Debugging) + +@implementation ASLayoutSpec (Debugging) + +#pragma mark - ASCII Art Helpers + ++ (NSString *)asciiArtStringForChildren:(NSArray *)children parentName:(NSString *)parentName direction:(ASStackLayoutDirection)direction +{ + NSMutableArray *childStrings = [NSMutableArray array]; + for (id layoutChild in children) { + NSString *childString = [layoutChild asciiArtString]; + if (childString) { + [childStrings addObject:childString]; + } + } + if (direction == ASStackLayoutDirectionHorizontal) { + return [ASAsciiArtBoxCreator horizontalBoxStringForChildren:childStrings parent:parentName]; + } + return [ASAsciiArtBoxCreator verticalBoxStringForChildren:childStrings parent:parentName]; +} + ++ (NSString *)asciiArtStringForChildren:(NSArray *)children parentName:(NSString *)parentName +{ + return [self asciiArtStringForChildren:children parentName:parentName direction:ASStackLayoutDirectionHorizontal]; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASOverlayLayoutSpec.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASOverlayLayoutSpec.h new file mode 100644 index 0000000..a29a72b --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASOverlayLayoutSpec.h @@ -0,0 +1,34 @@ +// +// ASOverlayLayoutSpec.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + This layout spec lays out a single layoutElement child and then overlays a layoutElement object on top of it streched to its size + */ +@interface ASOverlayLayoutSpec : ASLayoutSpec + +/** + * Overlay layoutElement of this layout spec + */ +@property (nonatomic) id overlay; + +/** + * Creates and returns an ASOverlayLayoutSpec object with a given child and an layoutElement that act as overlay. + * + * @param child A child that is laid out to determine the size of this spec. + * @param overlay A layoutElement object that is laid out over the child. + */ ++ (instancetype)overlayLayoutSpecWithChild:(id)child overlay:(id)overlay NS_RETURNS_RETAINED AS_WARN_UNUSED_RESULT; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASOverlayLayoutSpec.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASOverlayLayoutSpec.mm new file mode 100644 index 0000000..8a3640d --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASOverlayLayoutSpec.mm @@ -0,0 +1,87 @@ +// +// ASOverlayLayoutSpec.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import + +static NSUInteger const kUnderlayChildIndex = 0; +static NSUInteger const kOverlayChildIndex = 1; + +@implementation ASOverlayLayoutSpec + +#pragma mark - Class + ++ (instancetype)overlayLayoutSpecWithChild:(id)child overlay:(id)overlay NS_RETURNS_RETAINED +{ + return [[self alloc] initWithChild:child overlay:overlay]; +} + +#pragma mark - Lifecycle + +- (instancetype)initWithChild:(id)child overlay:(id)overlay +{ + if (!(self = [super init])) { + return nil; + } + self.child = child; + self.overlay = overlay; + return self; +} + +#pragma mark - Setter / Getter + +- (void)setChild:(id)child +{ + ASDisplayNodeAssertNotNil(child, @"Child that will be overlayed on shouldn't be nil"); + [super setChild:child atIndex:kUnderlayChildIndex]; +} + +- (id)child +{ + return [super childAtIndex:kUnderlayChildIndex]; +} + +- (void)setOverlay:(id)overlay +{ + ASDisplayNodeAssertNotNil(overlay, @"Overlay cannot be nil"); + [super setChild:overlay atIndex:kOverlayChildIndex]; +} + +- (id)overlay +{ + return [super childAtIndex:kOverlayChildIndex]; +} + +#pragma mark - ASLayoutSpec + +/** + First layout the contents, then fit the overlay on top of it. + */ +- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize + restrictedToSize:(ASLayoutElementSize)size + relativeToParentSize:(CGSize)parentSize +{ + ASLayout *contentsLayout = [self.child layoutThatFits:constrainedSize parentSize:parentSize]; + contentsLayout.position = CGPointZero; + ASLayout *rawSublayouts[2]; + int i = 0; + rawSublayouts[i++] = contentsLayout; + if (self.overlay) { + ASLayout *overlayLayout = [self.overlay layoutThatFits:ASSizeRangeMake(contentsLayout.size) + parentSize:contentsLayout.size]; + overlayLayout.position = CGPointZero; + rawSublayouts[i++] = overlayLayout; + } + + const auto sublayouts = [NSArray arrayByTransferring:rawSublayouts count:i]; + return [ASLayout layoutWithLayoutElement:self size:contentsLayout.size sublayouts:sublayouts]; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASRatioLayoutSpec.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASRatioLayoutSpec.h new file mode 100644 index 0000000..b70e6fe --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASRatioLayoutSpec.h @@ -0,0 +1,42 @@ +// +// ASRatioLayoutSpec.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@protocol ASLayoutElement; + +/** + Ratio layout spec + For when the content should respect a certain inherent ratio but can be scaled (think photos or videos) + The ratio passed is the ratio of height / width you expect + + For a ratio 0.5, the spec will have a flat rectangle shape + _ _ _ _ + | | + |_ _ _ _| + + For a ratio 2.0, the spec will be twice as tall as it is wide + _ _ + | | + | | + | | + |_ _| + + **/ +@interface ASRatioLayoutSpec : ASLayoutSpec + +@property (nonatomic) CGFloat ratio; + ++ (instancetype)ratioLayoutSpecWithRatio:(CGFloat)ratio child:(id)child NS_RETURNS_RETAINED AS_WARN_UNUSED_RESULT; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASRatioLayoutSpec.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASRatioLayoutSpec.mm new file mode 100644 index 0000000..ceda856 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASRatioLayoutSpec.mm @@ -0,0 +1,102 @@ +// +// ASRatioLayoutSpec.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import +#import +#import + +#import + +#import + +#pragma mark - ASRatioLayoutSpec + +@implementation ASRatioLayoutSpec +{ + CGFloat _ratio; +} + +#pragma mark - Lifecycle + ++ (instancetype)ratioLayoutSpecWithRatio:(CGFloat)ratio child:(id)child NS_RETURNS_RETAINED +{ + return [[self alloc] initWithRatio:ratio child:child]; +} + +- (instancetype)initWithRatio:(CGFloat)ratio child:(id)child; +{ + if (!(self = [super init])) { + return nil; + } + + ASDisplayNodeAssertNotNil(child, @"Child cannot be nil"); + ASDisplayNodeAssert(ratio > 0, @"Ratio should be strictly positive, but received %f", ratio); + _ratio = ratio; + self.child = child; + + return self; +} + +#pragma mark - Setter / Getter + +- (void)setRatio:(CGFloat)ratio +{ + ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); + _ratio = ratio; +} + +#pragma mark - ASLayoutElement + +- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize +{ + std::vector sizeOptions; + + if (ASPointsValidForSize(constrainedSize.max.width)) { + sizeOptions.push_back(ASSizeRangeClamp(constrainedSize, { + constrainedSize.max.width, + ASFloorPixelValue(_ratio * constrainedSize.max.width) + })); + } + + if (ASPointsValidForSize(constrainedSize.max.height)) { + sizeOptions.push_back(ASSizeRangeClamp(constrainedSize, { + ASFloorPixelValue(constrainedSize.max.height / _ratio), + constrainedSize.max.height + })); + } + + // Choose the size closest to the desired ratio. + const auto &bestSize = std::max_element(sizeOptions.begin(), sizeOptions.end(), [&](const CGSize &a, const CGSize &b){ + return std::fabs((a.height / a.width) - _ratio) > std::fabs((b.height / b.width) - _ratio); + }); + + // If there is no max size in *either* dimension, we can't apply the ratio, so just pass our size range through. + const ASSizeRange childRange = (bestSize == sizeOptions.end()) ? constrainedSize : ASSizeRangeIntersect(constrainedSize, ASSizeRangeMake(*bestSize, *bestSize)); + const CGSize parentSize = (bestSize == sizeOptions.end()) ? ASLayoutElementParentSizeUndefined : *bestSize; + ASLayout *sublayout = [self.child layoutThatFits:childRange parentSize:parentSize]; + sublayout.position = CGPointZero; + return [ASLayout layoutWithLayoutElement:self size:sublayout.size sublayouts:@[sublayout]]; +} + +@end + +#pragma mark - ASRatioLayoutSpec (Debugging) + +@implementation ASRatioLayoutSpec (Debugging) + +#pragma mark - ASLayoutElementAsciiArtProtocol + +- (NSString *)asciiArtName +{ + return [NSString stringWithFormat:@"%@ (%.1f)", NSStringFromClass([self class]), self.ratio]; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASRelativeLayoutSpec.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASRelativeLayoutSpec.h new file mode 100644 index 0000000..9b260d9 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASRelativeLayoutSpec.h @@ -0,0 +1,87 @@ +// +// ASRelativeLayoutSpec.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +/** + * How the child is positioned within the spec. + * + * The default option will position the child at point 0. + * Swift: use [] for the default behavior. + */ +typedef NS_ENUM(NSUInteger, ASRelativeLayoutSpecPosition) { + /** The child is positioned at point 0 */ + ASRelativeLayoutSpecPositionNone = 0, + /** The child is positioned at point 0 relatively to the layout axis (ie left / top most) */ + ASRelativeLayoutSpecPositionStart = 1, + /** The child is centered along the specified axis */ + ASRelativeLayoutSpecPositionCenter = 2, + /** The child is positioned at the maximum point of the layout axis (ie right / bottom most) */ + ASRelativeLayoutSpecPositionEnd = 3, +}; + +/** + * How much space the spec will take up. + * + * The default option will allow the spec to take up the maximum size possible. + * Swift: use [] for the default behavior. + */ +typedef NS_OPTIONS(NSUInteger, ASRelativeLayoutSpecSizingOption) { + /** The spec will take up the maximum size possible */ + ASRelativeLayoutSpecSizingOptionDefault, + /** The spec will take up the minimum size possible along the X axis */ + ASRelativeLayoutSpecSizingOptionMinimumWidth = 1 << 0, + /** The spec will take up the minimum size possible along the Y axis */ + ASRelativeLayoutSpecSizingOptionMinimumHeight = 1 << 1, + /** Convenience option to take up the minimum size along both the X and Y axis */ + ASRelativeLayoutSpecSizingOptionMinimumSize = ASRelativeLayoutSpecSizingOptionMinimumWidth | ASRelativeLayoutSpecSizingOptionMinimumHeight, +}; + +NS_ASSUME_NONNULL_BEGIN + +/** Lays out a single layoutElement child and positions it within the layout bounds according to vertical and horizontal positional specifiers. + * Can position the child at any of the 4 corners, or the middle of any of the 4 edges, as well as the center - similar to "9-part" image areas. + */ +@interface ASRelativeLayoutSpec : ASLayoutSpec + +// You may create a spec with alloc / init, then set any non-default properties; or use a convenience initialize that accepts all properties. +@property (nonatomic) ASRelativeLayoutSpecPosition horizontalPosition; +@property (nonatomic) ASRelativeLayoutSpecPosition verticalPosition; +@property (nonatomic) ASRelativeLayoutSpecSizingOption sizingOption; + +/*! + * @discussion convenience constructor for a ASRelativeLayoutSpec + * @param horizontalPosition how to position the item on the horizontal (x) axis + * @param verticalPosition how to position the item on the vertical (y) axis + * @param sizingOption how much size to take up + * @param child the child to layout + * @return a configured ASRelativeLayoutSpec + */ ++ (instancetype)relativePositionLayoutSpecWithHorizontalPosition:(ASRelativeLayoutSpecPosition)horizontalPosition + verticalPosition:(ASRelativeLayoutSpecPosition)verticalPosition + sizingOption:(ASRelativeLayoutSpecSizingOption)sizingOption + child:(id)child NS_RETURNS_RETAINED AS_WARN_UNUSED_RESULT; + +/*! + * @discussion convenience initializer for a ASRelativeLayoutSpec + * @param horizontalPosition how to position the item on the horizontal (x) axis + * @param verticalPosition how to position the item on the vertical (y) axis + * @param sizingOption how much size to take up + * @param child the child to layout + * @return a configured ASRelativeLayoutSpec + */ +- (instancetype)initWithHorizontalPosition:(ASRelativeLayoutSpecPosition)horizontalPosition + verticalPosition:(ASRelativeLayoutSpecPosition)verticalPosition + sizingOption:(ASRelativeLayoutSpecSizingOption)sizingOption + child:(id)child; + +@end + +NS_ASSUME_NONNULL_END + diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASRelativeLayoutSpec.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASRelativeLayoutSpec.mm new file mode 100644 index 0000000..f096ce5 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASRelativeLayoutSpec.mm @@ -0,0 +1,104 @@ +// +// ASRelativeLayoutSpec.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import + +#import + +@implementation ASRelativeLayoutSpec + +- (instancetype)initWithHorizontalPosition:(ASRelativeLayoutSpecPosition)horizontalPosition verticalPosition:(ASRelativeLayoutSpecPosition)verticalPosition sizingOption:(ASRelativeLayoutSpecSizingOption)sizingOption child:(id)child +{ + if (!(self = [super init])) { + return nil; + } + ASDisplayNodeAssertNotNil(child, @"Child cannot be nil"); + _horizontalPosition = horizontalPosition; + _verticalPosition = verticalPosition; + _sizingOption = sizingOption; + [self setChild:child]; + return self; +} + ++ (instancetype)relativePositionLayoutSpecWithHorizontalPosition:(ASRelativeLayoutSpecPosition)horizontalPosition verticalPosition:(ASRelativeLayoutSpecPosition)verticalPosition sizingOption:(ASRelativeLayoutSpecSizingOption)sizingOption child:(id)child NS_RETURNS_RETAINED +{ + return [[self alloc] initWithHorizontalPosition:horizontalPosition verticalPosition:verticalPosition sizingOption:sizingOption child:child]; +} + +- (void)setHorizontalPosition:(ASRelativeLayoutSpecPosition)horizontalPosition +{ + ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); + _horizontalPosition = horizontalPosition; +} + +- (void)setVerticalPosition:(ASRelativeLayoutSpecPosition)verticalPosition { + ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); + _verticalPosition = verticalPosition; +} + +- (void)setSizingOption:(ASRelativeLayoutSpecSizingOption)sizingOption +{ + ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); + _sizingOption = sizingOption; +} + +- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize +{ + // If we have a finite size in any direction, pass this so that the child can resolve percentages against it. + // Otherwise pass ASLayoutElementParentDimensionUndefined as the size will depend on the content + CGSize size = { + ASPointsValidForSize(constrainedSize.max.width) == NO ? ASLayoutElementParentDimensionUndefined : constrainedSize.max.width, + ASPointsValidForSize(constrainedSize.max.height) == NO ? ASLayoutElementParentDimensionUndefined : constrainedSize.max.height + }; + + // Layout the child + const CGSize minChildSize = { + (_horizontalPosition != ASRelativeLayoutSpecPositionNone) ? 0 : constrainedSize.min.width, + (_verticalPosition != ASRelativeLayoutSpecPositionNone) ? 0 : constrainedSize.min.height, + }; + ASLayout *sublayout = [self.child layoutThatFits:ASSizeRangeMake(minChildSize, constrainedSize.max) parentSize:size]; + + // If we have an undetermined height or width, use the child size to define the layout size + size = ASSizeRangeClamp(constrainedSize, { + isfinite(size.width) == NO ? sublayout.size.width : size.width, + isfinite(size.height) == NO ? sublayout.size.height : size.height + }); + + // If minimum size options are set, attempt to shrink the size to the size of the child + size = ASSizeRangeClamp(constrainedSize, { + MIN(size.width, (_sizingOption & ASRelativeLayoutSpecSizingOptionMinimumWidth) != 0 ? sublayout.size.width : size.width), + MIN(size.height, (_sizingOption & ASRelativeLayoutSpecSizingOptionMinimumHeight) != 0 ? sublayout.size.height : size.height) + }); + + // Compute the position for the child on each axis according to layout parameters + CGFloat xPosition = [self proportionOfAxisForAxisPosition:_horizontalPosition]; + CGFloat yPosition = [self proportionOfAxisForAxisPosition:_verticalPosition]; + + sublayout.position = { + ASRoundPixelValue((size.width - sublayout.size.width) * xPosition), + ASRoundPixelValue((size.height - sublayout.size.height) * yPosition) + }; + + return [ASLayout layoutWithLayoutElement:self size:size sublayouts:@[sublayout]]; +} + +- (CGFloat)proportionOfAxisForAxisPosition:(ASRelativeLayoutSpecPosition)position +{ + if (position == ASRelativeLayoutSpecPositionCenter) { + return 0.5f; + } else if (position == ASRelativeLayoutSpecPositionEnd) { + return 1.0f; + } else { + return 0.0f; + } +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASStackLayoutDefines.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASStackLayoutDefines.h new file mode 100644 index 0000000..de05439 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASStackLayoutDefines.h @@ -0,0 +1,148 @@ +// +// ASStackLayoutDefines.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +/** The direction children are stacked in */ +typedef NS_ENUM(unsigned char, ASStackLayoutDirection) { + /** Children are stacked vertically */ + ASStackLayoutDirectionVertical, + /** Children are stacked horizontally */ + ASStackLayoutDirectionHorizontal, +#if YOGA + /** Children are stacked vertically, but in reverse. Only used by Yoga spec. */ + ASStackLayoutDirectionVerticalReverse, + /** Children are stacked horizontally, but in reverse. Only used by Yoga spec. */ + ASStackLayoutDirectionHorizontalReverse, +#endif +}; + +/** If no children are flexible, how should this spec justify its children in the available space? */ +typedef NS_ENUM(unsigned char, ASStackLayoutJustifyContent) { + /** + On overflow, children overflow out of this spec's bounds on the right/bottom side. + On underflow, children are left/top-aligned within this spec's bounds. + */ + ASStackLayoutJustifyContentStart, + /** + On overflow, children are centered and overflow on both sides. + On underflow, children are centered within this spec's bounds in the stacking direction. + */ + ASStackLayoutJustifyContentCenter, + /** + On overflow, children overflow out of this spec's bounds on the left/top side. + On underflow, children are right/bottom-aligned within this spec's bounds. + */ + ASStackLayoutJustifyContentEnd, + /** + On overflow or if the stack has only 1 child, this value is identical to ASStackLayoutJustifyContentStart. + Otherwise, the starting edge of the first child is at the starting edge of the stack, + the ending edge of the last child is at the ending edge of the stack, and the remaining children + are distributed so that the spacing between any two adjacent ones is the same. + If there is a remaining space after spacing division, it is combined with the last spacing (i.e the one between the last 2 children). + */ + ASStackLayoutJustifyContentSpaceBetween, + /** + On overflow or if the stack has only 1 child, this value is identical to ASStackLayoutJustifyContentCenter. + Otherwise, children are distributed such that the spacing between any two adjacent ones is the same, + and the spacing between the first/last child and the stack edges is half the size of the spacing between children. + If there is a remaining space after spacing division, it is combined with the last spacing (i.e the one between the last child and the stack ending edge). + */ + ASStackLayoutJustifyContentSpaceAround +}; + +/** Orientation of children along cross axis */ +typedef NS_ENUM(unsigned char, ASStackLayoutAlignItems) { + /** Align children to start of cross axis */ + ASStackLayoutAlignItemsStart, + /** Align children with end of cross axis */ + ASStackLayoutAlignItemsEnd, + /** Center children on cross axis */ + ASStackLayoutAlignItemsCenter, + /** Expand children to fill cross axis */ + ASStackLayoutAlignItemsStretch, + /** Children align to their first baseline. Only available for horizontal stack spec */ + ASStackLayoutAlignItemsBaselineFirst, + /** Children align to their last baseline. Only available for horizontal stack spec */ + ASStackLayoutAlignItemsBaselineLast, + ASStackLayoutAlignItemsNotSet +}; + +/** + Each child may override their parent stack's cross axis alignment. + @see ASStackLayoutAlignItems + */ +typedef NS_ENUM(unsigned char, ASStackLayoutAlignSelf) { + /** Inherit alignment value from containing stack. */ + ASStackLayoutAlignSelfAuto, + /** Align to start of cross axis */ + ASStackLayoutAlignSelfStart, + /** Align with end of cross axis */ + ASStackLayoutAlignSelfEnd, + /** Center on cross axis */ + ASStackLayoutAlignSelfCenter, + /** Expand to fill cross axis */ + ASStackLayoutAlignSelfStretch, +}; + +/** Whether children are stacked into a single or multiple lines. */ +typedef NS_ENUM(unsigned char, ASStackLayoutFlexWrap) { + ASStackLayoutFlexWrapNoWrap, + ASStackLayoutFlexWrapWrap, +}; + +/** Orientation of lines along cross axis if there are multiple lines. */ +typedef NS_ENUM(unsigned char, ASStackLayoutAlignContent) { + ASStackLayoutAlignContentStart, + ASStackLayoutAlignContentCenter, + ASStackLayoutAlignContentEnd, + ASStackLayoutAlignContentSpaceBetween, + ASStackLayoutAlignContentSpaceAround, + ASStackLayoutAlignContentStretch, +}; + +/** Orientation of children along horizontal axis */ +typedef NS_ENUM(unsigned char, ASHorizontalAlignment) { + /** No alignment specified. Default value */ + ASHorizontalAlignmentNone, + /** Left aligned */ + ASHorizontalAlignmentLeft, + /** Center aligned */ + ASHorizontalAlignmentMiddle, + /** Right aligned */ + ASHorizontalAlignmentRight, + + // After 2.0 has landed, we'll add ASDISPLAYNODE_DEPRECATED here - for now, avoid triggering errors for projects with -Werror + /** @deprecated Use ASHorizontalAlignmentLeft instead */ + ASAlignmentLeft ASDISPLAYNODE_DEPRECATED = ASHorizontalAlignmentLeft, + /** @deprecated Use ASHorizontalAlignmentMiddle instead */ + ASAlignmentMiddle ASDISPLAYNODE_DEPRECATED = ASHorizontalAlignmentMiddle, + /** @deprecated Use ASHorizontalAlignmentRight instead */ + ASAlignmentRight ASDISPLAYNODE_DEPRECATED = ASHorizontalAlignmentRight, +}; + +/** Orientation of children along vertical axis */ +typedef NS_ENUM(unsigned char, ASVerticalAlignment) { + /** No alignment specified. Default value */ + ASVerticalAlignmentNone, + /** Top aligned */ + ASVerticalAlignmentTop, + /** Center aligned */ + ASVerticalAlignmentCenter, + /** Bottom aligned */ + ASVerticalAlignmentBottom, + + // After 2.0 has landed, we'll add ASDISPLAYNODE_DEPRECATED here - for now, avoid triggering errors for projects with -Werror + /** @deprecated Use ASVerticalAlignmentTop instead */ + ASAlignmentTop ASDISPLAYNODE_DEPRECATED = ASVerticalAlignmentTop, + /** @deprecated Use ASVerticalAlignmentCenter instead */ + ASAlignmentCenter ASDISPLAYNODE_DEPRECATED = ASVerticalAlignmentCenter, + /** @deprecated Use ASVerticalAlignmentBottom instead */ + ASAlignmentBottom ASDISPLAYNODE_DEPRECATED = ASVerticalAlignmentBottom, +}; diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASStackLayoutElement.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASStackLayoutElement.h new file mode 100644 index 0000000..10e2d50 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASStackLayoutElement.h @@ -0,0 +1,73 @@ +// +// ASStackLayoutElement.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * Layout options that can be defined for an ASLayoutElement being added to a ASStackLayoutSpec. + */ +@protocol ASStackLayoutElement + +/** + * @abstract Additional space to place before this object in the stacking direction. + * Used when attached to a stack layout. + */ +@property (nonatomic) CGFloat spacingBefore; + +/** + * @abstract Additional space to place after this object in the stacking direction. + * Used when attached to a stack layout. + */ +@property (nonatomic) CGFloat spacingAfter; + +/** + * @abstract If the sum of children's stack dimensions is less than the minimum size, how much should this component grow? + * This value represents the "flex grow factor" and determines how much this component should grow in relation to any + * other flexible children. + */ +@property (nonatomic) CGFloat flexGrow; + +/** + * @abstract If the sum of children's stack dimensions is greater than the maximum size, how much should this component shrink? + * This value represents the "flex shrink factor" and determines how much this component should shink in relation to + * other flexible children. + */ +@property (nonatomic) CGFloat flexShrink; + +/** + * @abstract Specifies the initial size in the stack dimension for this object. + * Defaults to ASDimensionAuto. + * Used when attached to a stack layout. + */ +@property (nonatomic) ASDimension flexBasis; + +/** + * @abstract Orientation of the object along cross axis, overriding alignItems. + * Defaults to ASStackLayoutAlignSelfAuto. + * Used when attached to a stack layout. + */ +@property (nonatomic) ASStackLayoutAlignSelf alignSelf; + +/** + * @abstract Used for baseline alignment. The distance from the top of the object to its baseline. + */ +@property (nonatomic) CGFloat ascender; + +/** + * @abstract Used for baseline alignment. The distance from the baseline of the object to its bottom. + */ +@property (nonatomic) CGFloat descender; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASStackLayoutSpec.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASStackLayoutSpec.h new file mode 100644 index 0000000..ccc2084 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASStackLayoutSpec.h @@ -0,0 +1,133 @@ +// +// ASStackLayoutSpec.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + A simple layout spec that stacks a list of children vertically or horizontally. + + - All children are initially laid out with the an infinite available size in the stacking direction. + - In the other direction, this spec's constraint is passed. + - The children's sizes are summed in the stacking direction. + - If this sum is less than this spec's minimum size in stacking direction, children with flexGrow are flexed. + - If it is greater than this spec's maximum size in the stacking direction, children with flexShrink are flexed. + - If, even after flexing, the sum is still greater than this spec's maximum size in the stacking direction, + justifyContent determines how children are laid out. + + For example: + + - Suppose stacking direction is Vertical, min-width=100, max-width=300, min-height=200, max-height=500. + - All children are laid out with min-width=100, max-width=300, min-height=0, max-height=INFINITY. + - If the sum of the children's heights is less than 200, children with flexGrow are flexed larger. + - If the sum of the children's heights is greater than 500, children with flexShrink are flexed smaller. + Each child is shrunk by `((sum of heights) - 500)/(number of flexShrink-able children)`. + - If the sum of the children's heights is greater than 500 even after flexShrink-able children are flexed, + justifyContent determines how children are laid out. + */ +@interface ASStackLayoutSpec : ASLayoutSpec + +/** + Specifies the direction children are stacked in. If horizontalAlignment and verticalAlignment were set, + they will be resolved again, causing justifyContent and alignItems to be updated accordingly + */ +@property (nonatomic) ASStackLayoutDirection direction; +/** The amount of space between each child. */ +@property (nonatomic) CGFloat spacing; +/** + Specifies how children are aligned horizontally. Depends on the stack direction, setting the alignment causes either + justifyContent or alignItems to be updated. The alignment will remain valid after future direction changes. + Thus, it is preferred to those properties + */ +@property (nonatomic) ASHorizontalAlignment horizontalAlignment; +/** + Specifies how children are aligned vertically. Depends on the stack direction, setting the alignment causes either + justifyContent or alignItems to be updated. The alignment will remain valid after future direction changes. + Thus, it is preferred to those properties + */ +@property (nonatomic) ASVerticalAlignment verticalAlignment; +/** The amount of space between each child. Defaults to ASStackLayoutJustifyContentStart */ +@property (nonatomic) ASStackLayoutJustifyContent justifyContent; +/** Orientation of children along cross axis. Defaults to ASStackLayoutAlignItemsStretch */ +@property (nonatomic) ASStackLayoutAlignItems alignItems; +/** Whether children are stacked into a single or multiple lines. Defaults to single line (ASStackLayoutFlexWrapNoWrap) */ +@property (nonatomic) ASStackLayoutFlexWrap flexWrap; +/** Orientation of lines along cross axis if there are multiple lines. Defaults to ASStackLayoutAlignContentStart */ +@property (nonatomic) ASStackLayoutAlignContent alignContent; +/** If the stack spreads on multiple lines using flexWrap, the amount of space between lines. */ +@property (nonatomic) CGFloat lineSpacing; +/** Whether this stack can dispatch to other threads, regardless of which thread it's running on */ +@property (nonatomic, getter=isConcurrent) BOOL concurrent; + +- (instancetype)init; + +/** + @param direction The direction of the stack view (horizontal or vertical) + @param spacing The spacing between the children + @param justifyContent If no children are flexible, this describes how to fill any extra space + @param alignItems Orientation of the children along the cross axis + @param children ASLayoutElement children to be positioned. + */ ++ (instancetype)stackLayoutSpecWithDirection:(ASStackLayoutDirection)direction + spacing:(CGFloat)spacing + justifyContent:(ASStackLayoutJustifyContent)justifyContent + alignItems:(ASStackLayoutAlignItems)alignItems + children:(NSArray> *)children NS_RETURNS_RETAINED AS_WARN_UNUSED_RESULT; + +/** + @param direction The direction of the stack view (horizontal or vertical) + @param spacing The spacing between the children + @param justifyContent If no children are flexible, this describes how to fill any extra space + @param alignItems Orientation of the children along the cross axis + @param flexWrap Whether children are stacked into a single or multiple lines + @param alignContent Orientation of lines along cross axis if there are multiple lines + @param children ASLayoutElement children to be positioned. + */ ++ (instancetype)stackLayoutSpecWithDirection:(ASStackLayoutDirection)direction + spacing:(CGFloat)spacing + justifyContent:(ASStackLayoutJustifyContent)justifyContent + alignItems:(ASStackLayoutAlignItems)alignItems + flexWrap:(ASStackLayoutFlexWrap)flexWrap + alignContent:(ASStackLayoutAlignContent)alignContent + children:(NSArray> *)children NS_RETURNS_RETAINED AS_WARN_UNUSED_RESULT; + +/** + @param direction The direction of the stack view (horizontal or vertical) + @param spacing The spacing between the children + @param justifyContent If no children are flexible, this describes how to fill any extra space + @param alignItems Orientation of the children along the cross axis + @param flexWrap Whether children are stacked into a single or multiple lines + @param alignContent Orientation of lines along cross axis if there are multiple lines + @param lineSpacing The spacing between lines + @param children ASLayoutElement children to be positioned. + */ ++ (instancetype)stackLayoutSpecWithDirection:(ASStackLayoutDirection)direction + spacing:(CGFloat)spacing + justifyContent:(ASStackLayoutJustifyContent)justifyContent + alignItems:(ASStackLayoutAlignItems)alignItems + flexWrap:(ASStackLayoutFlexWrap)flexWrap + alignContent:(ASStackLayoutAlignContent)alignContent + lineSpacing:(CGFloat)lineSpacing + children:(NSArray> *)children NS_RETURNS_RETAINED AS_WARN_UNUSED_RESULT; + +/** + * @return A stack layout spec with direction of ASStackLayoutDirectionVertical + **/ ++ (instancetype)verticalStackLayoutSpec NS_RETURNS_RETAINED AS_WARN_UNUSED_RESULT; + +/** + * @return A stack layout spec with direction of ASStackLayoutDirectionHorizontal + **/ ++ (instancetype)horizontalStackLayoutSpec NS_RETURNS_RETAINED AS_WARN_UNUSED_RESULT; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASStackLayoutSpec.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASStackLayoutSpec.mm new file mode 100644 index 0000000..976cc02 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASStackLayoutSpec.mm @@ -0,0 +1,216 @@ +// +// ASStackLayoutSpec.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import +#import + +#import +#import +#import +#import +#import +#import + +@implementation ASStackLayoutSpec + +- (instancetype)init +{ + return [self initWithDirection:ASStackLayoutDirectionHorizontal spacing:0.0 justifyContent:ASStackLayoutJustifyContentStart alignItems:ASStackLayoutAlignItemsStretch flexWrap:ASStackLayoutFlexWrapNoWrap alignContent:ASStackLayoutAlignContentStart lineSpacing:0.0 children:nil]; +} + ++ (instancetype)stackLayoutSpecWithDirection:(ASStackLayoutDirection)direction spacing:(CGFloat)spacing justifyContent:(ASStackLayoutJustifyContent)justifyContent alignItems:(ASStackLayoutAlignItems)alignItems children:(NSArray *)children NS_RETURNS_RETAINED +{ + return [[self alloc] initWithDirection:direction spacing:spacing justifyContent:justifyContent alignItems:alignItems flexWrap:ASStackLayoutFlexWrapNoWrap alignContent:ASStackLayoutAlignContentStart lineSpacing: 0.0 children:children]; +} + ++ (instancetype)stackLayoutSpecWithDirection:(ASStackLayoutDirection)direction spacing:(CGFloat)spacing justifyContent:(ASStackLayoutJustifyContent)justifyContent alignItems:(ASStackLayoutAlignItems)alignItems flexWrap:(ASStackLayoutFlexWrap)flexWrap alignContent:(ASStackLayoutAlignContent)alignContent children:(NSArray> *)children NS_RETURNS_RETAINED +{ + return [[self alloc] initWithDirection:direction spacing:spacing justifyContent:justifyContent alignItems:alignItems flexWrap:flexWrap alignContent:alignContent lineSpacing:0.0 children:children]; +} + ++ (instancetype)stackLayoutSpecWithDirection:(ASStackLayoutDirection)direction spacing:(CGFloat)spacing justifyContent:(ASStackLayoutJustifyContent)justifyContent alignItems:(ASStackLayoutAlignItems)alignItems flexWrap:(ASStackLayoutFlexWrap)flexWrap alignContent:(ASStackLayoutAlignContent)alignContent lineSpacing:(CGFloat)lineSpacing children:(NSArray> *)children NS_RETURNS_RETAINED +{ + return [[self alloc] initWithDirection:direction spacing:spacing justifyContent:justifyContent alignItems:alignItems flexWrap:flexWrap alignContent:alignContent lineSpacing:lineSpacing children:children]; +} + ++ (instancetype)verticalStackLayoutSpec NS_RETURNS_RETAINED +{ + ASStackLayoutSpec *stackLayoutSpec = [[self alloc] init]; + stackLayoutSpec.direction = ASStackLayoutDirectionVertical; + return stackLayoutSpec; +} + ++ (instancetype)horizontalStackLayoutSpec NS_RETURNS_RETAINED +{ + ASStackLayoutSpec *stackLayoutSpec = [[self alloc] init]; + stackLayoutSpec.direction = ASStackLayoutDirectionHorizontal; + return stackLayoutSpec; +} + +- (instancetype)initWithDirection:(ASStackLayoutDirection)direction spacing:(CGFloat)spacing justifyContent:(ASStackLayoutJustifyContent)justifyContent alignItems:(ASStackLayoutAlignItems)alignItems flexWrap:(ASStackLayoutFlexWrap)flexWrap alignContent:(ASStackLayoutAlignContent)alignContent lineSpacing:(CGFloat)lineSpacing children:(NSArray *)children +{ + if (!(self = [super init])) { + return nil; + } + _direction = direction; + _spacing = spacing; + _horizontalAlignment = ASHorizontalAlignmentNone; + _verticalAlignment = ASVerticalAlignmentNone; + _alignItems = alignItems; + _justifyContent = justifyContent; + _flexWrap = flexWrap; + _alignContent = alignContent; + _lineSpacing = lineSpacing; + + [self setChildren:children]; + return self; +} + +- (void)setDirection:(ASStackLayoutDirection)direction +{ + ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); + if (_direction != direction) { + _direction = direction; + [self resolveHorizontalAlignment]; + [self resolveVerticalAlignment]; + } +} + +- (void)setHorizontalAlignment:(ASHorizontalAlignment)horizontalAlignment +{ + ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); + if (_horizontalAlignment != horizontalAlignment) { + _horizontalAlignment = horizontalAlignment; + [self resolveHorizontalAlignment]; + } +} + +- (void)setVerticalAlignment:(ASVerticalAlignment)verticalAlignment +{ + ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); + if (_verticalAlignment != verticalAlignment) { + _verticalAlignment = verticalAlignment; + [self resolveVerticalAlignment]; + } +} + +- (void)setAlignItems:(ASStackLayoutAlignItems)alignItems +{ + ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); + ASDisplayNodeAssert(_horizontalAlignment == ASHorizontalAlignmentNone, @"Cannot set this property directly because horizontalAlignment is being used"); + ASDisplayNodeAssert(_verticalAlignment == ASVerticalAlignmentNone, @"Cannot set this property directly because verticalAlignment is being used"); + _alignItems = alignItems; +} + +- (void)setJustifyContent:(ASStackLayoutJustifyContent)justifyContent +{ + ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); + ASDisplayNodeAssert(_horizontalAlignment == ASHorizontalAlignmentNone, @"Cannot set this property directly because horizontalAlignment is being used"); + ASDisplayNodeAssert(_verticalAlignment == ASVerticalAlignmentNone, @"Cannot set this property directly because verticalAlignment is being used"); + _justifyContent = justifyContent; +} + +- (void)setSpacing:(CGFloat)spacing +{ + ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); + _spacing = spacing; +} + +- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize +{ + NSArray *children = self.children; + if (children.count == 0) { + return [ASLayout layoutWithLayoutElement:self size:constrainedSize.min]; + } + + as_activity_scope_verbose(as_activity_create("Calculate stack layout", AS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT)); + as_log_verbose(ASLayoutLog(), "Stack layout %@", self); + // Accessing the style and size property is pretty costly we create layout spec children we use to figure + // out the layout for each child + const auto stackChildren = AS::map(children, [&](const id child) -> ASStackLayoutSpecChild { + ASLayoutElementStyle *style = child.style; + return {child, style, style.size}; + }); + + const ASStackLayoutSpecStyle style = {.direction = _direction, .spacing = _spacing, .justifyContent = _justifyContent, .alignItems = _alignItems, .flexWrap = _flexWrap, .alignContent = _alignContent, .lineSpacing = _lineSpacing}; + + const auto unpositionedLayout = ASStackUnpositionedLayout::compute(stackChildren, style, constrainedSize, _concurrent); + const auto positionedLayout = ASStackPositionedLayout::compute(unpositionedLayout, style, constrainedSize); + + if (style.direction == ASStackLayoutDirectionVertical) { + self.style.ascender = stackChildren.front().style.ascender; + self.style.descender = stackChildren.back().style.descender; + } + + ASLayout *rawSublayouts[positionedLayout.items.size()]; + int i = 0; + for (const auto &item : positionedLayout.items) { + rawSublayouts[i++] = item.layout; + } + + const auto sublayouts = [NSArray arrayByTransferring:rawSublayouts count:i]; + return [ASLayout layoutWithLayoutElement:self size:positionedLayout.size sublayouts:sublayouts]; +} + +- (void)resolveHorizontalAlignment +{ + if (_direction == ASStackLayoutDirectionHorizontal) { + _justifyContent = justifyContent(_horizontalAlignment, _justifyContent); + } else { + _alignItems = alignment(_horizontalAlignment, _alignItems); + } +} + +- (void)resolveVerticalAlignment +{ + if (_direction == ASStackLayoutDirectionHorizontal) { + _alignItems = alignment(_verticalAlignment, _alignItems); + } else { + _justifyContent = justifyContent(_verticalAlignment, _justifyContent); + } +} + +- (NSMutableArray *)propertiesForDescription +{ + auto result = [super propertiesForDescription]; + + // Add our direction + switch (self.direction) { + case ASStackLayoutDirectionVertical: + [result insertObject:@{ (id)kCFNull: @"vertical" } atIndex:0]; + break; + case ASStackLayoutDirectionHorizontal: + [result insertObject:@{ (id)kCFNull: @"horizontal" } atIndex:0]; + break; +#if YOGA + case ASStackLayoutDirectionVerticalReverse: + case ASStackLayoutDirectionHorizontalReverse: + // Currently not handled. + ASDisplayNodeFailAssert(@"Reverse directions not implemented."); + break; +#endif + } + + return result; +} + +@end + +@implementation ASStackLayoutSpec (Debugging) + +#pragma mark - ASLayoutElementAsciiArtProtocol + +- (NSString *)asciiArtString +{ + return [ASLayoutSpec asciiArtStringForChildren:self.children parentName:[self asciiArtName] direction:self.direction]; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASYogaUtilities.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASYogaUtilities.h new file mode 100644 index 0000000..d5529fc --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASYogaUtilities.h @@ -0,0 +1,78 @@ +// +// ASYogaUtilities.h +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#if YOGA /* YOGA */ + +#import +#import +#import + +// Should pass a string literal, not an NSString as the first argument to ASYogaLog +#define ASYogaLog(x, ...) as_log_verbose(ASLayoutLog(), x, ##__VA_ARGS__); + +@interface ASDisplayNode (YogaHelpers) + ++ (ASDisplayNode *)yogaNode; ++ (ASDisplayNode *)yogaSpacerNode; ++ (ASDisplayNode *)yogaVerticalStack; ++ (ASDisplayNode *)yogaHorizontalStack; + +@end + +// pre-order, depth-first +ASDK_EXTERN void ASDisplayNodePerformBlockOnEveryYogaChild(ASDisplayNode *node, void(^block)(ASDisplayNode *node)); + +#pragma mark - Yoga Type Conversion Helpers + +ASDK_EXTERN YGAlign yogaAlignItems(ASStackLayoutAlignItems alignItems); +ASDK_EXTERN YGJustify yogaJustifyContent(ASStackLayoutJustifyContent justifyContent); +ASDK_EXTERN YGAlign yogaAlignSelf(ASStackLayoutAlignSelf alignSelf); +ASDK_EXTERN YGFlexDirection yogaFlexDirection(ASStackLayoutDirection direction); +ASDK_EXTERN float yogaFloatForCGFloat(CGFloat value); +ASDK_EXTERN float yogaDimensionToPoints(ASDimension dimension); +ASDK_EXTERN float yogaDimensionToPercent(ASDimension dimension); +ASDK_EXTERN ASDimension dimensionForEdgeWithEdgeInsets(YGEdge edge, ASEdgeInsets insets); + +ASDK_EXTERN void ASLayoutElementYogaUpdateMeasureFunc(YGNodeRef yogaNode, id layoutElement); +ASDK_EXTERN float ASLayoutElementYogaBaselineFunc(YGNodeRef yogaNode, const float width, const float height); +ASDK_EXTERN YGSize ASLayoutElementYogaMeasureFunc(YGNodeRef yogaNode, + float width, YGMeasureMode widthMode, + float height, YGMeasureMode heightMode); + +#pragma mark - Yoga Style Setter Helpers + +#define YGNODE_STYLE_SET_DIMENSION(yogaNode, property, dimension) \ + if (dimension.unit == ASDimensionUnitPoints) { \ + YGNodeStyleSet##property(yogaNode, yogaDimensionToPoints(dimension)); \ + } else if (dimension.unit == ASDimensionUnitFraction) { \ + YGNodeStyleSet##property##Percent(yogaNode, yogaDimensionToPercent(dimension)); \ + } else { \ + YGNodeStyleSet##property(yogaNode, YGUndefined); \ + }\ + +#define YGNODE_STYLE_SET_DIMENSION_WITH_EDGE(yogaNode, property, dimension, edge) \ + if (dimension.unit == ASDimensionUnitPoints) { \ + YGNodeStyleSet##property(yogaNode, edge, yogaDimensionToPoints(dimension)); \ + } else if (dimension.unit == ASDimensionUnitFraction) { \ + YGNodeStyleSet##property##Percent(yogaNode, edge, yogaDimensionToPercent(dimension)); \ + } else { \ + YGNodeStyleSet##property(yogaNode, edge, YGUndefined); \ + } \ + +#define YGNODE_STYLE_SET_FLOAT_WITH_EDGE(yogaNode, property, dimension, edge) \ + if (dimension.unit == ASDimensionUnitPoints) { \ + YGNodeStyleSet##property(yogaNode, edge, yogaDimensionToPoints(dimension)); \ + } else if (dimension.unit == ASDimensionUnitFraction) { \ + ASDisplayNodeAssert(NO, @"Unexpected Fraction value in applying ##property## values to YGNode"); \ + } else { \ + YGNodeStyleSet##property(yogaNode, edge, YGUndefined); \ + } \ + +#endif /* YOGA */ diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASYogaUtilities.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASYogaUtilities.mm new file mode 100644 index 0000000..68beede --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Layout/ASYogaUtilities.mm @@ -0,0 +1,249 @@ +// +// ASYogaUtilities.mm +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#if YOGA /* YOGA */ + +@implementation ASDisplayNode (YogaHelpers) + ++ (ASDisplayNode *)yogaNode +{ + ASDisplayNode *node = [[ASDisplayNode alloc] init]; + node.automaticallyManagesSubnodes = YES; + [node.style yogaNodeCreateIfNeeded]; + return node; +} + ++ (ASDisplayNode *)yogaSpacerNode +{ + ASDisplayNode *node = [ASDisplayNode yogaNode]; + node.style.flexGrow = 1.0f; + return node; +} + ++ (ASDisplayNode *)yogaVerticalStack +{ + ASDisplayNode *node = [self yogaNode]; + node.style.flexDirection = ASStackLayoutDirectionVertical; + return node; +} + ++ (ASDisplayNode *)yogaHorizontalStack +{ + ASDisplayNode *node = [self yogaNode]; + node.style.flexDirection = ASStackLayoutDirectionHorizontal; + return node; +} + +@end + +void ASDisplayNodePerformBlockOnEveryYogaChild(ASDisplayNode *node, void(^block)(ASDisplayNode *node)) +{ + if (node == nil) { + return; + } + block(node); + // We use the accessor here despite the copy, because the block may modify the yoga tree e.g. + // replacing a node. + for (ASDisplayNode *child in node.yogaChildren) { + ASDisplayNodePerformBlockOnEveryYogaChild(child, block); + } +} + +#pragma mark - Yoga Type Conversion Helpers + +YGAlign yogaAlignItems(ASStackLayoutAlignItems alignItems) +{ + switch (alignItems) { + case ASStackLayoutAlignItemsNotSet: return YGAlignAuto; + case ASStackLayoutAlignItemsStart: return YGAlignFlexStart; + case ASStackLayoutAlignItemsEnd: return YGAlignFlexEnd; + case ASStackLayoutAlignItemsCenter: return YGAlignCenter; + case ASStackLayoutAlignItemsStretch: return YGAlignStretch; + case ASStackLayoutAlignItemsBaselineFirst: return YGAlignBaseline; + // FIXME: WARNING, Yoga does not currently support last-baseline item alignment. + case ASStackLayoutAlignItemsBaselineLast: return YGAlignBaseline; + } +} + +YGJustify yogaJustifyContent(ASStackLayoutJustifyContent justifyContent) +{ + switch (justifyContent) { + case ASStackLayoutJustifyContentStart: return YGJustifyFlexStart; + case ASStackLayoutJustifyContentCenter: return YGJustifyCenter; + case ASStackLayoutJustifyContentEnd: return YGJustifyFlexEnd; + case ASStackLayoutJustifyContentSpaceBetween: return YGJustifySpaceBetween; + case ASStackLayoutJustifyContentSpaceAround: return YGJustifySpaceAround; + } +} + +YGAlign yogaAlignSelf(ASStackLayoutAlignSelf alignSelf) +{ + switch (alignSelf) { + case ASStackLayoutAlignSelfStart: return YGAlignFlexStart; + case ASStackLayoutAlignSelfCenter: return YGAlignCenter; + case ASStackLayoutAlignSelfEnd: return YGAlignFlexEnd; + case ASStackLayoutAlignSelfStretch: return YGAlignStretch; + case ASStackLayoutAlignSelfAuto: return YGAlignAuto; + } +} + +YGFlexDirection yogaFlexDirection(ASStackLayoutDirection direction) +{ + switch (direction) { + case ASStackLayoutDirectionVertical: + return YGFlexDirectionColumn; + case ASStackLayoutDirectionVerticalReverse: + return YGFlexDirectionColumnReverse; + case ASStackLayoutDirectionHorizontal: + return YGFlexDirectionRow; + case ASStackLayoutDirectionHorizontalReverse: + return YGFlexDirectionRowReverse; + } +} + +float yogaFloatForCGFloat(CGFloat value) +{ + if (value < CGFLOAT_MAX / 2) { + return value; + } else { + return YGUndefined; + } +} + +CGFloat cgFloatForYogaFloat(float yogaFloat, CGFloat undefinedDefault) +{ + return YGFloatIsUndefined(yogaFloat) ? undefinedDefault : yogaFloat; +} + +float yogaDimensionToPoints(ASDimension dimension) +{ + ASDisplayNodeCAssert(dimension.unit == ASDimensionUnitPoints, + @"Dimensions should not be type Fraction for this method: %f", dimension.value); + return yogaFloatForCGFloat(dimension.value); +} + +float yogaDimensionToPercent(ASDimension dimension) +{ + ASDisplayNodeCAssert(dimension.unit == ASDimensionUnitFraction, + @"Dimensions should not be type Points for this method: %f", dimension.value); + return 100.0 * yogaFloatForCGFloat(dimension.value); + +} + +ASDimension dimensionForEdgeWithEdgeInsets(YGEdge edge, ASEdgeInsets insets) +{ + switch (edge) { + case YGEdgeLeft: return insets.left; + case YGEdgeTop: return insets.top; + case YGEdgeRight: return insets.right; + case YGEdgeBottom: return insets.bottom; + case YGEdgeStart: return insets.start; + case YGEdgeEnd: return insets.end; + case YGEdgeHorizontal: return insets.horizontal; + case YGEdgeVertical: return insets.vertical; + case YGEdgeAll: return insets.all; + default: ASDisplayNodeCAssert(NO, @"YGEdge other than ASEdgeInsets is not supported."); + return ASDimensionAuto; + } +} + +void ASLayoutElementYogaUpdateMeasureFunc(YGNodeRef yogaNode, id layoutElement) +{ + if (yogaNode == NULL) { + return; + } + + BOOL shouldHaveMeasureFunc = [layoutElement implementsLayoutMethod]; + // How expensive is it to set a baselineFunc on all (leaf) nodes? + BOOL shouldHaveBaselineFunc = YES; + + if (layoutElement != nil) { + if (shouldHaveMeasureFunc || shouldHaveBaselineFunc) { + // Retain the Context object. This must be explicitly released with a + // __bridge_transfer - YGNodeFree() is not sufficient. + YGNodeSetContext(yogaNode, (__bridge_retained void *)layoutElement); + } + if (shouldHaveMeasureFunc) { + YGNodeSetMeasureFunc(yogaNode, &ASLayoutElementYogaMeasureFunc); + } + if (shouldHaveBaselineFunc) { + YGNodeSetBaselineFunc(yogaNode, &ASLayoutElementYogaBaselineFunc); + } + ASDisplayNodeCAssert(YGNodeGetContext(yogaNode) == (__bridge void *)layoutElement, + @"Yoga node context should contain layoutElement: %@", layoutElement); + } else { + // If we lack any of the conditions above, and currently have a measureFn/baselineFn/context, + // get rid of it. + // Release the __bridge_retained Context object. + __unused id element = (__bridge_transfer id)YGNodeGetContext(yogaNode); + YGNodeSetContext(yogaNode, NULL); + YGNodeSetMeasureFunc(yogaNode, NULL); + YGNodeSetBaselineFunc(yogaNode, NULL); + } +} + +float ASLayoutElementYogaBaselineFunc(YGNodeRef yogaNode, const float width, const float height) +{ + id layoutElement = (__bridge id)YGNodeGetContext(yogaNode); + ASDisplayNodeCAssert([layoutElement conformsToProtocol:@protocol(ASLayoutElement)], + @"Yoga context must be "); + + ASDisplayNode *displayNode = ASDynamicCast(layoutElement, ASDisplayNode); + + switch (displayNode.style.parentAlignStyle) { + case ASStackLayoutAlignItemsBaselineFirst: + return layoutElement.style.ascender; + case ASStackLayoutAlignItemsBaselineLast: + return height + layoutElement.style.descender; + default: + return 0; + } +} + +YGSize ASLayoutElementYogaMeasureFunc(YGNodeRef yogaNode, float width, YGMeasureMode widthMode, + float height, YGMeasureMode heightMode) +{ + id layoutElement = (__bridge id )YGNodeGetContext(yogaNode); + ASDisplayNodeCAssert([layoutElement conformsToProtocol:@protocol(ASLayoutElement)], @"Yoga context must be "); + + width = cgFloatForYogaFloat(width, CGFLOAT_MAX); + height = cgFloatForYogaFloat(height, CGFLOAT_MAX); + + ASSizeRange sizeRange; + sizeRange.min = CGSizeZero; + sizeRange.max = CGSizeMake(width, height); + if (widthMode == YGMeasureModeExactly) { + sizeRange.min.width = sizeRange.max.width; + } else { + // Mode is (YGMeasureModeAtMost | YGMeasureModeUndefined) + ASDimension minWidth = layoutElement.style.minWidth; + sizeRange.min.width = (minWidth.unit == ASDimensionUnitPoints ? yogaDimensionToPoints(minWidth) : 0.0); + } + if (heightMode == YGMeasureModeExactly) { + sizeRange.min.height = sizeRange.max.height; + } else { + // Mode is (YGMeasureModeAtMost | YGMeasureModeUndefined) + ASDimension minHeight = layoutElement.style.minHeight; + sizeRange.min.height = (minHeight.unit == ASDimensionUnitPoints ? yogaDimensionToPoints(minHeight) : 0.0); + } + + ASDisplayNodeCAssert(isnan(sizeRange.min.width) == NO && isnan(sizeRange.min.height) == NO, @"Yoga size range for measurement should not have NaN in minimum"); + if (isnan(sizeRange.max.width)) { + sizeRange.max.width = CGFLOAT_MAX; + } + if (isnan(sizeRange.max.height)) { + sizeRange.max.height = CGFLOAT_MAX; + } + + CGSize size = [[layoutElement layoutThatFits:sizeRange] size]; + return (YGSize){ .width = (float)size.width, .height = (float)size.height }; +} + +#endif /* YOGA */ diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASAbstractLayoutController+FrameworkPrivate.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASAbstractLayoutController+FrameworkPrivate.h new file mode 100644 index 0000000..f836fa0 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASAbstractLayoutController+FrameworkPrivate.h @@ -0,0 +1,21 @@ +// +// ASAbstractLayoutController+FrameworkPrivate.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +// +// The following methods are ONLY for use by _ASDisplayLayer, _ASDisplayView, and ASDisplayNode. +// These methods must never be called or overridden by other classes. +// + +#include + +@interface ASAbstractLayoutController (FrameworkPrivate) + ++ (std::vector>)defaultTuningParameters; + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASBasicImageDownloaderInternal.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASBasicImageDownloaderInternal.h new file mode 100644 index 0000000..ba06e0b --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASBasicImageDownloaderInternal.h @@ -0,0 +1,20 @@ +// +// ASBasicImageDownloaderInternal.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +@interface ASBasicImageDownloaderContext : NSObject + ++ (ASBasicImageDownloaderContext *)contextForURL:(NSURL *)URL; + +@property (nonatomic, readonly) NSURL *URL; +@property (nonatomic, weak) NSURLSessionTask *sessionTask; + +- (BOOL)isCancelled; +- (void)cancel; + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASBatchFetching.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASBatchFetching.h new file mode 100644 index 0000000..fec0c67 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASBatchFetching.h @@ -0,0 +1,73 @@ +// +// ASBatchFetching.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import + +NS_ASSUME_NONNULL_BEGIN + +@class ASBatchContext; +@protocol ASBatchFetchingDelegate; + +@protocol ASBatchFetchingScrollView + +- (BOOL)canBatchFetch; +- (ASBatchContext *)batchContext; +- (CGFloat)leadingScreensForBatching; +- (nullable id)batchFetchingDelegate; + +@end + +/** + @abstract Determine if batch fetching should begin based on the state of the parameters. + @discussion This method is broken into a category for unit testing purposes and should be used with the ASTableView and + * ASCollectionView batch fetching API. + @param scrollView The scroll view that in-flight fetches are happening. + @param scrollDirection The current scrolling direction of the scroll view. + @param scrollableDirections The possible scrolling directions of the scroll view. + @param contentOffset The offset that the scrollview will scroll to. + @param velocity The velocity of the scroll view (in points) at the moment the touch was released. + @return Whether or not the current state should proceed with batch fetching. + */ +ASDK_EXTERN BOOL ASDisplayShouldFetchBatchForScrollView(UIScrollView *scrollView, + ASScrollDirection scrollDirection, + ASScrollDirection scrollableDirections, + CGPoint contentOffset, + CGPoint velocity); + + +/** + @abstract Determine if batch fetching should begin based on the state of the parameters. + @param context The batch fetching context that contains knowledge about in-flight fetches. + @param scrollDirection The current scrolling direction of the scroll view. + @param scrollableDirections The possible scrolling directions of the scroll view. + @param bounds The bounds of the scrollview. + @param contentSize The content size of the scrollview. + @param targetOffset The offset that the scrollview will scroll to. + @param leadingScreens How many screens in the remaining distance will trigger batch fetching. + @param visible Whether the view is visible or not. + @param velocity The velocity of the scroll view (in points) at the moment the touch was released. + @param delegate The delegate to be consulted if needed. + @return Whether or not the current state should proceed with batch fetching. + @discussion This method is broken into a category for unit testing purposes and should be used with the ASTableView and + * ASCollectionView batch fetching API. + */ +ASDK_EXTERN BOOL ASDisplayShouldFetchBatchForContext(ASBatchContext *context, + ASScrollDirection scrollDirection, + ASScrollDirection scrollableDirections, + CGRect bounds, + CGSize contentSize, + CGPoint targetOffset, + CGFloat leadingScreens, + BOOL visible, + CGPoint velocity, + _Nullable id delegate); + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASBatchFetching.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASBatchFetching.mm new file mode 100644 index 0000000..cf3c100 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASBatchFetching.mm @@ -0,0 +1,99 @@ +// +// ASBatchFetching.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import + +BOOL ASDisplayShouldFetchBatchForScrollView(UIScrollView *scrollView, + ASScrollDirection scrollDirection, + ASScrollDirection scrollableDirections, + CGPoint contentOffset, + CGPoint velocity) +{ + // Don't fetch if the scroll view does not allow + if (![scrollView canBatchFetch]) { + return NO; + } + + // Check if we should batch fetch + ASBatchContext *context = scrollView.batchContext; + CGRect bounds = scrollView.bounds; + CGSize contentSize = scrollView.contentSize; + CGFloat leadingScreens = scrollView.leadingScreensForBatching; + id delegate = scrollView.batchFetchingDelegate; + BOOL visible = (scrollView.window != nil); + return ASDisplayShouldFetchBatchForContext(context, scrollDirection, scrollableDirections, bounds, contentSize, contentOffset, leadingScreens, visible, velocity, delegate); +} + +BOOL ASDisplayShouldFetchBatchForContext(ASBatchContext *context, + ASScrollDirection scrollDirection, + ASScrollDirection scrollableDirections, + CGRect bounds, + CGSize contentSize, + CGPoint targetOffset, + CGFloat leadingScreens, + BOOL visible, + CGPoint velocity, + id delegate) +{ + // Do not allow fetching if a batch is already in-flight and hasn't been completed or cancelled + if ([context isFetching]) { + return NO; + } + + // No fetching for null states + if (leadingScreens <= 0.0 || CGRectIsEmpty(bounds)) { + return NO; + } + + + CGFloat viewLength, offset, contentLength, velocityLength; + if (ASScrollDirectionContainsVerticalDirection(scrollableDirections)) { + viewLength = bounds.size.height; + offset = targetOffset.y; + contentLength = contentSize.height; + velocityLength = velocity.y; + } else { // horizontal / right + viewLength = bounds.size.width; + offset = targetOffset.x; + contentLength = contentSize.width; + velocityLength = velocity.x; + } + + BOOL hasSmallContent = contentLength < viewLength; + if (hasSmallContent) { + return YES; + } + + // If we are not visible, but we do have enough content to fill visible area, + // don't batch fetch. + if (visible == NO) { + return NO; + } + + // If they are scrolling toward the head of content, don't batch fetch. + BOOL isScrollingTowardHead = (ASScrollDirectionContainsUp(scrollDirection) || ASScrollDirectionContainsLeft(scrollDirection)); + if (isScrollingTowardHead) { + return NO; + } + + CGFloat triggerDistance = viewLength * leadingScreens; + CGFloat remainingDistance = contentLength - viewLength - offset; + BOOL result = remainingDistance <= triggerDistance; + + if (delegate != nil && velocityLength > 0.0) { + // Don't need to get absolute value of remaining time + // because both remainingDistance and velocityLength are positive when scrolling toward tail + NSTimeInterval remainingTime = remainingDistance / (velocityLength * 1000); + result = [delegate shouldFetchBatchWithRemainingTime:remainingTime hint:result]; + } + + return result; +} diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASCellNode+Internal.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASCellNode+Internal.h new file mode 100644 index 0000000..0ec70ca --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASCellNode+Internal.h @@ -0,0 +1,74 @@ +// +// ASCellNode+Internal.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@class ASCollectionElement; + +@protocol ASCellNodeInteractionDelegate + +/** + * Notifies the delegate that a specified cell node invalidates it's size what could result into a size change. + * + * @param node A node informing the delegate about the relayout. + */ +- (void)nodeDidInvalidateSize:(ASCellNode *)node; + +/* + * Methods to be called whenever the selection or highlight state changes + * on ASCellNode. UIKit internally stores these values to update reusable cells. + */ + +- (void)nodeSelectedStateDidChange:(ASCellNode *)node; +- (void)nodeHighlightedStateDidChange:(ASCellNode *)node; + +@end + +@interface ASCellNode () + +@property (nonatomic, weak) id interactionDelegate; + +/* + * Back-pointer to the containing scrollView instance, set only for visible cells. Used for Cell Visibility Event callbacks. + */ +@property (nonatomic, weak) UIScrollView *scrollView; + +- (void)__setSelectedFromUIKit:(BOOL)selected; +- (void)__setHighlightedFromUIKit:(BOOL)highlighted; + +/** + * @note This could be declared @c copy, but since this is only settable internally, we can ensure + * that it's always safe simply to retain it, and copy if needed. Since @c UICollectionViewLayoutAttributes + * is always mutable, @c copy is never "free" like it is for e.g. NSString. + */ +@property (nullable, nonatomic) UICollectionViewLayoutAttributes *layoutAttributes; + +@property (weak, nullable) ASCollectionElement *collectionElement; + +@property (nonatomic, readonly) BOOL shouldUseUIKitCell; + +@end + +@class ASWrapperCellNode; + +typedef CGSize (^ASSizeForItemBlock)(ASWrapperCellNode *node, CGSize collectionSize); +typedef UICollectionViewCell * _Nonnull(^ASCellForItemBlock)(ASWrapperCellNode *node); +typedef UICollectionReusableView * _Nonnull(^ASViewForSupplementaryBlock)(ASWrapperCellNode *node); + +@interface ASWrapperCellNode : ASCellNode + +@property (nonatomic, readonly) ASSizeForItemBlock sizeForItemBlock; +@property (nonatomic, readonly) ASCellForItemBlock cellForItemBlock; +@property (nonatomic, readonly) ASViewForSupplementaryBlock viewForSupplementaryBlock; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASCollectionLayout.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASCollectionLayout.h new file mode 100644 index 0000000..c31114e --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASCollectionLayout.h @@ -0,0 +1,50 @@ +// +// ASCollectionLayout.h +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import + +@protocol ASCollectionLayoutDelegate; +@class ASElementMap, ASCollectionLayout, ASCollectionNode; + +NS_ASSUME_NONNULL_BEGIN + +AS_SUBCLASSING_RESTRICTED +@interface ASCollectionLayout : UICollectionViewLayout + +/** + * The collection node object currently using this layout object. + * + * @discussion The collection node object sets the value of this property when a new layout object is assigned to it. + * + * @discussion To get the truth on the current state of the collection, call methods on the collection node or the data source rather than the collection view because: + * 1. The view might not yet be allocated. + * 2. The collection node and data source are thread-safe. + */ +@property (nonatomic, weak) ASCollectionNode *collectionNode; + +@property (nonatomic, readonly) id layoutDelegate; + +/** + * Initializes with a layout delegate. + * + * @discussion For developers' convenience, the delegate is retained by this layout object, similar to UICollectionView retains its UICollectionViewLayout object. + * + * @discussion For simplicity, the delegate is read-only. If a new layout delegate is needed, construct a new layout object with that delegate and notify ASCollectionView about it. + * This ensures the underlying UICollectionView purges its cache and properly loads the new layout. + */ +- (instancetype)initWithLayoutDelegate:(id)layoutDelegate NS_DESIGNATED_INITIALIZER; + +- (instancetype)init __unavailable; + +- (instancetype)initWithCoder:(NSCoder *)aDecoder __unavailable; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASCollectionLayout.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASCollectionLayout.mm new file mode 100644 index 0000000..e69baa4 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASCollectionLayout.mm @@ -0,0 +1,396 @@ +// +// ASCollectionLayout.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + +static const ASRangeTuningParameters kASDefaultMeasureRangeTuningParameters = { + .leadingBufferScreenfuls = 2.0, + .trailingBufferScreenfuls = 2.0 +}; + +static const ASScrollDirection kASStaticScrollDirection = (ASScrollDirectionRight | ASScrollDirectionDown); + +@interface ASCollectionLayout () { + ASCollectionLayoutCache *_layoutCache; + ASCollectionLayoutState *_layout; // Main thread only. + + struct { + unsigned int implementsAdditionalInfoForLayoutWithElements:1; + } _layoutDelegateFlags; +} + +@end + +@implementation ASCollectionLayout + +- (instancetype)initWithLayoutDelegate:(id)layoutDelegate +{ + self = [super init]; + if (self) { + ASDisplayNodeAssertNotNil(layoutDelegate, @"Collection layout delegate cannot be nil"); + _layoutDelegate = layoutDelegate; + _layoutDelegateFlags.implementsAdditionalInfoForLayoutWithElements = [layoutDelegate respondsToSelector:@selector(additionalInfoForLayoutWithElements:)]; + _layoutCache = [[ASCollectionLayoutCache alloc] init]; + } + return self; +} + +#pragma mark - ASDataControllerLayoutDelegate + +- (ASCollectionLayoutContext *)layoutContextWithElements:(ASElementMap *)elements +{ + ASDisplayNodeAssertMainThread(); + + Class layoutDelegateClass = [_layoutDelegate class]; + ASCollectionLayoutCache *layoutCache = _layoutCache; + ASCollectionNode *collectionNode = _collectionNode; + if (collectionNode == nil) { + return [[ASCollectionLayoutContext alloc] initWithViewportSize:CGSizeZero + initialContentOffset:CGPointZero + scrollableDirections:ASScrollDirectionNone + elements:[[ASElementMap alloc] init] + layoutDelegateClass:layoutDelegateClass + layoutCache:layoutCache + additionalInfo:nil]; + } + + ASScrollDirection scrollableDirections = [_layoutDelegate scrollableDirections]; + CGSize viewportSize = [ASCollectionLayout _viewportSizeForCollectionNode:collectionNode scrollableDirections:scrollableDirections]; + CGPoint contentOffset = collectionNode.contentOffset; + + id additionalInfo = nil; + if (_layoutDelegateFlags.implementsAdditionalInfoForLayoutWithElements) { + additionalInfo = [_layoutDelegate additionalInfoForLayoutWithElements:elements]; + } + + return [[ASCollectionLayoutContext alloc] initWithViewportSize:viewportSize + initialContentOffset:contentOffset + scrollableDirections:scrollableDirections + elements:elements + layoutDelegateClass:layoutDelegateClass + layoutCache:layoutCache + additionalInfo:additionalInfo]; +} + ++ (ASCollectionLayoutState *)calculateLayoutWithContext:(ASCollectionLayoutContext *)context +{ + if (context.elements == nil) { + return [[ASCollectionLayoutState alloc] initWithContext:context]; + } + + ASCollectionLayoutState *layout = [context.layoutDelegateClass calculateLayoutWithContext:context]; + [context.layoutCache setLayout:layout forContext:context]; + + // Measure elements in the measure range ahead of time + CGSize viewportSize = context.viewportSize; + CGPoint contentOffset = context.initialContentOffset; + CGRect initialRect = CGRectMake(contentOffset.x, contentOffset.y, viewportSize.width, viewportSize.height); + CGRect measureRect = CGRectExpandToRangeWithScrollableDirections(initialRect, + kASDefaultMeasureRangeTuningParameters, + context.scrollableDirections, + kASStaticScrollDirection); + // The first call to -layoutAttributesForElementsInRect: will be with a rect that is way bigger than initialRect here. + // If we only block on initialRect, a few elements that are outside of initialRect but inside measureRect + // may not be available by the time -layoutAttributesForElementsInRect: is called. + // Since this method is usually run off main, let's spawn more threads to measure and block on all elements in measureRect. + [self _measureElementsInRect:measureRect blockingRect:measureRect layout:layout]; + + return layout; +} + +#pragma mark - UICollectionViewLayout overrides + +- (void)prepareLayout +{ + ASDisplayNodeAssertMainThread(); + [super prepareLayout]; + + ASCollectionLayoutContext *context = [self layoutContextWithElements:_collectionNode.visibleElements]; + if (_layout != nil && ASObjectIsEqual(_layout.context, context)) { + // The existing layout is still valid. No-op + return; + } + + if (ASCollectionLayoutState *cachedLayout = [_layoutCache layoutForContext:context]) { + _layout = cachedLayout; + } else { + // A new layout is needed now. Calculate and apply it immediately + _layout = [ASCollectionLayout calculateLayoutWithContext:context]; + } +} + +- (void)invalidateLayout +{ + ASDisplayNodeAssertMainThread(); + [super invalidateLayout]; + if (_layout != nil) { + [_layoutCache removeLayoutForContext:_layout.context]; + _layout = nil; + } +} + +/** + * NOTE: It is suggested practice on the Web to override invalidationContextForInteractivelyMovingItems… and call out to the + * data source to move the item (so that if e.g. the item size depends on the data, you get the data you expect). However, as of iOS 11 this + * doesn't work, because UICV machinery will also call out to the data source to move the item after the interaction is done. The result is + * that your data source state will be incorrect due to this last move call. Plus it's just an API violation. + * + * Things tried: + * - Doing the speculative data source moves, and then UNDOING the last one in invalidationContextForEndingInteractiveMovementOfItems… + * but this does not work because the UICV machinery informs its data source before it calls that method on us, so we are too late. + * + * The correct practice is to use the UIDataSourceTranslating API introduced in iOS 11. Currently Texture does not support this API but we can + * build it if there is demand. We could add an id field onto the layout context object, and the layout client can + * use data source index paths when it reads nodes or other data source data. + */ + +- (CGSize)collectionViewContentSize +{ + ASDisplayNodeAssertMainThread(); + // The content size can be queried right after a layout invalidation (https://github.com/TextureGroup/Texture/pull/509). + // In that case, return zero. + return _layout ? _layout.contentSize : CGSizeZero; +} + +- (NSArray *)layoutAttributesForElementsInRect:(CGRect)blockingRect +{ + ASDisplayNodeAssertMainThread(); + if (CGRectIsEmpty(blockingRect)) { + return nil; + } + + // Measure elements in the measure range, block on the requested rect + CGRect measureRect = CGRectExpandToRangeWithScrollableDirections(blockingRect, + kASDefaultMeasureRangeTuningParameters, + _layout.context.scrollableDirections, + kASStaticScrollDirection); + [ASCollectionLayout _measureElementsInRect:measureRect blockingRect:blockingRect layout:_layout]; + + NSArray *result = [_layout layoutAttributesForElementsInRect:blockingRect]; + + ASElementMap *elements = _layout.context.elements; + for (UICollectionViewLayoutAttributes *attrs in result) { + ASCollectionElement *element = [elements elementForLayoutAttributes:attrs]; + ASCollectionLayoutSetSizeToElement(attrs.frame.size, element); + } + + return result; +} + +- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath +{ + ASDisplayNodeAssertMainThread(); + + ASCollectionElement *element = [_layout.context.elements elementForItemAtIndexPath:indexPath]; + UICollectionViewLayoutAttributes *attrs = [_layout layoutAttributesForElement:element]; + + ASCellNode *node = element.node; + CGSize elementSize = attrs.frame.size; + if (! CGSizeEqualToSize(elementSize, node.calculatedSize)) { + [node layoutThatFits:ASCollectionLayoutElementSizeRangeFromSize(elementSize)]; + } + + ASCollectionLayoutSetSizeToElement(attrs.frame.size, element); + return attrs; +} + +- (UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath +{ + ASCollectionElement *element = [_layout.context.elements supplementaryElementOfKind:elementKind atIndexPath:indexPath]; + UICollectionViewLayoutAttributes *attrs = [_layout layoutAttributesForElement:element]; + + ASCellNode *node = element.node; + CGSize elementSize = attrs.frame.size; + if (! CGSizeEqualToSize(elementSize, node.calculatedSize)) { + [node layoutThatFits:ASCollectionLayoutElementSizeRangeFromSize(elementSize)]; + } + + ASCollectionLayoutSetSizeToElement(attrs.frame.size, element); + return attrs; +} + +- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds +{ + return (! CGSizeEqualToSize([ASCollectionLayout _boundsForCollectionNode:_collectionNode], newBounds.size)); +} + +#pragma mark - Private methods + ++ (CGSize)_boundsForCollectionNode:(nonnull ASCollectionNode *)collectionNode +{ + if (collectionNode == nil) { + return CGSizeZero; + } + + if (!collectionNode.isNodeLoaded) { + // TODO consider calculatedSize as well + return collectionNode.threadSafeBounds.size; + } + + ASDisplayNodeAssertMainThread(); + return collectionNode.view.bounds.size; +} + ++ (CGSize)_viewportSizeForCollectionNode:(nonnull ASCollectionNode *)collectionNode scrollableDirections:(ASScrollDirection)scrollableDirections +{ + if (collectionNode == nil) { + return CGSizeZero; + } + + CGSize result = [ASCollectionLayout _boundsForCollectionNode:collectionNode]; + // TODO: Consider using adjustedContentInset on iOS 11 and later, to include the safe area of the scroll view + UIEdgeInsets contentInset = collectionNode.contentInset; + if (ASScrollDirectionContainsHorizontalDirection(scrollableDirections)) { + result.height -= (contentInset.top + contentInset.bottom); + } else { + result.width -= (contentInset.left + contentInset.right); + } + return result; +} + +/** + * Measures all elements in the specified rect and blocks the calling thread while measuring those in the blocking rect. + */ ++ (void)_measureElementsInRect:(CGRect)rect blockingRect:(CGRect)blockingRect layout:(ASCollectionLayoutState *)layout +{ + if (CGRectIsEmpty(rect) || layout.context.elements == nil) { + return; + } + BOOL hasBlockingRect = !CGRectIsEmpty(blockingRect); + if (hasBlockingRect && CGRectContainsRect(rect, blockingRect) == NO) { + ASDisplayNodeCAssert(NO, @"Blocking rect, if specified, must be within the other (outer) rect"); + return; + } + + // Step 1: Clamp the specified rects between the bounds of content rect + CGSize contentSize = layout.contentSize; + CGRect contentRect = CGRectMake(0, 0, contentSize.width, contentSize.height); + rect = CGRectIntersection(contentRect, rect); + if (CGRectIsNull(rect)) { + return; + } + if (hasBlockingRect) { + blockingRect = CGRectIntersection(contentRect, blockingRect); + hasBlockingRect = !CGRectIsNull(blockingRect); + } + + // Step 2: Get layout attributes of all elements within the specified outer rect + ASPageToLayoutAttributesTable *attrsTable = [layout getAndRemoveUnmeasuredLayoutAttributesPageTableInRect:rect]; + if (attrsTable.count == 0) { + // No elements in this rect! Bail early + return; + } + + // Step 3: Split all those attributes into blocking and non-blocking buckets + // Use ordered sets here because some items may span multiple pages, and the sets will be accessed by indexes later on. + ASCollectionLayoutContext *context = layout.context; + CGSize pageSize = context.viewportSize; + NSMutableOrderedSet *blockingAttrs = hasBlockingRect ? [NSMutableOrderedSet orderedSet] : nil; + NSMutableOrderedSet *nonBlockingAttrs = [NSMutableOrderedSet orderedSet]; + for (id pagePtr in attrsTable) { + ASPageCoordinate page = (ASPageCoordinate)pagePtr; + NSArray *attrsInPage = [attrsTable objectForPage:page]; + // Calculate the page's rect but only if it's going to be used. + CGRect pageRect = hasBlockingRect ? ASPageCoordinateGetPageRect(page, pageSize) : CGRectZero; + + if (hasBlockingRect && CGRectContainsRect(blockingRect, pageRect)) { + // The page fits well within the blocking rect. All attributes in this page are blocking. + [blockingAttrs addObjectsFromArray:attrsInPage]; + } else if (hasBlockingRect && CGRectIntersectsRect(blockingRect, pageRect)) { + // The page intersects the blocking rect. Some elements in this page are blocking, some are not. + for (UICollectionViewLayoutAttributes *attrs in attrsInPage) { + if (CGRectIntersectsRect(blockingRect, attrs.frame)) { + [blockingAttrs addObject:attrs]; + } else { + [nonBlockingAttrs addObject:attrs]; + } + } + } else { + // The page doesn't intersect the blocking rect. All elements in this page are non-blocking. + [nonBlockingAttrs addObjectsFromArray:attrsInPage]; + } + } + + // Step 4: Allocate and measure blocking elements' node + ASElementMap *elements = context.elements; + dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + if (NSUInteger count = blockingAttrs.count) { + ASDispatchApply(count, queue, 0, ^(size_t i) { + UICollectionViewLayoutAttributes *attrs = blockingAttrs[i]; + ASCellNode *node; + if (attrs.representedElementKind == nil) { + node = [elements elementForItemAtIndexPath:attrs.indexPath].node; + } else { + node = [elements supplementaryElementOfKind:attrs.representedElementKind atIndexPath:attrs.indexPath].node; + } + CGSize expectedSize = attrs.frame.size; + if (! CGSizeEqualToSize(expectedSize, node.calculatedSize)) { + [node layoutThatFits:ASCollectionLayoutElementSizeRangeFromSize(expectedSize)]; + } + }); + } + + // Step 5: Allocate and measure non-blocking ones + if (NSUInteger count = nonBlockingAttrs.count) { + __weak ASElementMap *weakElements = elements; + ASDispatchAsync(count, queue, 0, ^(size_t i) { + __strong ASElementMap *strongElements = weakElements; + if (strongElements) { + UICollectionViewLayoutAttributes *attrs = nonBlockingAttrs[i]; + ASCellNode *node; + if (attrs.representedElementKind == nil) { + node = [elements elementForItemAtIndexPath:attrs.indexPath].node; + } else { + node = [elements supplementaryElementOfKind:attrs.representedElementKind atIndexPath:attrs.indexPath].node; + } + CGSize expectedSize = attrs.frame.size; + if (! CGSizeEqualToSize(expectedSize, node.calculatedSize)) { + [node layoutThatFits:ASCollectionLayoutElementSizeRangeFromSize(expectedSize)]; + } + } + }); + } +} + +# pragma mark - Convenient inline functions + +ASDISPLAYNODE_INLINE ASSizeRange ASCollectionLayoutElementSizeRangeFromSize(CGSize size) +{ + // The layout delegate consulted us that this element must fit within this size, + // and the only way to achieve that without asking it again is to use an exact size range here. + return ASSizeRangeMake(size); +} + +ASDISPLAYNODE_INLINE void ASCollectionLayoutSetSizeToElement(CGSize size, ASCollectionElement *element) +{ + if (ASCellNode *node = element.node) { + if (! CGSizeEqualToSize(size, node.frame.size)) { + CGRect frame = CGRectZero; + frame.size = size; + node.frame = frame; + } + } +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASCollectionLayoutCache.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASCollectionLayoutCache.h new file mode 100644 index 0000000..3efad85 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASCollectionLayoutCache.h @@ -0,0 +1,30 @@ +// +// ASCollectionLayoutCache.h +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@class ASCollectionLayoutContext, ASCollectionLayoutState; + +/// A thread-safe cache for ASCollectionLayoutContext-ASCollectionLayoutState pairs +AS_SUBCLASSING_RESTRICTED +@interface ASCollectionLayoutCache : NSObject + +- (nullable ASCollectionLayoutState *)layoutForContext:(ASCollectionLayoutContext *)context; + +- (void)setLayout:(ASCollectionLayoutState *)layout forContext:(ASCollectionLayoutContext *)context; + +- (void)removeLayoutForContext:(ASCollectionLayoutContext *)context; + +- (void)removeAllLayouts; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASCollectionLayoutCache.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASCollectionLayoutCache.mm new file mode 100644 index 0000000..a5d6d11 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASCollectionLayoutCache.mm @@ -0,0 +1,88 @@ +// +// ASCollectionLayoutCache.mm +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import +#import +#import +#import + +using AS::MutexLocker; + +@implementation ASCollectionLayoutCache { + AS::Mutex __instanceLock__; + + /** + * The underlying data structure of this cache. + * + * The outer map table is a weak to strong table. That is because ASCollectionLayoutContext doesn't (and shouldn't) + * hold a strong reference on its element map. As a result, this cache should handle the case in which + * an element map no longer exists and all contexts and layouts associated with it should be cleared. + * + * The inner map table is a standard strong to strong map. + * Since different ASCollectionLayoutContext objects with the same content are considered equal, + * "object pointer personality" can't be used as a key option. + */ + NSMapTable *> *_map; +} + +- (instancetype)init +{ + self = [super init]; + if (self) { + _map = [NSMapTable mapTableWithKeyOptions:(NSMapTableWeakMemory | NSMapTableObjectPointerPersonality) valueOptions:NSMapTableStrongMemory]; + } + return self; +} + +- (ASCollectionLayoutState *)layoutForContext:(ASCollectionLayoutContext *)context +{ + ASElementMap *elements = context.elements; + if (elements == nil) { + return nil; + } + + MutexLocker l(__instanceLock__); + return [[_map objectForKey:elements] objectForKey:context]; +} + +- (void)setLayout:(ASCollectionLayoutState *)layout forContext:(ASCollectionLayoutContext *)context +{ + ASElementMap *elements = context.elements; + if (layout == nil || elements == nil) { + return; + } + + MutexLocker l(__instanceLock__); + auto innerMap = [_map objectForKey:elements]; + if (innerMap == nil) { + innerMap = [NSMapTable strongToStrongObjectsMapTable]; + [_map setObject:innerMap forKey:elements]; + } + [innerMap setObject:layout forKey:context]; +} + +- (void)removeLayoutForContext:(ASCollectionLayoutContext *)context +{ + ASElementMap *elements = context.elements; + if (elements == nil) { + return; + } + + MutexLocker l(__instanceLock__); + [[_map objectForKey:elements] removeObjectForKey:context]; +} + +- (void)removeAllLayouts +{ + MutexLocker l(__instanceLock__); + [_map removeAllObjects]; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASCollectionLayoutContext+Private.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASCollectionLayoutContext+Private.h new file mode 100644 index 0000000..12e1d9d --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASCollectionLayoutContext+Private.h @@ -0,0 +1,32 @@ +// +// ASCollectionLayoutContext+Private.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +@class ASCollectionLayoutCache; +@protocol ASCollectionLayoutDelegate; + +NS_ASSUME_NONNULL_BEGIN + +@interface ASCollectionLayoutContext (Private) + +@property (nonatomic, readonly) Class layoutDelegateClass; +@property (nonatomic, weak, readonly) ASCollectionLayoutCache *layoutCache; + +- (instancetype)initWithViewportSize:(CGSize)viewportSize + initialContentOffset:(CGPoint)initialContentOffset + scrollableDirections:(ASScrollDirection)scrollableDirections + elements:(ASElementMap *)elements + layoutDelegateClass:(Class)layoutDelegateClass + layoutCache:(ASCollectionLayoutCache *)layoutCache + additionalInfo:(nullable id)additionalInfo; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASCollectionLayoutDefines.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASCollectionLayoutDefines.h new file mode 100644 index 0000000..6c7ff9d --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASCollectionLayoutDefines.h @@ -0,0 +1,19 @@ +// +// ASCollectionLayoutDefines.h +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +ASDK_EXTERN ASSizeRange ASSizeRangeForCollectionLayoutThatFitsViewportSize(CGSize viewportSize, ASScrollDirection scrollableDirections) AS_WARN_UNUSED_RESULT; + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASCollectionLayoutDefines.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASCollectionLayoutDefines.mm new file mode 100644 index 0000000..e386575 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASCollectionLayoutDefines.mm @@ -0,0 +1,23 @@ +// +// ASCollectionLayoutDefines.mm +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +ASSizeRange ASSizeRangeForCollectionLayoutThatFitsViewportSize(CGSize viewportSize, ASScrollDirection scrollableDirections) +{ + ASSizeRange sizeRange = ASSizeRangeUnconstrained; + if (ASScrollDirectionContainsVerticalDirection(scrollableDirections) == NO) { + sizeRange.min.height = viewportSize.height; + sizeRange.max.height = viewportSize.height; + } + if (ASScrollDirectionContainsHorizontalDirection(scrollableDirections) == NO) { + sizeRange.min.width = viewportSize.width; + sizeRange.max.width = viewportSize.width; + } + return sizeRange; +} diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASCollectionLayoutState+Private.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASCollectionLayoutState+Private.h new file mode 100644 index 0000000..c964cf3 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASCollectionLayoutState+Private.h @@ -0,0 +1,25 @@ +// +// ASCollectionLayoutState+Private.h +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface ASCollectionLayoutState (Private) + +/** + * Remove and returns layout attributes for unmeasured elements that intersect the specified rect + * + * @discussion This method is atomic and thread-safe + */ +- (nullable ASPageToLayoutAttributesTable *)getAndRemoveUnmeasuredLayoutAttributesPageTableInRect:(CGRect)rect; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASCollectionView+Undeprecated.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASCollectionView+Undeprecated.h new file mode 100644 index 0000000..0a9e0d9 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASCollectionView+Undeprecated.h @@ -0,0 +1,300 @@ +// +// ASCollectionView+Undeprecated.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * Currently our public collection API is on @c ASCollectionNode and the @c ASCollectionView + * API is deprecated, but the implementations still live in the view. + * + * This category lets us avoid deprecation warnings everywhere internally. + * In the future, the @c ASCollectionView public API will be eliminated and so will this file. + */ +@interface ASCollectionView (Undeprecated) + +/** + * The object that acts as the asynchronous delegate of the collection view + * + * @discussion The delegate must adopt the ASCollectionDelegate protocol. The collection view maintains a weak reference to the delegate object. + * + * The delegate object is responsible for providing size constraints for nodes and indicating whether batch fetching should begin. + */ +@property (nonatomic, weak) id asyncDelegate; + +/** + * The object that acts as the asynchronous data source of the collection view + * + * @discussion The datasource must adopt the ASCollectionDataSource protocol. The collection view maintains a weak reference to the datasource object. + * + * The datasource object is responsible for providing nodes or node creation blocks to the collection view. + */ +@property (nonatomic, weak) id asyncDataSource; + +/** + * Initializes an ASCollectionView + * + * @discussion Initializes and returns a newly allocated collection view object with the specified layout. + * + * @param layout The layout object to use for organizing items. The collection view stores a strong reference to the specified object. Must not be nil. + */ +- (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout *)layout; + +/** + * Initializes an ASCollectionView + * + * @discussion Initializes and returns a newly allocated collection view object with the specified frame and layout. + * + * @param frame The frame rectangle for the collection view, measured in points. The origin of the frame is relative to the superview in which you plan to add it. This frame is passed to the superclass during initialization. + * @param layout The layout object to use for organizing items. The collection view stores a strong reference to the specified object. Must not be nil. + */ +- (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout; + +@property (nonatomic) CGFloat leadingScreensForBatching; + +@property (nonatomic) BOOL inverted; + +@property (nonatomic, readonly) ASScrollDirection scrollDirection; + +@property (nonatomic, readonly) ASScrollDirection scrollableDirections; + +@property (nonatomic, weak) id layoutInspector; + +@property (nonatomic) UIEdgeInsets contentInset; + +@property (nonatomic) CGPoint contentOffset; + +/** + * Tuning parameters for a range type in full mode. + * + * @param rangeType The range type to get the tuning parameters for. + * + * @return A tuning parameter value for the given range type in full mode. + * + * @see ASLayoutRangeMode + * @see ASLayoutRangeType + */ +- (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType AS_WARN_UNUSED_RESULT; + +/** + * Set the tuning parameters for a range type in full mode. + * + * @param tuningParameters The tuning parameters to store for a range type. + * @param rangeType The range type to set the tuning parameters for. + * + * @see ASLayoutRangeMode + * @see ASLayoutRangeType + */ +- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType; + +/** + * Tuning parameters for a range type in the specified mode. + * + * @param rangeMode The range mode to get the running parameters for. + * @param rangeType The range type to get the tuning parameters for. + * + * @return A tuning parameter value for the given range type in the given mode. + * + * @see ASLayoutRangeMode + * @see ASLayoutRangeType + */ +- (ASRangeTuningParameters)tuningParametersForRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType AS_WARN_UNUSED_RESULT; + +/** + * Set the tuning parameters for a range type in the specified mode. + * + * @param tuningParameters The tuning parameters to store for a range type. + * @param rangeMode The range mode to set the running parameters for. + * @param rangeType The range type to set the tuning parameters for. + * + * @see ASLayoutRangeMode + * @see ASLayoutRangeType + */ +- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType; + +- (nullable __kindof UICollectionViewCell *)cellForItemAtIndexPath:(NSIndexPath *)indexPath; + +@property (nonatomic, readonly) NSArray *indexPathsForVisibleItems; + +@property (nonatomic, readonly, nullable) NSArray *indexPathsForSelectedItems; + +/** + * Scrolls the collection to the given item. + * + * @param indexPath The index path of the item. + * @param scrollPosition Where the row should end up after the scroll. + * @param animated Whether the scroll should be animated or not. + */ +- (void)scrollToItemAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UICollectionViewScrollPosition)scrollPosition animated:(BOOL)animated; + +- (void)selectItemAtIndexPath:(NSIndexPath *)indexPath animated:(BOOL)animated scrollPosition:(UICollectionViewScrollPosition)scrollPosition; + +/** + * Perform a batch of updates asynchronously, optionally disabling all animations in the batch. This method must be called from the main thread. + * The asyncDataSource must be updated to reflect the changes before the update block completes. + * + * @param animated NO to disable animations for this batch + * @param updates The block that performs the relevant insert, delete, reload, or move operations. + * @param completion A completion handler block to execute when all of the operations are finished. This block takes a single + * Boolean parameter that contains the value YES if all of the related animations completed successfully or + * NO if they were interrupted. This parameter may be nil. If supplied, the block is run on the main thread. + */ +- (void)performBatchAnimated:(BOOL)animated updates:(nullable AS_NOESCAPE void (^)(void))updates completion:(nullable void (^)(BOOL finished))completion; + +/** + * Perform a batch of updates asynchronously. This method must be called from the main thread. + * The asyncDataSource must be updated to reflect the changes before update block completes. + * + * @param updates The block that performs the relevant insert, delete, reload, or move operations. + * @param completion A completion handler block to execute when all of the operations are finished. This block takes a single + * Boolean parameter that contains the value YES if all of the related animations completed successfully or + * NO if they were interrupted. This parameter may be nil. If supplied, the block is run on the main thread. + */ +- (void)performBatchUpdates:(nullable AS_NOESCAPE void (^)(void))updates completion:(nullable void (^)(BOOL finished))completion; + +/** + * Triggers a relayout of all nodes. + * + * @discussion This method invalidates and lays out every cell node in the collection. + */ +- (void)relayoutItems; + +/** + * Blocks execution of the main thread until all section and row updates are committed. This method must be called from the main thread. + */ +- (void)waitUntilAllUpdatesAreCommitted; + +/** + * Registers the given kind of supplementary node for use in creating node-backed supplementary views. + * + * @param elementKind The kind of supplementary node that will be requested through the data source. + * + * @discussion Use this method to register support for the use of supplementary nodes in place of the default + * `registerClass:forSupplementaryViewOfKind:withReuseIdentifier:` and `registerNib:forSupplementaryViewOfKind:withReuseIdentifier:` + * methods. This method will register an internal backing view that will host the contents of the supplementary nodes + * returned from the data source. + */ +- (void)registerSupplementaryNodeOfKind:(NSString *)elementKind; + +/** + * Inserts one or more sections. + * + * @param sections An index set that specifies the sections to insert. + * + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes + * before this method is called. + */ +- (void)insertSections:(NSIndexSet *)sections; + +/** + * Deletes one or more sections. + * + * @param sections An index set that specifies the sections to delete. + * + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes + * before this method is called. + */ +- (void)deleteSections:(NSIndexSet *)sections; + +/** + * Reloads the specified sections. + * + * @param sections An index set that specifies the sections to reload. + * + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes + * before this method is called. + */ +- (void)reloadSections:(NSIndexSet *)sections; + +/** + * Moves a section to a new location. + * + * @param section The index of the section to move. + * + * @param newSection The index that is the destination of the move for the section. + * + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes + * before this method is called. + */ +- (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection; + +/** + * Inserts items at the locations identified by an array of index paths. + * + * @param indexPaths An array of NSIndexPath objects, each representing an item index and section index that together identify an item. + * + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes + * before this method is called. + */ +- (void)insertItemsAtIndexPaths:(NSArray *)indexPaths; + +/** + * Deletes the items specified by an array of index paths. + * + * @param indexPaths An array of NSIndexPath objects identifying the items to delete. + * + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes + * before this method is called. + */ +- (void)deleteItemsAtIndexPaths:(NSArray *)indexPaths; + +/** + * Reloads the specified items. + * + * @param indexPaths An array of NSIndexPath objects identifying the items to reload. + * + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes + * before this method is called. + */ +- (void)reloadItemsAtIndexPaths:(NSArray *)indexPaths; + +/** + * Moves the item at a specified location to a destination location. + * + * @param indexPath The index path identifying the item to move. + * + * @param newIndexPath The index path that is the destination of the move for the item. + * + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes + * before this method is called. + */ +- (void)moveItemAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath; + +/** + * Similar to -visibleCells. + * + * @return an array containing the nodes being displayed on screen. + */ +- (NSArray<__kindof ASCellNode *> *)visibleNodes AS_WARN_UNUSED_RESULT; + +/** + * Similar to -indexPathForCell:. + * + * @param cellNode a cellNode in the collection view + * + * @return The index path for this cell node. + * + * @discussion This index path returned by this method is in the _view's_ index space + * and should only be used with @c ASCollectionView directly. To get an index path suitable + * for use with your data source and @c ASCollectionNode, call @c indexPathForNode: on the + * collection node instead. + */ +- (nullable NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode AS_WARN_UNUSED_RESULT; + +/** + * Invalidates and recalculates the cached sizes stored for pass-through cells used in interop mode. + */ +- (void)invalidateFlowLayoutDelegateMetrics; + +- (void)setContentOffset:(CGPoint)contentOffset animated:(BOOL)animated; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASCollectionViewFlowLayoutInspector.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASCollectionViewFlowLayoutInspector.h new file mode 100644 index 0000000..0272da1 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASCollectionViewFlowLayoutInspector.h @@ -0,0 +1,30 @@ +// +// ASCollectionViewFlowLayoutInspector.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@class ASCollectionView; +@class UICollectionViewFlowLayout; + +/** + * A layout inspector implementation specific for the sizing behavior of UICollectionViewFlowLayouts + */ +AS_SUBCLASSING_RESTRICTED +@interface ASCollectionViewFlowLayoutInspector : NSObject + +@property (nonatomic, weak, readonly) UICollectionViewFlowLayout *layout; + +- (instancetype)init NS_UNAVAILABLE; +- (instancetype)initWithFlowLayout:(UICollectionViewFlowLayout *)flowLayout NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASCollectionViewFlowLayoutInspector.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASCollectionViewFlowLayoutInspector.mm new file mode 100644 index 0000000..52d341c --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASCollectionViewFlowLayoutInspector.mm @@ -0,0 +1,156 @@ +// +// ASCollectionViewFlowLayoutInspector.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import +#import +#import + +#define kDefaultItemSize CGSizeMake(50, 50) + +#pragma mark - ASCollectionViewFlowLayoutInspector + +@interface ASCollectionViewFlowLayoutInspector () +@property (nonatomic, weak) UICollectionViewFlowLayout *layout; +@end + +@implementation ASCollectionViewFlowLayoutInspector { + struct { + unsigned int implementsSizeRangeForHeader:1; + unsigned int implementsReferenceSizeForHeader:1; + unsigned int implementsSizeRangeForFooter:1; + unsigned int implementsReferenceSizeForFooter:1; + unsigned int implementsConstrainedSizeForNodeAtIndexPathDeprecated:1; + unsigned int implementsConstrainedSizeForItemAtIndexPath:1; + } _delegateFlags; +} + +#pragma mark Lifecycle + +- (instancetype)initWithFlowLayout:(UICollectionViewFlowLayout *)flowLayout; +{ + NSParameterAssert(flowLayout); + + self = [super init]; + if (self != nil) { + _layout = flowLayout; + } + return self; +} + +#pragma mark ASCollectionViewLayoutInspecting + +- (void)didChangeCollectionViewDelegate:(id)delegate; +{ + if (delegate == nil) { + memset(&_delegateFlags, 0, sizeof(_delegateFlags)); + } else { + _delegateFlags.implementsSizeRangeForHeader = [delegate respondsToSelector:@selector(collectionNode:sizeRangeForHeaderInSection:)]; + _delegateFlags.implementsReferenceSizeForHeader = [delegate respondsToSelector:@selector(collectionView:layout:referenceSizeForHeaderInSection:)]; + _delegateFlags.implementsSizeRangeForFooter = [delegate respondsToSelector:@selector(collectionNode:sizeRangeForFooterInSection:)]; + _delegateFlags.implementsReferenceSizeForFooter = [delegate respondsToSelector:@selector(collectionView:layout:referenceSizeForFooterInSection:)]; + _delegateFlags.implementsConstrainedSizeForNodeAtIndexPathDeprecated = [delegate respondsToSelector:@selector(collectionView:constrainedSizeForNodeAtIndexPath:)]; + _delegateFlags.implementsConstrainedSizeForItemAtIndexPath = [delegate respondsToSelector:@selector(collectionNode:constrainedSizeForItemAtIndexPath:)]; + } +} + +- (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath +{ + ASSizeRange result = ASSizeRangeUnconstrained; + if (_delegateFlags.implementsConstrainedSizeForItemAtIndexPath) { + result = [collectionView.asyncDelegate collectionNode:collectionView.collectionNode constrainedSizeForItemAtIndexPath:indexPath]; + } else if (_delegateFlags.implementsConstrainedSizeForNodeAtIndexPathDeprecated) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + result = [collectionView.asyncDelegate collectionView:collectionView constrainedSizeForNodeAtIndexPath:indexPath]; +#pragma clang diagnostic pop + } else { + // With 2.0 `collectionView:constrainedSizeForNodeAtIndexPath:` was moved to the delegate. Assert if not implemented on the delegate but on the data source + ASDisplayNodeAssert([collectionView.asyncDataSource respondsToSelector:@selector(collectionView:constrainedSizeForNodeAtIndexPath:)] == NO, @"collectionView:constrainedSizeForNodeAtIndexPath: was moved from the ASCollectionDataSource to the ASCollectionDelegate."); + } + + // If we got no size range: + if (ASSizeRangeEqualToSizeRange(result, ASSizeRangeUnconstrained)) { + // Use itemSize if they set it. + CGSize itemSize = _layout.itemSize; + if (CGSizeEqualToSize(itemSize, kDefaultItemSize) == NO) { + result = ASSizeRangeMake(itemSize, itemSize); + } else { + // Compute constraint from scroll direction otherwise. + result = NodeConstrainedSizeForScrollDirection(collectionView); + } + } + + return result; +} + +- (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath +{ + ASSizeRange result = ASSizeRangeZero; + if (ASObjectIsEqual(kind, UICollectionElementKindSectionHeader)) { + if (_delegateFlags.implementsSizeRangeForHeader) { + result = [[self delegateForCollectionView:collectionView] collectionNode:collectionView.collectionNode sizeRangeForHeaderInSection:indexPath.section]; + } else if (_delegateFlags.implementsReferenceSizeForHeader) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + CGSize exactSize = [[self delegateForCollectionView:collectionView] collectionView:collectionView layout:_layout referenceSizeForHeaderInSection:indexPath.section]; +#pragma clang diagnostic pop + result = ASSizeRangeMake(exactSize); + } else { + result = ASSizeRangeMake(_layout.headerReferenceSize); + } + } else if (ASObjectIsEqual(kind, UICollectionElementKindSectionFooter)) { + if (_delegateFlags.implementsSizeRangeForFooter) { + result = [[self delegateForCollectionView:collectionView] collectionNode:collectionView.collectionNode sizeRangeForFooterInSection:indexPath.section]; + } else if (_delegateFlags.implementsReferenceSizeForFooter) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + CGSize exactSize = [[self delegateForCollectionView:collectionView] collectionView:collectionView layout:_layout referenceSizeForFooterInSection:indexPath.section]; +#pragma clang diagnostic pop + result = ASSizeRangeMake(exactSize); + } else { + result = ASSizeRangeMake(_layout.footerReferenceSize); + } + } else { + ASDisplayNodeFailAssert(@"Unexpected supplementary kind: %@", kind); + return ASSizeRangeZero; + } + + if (_layout.scrollDirection == UICollectionViewScrollDirectionVertical) { + result.min.width = result.max.width = CGRectGetWidth(collectionView.bounds); + } else { + result.min.height = result.max.height = CGRectGetHeight(collectionView.bounds); + } + return result; +} + +- (NSUInteger)collectionView:(ASCollectionView *)collectionView supplementaryNodesOfKind:(NSString *)kind inSection:(NSUInteger)section +{ + ASSizeRange constraint = [self collectionView:collectionView constrainedSizeForSupplementaryNodeOfKind:kind atIndexPath:[NSIndexPath indexPathForItem:0 inSection:section]]; + if (_layout.scrollDirection == UICollectionViewScrollDirectionVertical) { + return (constraint.max.height > 0 ? 1 : 0); + } else { + return (constraint.max.width > 0 ? 1 : 0); + } +} + +- (ASScrollDirection)scrollableDirections +{ + return (self.layout.scrollDirection == UICollectionViewScrollDirectionHorizontal) ? ASScrollDirectionHorizontalDirections : ASScrollDirectionVerticalDirections; +} + +#pragma mark - Private helpers + +- (id)delegateForCollectionView:(ASCollectionView *)collectionView +{ + return (id)collectionView.asyncDelegate; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASControlNode+Private.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASControlNode+Private.h new file mode 100644 index 0000000..02f54a2 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASControlNode+Private.h @@ -0,0 +1,18 @@ +// +// ASControlNode+Private.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +@interface ASControlNode (Private) + +#if TARGET_OS_TV +- (void)_pressDown; +#endif + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASControlTargetAction.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASControlTargetAction.h new file mode 100644 index 0000000..5a3595c --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASControlTargetAction.h @@ -0,0 +1,32 @@ +// +// ASControlTargetAction.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +/** + @abstract ASControlTargetAction stores target action pairs registered for specific ASControlNodeEvent values. + */ +@interface ASControlTargetAction : NSObject + +/** + The action to be called on the registered target. + */ +@property (nonatomic) SEL action; + +/** + Event handler target. The specified action will be called on this object. + */ +@property (nonatomic, weak) id target; + +/** + Indicated whether this target was created without a target, so the action should travel up in the responder chain. + */ +@property (nonatomic, readonly) BOOL createdWithNoTarget; + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASControlTargetAction.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASControlTargetAction.mm new file mode 100644 index 0000000..41cc113 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASControlTargetAction.mm @@ -0,0 +1,65 @@ +// +// ASControlTargetAction.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +@implementation ASControlTargetAction +{ + __weak id _target; + BOOL _createdWithNoTarget; +} + +- (void)setTarget:(id)target { + _target = target; + + if (!target) { + _createdWithNoTarget = YES; + } +} + +- (id)target { + return _target; +} + +- (BOOL)isEqual:(id)object { + if (![object isKindOfClass:[ASControlTargetAction class]]) { + return NO; + } + + ASControlTargetAction *otherObject = (ASControlTargetAction *)object; + + BOOL areTargetsEqual; + + if (self.target != nil && otherObject.target != nil && self.target == otherObject.target) { + areTargetsEqual = YES; + } + else if (self.target == nil && otherObject.target == nil && self.createdWithNoTarget && otherObject.createdWithNoTarget) { + areTargetsEqual = YES; + } + else { + areTargetsEqual = NO; + } + + if (!areTargetsEqual) { + return NO; + } + + if (self.action && otherObject.action && self.action == otherObject.action) { + return YES; + } + else { + return NO; + } +} + +- (NSUInteger)hash { + return [self.target hash]; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDefaultPlayButton.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDefaultPlayButton.h new file mode 100644 index 0000000..e3bc152 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDefaultPlayButton.h @@ -0,0 +1,14 @@ +// +// ASDefaultPlayButton.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +@interface ASDefaultPlayButton : ASButtonNode + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDefaultPlayButton.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDefaultPlayButton.mm new file mode 100644 index 0000000..589bda9 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDefaultPlayButton.mm @@ -0,0 +1,66 @@ +// +// ASDefaultPlayButton.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +@implementation ASDefaultPlayButton + +- (instancetype)init +{ + if (!(self = [super init])) { + return nil; + } + + self.opaque = NO; + + return self; +} + ++ (void)drawRect:(CGRect)bounds withParameters:(id)parameters isCancelled:(asdisplaynode_iscancelled_block_t)isCancelledBlock isRasterizing:(BOOL)isRasterizing +{ + CGFloat originX = bounds.size.width/4; + CGRect buttonBounds = CGRectMake(originX, bounds.size.height/4, bounds.size.width/2, bounds.size.height/2); + CGFloat widthHeight = buttonBounds.size.width; + + //When the video isn't a square, the lower bound should be used to figure out the circle size + if (bounds.size.width < bounds.size.height) { + widthHeight = bounds.size.width/2; + originX = (bounds.size.width - widthHeight)/2; + buttonBounds = CGRectMake(originX, (bounds.size.height - widthHeight)/2, widthHeight, widthHeight); + } + if (bounds.size.width > bounds.size.height) { + widthHeight = bounds.size.height/2; + originX = (bounds.size.width - widthHeight)/2; + buttonBounds = CGRectMake(originX, (bounds.size.height - widthHeight)/2, widthHeight, widthHeight); + } + + CGContextRef context = UIGraphicsGetCurrentContext(); + + // Circle Drawing + UIBezierPath *ovalPath = [UIBezierPath bezierPathWithOvalInRect: buttonBounds]; + [[UIColor colorWithWhite:0.0 alpha:0.5] setFill]; + [ovalPath fill]; + + // Triangle Drawing + CGContextSaveGState(context); + + UIBezierPath *trianglePath = [UIBezierPath bezierPath]; + [trianglePath moveToPoint:CGPointMake(originX + widthHeight/3, bounds.size.height/4 + (bounds.size.height/2)/4)]; + [trianglePath addLineToPoint:CGPointMake(originX + widthHeight/3, bounds.size.height - bounds.size.height/4 - (bounds.size.height/2)/4)]; + [trianglePath addLineToPoint:CGPointMake(bounds.size.width - originX - widthHeight/4, bounds.size.height/2)]; + + [trianglePath closePath]; + [[UIColor colorWithWhite:0.9 alpha:0.9] setFill]; + [trianglePath fill]; + + CGContextRestoreGState(context); +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDefaultPlaybackButton.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDefaultPlaybackButton.h new file mode 100644 index 0000000..ae7e245 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDefaultPlaybackButton.h @@ -0,0 +1,19 @@ +// +// ASDefaultPlaybackButton.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +typedef NS_ENUM(NSInteger, ASDefaultPlaybackButtonType) { + ASDefaultPlaybackButtonTypePlay, + ASDefaultPlaybackButtonTypePause +}; + +@interface ASDefaultPlaybackButton : ASControlNode +@property (nonatomic) ASDefaultPlaybackButtonType buttonType; +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDefaultPlaybackButton.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDefaultPlaybackButton.mm new file mode 100644 index 0000000..deda7e8 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDefaultPlaybackButton.mm @@ -0,0 +1,84 @@ +// +// ASDefaultPlaybackButton.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +@interface ASDefaultPlaybackButton() +{ + ASDefaultPlaybackButtonType _buttonType; +} +@end + +@implementation ASDefaultPlaybackButton +- (instancetype)init +{ + if (!(self = [super init])) { + return nil; + } + + self.opaque = NO; + + return self; +} + +- (void)setButtonType:(ASDefaultPlaybackButtonType)buttonType +{ + ASDefaultPlaybackButtonType oldType = _buttonType; + _buttonType = buttonType; + + if (oldType != _buttonType) { + [self setNeedsDisplay]; + } +} + +- (nullable NSDictionary *)drawParametersForAsyncLayer:(_ASDisplayLayer *)layer +{ + return @{ + @"buttonType" : @(self.buttonType), + @"color" : self.tintColor + }; +} + ++ (void)drawRect:(CGRect)bounds withParameters:(NSDictionary *)parameters isCancelled:(asdisplaynode_iscancelled_block_t)isCancelledBlock isRasterizing:(BOOL)isRasterizing +{ + ASDefaultPlaybackButtonType buttonType = (ASDefaultPlaybackButtonType)[parameters[@"buttonType"] intValue]; + UIColor *color = parameters[@"color"]; + + CGContextRef context = UIGraphicsGetCurrentContext(); + CGContextSaveGState(context); + UIBezierPath* bezierPath = [UIBezierPath bezierPath]; + if (buttonType == ASDefaultPlaybackButtonTypePlay) { + [bezierPath moveToPoint: CGPointMake(0, 0)]; + [bezierPath addLineToPoint: CGPointMake(0, bounds.size.height)]; + [bezierPath addLineToPoint: CGPointMake(bounds.size.width, bounds.size.height/2)]; + [bezierPath addLineToPoint: CGPointMake(0, 0)]; + [bezierPath closePath]; + } else if (buttonType == ASDefaultPlaybackButtonTypePause) { + CGFloat pauseSingleLineWidth = bounds.size.width / 3.0; + [bezierPath moveToPoint: CGPointMake(0, bounds.size.height)]; + [bezierPath addLineToPoint: CGPointMake(pauseSingleLineWidth, bounds.size.height)]; + [bezierPath addLineToPoint: CGPointMake(pauseSingleLineWidth, 0)]; + [bezierPath addLineToPoint: CGPointMake(0, 0)]; + [bezierPath addLineToPoint: CGPointMake(0, bounds.size.height)]; + [bezierPath closePath]; + [bezierPath moveToPoint: CGPointMake(pauseSingleLineWidth * 2, 0)]; + [bezierPath addLineToPoint: CGPointMake(pauseSingleLineWidth * 2, bounds.size.height)]; + [bezierPath addLineToPoint: CGPointMake(bounds.size.width, bounds.size.height)]; + [bezierPath addLineToPoint: CGPointMake(bounds.size.width, 0)]; + [bezierPath addLineToPoint: CGPointMake(pauseSingleLineWidth * 2, 0)]; + [bezierPath closePath]; + } + + [color setFill]; + [bezierPath fill]; + + CGContextRestoreGState(context); +} +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDispatch.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDispatch.h new file mode 100644 index 0000000..caccf74 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDispatch.h @@ -0,0 +1,27 @@ +// +// ASDispatch.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +/** + * Like dispatch_apply, but you can set the thread count. 0 means 2*active CPUs. + * + * Note: The actual number of threads may be lower than threadCount, if libdispatch + * decides the system can't handle it. In reality this rarely happens. + */ +ASDK_EXTERN void ASDispatchApply(size_t iterationCount, dispatch_queue_t queue, NSUInteger threadCount, NS_NOESCAPE void(^work)(size_t i)); + +/** + * Like dispatch_async, but you can set the thread count. 0 means 2*active CPUs. + * + * Note: The actual number of threads may be lower than threadCount, if libdispatch + * decides the system can't handle it. In reality this rarely happens. + */ +ASDK_EXTERN void ASDispatchAsync(size_t iterationCount, dispatch_queue_t queue, NSUInteger threadCount, NS_NOESCAPE void(^work)(size_t i)); diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDispatch.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDispatch.mm new file mode 100644 index 0000000..769a918 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDispatch.mm @@ -0,0 +1,63 @@ +// +// ASDispatch.mm +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + + +// Prefer C atomics in this file because ObjC blocks can't capture C++ atomics well. +#import + +/** + * Like dispatch_apply, but you can set the thread count. 0 means 2*active CPUs. + * + * Note: The actual number of threads may be lower than threadCount, if libdispatch + * decides the system can't handle it. In reality this rarely happens. + */ +void ASDispatchApply(size_t iterationCount, dispatch_queue_t queue, NSUInteger threadCount, NS_NOESCAPE void(^work)(size_t i)) { + if (threadCount == 0) { + if (ASActivateExperimentalFeature(ASExperimentalDispatchApply)) { + dispatch_apply(iterationCount, queue, work); + return; + } + threadCount = NSProcessInfo.processInfo.activeProcessorCount * 2; + } + dispatch_group_t group = dispatch_group_create(); + __block atomic_size_t counter = ATOMIC_VAR_INIT(0); + for (NSUInteger t = 0; t < threadCount; t++) { + dispatch_group_async(group, queue, ^{ + size_t i; + while ((i = atomic_fetch_add(&counter, 1)) < iterationCount) { + work(i); + } + }); + } + dispatch_group_wait(group, DISPATCH_TIME_FOREVER); +}; + +/** + * Like dispatch_async, but you can set the thread count. 0 means 2*active CPUs. + * + * Note: The actual number of threads may be lower than threadCount, if libdispatch + * decides the system can't handle it. In reality this rarely happens. + */ +void ASDispatchAsync(size_t iterationCount, dispatch_queue_t queue, NSUInteger threadCount, NS_NOESCAPE void(^work)(size_t i)) { + if (threadCount == 0) { + threadCount = NSProcessInfo.processInfo.activeProcessorCount * 2; + } + __block atomic_size_t counter = ATOMIC_VAR_INIT(0); + for (NSUInteger t = 0; t < threadCount; t++) { + dispatch_async(queue, ^{ + size_t i; + while ((i = atomic_fetch_add(&counter, 1)) < iterationCount) { + work(i); + } + }); + } +}; + diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDisplayNode+AsyncDisplay.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDisplayNode+AsyncDisplay.mm new file mode 100644 index 0000000..fd77a06 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDisplayNode+AsyncDisplay.mm @@ -0,0 +1,494 @@ +// +// ASDisplayNode+AsyncDisplay.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import +#import +#import +#import +#import + +using AS::MutexLocker; + +@interface ASDisplayNode () <_ASDisplayLayerDelegate> +@end + +@implementation ASDisplayNode (AsyncDisplay) + +#if ASDISPLAYNODE_DELAY_DISPLAY + #define ASDN_DELAY_FOR_DISPLAY() usleep( (long)(0.1 * USEC_PER_SEC) ) +#else + #define ASDN_DELAY_FOR_DISPLAY() +#endif + +#define CHECK_CANCELLED_AND_RETURN_NIL(expr) if (isCancelledBlock()) { \ + expr; \ + return nil; \ + } \ + +- (NSObject *)drawParameters +{ + __instanceLock__.lock(); + BOOL implementsDrawParameters = _flags.implementsDrawParameters; + __instanceLock__.unlock(); + + if (implementsDrawParameters) { + return [self drawParametersForAsyncLayer:self.asyncLayer]; + } else { + return nil; + } +} + +- (void)_recursivelyRasterizeSelfAndSublayersWithIsCancelledBlock:(asdisplaynode_iscancelled_block_t)isCancelledBlock displayBlocks:(NSMutableArray *)displayBlocks +{ + // Skip subtrees that are hidden or zero alpha. + if (self.isHidden || self.alpha <= 0.0) { + return; + } + + __instanceLock__.lock(); + BOOL rasterizingFromAscendent = (_hierarchyState & ASHierarchyStateRasterized); + __instanceLock__.unlock(); + + // if super node is rasterizing descendants, subnodes will not have had layout calls because they don't have layers + if (rasterizingFromAscendent) { + [self __layout]; + } + + // Capture these outside the display block so they are retained. + UIColor *backgroundColor = self.backgroundColor; + CGRect bounds = self.bounds; + CGFloat cornerRadius = self.cornerRadius; + BOOL clipsToBounds = self.clipsToBounds; + + CGRect frame; + + // If this is the root container node, use a frame with a zero origin to draw into. If not, calculate the correct frame using the node's position, transform and anchorPoint. + if (self.rasterizesSubtree) { + frame = CGRectMake(0.0f, 0.0f, bounds.size.width, bounds.size.height); + } else { + CGPoint position = self.position; + CGPoint anchorPoint = self.anchorPoint; + + // Pretty hacky since full 3D transforms aren't actually supported, but attempt to compute the transformed frame of this node so that we can composite it into approximately the right spot. + CGAffineTransform transform = CATransform3DGetAffineTransform(self.transform); + CGSize scaledBoundsSize = CGSizeApplyAffineTransform(bounds.size, transform); + CGPoint origin = CGPointMake(position.x - scaledBoundsSize.width * anchorPoint.x, + position.y - scaledBoundsSize.height * anchorPoint.y); + frame = CGRectMake(origin.x, origin.y, bounds.size.width, bounds.size.height); + } + + // Get the display block for this node. + asyncdisplaykit_async_transaction_operation_block_t displayBlock = [self _displayBlockWithAsynchronous:NO isCancelledBlock:isCancelledBlock rasterizing:YES]; + + // We'll display something if there is a display block, clipping, translation and/or a background color. + BOOL shouldDisplay = displayBlock || backgroundColor || CGPointEqualToPoint(CGPointZero, frame.origin) == NO || clipsToBounds; + + // If we should display, then push a transform, draw the background color, and draw the contents. + // The transform is popped in a block added after the recursion into subnodes. + if (shouldDisplay) { + dispatch_block_t pushAndDisplayBlock = ^{ + // Push transform relative to parent. + CGContextRef context = UIGraphicsGetCurrentContext(); + CGContextSaveGState(context); + + CGContextTranslateCTM(context, frame.origin.x, frame.origin.y); + + //support cornerRadius + if (rasterizingFromAscendent && clipsToBounds) { + if (cornerRadius) { + [[UIBezierPath bezierPathWithRoundedRect:bounds cornerRadius:cornerRadius] addClip]; + } else { + CGContextClipToRect(context, bounds); + } + } + + // Fill background if any. + CGColorRef backgroundCGColor = backgroundColor.CGColor; + if (backgroundColor && CGColorGetAlpha(backgroundCGColor) > 0.0) { + CGContextSetFillColorWithColor(context, backgroundCGColor); + CGContextFillRect(context, bounds); + } + + // If there is a display block, call it to get the image, then copy the image into the current context (which is the rasterized container's backing store). + if (displayBlock) { + UIImage *image = (UIImage *)displayBlock(); + if (image) { + BOOL opaque = ASImageAlphaInfoIsOpaque(CGImageGetAlphaInfo(image.CGImage)); + CGBlendMode blendMode = opaque ? kCGBlendModeCopy : kCGBlendModeNormal; + [image drawInRect:bounds blendMode:blendMode alpha:1]; + } + } + }; + [displayBlocks addObject:pushAndDisplayBlock]; + } + + // Recursively capture displayBlocks for all descendants. + for (ASDisplayNode *subnode in self.subnodes) { + [subnode _recursivelyRasterizeSelfAndSublayersWithIsCancelledBlock:isCancelledBlock displayBlocks:displayBlocks]; + } + + // If we pushed a transform, pop it by adding a display block that does nothing other than that. + if (shouldDisplay) { + // Since this block is pure, we can store it statically. + static dispatch_block_t popBlock; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + popBlock = ^{ + CGContextRef context = UIGraphicsGetCurrentContext(); + CGContextRestoreGState(context); + }; + }); + [displayBlocks addObject:popBlock]; + } +} + +- (asyncdisplaykit_async_transaction_operation_block_t)_displayBlockWithAsynchronous:(BOOL)asynchronous + isCancelledBlock:(asdisplaynode_iscancelled_block_t)isCancelledBlock + rasterizing:(BOOL)rasterizing +{ + ASDisplayNodeAssertMainThread(); + + asyncdisplaykit_async_transaction_operation_block_t displayBlock = nil; + ASDisplayNodeFlags flags; + + __instanceLock__.lock(); + + flags = _flags; + + // We always create a graphics context, unless a -display method is used, OR if we are a subnode drawing into a rasterized parent. + BOOL shouldCreateGraphicsContext = (flags.implementsImageDisplay == NO && rasterizing == NO); + BOOL shouldBeginRasterizing = (rasterizing == NO && flags.rasterizesSubtree); + BOOL usesImageDisplay = flags.implementsImageDisplay; + BOOL usesDrawRect = flags.implementsDrawRect; + + if (usesImageDisplay == NO && usesDrawRect == NO && shouldBeginRasterizing == NO) { + // Early exit before requesting more expensive properties like bounds and opaque from the layer. + __instanceLock__.unlock(); + return nil; + } + + BOOL opaque = self.opaque; + CGRect bounds = self.bounds; + UIColor *backgroundColor = self.backgroundColor; + CGColorRef borderColor = self.borderColor; + CGFloat borderWidth = self.borderWidth; + CGFloat contentsScaleForDisplay = _contentsScaleForDisplay; + + __instanceLock__.unlock(); + + // Capture drawParameters from delegate on main thread, if this node is displaying itself rather than recursively rasterizing. + id drawParameters = (shouldBeginRasterizing == NO ? [self drawParameters] : nil); + + // Only the -display methods should be called if we can't size the graphics buffer to use. + if (CGRectIsEmpty(bounds) && (shouldBeginRasterizing || shouldCreateGraphicsContext)) { + return nil; + } + + ASDisplayNodeAssert(contentsScaleForDisplay != 0.0, @"Invalid contents scale"); + ASDisplayNodeAssert(rasterizing || !(_hierarchyState & ASHierarchyStateRasterized), + @"Rasterized descendants should never display unless being drawn into the rasterized container."); + + if (shouldBeginRasterizing) { + // Collect displayBlocks for all descendants. + NSMutableArray *displayBlocks = [[NSMutableArray alloc] init]; + [self _recursivelyRasterizeSelfAndSublayersWithIsCancelledBlock:isCancelledBlock displayBlocks:displayBlocks]; + CHECK_CANCELLED_AND_RETURN_NIL(); + + // If [UIColor clearColor] or another semitransparent background color is used, include alpha channel when rasterizing. + // Unlike CALayer drawing, we include the backgroundColor as a base during rasterization. + opaque = opaque && CGColorGetAlpha(backgroundColor.CGColor) == 1.0f; + + displayBlock = ^id{ + CHECK_CANCELLED_AND_RETURN_NIL(); + + UIImage *image = ASGraphicsCreateImage(self.primitiveTraitCollection, bounds.size, opaque, contentsScaleForDisplay, nil, isCancelledBlock, ^{ + for (dispatch_block_t block in displayBlocks) { + if (isCancelledBlock()) return; + block(); + } + }); + + ASDN_DELAY_FOR_DISPLAY(); + return image; + }; + } else { + displayBlock = ^id{ + CHECK_CANCELLED_AND_RETURN_NIL(); + + __block UIImage *image = nil; + void (^workWithContext)() = ^{ + CGContextRef currentContext = UIGraphicsGetCurrentContext(); + + if (shouldCreateGraphicsContext && !currentContext) { + ASDisplayNodeAssert(NO, @"Failed to create a CGContext (size: %@)", NSStringFromCGSize(bounds.size)); + return; + } + + // For -display methods, we don't have a context, and thus will not call the _willDisplayNodeContentWithRenderingContext or + // _didDisplayNodeContentWithRenderingContext blocks. It's up to the implementation of -display... to do what it needs. + [self __willDisplayNodeContentWithRenderingContext:currentContext drawParameters:drawParameters]; + + if (usesImageDisplay) { // If we are using a display method, we'll get an image back directly. + image = [self.class displayWithParameters:drawParameters isCancelled:isCancelledBlock]; + } else if (usesDrawRect) { // If we're using a draw method, this will operate on the currentContext. + [self.class drawRect:bounds withParameters:drawParameters isCancelled:isCancelledBlock isRasterizing:rasterizing]; + } + + [self __didDisplayNodeContentWithRenderingContext:currentContext image:&image drawParameters:drawParameters backgroundColor:backgroundColor borderWidth:borderWidth borderColor:borderColor]; + ASDN_DELAY_FOR_DISPLAY(); + }; + + if (shouldCreateGraphicsContext) { + return ASGraphicsCreateImage(self.primitiveTraitCollection, bounds.size, opaque, contentsScaleForDisplay, nil, isCancelledBlock, workWithContext); + } else { + workWithContext(); + return image; + } + }; + } + + /** + If we're profiling, wrap the display block with signpost start and end. + Color the interval red if cancelled, green otherwise. + */ +#if AS_SIGNPOST_ENABLE + unowned id ptrSelf = (id)self; + displayBlock = ^{ + ASSignpostStart(LayerDisplay, ptrSelf, "%@", ASObjectDescriptionMakeTiny(ptrSelf)); + id result = displayBlock(); + ASSignpostEnd(LayerDisplay, ptrSelf, "(%d %d), canceled: %d", (int)bounds.size.width, (int)bounds.size.height, (int)isCancelledBlock()); + return result; + }; +#endif + + return displayBlock; +} + +- (void)__willDisplayNodeContentWithRenderingContext:(CGContextRef)context drawParameters:(id _Nullable)drawParameters +{ + if (context) { + __instanceLock__.lock(); + ASCornerRoundingType cornerRoundingType = _cornerRoundingType; + CGFloat cornerRadius = _cornerRadius; + ASDisplayNodeContextModifier willDisplayNodeContentWithRenderingContext = _willDisplayNodeContentWithRenderingContext; + CACornerMask maskedCorners = _maskedCorners; + __instanceLock__.unlock(); + + if (cornerRoundingType == ASCornerRoundingTypePrecomposited && cornerRadius > 0.0) { + ASDisplayNodeAssert(context == UIGraphicsGetCurrentContext(), @"context is expected to be pushed on UIGraphics stack %@", self); + // TODO: This clip path should be removed if we are rasterizing. + CGRect boundingBox = CGContextGetClipBoundingBox(context); + CGSize radii = CGSizeMake(cornerRadius, cornerRadius); + [[UIBezierPath bezierPathWithRoundedRect:boundingBox byRoundingCorners:maskedCorners cornerRadii:radii] addClip]; + } + + if (willDisplayNodeContentWithRenderingContext) { + willDisplayNodeContentWithRenderingContext(context, drawParameters); + } + } + +} +- (void)__didDisplayNodeContentWithRenderingContext:(CGContextRef)context image:(UIImage **)image drawParameters:(id _Nullable)drawParameters backgroundColor:(UIColor *)backgroundColor borderWidth:(CGFloat)borderWidth borderColor:(CGColorRef)borderColor +{ + if (context == NULL && *image == NULL) { + return; + } + + __instanceLock__.lock(); + ASCornerRoundingType cornerRoundingType = _cornerRoundingType; + CGFloat cornerRadius = _cornerRadius; + CGFloat contentsScale = _contentsScaleForDisplay; + ASDisplayNodeContextModifier didDisplayNodeContentWithRenderingContext = _didDisplayNodeContentWithRenderingContext; + CACornerMask maskedCorners = _maskedCorners; + __instanceLock__.unlock(); + + if (context != NULL) { + if (didDisplayNodeContentWithRenderingContext) { + didDisplayNodeContentWithRenderingContext(context, drawParameters); + } + } + + if (cornerRoundingType == ASCornerRoundingTypePrecomposited && cornerRadius > 0.0f) { + CGRect bounds = CGRectZero; + if (context == NULL) { + bounds = self.threadSafeBounds; + bounds.size.width *= contentsScale; + bounds.size.height *= contentsScale; + CGFloat white = 0.0f, alpha = 0.0f; + [backgroundColor getWhite:&white alpha:&alpha]; + UIGraphicsBeginImageContextWithOptions(bounds.size, (alpha == 1.0f), contentsScale); + [*image drawInRect:bounds]; + } else { + bounds = CGContextGetClipBoundingBox(context); + } + + ASDisplayNodeAssert(UIGraphicsGetCurrentContext(), @"context is expected to be pushed on UIGraphics stack %@", self); + + UIBezierPath *roundedHole = [UIBezierPath bezierPathWithRect:bounds]; + CGSize radii = CGSizeMake(cornerRadius * contentsScale, cornerRadius * contentsScale); + [roundedHole appendPath:[UIBezierPath bezierPathWithRoundedRect:bounds + byRoundingCorners:maskedCorners + cornerRadii:radii]]; + roundedHole.usesEvenOddFillRule = YES; + + UIBezierPath *roundedPath = nil; + if (borderWidth > 0.0f) { // Don't create roundedPath and stroke if borderWidth is 0.0 + CGFloat strokeThickness = borderWidth * contentsScale; + CGFloat strokeInset = ((strokeThickness + 1.0f) / 2.0f) - 1.0f; + roundedPath = [UIBezierPath bezierPathWithRoundedRect:CGRectInset(bounds, strokeInset, strokeInset) + cornerRadius:_cornerRadius * contentsScale]; + roundedPath.lineWidth = strokeThickness; + [[UIColor colorWithCGColor:borderColor] setStroke]; + } + + // Punch out the corners by copying the backgroundColor over them. + // This works for everything from clearColor to opaque colors. + [backgroundColor setFill]; + [roundedHole fillWithBlendMode:kCGBlendModeCopy alpha:1.0f]; + + [roundedPath stroke]; // Won't do anything if borderWidth is 0 and roundedPath is nil. + + if (*image) { + *image = UIGraphicsGetImageFromCurrentImageContext(); + } + if (context == NULL) { + UIGraphicsEndImageContext(); + } + } +} + +- (void)displayAsyncLayer:(_ASDisplayLayer *)asyncLayer asynchronously:(BOOL)asynchronously +{ + ASDisplayNodeAssertMainThread(); + + __instanceLock__.lock(); + + if (_hierarchyState & ASHierarchyStateRasterized) { + __instanceLock__.unlock(); + return; + } + + CALayer *layer = _layer; + BOOL rasterizesSubtree = _flags.rasterizesSubtree; + + __instanceLock__.unlock(); + + // for async display, capture the current displaySentinel value to bail early when the job is executed if another is + // enqueued + // for sync display, do not support cancellation + + // FIXME: what about the degenerate case where we are calling setNeedsDisplay faster than the jobs are dequeuing + // from the displayQueue? Need to not cancel early fails from displaySentinel changes. + asdisplaynode_iscancelled_block_t isCancelledBlock = nil; + if (asynchronously) { + uint displaySentinelValue = ++_displaySentinel; + __weak ASDisplayNode *weakSelf = self; + isCancelledBlock = ^BOOL{ + __strong ASDisplayNode *self = weakSelf; + return self == nil || (displaySentinelValue != self->_displaySentinel.load()); + }; + } else { + isCancelledBlock = ^BOOL{ + return NO; + }; + } + + // Set up displayBlock to call either display or draw on the delegate and return a UIImage contents + asyncdisplaykit_async_transaction_operation_block_t displayBlock = [self _displayBlockWithAsynchronous:asynchronously isCancelledBlock:isCancelledBlock rasterizing:NO]; + + if (!displayBlock) { + return; + } + + ASDisplayNodeAssert(layer, @"Expect _layer to be not nil"); + + // This block is called back on the main thread after rendering at the completion of the current async transaction, or immediately if !asynchronously + asyncdisplaykit_async_transaction_operation_completion_block_t completionBlock = ^(id value, BOOL canceled){ + ASDisplayNodeCAssertMainThread(); + if (!canceled && !isCancelledBlock()) { + UIImage *image = (UIImage *)value; + BOOL stretchable = (NO == UIEdgeInsetsEqualToEdgeInsets(image.capInsets, UIEdgeInsetsZero)); + if (stretchable) { + ASDisplayNodeSetResizableContents(layer, image); + } else { + layer.contentsScale = self.contentsScale; + layer.contents = (id)image.CGImage; + } + [self didDisplayAsyncLayer:self.asyncLayer]; + + if (rasterizesSubtree) { + ASDisplayNodePerformBlockOnEverySubnode(self, NO, ^(ASDisplayNode * _Nonnull node) { + [node didDisplayAsyncLayer:node.asyncLayer]; + }); + } + } + }; + + // Call willDisplay immediately in either case + [self willDisplayAsyncLayer:self.asyncLayer asynchronously:asynchronously]; + + if (rasterizesSubtree) { + ASDisplayNodePerformBlockOnEverySubnode(self, NO, ^(ASDisplayNode * _Nonnull node) { + [node willDisplayAsyncLayer:node.asyncLayer asynchronously:asynchronously]; + }); + } + + if (asynchronously) { + // Async rendering operations are contained by a transaction, which allows them to proceed and concurrently + // while synchronizing the final application of the results to the layer's contents property (completionBlock). + + // First, look to see if we are expected to join a parent's transaction container. + CALayer *containerLayer = layer.asyncdisplaykit_parentTransactionContainer ? : layer; + + // In the case that a transaction does not yet exist (such as for an individual node outside of a container), + // this call will allocate the transaction and add it to _ASAsyncTransactionGroup. + // It will automatically commit the transaction at the end of the runloop. + _ASAsyncTransaction *transaction = containerLayer.asyncdisplaykit_asyncTransaction; + + // Adding this displayBlock operation to the transaction will start it IMMEDIATELY. + // The only function of the transaction commit is to gate the calling of the completionBlock. + [transaction addOperationWithBlock:displayBlock priority:self.drawingPriority queue:[_ASDisplayLayer displayQueue] completion:completionBlock]; + } else { + UIImage *contents = (UIImage *)displayBlock(); + completionBlock(contents, NO); + } +} + +- (void)cancelDisplayAsyncLayer:(_ASDisplayLayer *)asyncLayer +{ + _displaySentinel.fetch_add(1); +} + +- (ASDisplayNodeContextModifier)willDisplayNodeContentWithRenderingContext +{ + MutexLocker l(__instanceLock__); + return _willDisplayNodeContentWithRenderingContext; +} + +- (ASDisplayNodeContextModifier)didDisplayNodeContentWithRenderingContext +{ + MutexLocker l(__instanceLock__); + return _didDisplayNodeContentWithRenderingContext; +} + +- (void)setWillDisplayNodeContentWithRenderingContext:(ASDisplayNodeContextModifier)contextModifier +{ + MutexLocker l(__instanceLock__); + _willDisplayNodeContentWithRenderingContext = contextModifier; +} + +- (void)setDidDisplayNodeContentWithRenderingContext:(ASDisplayNodeContextModifier)contextModifier; +{ + MutexLocker l(__instanceLock__); + _didDisplayNodeContentWithRenderingContext = contextModifier; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDisplayNode+DebugTiming.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDisplayNode+DebugTiming.h new file mode 100644 index 0000000..f693522 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDisplayNode+DebugTiming.h @@ -0,0 +1,21 @@ +// +// ASDisplayNode+DebugTiming.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +@interface ASDisplayNode (DebugTiming) + +@property (nonatomic, readonly) NSTimeInterval debugTimeToCreateView; +@property (nonatomic, readonly) NSTimeInterval debugTimeToApplyPendingState; +@property (nonatomic, readonly) NSTimeInterval debugTimeToAddSubnodeViews; +@property (nonatomic, readonly) NSTimeInterval debugTimeForDidLoad; + +- (NSTimeInterval)debugAllCreationTime; + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDisplayNode+DebugTiming.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDisplayNode+DebugTiming.mm new file mode 100644 index 0000000..d9311a1 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDisplayNode+DebugTiming.mm @@ -0,0 +1,85 @@ +// +// ASDisplayNode+DebugTiming.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +@implementation ASDisplayNode (DebugTiming) + +#if TIME_DISPLAYNODE_OPS +- (NSTimeInterval)debugTimeToCreateView +{ + return _debugTimeToCreateView; +} + +- (NSTimeInterval)debugTimeToApplyPendingState +{ + return _debugTimeToApplyPendingState; +} + +- (NSTimeInterval)debugTimeToAddSubnodeViews +{ + return _debugTimeToAddSubnodeViews; +} + +- (NSTimeInterval)debugTimeForDidLoad +{ + return _debugTimeForDidLoad; +} + +- (NSTimeInterval)debugAllCreationTime +{ + return self.debugTimeToCreateView + self.debugTimeToApplyPendingState + self.debugTimeToAddSubnodeViews + self.debugTimeForDidLoad; +} + +// This would over-count views that are created in the parent's didload or addsubnodesubviews, so we need to take a more basic approach +//- (NSTimeInterval)debugRecursiveAllCreationTime +//{ +// __block NSTimeInterval total = 0; +// ASDisplayNodeFindAllSubnodes(self, ^(ASDisplayNode *n){ +// total += self.debugTimeToCreateView; +// total += self.debugTimeToApplyPendingState; +// total += self.debugTimeToAddSubnodeViews; +// total += self.debugTimeForDidLoad; +// return NO; +// }); +// return total; +//} + +#else + +// These ivars are compiled out so we don't have the info available +- (NSTimeInterval)debugTimeToCreateView +{ + return -1; +} + +- (NSTimeInterval)debugTimeToApplyPendingState +{ + return -1; +} + +- (NSTimeInterval)debugTimeToAddSubnodeViews +{ + return -1; +} + +- (NSTimeInterval)debugTimeForDidLoad +{ + return -1; +} + +- (NSTimeInterval)debugAllCreationTime +{ + return -1; +} + +#endif + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDisplayNode+FrameworkPrivate.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDisplayNode+FrameworkPrivate.h new file mode 100644 index 0000000..ce53e79 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDisplayNode+FrameworkPrivate.h @@ -0,0 +1,335 @@ +// +// ASDisplayNode+FrameworkPrivate.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +// +// The following methods are ONLY for use by _ASDisplayLayer, _ASDisplayView, and ASDisplayNode. +// These methods must never be called or overridden by other classes. +// + +#import +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@protocol ASInterfaceStateDelegate; + +/** + Hierarchy state is propagated from nodes to all of their children when certain behaviors are required from the subtree. + Examples include rasterization and external driving of the .interfaceState property. + By passing this information explicitly, performance is optimized by avoiding iteration up the supernode chain. + Lastly, this avoidance of supernode traversal protects against the possibility of deadlocks when a supernode is + simultaneously attempting to materialize views / layers for its subtree (as many related methods require property locking) + + Note: as the hierarchy deepens, more state properties may be enabled. However, state properties may never be disabled / + cancelled below the point they are enabled. They continue to the leaves of the hierarchy. + */ + +typedef NS_OPTIONS(unsigned char, ASHierarchyState) +{ + /** The node may or may not have a supernode, but no supernode has a special hierarchy-influencing option enabled. */ + ASHierarchyStateNormal = 0, + /** The node has a supernode with .rasterizesSubtree = YES. + Note: the root node of the rasterized subtree (the one with the property set on it) will NOT have this state set. */ + ASHierarchyStateRasterized = 1 << 0, + /** The node or one of its supernodes is managed by a class like ASRangeController. Most commonly, these nodes are + ASCellNode objects or a subnode of one, and are used in ASTableView or ASCollectionView. + These nodes also receive regular updates to the .interfaceState property with more detailed status information. */ + ASHierarchyStateRangeManaged = 1 << 1, + /** Down-propagated version of _flags.visibilityNotificationsDisabled. This flag is very rarely set, but by having it + locally available to nodes, they do not have to walk up supernodes at the critical points it is checked. */ + ASHierarchyStateTransitioningSupernodes = 1 << 2, + /** One of the supernodes of this node is performing a transition. + Any layout calculated during this state should not be applied immediately, but pending until later. */ + ASHierarchyStateLayoutPending = 1 << 3, +}; + +ASDISPLAYNODE_INLINE BOOL ASHierarchyStateIncludesLayoutPending(ASHierarchyState hierarchyState) +{ + return ((hierarchyState & ASHierarchyStateLayoutPending) == ASHierarchyStateLayoutPending); +} + +ASDISPLAYNODE_INLINE BOOL ASHierarchyStateIncludesRangeManaged(ASHierarchyState hierarchyState) +{ + return ((hierarchyState & ASHierarchyStateRangeManaged) == ASHierarchyStateRangeManaged); +} + +ASDISPLAYNODE_INLINE BOOL ASHierarchyStateIncludesRasterized(ASHierarchyState hierarchyState) +{ + return ((hierarchyState & ASHierarchyStateRasterized) == ASHierarchyStateRasterized); +} + +ASDISPLAYNODE_INLINE BOOL ASHierarchyStateIncludesTransitioningSupernodes(ASHierarchyState hierarchyState) +{ + return ((hierarchyState & ASHierarchyStateTransitioningSupernodes) == ASHierarchyStateTransitioningSupernodes); +} + +__unused static NSString * _Nonnull NSStringFromASHierarchyState(ASHierarchyState hierarchyState) +{ + NSMutableArray *states = [NSMutableArray array]; + if (hierarchyState == ASHierarchyStateNormal) { + [states addObject:@"Normal"]; + } + if (ASHierarchyStateIncludesRangeManaged(hierarchyState)) { + [states addObject:@"RangeManaged"]; + } + if (ASHierarchyStateIncludesLayoutPending(hierarchyState)) { + [states addObject:@"LayoutPending"]; + } + if (ASHierarchyStateIncludesRasterized(hierarchyState)) { + [states addObject:@"Rasterized"]; + } + if (ASHierarchyStateIncludesTransitioningSupernodes(hierarchyState)) { + [states addObject:@"TransitioningSupernodes"]; + } + return [NSString stringWithFormat:@"{ %@ }", [states componentsJoinedByString:@" | "]]; +} + +#define HIERARCHY_STATE_DELTA(Name) ({ \ + if ((oldState & ASHierarchyState##Name) != (newState & ASHierarchyState##Name)) { \ + [changes appendFormat:@"%c%s ", (newState & ASHierarchyState##Name ? '+' : '-'), #Name]; \ + } \ +}) + +__unused static NSString * _Nonnull NSStringFromASHierarchyStateChange(ASHierarchyState oldState, ASHierarchyState newState) +{ + if (oldState == newState) { + return @"{ }"; + } + + NSMutableString *changes = [NSMutableString stringWithString:@"{ "]; + HIERARCHY_STATE_DELTA(Rasterized); + HIERARCHY_STATE_DELTA(RangeManaged); + HIERARCHY_STATE_DELTA(TransitioningSupernodes); + HIERARCHY_STATE_DELTA(LayoutPending); + [changes appendString:@"}"]; + return changes; +} + +#undef HIERARCHY_STATE_DELTA + +@interface ASDisplayNode () + +// The view class to use when creating a new display node instance. Defaults to _ASDisplayView. ++ (Class)viewClass; + +// Thread safe way to access the bounds of the node +@property (nonatomic) CGRect threadSafeBounds; + +// Returns the bounds of the node without reaching the view or layer +- (CGRect)_locked_threadSafeBounds; + +// The -pendingInterfaceState holds the value that will be applied to -interfaceState by the +// ASCATransactionQueue. If already applied, it matches -interfaceState. Thread-safe access. +@property (nonatomic, readonly) ASInterfaceState pendingInterfaceState; + +// These methods are recursive, and either union or remove the provided interfaceState to all sub-elements. +- (void)enterInterfaceState:(ASInterfaceState)interfaceState; +- (void)exitInterfaceState:(ASInterfaceState)interfaceState; +- (void)recursivelySetInterfaceState:(ASInterfaceState)interfaceState; + +// These methods are recursive, and either union or remove the provided hierarchyState to all sub-elements. +- (void)enterHierarchyState:(ASHierarchyState)hierarchyState; +- (void)exitHierarchyState:(ASHierarchyState)hierarchyState; + +// Changed before calling willEnterHierarchy / didExitHierarchy. +@property (readonly, getter = isInHierarchy) BOOL inHierarchy; +// Call willEnterHierarchy if necessary and set inHierarchy = YES if visibility notifications are enabled on all of its parents +- (void)__enterHierarchy; +// Call didExitHierarchy if necessary and set inHierarchy = NO if visibility notifications are enabled on all of its parents +- (void)__exitHierarchy; + +/** + * @abstract Returns the Hierarchy State of the node. + * + * @return The current ASHierarchyState of the node, indicating whether it is rasterized or managed by a range controller. + * + * @see ASInterfaceState + */ +@property (nonatomic) ASHierarchyState hierarchyState; + +/** + * Represent the current custom action in representation for the node + */ +@property (nonatomic, weak) UIAccessibilityCustomAction *accessibilityCustomAction; + +/** + * @abstract Return if the node is range managed or not + * + * @discussion Currently only set interface state on nodes in table and collection views. For other nodes, if they are + * in the hierarchy we enable all ASInterfaceState types with `ASInterfaceStateInHierarchy`, otherwise `None`. + */ +- (BOOL)supportsRangeManagedInterfaceState; + +- (BOOL)_locked_displaysAsynchronously; + +// The two methods below will eventually be exposed, but their names are subject to change. +/** + * @abstract Ensure that all rendering is complete for this node and its descendants. + * + * @discussion Calling this method on the main thread after a node is added to the view hierarchy will ensure that + * placeholder states are never visible to the user. It is used by ASTableView, ASCollectionView, and ASDKViewController + * to implement their respective ".neverShowPlaceholders" option. + * + * If all nodes have layer.contents set and/or their layer does not have -needsDisplay set, the method will return immediately. + * + * This method is capable of handling a mixed set of nodes, with some not having started display, some in progress on an + * asynchronous display operation, and some already finished. + * + * In order to guarantee against deadlocks, this method should only be called on the main thread. + * It may block on the private queue, [_ASDisplayLayer displayQueue] + */ +- (void)recursivelyEnsureDisplaySynchronously:(BOOL)synchronously; + +/** + * @abstract Calls -didExitPreloadState on the receiver and its subnode hierarchy. + * + * @discussion Clears any memory-intensive preloaded content. + * This method is used to notify the node that it should purge any content that is both expensive to fetch and to + * retain in memory. + * + * @see [ASDisplayNode(Subclassing) didExitPreloadState] and [ASDisplayNode(Subclassing) didEnterPreloadState] + */ +- (void)recursivelyClearPreloadedData; + +/** + * @abstract Calls -didEnterPreloadState on the receiver and its subnode hierarchy. + * + * @discussion Fetches content from remote sources for the current node and all subnodes. + * + * @see [ASDisplayNode(Subclassing) didEnterPreloadState] and [ASDisplayNode(Subclassing) didExitPreloadState] + */ +- (void)recursivelyPreload; + +/** + * @abstract Triggers a recursive call to -didEnterPreloadState when the node has an interfaceState of ASInterfaceStatePreload + */ +- (void)setNeedsPreload; + +/** + * @abstract Allows a node to bypass all ensureDisplay passes. Defaults to NO. + * + * @discussion Nodes that are expensive to draw and expected to have placeholder even with + * .neverShowPlaceholders enabled should set this to YES. + * + * ASImageNode uses the default of NO, as it is often used for UI images that are expected to synchronize with ensureDisplay. + * + * ASNetworkImageNode and ASMultiplexImageNode set this to YES, because they load data from a database or server, + * and are expected to support a placeholder state given that display is often blocked on slow data fetching. + */ +@property BOOL shouldBypassEnsureDisplay; + +/** + * @abstract Checks whether a node should be scheduled for display, considering its current and new interface states. + */ +- (BOOL)shouldScheduleDisplayWithNewInterfaceState:(ASInterfaceState)newInterfaceState; + +/** + * @abstract safeAreaInsets will fallback to this value if the corresponding UIKit property is not available + * (due to an old iOS version). + * + * @discussion This should be set by the owning view controller based on it's layout guides. + * If this is not a view controllet's node the value will be calculated automatically by the parent node. + */ +@property (nonatomic) UIEdgeInsets fallbackSafeAreaInsets; + +/** + * @abstract Indicates if this node is a view controller's root node. Defaults to NO. + * + * @discussion Set to YES in -[ASDKViewController initWithNode:]. + * + * YES here only means that this node is used as an ASDKViewController node. It doesn't mean that this node is a root of + * ASDisplayNode hierarchy, e.g. when its view controller is parented by another ASDKViewController. + */ +@property (nonatomic, getter=isViewControllerRoot) BOOL viewControllerRoot; + +@end + + +@interface ASDisplayNode (ASLayoutInternal) + +/** + * @abstract Informs the root node that the intrinsic size of the receiver is no longer valid. + * + * @discussion The size of a root node is determined by each subnode. Calling invalidateSize will let the root node know + * that the intrinsic size of the receiver node is no longer valid and a resizing of the root node needs to happen. + */ +- (void)_u_setNeedsLayoutFromAbove; + +/** + * @abstract Subclass hook for nodes that are acting as root nodes. This method is called if one of the subnodes + * size is invalidated and may need to result in a different size as the current calculated size. + */ +- (void)_rootNodeDidInvalidateSize; + +/** + * This method will confirm that the layout is up to date (and update if needed). + * Importantly, it will also APPLY the layout to all of our subnodes if (unless parent is transitioning). + */ +- (void)_u_measureNodeWithBoundsIfNecessary:(CGRect)bounds; + +/** + * Layout all of the subnodes based on the sublayouts + */ +- (void)_layoutSublayouts; + +@end + +@interface ASDisplayNode (ASLayoutTransitionInternal) + +/** + * If one or multiple layout transitions are in flight this methods returns if the current layout transition that + * happens in in this particular thread was invalidated through another thread is starting a transition for this node + */ +- (BOOL)_isLayoutTransitionInvalid; + +/** + * Same as @c -_isLayoutTransitionInvalid but must be called with the node's instance lock held. + */ +- (BOOL)_locked_isLayoutTransitionInvalid; + +/** + * Internal method that can be overriden by subclasses to add specific behavior after the measurement of a layout + * transition did finish. + */ +- (void)_layoutTransitionMeasurementDidFinish; + +/** + * Informs the node that the pending layout transition did complete + */ +- (void)_completePendingLayoutTransition; + +/** + * Called if the pending layout transition did complete + */ +- (void)_pendingLayoutTransitionDidComplete; + +@end + +/** + * Defines interactive accessibility traits which will be exposed as UIAccessibilityCustomActions + * for nodes within nodes that have isAccessibilityContainer is YES + */ +NS_INLINE UIAccessibilityTraits ASInteractiveAccessibilityTraitsMask() { + return UIAccessibilityTraitLink | UIAccessibilityTraitKeyboardKey | UIAccessibilityTraitButton; +} + +@interface ASDisplayNode (AccessibilityInternal) +- (NSArray *)accessibilityElements; +@end; + +@interface UIView (ASDisplayNodeInternal) +@property (nullable, weak) ASDisplayNode *asyncdisplaykit_node; +@end + +@interface CALayer (ASDisplayNodeInternal) +@property (nullable, weak) ASDisplayNode *asyncdisplaykit_node; +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDisplayNode+UIViewBridge.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDisplayNode+UIViewBridge.mm new file mode 100644 index 0000000..42dbbfc --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDisplayNode+UIViewBridge.mm @@ -0,0 +1,1474 @@ +// +// ASDisplayNode+UIViewBridge.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import +#import +#import +#import +#import + +/** + * The following macros are conveniences to help in the common tasks related to the bridging that ASDisplayNode does to UIView and CALayer. + * In general, a property can either be: + * - Always sent to the layer or view's layer + * use _getFromLayer / _setToLayer + * - Bridged to the view if view-backed or the layer if layer-backed + * use _getFromViewOrLayer / _setToViewOrLayer / _messageToViewOrLayer + * - Only applicable if view-backed + * use _setToViewOnly / _getFromViewOnly + * - Has differing types on views and layers, or custom ASDisplayNode-specific behavior is desired + * manually implement + * + * _bridge_prologue_write is defined to take the node's property lock. Add it at the beginning of any bridged property setters. + * _bridge_prologue_read is defined to take the node's property lock and enforce thread affinity. Add it at the beginning of any bridged property getters. + */ + +#define DISPLAYNODE_USE_LOCKS 1 + +#if DISPLAYNODE_USE_LOCKS +#define _bridge_prologue_read AS::MutexLocker l(__instanceLock__); ASDisplayNodeAssertThreadAffinity(self) +#define _bridge_prologue_write AS::MutexLocker l(__instanceLock__) +#else +#define _bridge_prologue_read ASDisplayNodeAssertThreadAffinity(self) +#define _bridge_prologue_write +#endif + +/// Returns YES if the property set should be applied to view/layer immediately. +/// Side Effect: Registers the node with the shared ASPendingStateController if +/// the property cannot be immediately applied and the node does not already have pending changes. +/// This function must be called with the node's lock already held (after _bridge_prologue_write). +/// *warning* the lock should *not* be released until the pending state is updated if this method +/// returns NO. Otherwise, the pending state can be scheduled and flushed *before* you get a chance +/// to apply it. +ASDISPLAYNODE_INLINE BOOL ASDisplayNodeShouldApplyBridgedWriteToView(ASDisplayNode *node) { + BOOL loaded = _loaded(node); + if (ASDisplayNodeThreadIsMain()) { + return loaded; + } else { + if (loaded && !ASDisplayNodeGetPendingState(node).hasChanges) { + [[ASPendingStateController sharedInstance] registerNode:node]; + } + return NO; + } +}; + +#define _getFromViewOrLayer(layerProperty, viewAndPendingViewStateProperty) _loaded(self) ? \ + (_view ? _view.viewAndPendingViewStateProperty : _layer.layerProperty )\ + : ASDisplayNodeGetPendingState(self).viewAndPendingViewStateProperty + +#define _setToViewOrLayer(layerProperty, layerValueExpr, viewAndPendingViewStateProperty, viewAndPendingViewStateExpr) BOOL shouldApply = ASDisplayNodeShouldApplyBridgedWriteToView(self); \ + if (shouldApply) { (_view ? _view.viewAndPendingViewStateProperty = (viewAndPendingViewStateExpr) : _layer.layerProperty = (layerValueExpr)); } else { ASDisplayNodeGetPendingState(self).viewAndPendingViewStateProperty = (viewAndPendingViewStateExpr); } + +#define _setToViewOnly(viewAndPendingViewStateProperty, viewAndPendingViewStateExpr) BOOL shouldApply = ASDisplayNodeShouldApplyBridgedWriteToView(self); \ +if (shouldApply) { _view.viewAndPendingViewStateProperty = (viewAndPendingViewStateExpr); } else { ASDisplayNodeGetPendingState(self).viewAndPendingViewStateProperty = (viewAndPendingViewStateExpr); } + +#define _getFromViewOnly(viewAndPendingViewStateProperty) _loaded(self) ? _view.viewAndPendingViewStateProperty : ASDisplayNodeGetPendingState(self).viewAndPendingViewStateProperty + +#define _getFromLayer(layerProperty) _loaded(self) ? _layer.layerProperty : ASDisplayNodeGetPendingState(self).layerProperty + +#define _setToLayer(layerProperty, layerValueExpr) BOOL shouldApply = ASDisplayNodeShouldApplyBridgedWriteToView(self); \ +if (shouldApply) { _layer.layerProperty = (layerValueExpr); } else { ASDisplayNodeGetPendingState(self).layerProperty = (layerValueExpr); } + +/** + * This category implements certain frequently-used properties and methods of UIView and CALayer so that ASDisplayNode clients can just call the view/layer methods on the node, + * with minimal loss in performance. Unlike UIView and CALayer methods, these can be called from a non-main thread until the view or layer is created. + * This allows text sizing in -calculateSizeThatFits: (essentially a simplified layout) to happen off the main thread + * without any CALayer or UIView actually existing while still being able to set and read properties from ASDisplayNode instances. + */ +@implementation ASDisplayNode (UIViewBridge) + +#if TARGET_OS_TV +// Focus Engine +- (BOOL)canBecomeFocused +{ + return NO; +} + +- (void)setNeedsFocusUpdate +{ + ASDisplayNodeAssertMainThread(); + [_view setNeedsFocusUpdate]; +} + +- (void)updateFocusIfNeeded +{ + ASDisplayNodeAssertMainThread(); + [_view updateFocusIfNeeded]; +} + +- (BOOL)shouldUpdateFocusInContext:(UIFocusUpdateContext *)context +{ + return NO; +} + +- (void)didUpdateFocusInContext:(UIFocusUpdateContext *)context withAnimationCoordinator:(UIFocusAnimationCoordinator *)coordinator +{ + +} + +- (UIView *)preferredFocusedView +{ + if (self.nodeLoaded) { + return _view; + } + else { + return nil; + } +} +#endif + +- (BOOL)canBecomeFirstResponder +{ + if (_view == nil) { + // By default we return NO if not view is created yet + return NO; + } + return [_view canBecomeFirstResponder]; +} + +- (BOOL)becomeFirstResponder +{ + ASDisplayNodeAssertMainThread(); + + // Note: This implicitly loads the view if it hasn't been loaded yet. + [self view]; + + if (![self canBecomeFirstResponder]) { + return NO; + } + return [_view becomeFirstResponder]; +} + +- (BOOL)canResignFirstResponder +{ + ASDisplayNodeAssertMainThread(); + + if (_view == nil) { + // By default we return YES if no view is created yet + return YES; + } + return [_view canResignFirstResponder]; +} + +- (BOOL)resignFirstResponder +{ + ASDisplayNodeAssertMainThread(); + + // Note: This implicitly loads the view if it hasn't been loaded yet. + [self view]; + + if (![self canResignFirstResponder]) { + return NO; + } + return [_view resignFirstResponder]; +} + +- (BOOL)isFirstResponder +{ + ASDisplayNodeAssertMainThread(); + if (_view == nil) { + // If no view is created yet we can just return NO as it's unlikely it's the first responder + return NO; + } + return [_view isFirstResponder]; +} + +- (BOOL)canPerformAction:(SEL)action withSender:(id)sender +{ + ASDisplayNodeAssertMainThread(); + return !self.layerBacked && [self.view canPerformAction:action withSender:sender]; +} + +- (CGFloat)alpha +{ + _bridge_prologue_read; + return _getFromViewOrLayer(opacity, alpha); +} + +- (void)setAlpha:(CGFloat)newAlpha +{ + _bridge_prologue_write; + _setToViewOrLayer(opacity, newAlpha, alpha, newAlpha); +} + +- (CGFloat)cornerRadius +{ + AS::MutexLocker l(__instanceLock__); + return _cornerRadius; +} + +- (void)setCornerRadius:(CGFloat)newCornerRadius +{ + [self updateCornerRoundingWithType:self.cornerRoundingType + cornerRadius:newCornerRadius + maskedCorners:self.maskedCorners]; +} + +- (ASCornerRoundingType)cornerRoundingType +{ + AS::MutexLocker l(__instanceLock__); + return _cornerRoundingType; +} + +- (void)setCornerRoundingType:(ASCornerRoundingType)newRoundingType +{ + [self updateCornerRoundingWithType:newRoundingType cornerRadius:self.cornerRadius maskedCorners:self.maskedCorners]; +} + +- (CACornerMask)maskedCorners +{ + AS::MutexLocker l(__instanceLock__); + return _maskedCorners; +} + +- (void)setMaskedCorners:(CACornerMask)newMaskedCorners +{ + [self updateCornerRoundingWithType:self.cornerRoundingType + cornerRadius:self.cornerRadius + maskedCorners:newMaskedCorners]; +} + +- (NSString *)contentsGravity +{ + _bridge_prologue_read; + return _getFromLayer(contentsGravity); +} + +- (void)setContentsGravity:(NSString *)newContentsGravity +{ + _bridge_prologue_write; + _setToLayer(contentsGravity, newContentsGravity); +} + +- (CGRect)contentsRect +{ + _bridge_prologue_read; + return _getFromLayer(contentsRect); +} + +- (void)setContentsRect:(CGRect)newContentsRect +{ + _bridge_prologue_write; + _setToLayer(contentsRect, newContentsRect); +} + +- (CGRect)contentsCenter +{ + _bridge_prologue_read; + return _getFromLayer(contentsCenter); +} + +- (void)setContentsCenter:(CGRect)newContentsCenter +{ + _bridge_prologue_write; + _setToLayer(contentsCenter, newContentsCenter); +} + +- (CGFloat)contentsScale +{ + _bridge_prologue_read; + return _getFromLayer(contentsScale); +} + +- (void)setContentsScale:(CGFloat)newContentsScale +{ + _bridge_prologue_write; + _setToLayer(contentsScale, newContentsScale); +} + +- (CGFloat)rasterizationScale +{ + _bridge_prologue_read; + return _getFromLayer(rasterizationScale); +} + +- (void)setRasterizationScale:(CGFloat)newRasterizationScale +{ + _bridge_prologue_write; + _setToLayer(rasterizationScale, newRasterizationScale); +} + +- (CGRect)bounds +{ + _bridge_prologue_read; + return _getFromViewOrLayer(bounds, bounds); +} + +- (void)setBounds:(CGRect)newBounds +{ + _bridge_prologue_write; + _setToViewOrLayer(bounds, newBounds, bounds, newBounds); + self.threadSafeBounds = newBounds; +} + +- (CGRect)frame +{ + _bridge_prologue_read; + + // Frame is only defined when transform is identity. +//#if DEBUG +// // Checking if the transform is identity is expensive, so disable when unnecessary. We have assertions on in Release, so DEBUG is the only way I know of. +// ASDisplayNodeAssert(CATransform3DIsIdentity(self.transform), @"-[ASDisplayNode frame] - self.transform must be identity in order to use the frame property. (From Apple's UIView documentation: If the transform property is not the identity transform, the value of this property is undefined and therefore should be ignored.)"); +//#endif + + CGPoint position = self.position; + CGRect bounds = self.bounds; + CGPoint anchorPoint = self.anchorPoint; + CGPoint origin = CGPointMake(position.x - bounds.size.width * anchorPoint.x, + position.y - bounds.size.height * anchorPoint.y); + return CGRectMake(origin.x, origin.y, bounds.size.width, bounds.size.height); +} + +- (void)setFrame:(CGRect)rect +{ + BOOL setToView = NO; + BOOL setToLayer = NO; + CGRect newBounds = CGRectZero; + CGPoint newPosition = CGPointZero; + BOOL nodeLoaded = NO; + BOOL isMainThread = ASDisplayNodeThreadIsMain(); + { + _bridge_prologue_write; + + // For classes like ASTableNode, ASCollectionNode, ASScrollNode and similar - make sure UIView gets setFrame: + struct ASDisplayNodeFlags flags = _flags; + BOOL specialPropertiesHandling = ASDisplayNodeNeedsSpecialPropertiesHandling(checkFlag(Synchronous), flags.layerBacked); + + nodeLoaded = _loaded(self); + if (!specialPropertiesHandling) { + BOOL canReadProperties = isMainThread || !nodeLoaded; + if (canReadProperties) { + // We don't have to set frame directly, and we can read current properties. + // Compute a new bounds and position and set them on self. + CALayer *layer = _layer; + CGPoint origin = (nodeLoaded ? layer.bounds.origin : self.bounds.origin); + CGPoint anchorPoint = (nodeLoaded ? layer.anchorPoint : self.anchorPoint); + + ASBoundsAndPositionForFrame(rect, origin, anchorPoint, &newBounds, &newPosition); + + if (ASIsCGRectValidForLayout(newBounds) == NO || ASIsCGPositionValidForLayout(newPosition) == NO) { + ASDisplayNodeAssertNonFatal(NO, @"-[ASDisplayNode setFrame:] - The new frame (%@) is invalid and unsafe to be set.", NSStringFromCGRect(rect)); + return; + } + + if (nodeLoaded) { + setToLayer = YES; + } else { + self.bounds = newBounds; + self.position = newPosition; + } + } else { + // We don't have to set frame directly, but we can't read properties. + // Store the frame in our pending state, and it'll get decomposed into + // bounds and position when the pending state is applied. + _ASPendingState *pendingState = ASDisplayNodeGetPendingState(self); + if (nodeLoaded && !pendingState.hasChanges) { + [[ASPendingStateController sharedInstance] registerNode:self]; + } + pendingState.frame = rect; + } + } else { + if (nodeLoaded && isMainThread) { + // We do have to set frame directly, and we're on main thread with a loaded node. + // Just set the frame on the view. + // NOTE: Frame is only defined when transform is identity because we explicitly diverge from CALayer behavior and define frame without transform. + setToView = YES; + } else { + // We do have to set frame directly, but either the node isn't loaded or we're on a non-main thread. + // Set the frame on the pending state, and it'll call setFrame: when applied. + _ASPendingState *pendingState = ASDisplayNodeGetPendingState(self); + if (nodeLoaded && !pendingState.hasChanges) { + [[ASPendingStateController sharedInstance] registerNode:self]; + } + pendingState.frame = rect; + } + } + } + + if (setToView) { + ASDisplayNodeAssertTrue(nodeLoaded && isMainThread); + _view.frame = rect; + } else if (setToLayer) { + ASDisplayNodeAssertTrue(nodeLoaded && isMainThread); + _layer.bounds = newBounds; + _layer.position = newPosition; + } +} + +- (void)setNeedsDisplay +{ + BOOL isRasterized = NO; + BOOL shouldApply = NO; + id viewOrLayer = nil; + { + _bridge_prologue_write; + isRasterized = _hierarchyState & ASHierarchyStateRasterized; + shouldApply = ASDisplayNodeShouldApplyBridgedWriteToView(self); + viewOrLayer = _view ?: _layer; + + if (isRasterized == NO && shouldApply == NO) { + // We can't release the lock before applying to pending state, or it may be flushed before it can be applied. + [ASDisplayNodeGetPendingState(self) setNeedsDisplay]; + } + } + + if (isRasterized) { + ASPerformBlockOnMainThread(^{ + // The below operation must be performed on the main thread to ensure against an extremely rare deadlock, where a parent node + // begins materializing the view / layer hierarchy (locking itself or a descendant) while this node walks up + // the tree and requires locking that node to access .rasterizesSubtree. + // For this reason, this method should be avoided when possible. Use _hierarchyState & ASHierarchyStateRasterized. + ASDisplayNodeAssertMainThread(); + ASDisplayNode *rasterizedContainerNode = self.supernode; + while (rasterizedContainerNode) { + if (rasterizedContainerNode.rasterizesSubtree) { + break; + } + rasterizedContainerNode = rasterizedContainerNode.supernode; + } + [rasterizedContainerNode setNeedsDisplay]; + }); + } else { + if (shouldApply) { + // If not rasterized, and the node is loaded (meaning we certainly have a view or layer), send a + // message to the view/layer first. This is because __setNeedsDisplay calls as scheduleNodeForDisplay, + // which may call -displayIfNeeded. We want to ensure the needsDisplay flag is set now, and then cleared. + [viewOrLayer setNeedsDisplay]; + } + [self __setNeedsDisplay]; + } +} + +- (void)setNeedsLayout +{ + BOOL shouldApply = NO; + BOOL loaded = NO; + id viewOrLayer = nil; + { + _bridge_prologue_write; + shouldApply = ASDisplayNodeShouldApplyBridgedWriteToView(self); + loaded = _loaded(self); + viewOrLayer = _view ?: _layer; + if (shouldApply == NO && loaded) { + // The node is loaded but we're not on main. + // We will call [self __setNeedsLayout] when we apply the pending state. + // We need to call it on main if the node is loaded to support automatic subnode management. + // We can't release the lock before applying to pending state, or it may be flushed before it can be applied. + [ASDisplayNodeGetPendingState(self) setNeedsLayout]; + } + } + + if (shouldApply) { + // The node is loaded and we're on main. + // Quite the opposite of setNeedsDisplay, we must call __setNeedsLayout before messaging + // the view or layer to ensure that measurement and implicitly added subnodes have been handled. + [self __setNeedsLayout]; + [viewOrLayer setNeedsLayout]; + } else if (loaded == NO) { + // The node is not loaded and we're not on main. + [self __setNeedsLayout]; + } +} + +- (void)layoutIfNeeded +{ + BOOL shouldApply = NO; + BOOL loaded = NO; + id viewOrLayer = nil; + { + _bridge_prologue_write; + shouldApply = ASDisplayNodeShouldApplyBridgedWriteToView(self); + loaded = _loaded(self); + viewOrLayer = _view ?: _layer; + if (shouldApply == NO && loaded) { + // The node is loaded but we're not on main. + // We will call layoutIfNeeded on the view or layer when we apply the pending state. __layout will in turn be called on us (see -[_ASDisplayLayer layoutSublayers]). + // We need to call it on main if the node is loaded to support automatic subnode management. + // We can't release the lock before applying to pending state, or it may be flushed before it can be applied. + [ASDisplayNodeGetPendingState(self) layoutIfNeeded]; + } + } + + if (shouldApply) { + // The node is loaded and we're on main. + // Message the view or layer which in turn will call __layout on us (see -[_ASDisplayLayer layoutSublayers]). + [viewOrLayer layoutIfNeeded]; + } else if (loaded == NO) { + // The node is not loaded and we're not on main. + [self __layout]; + } +} + +- (BOOL)isOpaque +{ + _bridge_prologue_read; + return _getFromViewOrLayer(opaque, opaque); +} + + +- (void)setOpaque:(BOOL)newOpaque +{ + _bridge_prologue_write; + BOOL shouldApply = ASDisplayNodeShouldApplyBridgedWriteToView(self); + + if (shouldApply) { + /* + NOTE: The values of `opaque` can be different between a view and layer. + + In debugging on Xcode 11 I saw the following in lldb: + - Initially for a new ASDisplayNode layer.isOpaque and _view.isOpaque are true + - Set the backgroundColor of the node to a valid UIColor + Expected: layer.isOpaque and view.isOpaque would be equal and true + Actual: view.isOpaque is true and layer.isOpaque is now false + + This broke some unit tests for view-backed nodes so I think we need to read directly from the view and can't rely on the layers value at this point. + */ + BOOL oldOpaque = _layer.opaque; + if (!_flags.layerBacked) { + oldOpaque = _view.opaque; + _view.opaque = newOpaque; + } + _layer.opaque = newOpaque; + if (oldOpaque != newOpaque) { + [self setNeedsDisplay]; + } + } else { + // NOTE: If we're in the background, we cannot read the current value of self.opaque (if loaded). + // When the pending state is applied to the view on main, we will call `setNeedsDisplay` if + // the new opaque value doesn't match the one on the layer. + ASDisplayNodeGetPendingState(self).opaque = newOpaque; + } +} + +- (BOOL)isUserInteractionEnabled +{ + _bridge_prologue_read; + if (_flags.layerBacked) return NO; + return _getFromViewOnly(userInteractionEnabled); +} + +- (void)setUserInteractionEnabled:(BOOL)enabled +{ + _bridge_prologue_write; + _setToViewOnly(userInteractionEnabled, enabled); +} +#if TARGET_OS_IOS +- (BOOL)isExclusiveTouch +{ + _bridge_prologue_read; + return _getFromViewOnly(exclusiveTouch); +} + +- (void)setExclusiveTouch:(BOOL)exclusiveTouch +{ + _bridge_prologue_write; + _setToViewOnly(exclusiveTouch, exclusiveTouch); +} +#endif +- (BOOL)clipsToBounds +{ + _bridge_prologue_read; + return _getFromViewOrLayer(masksToBounds, clipsToBounds); +} + +- (void)setClipsToBounds:(BOOL)clips +{ + _bridge_prologue_write; + _setToViewOrLayer(masksToBounds, clips, clipsToBounds, clips); +} + +- (CGPoint)anchorPoint +{ + _bridge_prologue_read; + return _getFromLayer(anchorPoint); +} + +- (void)setAnchorPoint:(CGPoint)newAnchorPoint +{ + _bridge_prologue_write; + _setToLayer(anchorPoint, newAnchorPoint); +} + +- (CGPoint)position +{ + _bridge_prologue_read; + return _getFromLayer(position); +} + +- (void)setPosition:(CGPoint)newPosition +{ + _bridge_prologue_write; + _setToLayer(position, newPosition); +} + +- (CGFloat)zPosition +{ + _bridge_prologue_read; + return _getFromLayer(zPosition); +} + +- (void)setZPosition:(CGFloat)newPosition +{ + _bridge_prologue_write; + _setToLayer(zPosition, newPosition); +} + +- (CATransform3D)transform +{ + _bridge_prologue_read; + return _getFromLayer(transform); +} + +- (void)setTransform:(CATransform3D)newTransform +{ + _bridge_prologue_write; + _setToLayer(transform, newTransform); +} + +- (CATransform3D)subnodeTransform +{ + _bridge_prologue_read; + return _getFromLayer(sublayerTransform); +} + +- (void)setSubnodeTransform:(CATransform3D)newSubnodeTransform +{ + _bridge_prologue_write; + _setToLayer(sublayerTransform, newSubnodeTransform); +} + +- (id)contents +{ + _bridge_prologue_read; + return _getFromLayer(contents); +} + +- (void)setContents:(id)newContents +{ + _bridge_prologue_write; + _setToLayer(contents, newContents); +} + +- (BOOL)isHidden +{ + _bridge_prologue_read; + return _getFromViewOrLayer(hidden, hidden); +} + +- (void)setHidden:(BOOL)flag +{ + _bridge_prologue_write; + _setToViewOrLayer(hidden, flag, hidden, flag); +} + +- (BOOL)needsDisplayOnBoundsChange +{ + _bridge_prologue_read; + return _getFromLayer(needsDisplayOnBoundsChange); +} + +- (void)setNeedsDisplayOnBoundsChange:(BOOL)flag +{ + _bridge_prologue_write; + _setToLayer(needsDisplayOnBoundsChange, flag); +} + +- (BOOL)autoresizesSubviews +{ + _bridge_prologue_read; + ASDisplayNodeAssert(!_flags.layerBacked, @"Danger: this property is undefined on layer-backed nodes."); + return _getFromViewOnly(autoresizesSubviews); +} + +- (void)setAutoresizesSubviews:(BOOL)flag +{ + _bridge_prologue_write; + ASDisplayNodeAssert(!_flags.layerBacked, @"Danger: this property is undefined on layer-backed nodes."); + _setToViewOnly(autoresizesSubviews, flag); +} + +- (UIViewAutoresizing)autoresizingMask +{ + _bridge_prologue_read; + ASDisplayNodeAssert(!_flags.layerBacked, @"Danger: this property is undefined on layer-backed nodes."); + return _getFromViewOnly(autoresizingMask); +} + +- (void)setAutoresizingMask:(UIViewAutoresizing)mask +{ + _bridge_prologue_write; + ASDisplayNodeAssert(!_flags.layerBacked, @"Danger: this property is undefined on layer-backed nodes."); + _setToViewOnly(autoresizingMask, mask); +} + +- (UIViewContentMode)contentMode +{ + _bridge_prologue_read; + if (_loaded(self)) { + if (_flags.layerBacked) { + return ASDisplayNodeUIContentModeFromCAContentsGravity(_layer.contentsGravity); + } else { + return _view.contentMode; + } + } else { + return ASDisplayNodeGetPendingState(self).contentMode; + } +} + +- (void)setContentMode:(UIViewContentMode)contentMode +{ + _bridge_prologue_write; + BOOL shouldApply = ASDisplayNodeShouldApplyBridgedWriteToView(self); + if (shouldApply) { + if (_flags.layerBacked) { + _layer.contentsGravity = ASDisplayNodeCAContentsGravityFromUIContentMode(contentMode); + } else { + _view.contentMode = contentMode; + } + } else { + ASDisplayNodeGetPendingState(self).contentMode = contentMode; + } +} + +- (UIColor *)backgroundColor +{ + _bridge_prologue_read; + if (_loaded(self)) { + /* + Note: We can no longer rely simply on the layers backgroundColor value if the color is set directly on `_view` + There is no longer a 1:1 mapping between _view.backgroundColor and _layer.backgroundColor after testing in iOS 13 / Xcode 11 so we should prefer one or the other depending on the backing type for the node (view or layer) + */ + if (_flags.layerBacked) { + return _backgroundColor; + } else { + return _view.backgroundColor; + } + } + return ASDisplayNodeGetPendingState(self).backgroundColor; +} + +- (void)setBackgroundColor:(UIColor *)newBackgroundColor +{ + _bridge_prologue_write; + BOOL shouldApply = ASDisplayNodeShouldApplyBridgedWriteToView(self); + if (shouldApply) { + UIColor *oldBackgroundColor = _backgroundColor; + _backgroundColor = newBackgroundColor; + if (_flags.layerBacked) { + _layer.backgroundColor = _backgroundColor.CGColor; + } else { + /* + NOTE: Setting to the view and layer individually is necessary. + + As observed in lldb, the view does not appear to immediately propagate background color to the layer and actually clears it's value (`nil`) initially. This was caught by our snapshot tests. + + Given that UIColor / UIView has dynamic capabilties now, we should set directly to the view and make sure that the layers value is consistent here. + + */ + _view.backgroundColor = _backgroundColor; + // Gather the CGColorRef from the view incase there are any changes it might apply to which CGColorRef is returned for dynamic colors + _layer.backgroundColor = _view.backgroundColor.CGColor; + } + + if (![oldBackgroundColor isEqual:newBackgroundColor]) { + [self setNeedsDisplay]; + } + } else { + // NOTE: If we're in the background, we cannot read the current value of bgcolor (if loaded). + // When the pending state is applied to the view on main, we will call `setNeedsDisplay` if + // the new background color doesn't match the one on the layer. + _backgroundColor = newBackgroundColor; + ASDisplayNodeGetPendingState(self).backgroundColor = newBackgroundColor; + } +} + +- (UIColor *)tintColor +{ + __instanceLock__.lock(); + UIColor *retVal = nil; + BOOL shouldAscend = NO; + if (_flags.layerBacked) { + retVal = _tintColor; + // The first nondefault tint color value in the node’s hierarchy, ascending from and starting with the node itself. + shouldAscend = (retVal == nil); + } else { + ASDisplayNodeAssertThreadAffinity(self); + retVal = _getFromViewOnly(tintColor); + } + __instanceLock__.unlock(); + return shouldAscend ? self.supernode.tintColor : retVal; +} + +- (void)setTintColor:(UIColor *)color +{ + // Handle locking manually since we unlock to notify subclasses when tint color changes + __instanceLock__.lock(); + if (_flags.layerBacked) { + if (![_tintColor isEqual:color]) { + _tintColor = color; + + if (_loaded(self)) { + // Tint color has changed. Unlock here before calling subclasses and exit-early + __instanceLock__.unlock(); + [self tintColorDidChange]; + return; + } + } + } else { + _tintColor = color; + _setToViewOnly(tintColor, color); + } + __instanceLock__.unlock(); +} + +- (void)tintColorDidChange +{ + // ignore this, allow subclasses to be notified +} + +- (CGColorRef)shadowColor +{ + _bridge_prologue_read; + return _getFromLayer(shadowColor); +} + +- (void)setShadowColor:(CGColorRef)colorValue +{ + _bridge_prologue_write; + _setToLayer(shadowColor, colorValue); +} + +- (CGFloat)shadowOpacity +{ + _bridge_prologue_read; + return _getFromLayer(shadowOpacity); +} + +- (void)setShadowOpacity:(CGFloat)opacity +{ + _bridge_prologue_write; + _setToLayer(shadowOpacity, opacity); +} + +- (CGSize)shadowOffset +{ + _bridge_prologue_read; + return _getFromLayer(shadowOffset); +} + +- (void)setShadowOffset:(CGSize)offset +{ + _bridge_prologue_write; + _setToLayer(shadowOffset, offset); +} + +- (CGFloat)shadowRadius +{ + _bridge_prologue_read; + return _getFromLayer(shadowRadius); +} + +- (void)setShadowRadius:(CGFloat)radius +{ + _bridge_prologue_write; + _setToLayer(shadowRadius, radius); +} + +- (CGFloat)borderWidth +{ + _bridge_prologue_read; + return _getFromLayer(borderWidth); +} + +- (void)setBorderWidth:(CGFloat)width +{ + _bridge_prologue_write; + _setToLayer(borderWidth, width); +} + +- (CGColorRef)borderColor +{ + _bridge_prologue_read; + return _getFromLayer(borderColor); +} + +- (void)setBorderColor:(CGColorRef)colorValue +{ + _bridge_prologue_write; + _setToLayer(borderColor, colorValue); +} + +- (BOOL)allowsGroupOpacity +{ + _bridge_prologue_read; + return _getFromLayer(allowsGroupOpacity); +} + +- (void)setAllowsGroupOpacity:(BOOL)allowsGroupOpacity +{ + _bridge_prologue_write; + _setToLayer(allowsGroupOpacity, allowsGroupOpacity); +} + +- (BOOL)allowsEdgeAntialiasing +{ + _bridge_prologue_read; + return _getFromLayer(allowsEdgeAntialiasing); +} + +- (void)setAllowsEdgeAntialiasing:(BOOL)allowsEdgeAntialiasing +{ + _bridge_prologue_write; + _setToLayer(allowsEdgeAntialiasing, allowsEdgeAntialiasing); +} + +- (CAEdgeAntialiasingMask)edgeAntialiasingMask +{ + _bridge_prologue_read; + return _getFromLayer(edgeAntialiasingMask); +} + +- (void)setEdgeAntialiasingMask:(CAEdgeAntialiasingMask)edgeAntialiasingMask +{ + _bridge_prologue_write; + _setToLayer(edgeAntialiasingMask, edgeAntialiasingMask); +} + +- (UISemanticContentAttribute)semanticContentAttribute +{ + _bridge_prologue_read; + return _getFromViewOnly(semanticContentAttribute); +} + +- (void)setSemanticContentAttribute:(UISemanticContentAttribute)semanticContentAttribute +{ + _bridge_prologue_write; + _setToViewOnly(semanticContentAttribute, semanticContentAttribute); +#if YOGA + [self semanticContentAttributeDidChange:semanticContentAttribute]; +#endif +} + +- (UIEdgeInsets)layoutMargins +{ + _bridge_prologue_read; + ASDisplayNodeAssert(!_flags.layerBacked, @"Danger: this property is undefined on layer-backed nodes."); + UIEdgeInsets margins = _getFromViewOnly(layoutMargins); + + if (!AS_AT_LEAST_IOS11 && self.insetsLayoutMarginsFromSafeArea) { + UIEdgeInsets safeArea = self.safeAreaInsets; + margins = ASConcatInsets(margins, safeArea); + } + + return margins; +} + +- (void)setLayoutMargins:(UIEdgeInsets)layoutMargins +{ + _bridge_prologue_write; + ASDisplayNodeAssert(!_flags.layerBacked, @"Danger: this property is undefined on layer-backed nodes."); + _setToViewOnly(layoutMargins, layoutMargins); +} + +- (BOOL)preservesSuperviewLayoutMargins +{ + _bridge_prologue_read; + ASDisplayNodeAssert(!_flags.layerBacked, @"Danger: this property is undefined on layer-backed nodes."); + return _getFromViewOnly(preservesSuperviewLayoutMargins); +} + +- (void)setPreservesSuperviewLayoutMargins:(BOOL)preservesSuperviewLayoutMargins +{ + _bridge_prologue_write; + ASDisplayNodeAssert(!_flags.layerBacked, @"Danger: this property is undefined on layer-backed nodes."); + _setToViewOnly(preservesSuperviewLayoutMargins, preservesSuperviewLayoutMargins); +} + +- (void)layoutMarginsDidChange +{ + ASDisplayNodeAssertMainThread(); + + if (self.automaticallyRelayoutOnLayoutMarginsChanges) { + [self setNeedsLayout]; + } +} + +- (UIEdgeInsets)safeAreaInsets +{ + _bridge_prologue_read; + + if (AS_AVAILABLE_IOS_TVOS(11.0, 11.0)) { + if (!_flags.layerBacked && _loaded(self)) { + return self.view.safeAreaInsets; + } + } + return _fallbackSafeAreaInsets; +} + +- (BOOL)insetsLayoutMarginsFromSafeArea +{ + _bridge_prologue_read; + + return [self _locked_insetsLayoutMarginsFromSafeArea]; +} + +- (void)setInsetsLayoutMarginsFromSafeArea:(BOOL)insetsLayoutMarginsFromSafeArea +{ + ASDisplayNodeAssertThreadAffinity(self); + BOOL shouldNotifyAboutUpdate; + { + _bridge_prologue_write; + + _flags.fallbackInsetsLayoutMarginsFromSafeArea = insetsLayoutMarginsFromSafeArea; + + if (AS_AVAILABLE_IOS_TVOS(11.0, 11.0)) { + if (!_flags.layerBacked) { + _setToViewOnly(insetsLayoutMarginsFromSafeArea, insetsLayoutMarginsFromSafeArea); + } + } + + shouldNotifyAboutUpdate = _loaded(self) && (!AS_AT_LEAST_IOS11 || _flags.layerBacked); + } + + if (shouldNotifyAboutUpdate) { + [self layoutMarginsDidChange]; + } +} + +- (NSDictionary> *)actions +{ + _bridge_prologue_read; + return _getFromLayer(actions); +} + +- (void)setActions:(NSDictionary> *)actions +{ + _bridge_prologue_write; + _setToLayer(actions, actions); +} + +- (void)safeAreaInsetsDidChange +{ + ASDisplayNodeAssertMainThread(); + + if (self.automaticallyRelayoutOnSafeAreaChanges) { + [self setNeedsLayout]; + } + + [self _fallbackUpdateSafeAreaOnChildren]; +} + +@end + +@implementation ASDisplayNode (InternalPropertyBridge) + +- (CGFloat)layerCornerRadius +{ + _bridge_prologue_read; + return _getFromLayer(cornerRadius); +} + +- (void)setLayerCornerRadius:(CGFloat)newLayerCornerRadius +{ + _bridge_prologue_write; + _setToLayer(cornerRadius, newLayerCornerRadius); +} + +- (CACornerMask)layerMaskedCorners +{ + _bridge_prologue_read; + if (AS_AVAILABLE_IOS_TVOS(11, 11)) { + return _getFromLayer(maskedCorners); + } else { + return kASCACornerAllCorners; + } +} + +- (void)setLayerMaskedCorners:(CACornerMask)newLayerMaskedCorners +{ + _bridge_prologue_write; + if (AS_AVAILABLE_IOS_TVOS(11, 11)) { + _setToLayer(maskedCorners, newLayerMaskedCorners); + } else { + ASDisplayNodeAssert(newLayerMaskedCorners == kASCACornerAllCorners, + @"Cannot change maskedCorners property in iOS < 11 while using DefaultSlowCALayer rounding."); + } +} + +- (BOOL)_locked_insetsLayoutMarginsFromSafeArea +{ + DISABLED_ASAssertLocked(__instanceLock__); + if (AS_AVAILABLE_IOS_TVOS(11.0, 11.0)) { + if (!_flags.layerBacked) { + return _getFromViewOnly(insetsLayoutMarginsFromSafeArea); + } + } + return _flags.fallbackInsetsLayoutMarginsFromSafeArea; +} + +@end + +#pragma mark - UIViewBridgeAccessibility + +// ASDK supports accessibility for view or layer backed nodes. To be able to provide support for layer backed +// nodes, properties for all of the UIAccessibility protocol defined properties need to be provided an held in sync +// between node and view + +// Helper function with following logic: +// - If the node is not loaded yet use the property from the pending state +// - In case the node is loaded +// - Check if the node has a view and get the value from the view if loaded or from the pending state +// - If view is not available, e.g. the node is layer backed return the property value +#define _getAccessibilityFromViewOrProperty(nodeProperty, viewAndPendingViewStateProperty) _loaded(self) ? \ +(_view ? _view.viewAndPendingViewStateProperty : nodeProperty )\ +: ASDisplayNodeGetPendingState(self).viewAndPendingViewStateProperty + +// Helper function to set property values on pending state or view and property if loaded +#define _setAccessibilityToViewAndProperty(nodeProperty, nodeValueExpr, viewAndPendingViewStateProperty, viewAndPendingViewStateExpr) \ +nodeProperty = nodeValueExpr; _setToViewOnly(viewAndPendingViewStateProperty, viewAndPendingViewStateExpr) + +@implementation ASDisplayNode (UIViewBridgeAccessibility) + +// Walks up the view tree to nil out all the cached accsesibilityElements. This is required when changing +// accessibility properties like accessibilityViewIsModal. +- (void)invalidateAccessibilityElements +{ + // If we are not caching accessibilityElements we don't need to do anything here. + if (ASActivateExperimentalFeature(ASExperimentalDoNotCacheAccessibilityElements)) { + return; + } + + // we want to check if we are on the main thread first, since _loaded checks the layer and can only be done on main + if (ASDisplayNodeThreadIsMain() && _loaded(self)) { + self.view.accessibilityElements = nil; + [self.supernode invalidateAccessibilityElements]; + } +} + +- (BOOL)isAccessibilityElement +{ + _bridge_prologue_read; + return _getAccessibilityFromViewOrProperty(_flags.isAccessibilityElement, isAccessibilityElement); +} + +- (void)setIsAccessibilityElement:(BOOL)isAccessibilityElement +{ + _bridge_prologue_write; + _setAccessibilityToViewAndProperty(_flags.isAccessibilityElement, isAccessibilityElement, isAccessibilityElement, isAccessibilityElement); +} + +- (NSString *)accessibilityLabel +{ + _bridge_prologue_read; + return _getAccessibilityFromViewOrProperty(_accessibilityLabel, accessibilityLabel); +} + +- (void)setAccessibilityLabel:(NSString *)accessibilityLabel +{ + _bridge_prologue_write; + NSString *oldAccessibilityLabel = _getFromViewOnly(accessibilityLabel); + _setAccessibilityToViewAndProperty(_accessibilityLabel, accessibilityLabel, accessibilityLabel, accessibilityLabel); + if (AS_AVAILABLE_IOS_TVOS(11, 11)) { + NSAttributedString *accessibilityAttributedLabel = accessibilityLabel ? [[NSAttributedString alloc] initWithString:accessibilityLabel] : nil; + _setAccessibilityToViewAndProperty(_accessibilityAttributedLabel, accessibilityAttributedLabel, accessibilityAttributedLabel, accessibilityAttributedLabel); + } + + // We need to update action name when it's changed to reflect the latest state. + // Note: Update the custom action itself won't work when a11y is inside a list of custom actions + // in which one action results in a name change in the next action. In that case the UIAccessibility + // will hold the old action strongly until a11y jumps out of the list of custom actions. + // Thus we can only update name in place to have the change take effect. + BOOL needsUpdateActionName = self.isNodeLoaded && ![oldAccessibilityLabel isEqualToString:accessibilityLabel] && (0 != (_accessibilityTraits & ASInteractiveAccessibilityTraitsMask())); + if (needsUpdateActionName) { + self.accessibilityCustomAction.name = accessibilityLabel; + } +} + + +- (NSAttributedString *)accessibilityAttributedLabel +{ + _bridge_prologue_read; + return _getAccessibilityFromViewOrProperty(_accessibilityAttributedLabel, accessibilityAttributedLabel); +} + +- (void)setAccessibilityAttributedLabel:(NSAttributedString *)accessibilityAttributedLabel +{ + _bridge_prologue_write; + { _setAccessibilityToViewAndProperty(_accessibilityAttributedLabel, accessibilityAttributedLabel, accessibilityAttributedLabel, accessibilityAttributedLabel); } + { _setAccessibilityToViewAndProperty(_accessibilityLabel, accessibilityAttributedLabel.string, accessibilityLabel, accessibilityAttributedLabel.string); } +} + +- (NSString *)accessibilityHint +{ + _bridge_prologue_read; + return _getAccessibilityFromViewOrProperty(_accessibilityHint, accessibilityHint); +} + +- (void)setAccessibilityHint:(NSString *)accessibilityHint +{ + _bridge_prologue_write; + _setAccessibilityToViewAndProperty(_accessibilityHint, accessibilityHint, accessibilityHint, accessibilityHint); + if (AS_AVAILABLE_IOS_TVOS(11, 11)) { + NSAttributedString *accessibilityAttributedHint = accessibilityHint ? [[NSAttributedString alloc] initWithString:accessibilityHint] : nil; + _setAccessibilityToViewAndProperty(_accessibilityAttributedHint, accessibilityAttributedHint, accessibilityAttributedHint, accessibilityAttributedHint); + } +} + +- (NSAttributedString *)accessibilityAttributedHint +{ + _bridge_prologue_read; + return _getAccessibilityFromViewOrProperty(_accessibilityAttributedHint, accessibilityAttributedHint); +} + +- (void)setAccessibilityAttributedHint:(NSAttributedString *)accessibilityAttributedHint +{ + _bridge_prologue_write; + { _setAccessibilityToViewAndProperty(_accessibilityAttributedHint, accessibilityAttributedHint, accessibilityAttributedHint, accessibilityAttributedHint); } + + { _setAccessibilityToViewAndProperty(_accessibilityHint, accessibilityAttributedHint.string, accessibilityHint, accessibilityAttributedHint.string); } +} + +- (NSString *)accessibilityValue +{ + _bridge_prologue_read; + return _getAccessibilityFromViewOrProperty(_accessibilityValue, accessibilityValue); +} + +- (void)setAccessibilityValue:(NSString *)accessibilityValue +{ + _bridge_prologue_write; + _setAccessibilityToViewAndProperty(_accessibilityValue, accessibilityValue, accessibilityValue, accessibilityValue); + if (AS_AVAILABLE_IOS_TVOS(11, 11)) { + NSAttributedString *accessibilityAttributedValue = accessibilityValue ? [[NSAttributedString alloc] initWithString:accessibilityValue] : nil; + _setAccessibilityToViewAndProperty(_accessibilityAttributedValue, accessibilityAttributedValue, accessibilityAttributedValue, accessibilityAttributedValue); + } +} + +- (NSAttributedString *)accessibilityAttributedValue +{ + _bridge_prologue_read; + return _getAccessibilityFromViewOrProperty(_accessibilityAttributedValue, accessibilityAttributedValue); +} + +- (void)setAccessibilityAttributedValue:(NSAttributedString *)accessibilityAttributedValue +{ + _bridge_prologue_write; + { _setAccessibilityToViewAndProperty(_accessibilityAttributedValue, accessibilityAttributedValue, accessibilityAttributedValue, accessibilityAttributedValue); } + { _setAccessibilityToViewAndProperty(_accessibilityValue, accessibilityAttributedValue.string, accessibilityValue, accessibilityAttributedValue.string); } +} + +- (UIAccessibilityTraits)accessibilityTraits +{ + _bridge_prologue_read; + return _getAccessibilityFromViewOrProperty(_accessibilityTraits, accessibilityTraits); +} + +- (void)setAccessibilityTraits:(UIAccessibilityTraits)accessibilityTraits +{ + _bridge_prologue_write; + _setAccessibilityToViewAndProperty(_accessibilityTraits, accessibilityTraits, accessibilityTraits, accessibilityTraits); +} + +- (CGRect)accessibilityFrame +{ + _bridge_prologue_read; + return _getAccessibilityFromViewOrProperty(_accessibilityFrame, accessibilityFrame); +} + +- (void)setAccessibilityFrame:(CGRect)accessibilityFrame +{ + _bridge_prologue_write; + _setAccessibilityToViewAndProperty(_accessibilityFrame, accessibilityFrame, accessibilityFrame, accessibilityFrame); +} + +- (NSString *)accessibilityLanguage +{ + _bridge_prologue_read; + return _getAccessibilityFromViewOrProperty(_accessibilityLanguage, accessibilityLanguage); +} + +- (void)setAccessibilityLanguage:(NSString *)accessibilityLanguage +{ + _bridge_prologue_write; + _setAccessibilityToViewAndProperty(_accessibilityLanguage, accessibilityLanguage, accessibilityLanguage, accessibilityLanguage); +} + +- (BOOL)accessibilityElementsHidden +{ + _bridge_prologue_read; + return _getAccessibilityFromViewOrProperty(_flags.accessibilityElementsHidden, accessibilityElementsHidden); +} + +- (void)setAccessibilityElementsHidden:(BOOL)accessibilityElementsHidden +{ + _bridge_prologue_write; + BOOL oldHiddenValue = _getFromViewOnly(accessibilityElementsHidden); + _setAccessibilityToViewAndProperty(_flags.accessibilityElementsHidden, accessibilityElementsHidden, accessibilityElementsHidden, accessibilityElementsHidden); + + // if we made a change, we need to clear the view's accessibilityElements cache. + if (!ASActivateExperimentalFeature(ASExperimentalDoNotCacheAccessibilityElements) && self.isNodeLoaded && oldHiddenValue != accessibilityElementsHidden) { + [self invalidateAccessibilityElements]; + } +} + +- (BOOL)accessibilityViewIsModal +{ + _bridge_prologue_read; + return _getAccessibilityFromViewOrProperty(_flags.accessibilityViewIsModal, accessibilityViewIsModal); +} + +- (void)setAccessibilityViewIsModal:(BOOL)accessibilityViewIsModal +{ + _bridge_prologue_write; + BOOL oldAccessibilityViewIsModal = _getFromViewOnly(accessibilityViewIsModal); + _setAccessibilityToViewAndProperty(_flags.accessibilityViewIsModal, accessibilityViewIsModal, accessibilityViewIsModal, accessibilityViewIsModal); + + // if we made a change, we need to clear the view's accessibilityElements cache. + if (!ASActivateExperimentalFeature(ASExperimentalDoNotCacheAccessibilityElements) && self.isNodeLoaded && oldAccessibilityViewIsModal != accessibilityViewIsModal) { + [self invalidateAccessibilityElements]; + } +} + + +- (BOOL)shouldGroupAccessibilityChildren +{ + _bridge_prologue_read; + return _getAccessibilityFromViewOrProperty(_flags.shouldGroupAccessibilityChildren, shouldGroupAccessibilityChildren); +} + +- (void)setShouldGroupAccessibilityChildren:(BOOL)shouldGroupAccessibilityChildren +{ + _bridge_prologue_write; + _setAccessibilityToViewAndProperty(_flags.shouldGroupAccessibilityChildren, shouldGroupAccessibilityChildren, shouldGroupAccessibilityChildren, shouldGroupAccessibilityChildren); +} + +- (NSString *)accessibilityIdentifier +{ + _bridge_prologue_read; + return _getAccessibilityFromViewOrProperty(_accessibilityIdentifier, accessibilityIdentifier); +} + +- (void)setAccessibilityIdentifier:(NSString *)accessibilityIdentifier +{ + _bridge_prologue_write; + _setAccessibilityToViewAndProperty(_accessibilityIdentifier, accessibilityIdentifier, accessibilityIdentifier, accessibilityIdentifier); +} + +- (void)setAccessibilityNavigationStyle:(UIAccessibilityNavigationStyle)accessibilityNavigationStyle +{ + _bridge_prologue_write; + _setAccessibilityToViewAndProperty(_accessibilityNavigationStyle, accessibilityNavigationStyle, accessibilityNavigationStyle, accessibilityNavigationStyle); +} + +- (UIAccessibilityNavigationStyle)accessibilityNavigationStyle +{ + _bridge_prologue_read; + return _getAccessibilityFromViewOrProperty(_accessibilityNavigationStyle, accessibilityNavigationStyle); +} + +- (void)setAccessibilityCustomActions:(NSArray *)accessibilityCustomActions +{ + _bridge_prologue_write; + _setAccessibilityToViewAndProperty(_accessibilityCustomActions, accessibilityCustomActions, accessibilityCustomActions, accessibilityCustomActions); +} + +- (NSArray *)accessibilityCustomActions +{ + _bridge_prologue_read; + return _getAccessibilityFromViewOrProperty(_accessibilityCustomActions, accessibilityCustomActions); +} + +#if TARGET_OS_TV +- (void)setAccessibilityHeaderElements:(NSArray *)accessibilityHeaderElements +{ + _bridge_prologue_write; + _setAccessibilityToViewAndProperty(_accessibilityHeaderElements, accessibilityHeaderElements, accessibilityHeaderElements, accessibilityHeaderElements); +} + +- (NSArray *)accessibilityHeaderElements +{ + _bridge_prologue_read; + return _getAccessibilityFromViewOrProperty(_accessibilityHeaderElements, accessibilityHeaderElements); +} +#endif + +- (void)setAccessibilityActivationPoint:(CGPoint)accessibilityActivationPoint +{ + _bridge_prologue_write; + _setAccessibilityToViewAndProperty(_accessibilityActivationPoint, accessibilityActivationPoint, accessibilityActivationPoint, accessibilityActivationPoint); +} + +- (CGPoint)accessibilityActivationPoint +{ + _bridge_prologue_read; + return _getAccessibilityFromViewOrProperty(_accessibilityActivationPoint, accessibilityActivationPoint); +} + +- (void)setAccessibilityPath:(UIBezierPath *)accessibilityPath +{ + _bridge_prologue_write; + _setAccessibilityToViewAndProperty(_accessibilityPath, accessibilityPath, accessibilityPath, accessibilityPath); +} + +- (UIBezierPath *)accessibilityPath +{ + _bridge_prologue_read; + return _getAccessibilityFromViewOrProperty(_accessibilityPath, accessibilityPath); +} + +- (NSInteger)accessibilityElementCount +{ + _bridge_prologue_read; + return _getFromViewOnly(accessibilityElementCount); +} + +@end + + +#pragma mark - ASAsyncTransactionContainer + +@implementation ASDisplayNode (ASAsyncTransactionContainer) + +- (BOOL)asyncdisplaykit_isAsyncTransactionContainer +{ + _bridge_prologue_read; + return _getFromViewOrLayer(asyncdisplaykit_isAsyncTransactionContainer, asyncdisplaykit_isAsyncTransactionContainer); +} + +- (void)asyncdisplaykit_setAsyncTransactionContainer:(BOOL)asyncTransactionContainer +{ + _bridge_prologue_write; + _setToViewOrLayer(asyncdisplaykit_asyncTransactionContainer, asyncTransactionContainer, asyncdisplaykit_asyncTransactionContainer, asyncTransactionContainer); +} + +- (ASAsyncTransactionContainerState)asyncdisplaykit_asyncTransactionContainerState +{ + ASDisplayNodeAssertMainThread(); + return [_layer asyncdisplaykit_asyncTransactionContainerState]; +} + +- (void)asyncdisplaykit_cancelAsyncTransactions +{ + ASDisplayNodeAssertMainThread(); + [_layer asyncdisplaykit_cancelAsyncTransactions]; +} + +- (void)asyncdisplaykit_setCurrentAsyncTransaction:(_ASAsyncTransaction *)transaction +{ + _layer.asyncdisplaykit_currentAsyncTransaction = transaction; +} + +- (_ASAsyncTransaction *)asyncdisplaykit_currentAsyncTransaction +{ + return _layer.asyncdisplaykit_currentAsyncTransaction; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDisplayNodeCornerLayerDelegate.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDisplayNodeCornerLayerDelegate.h new file mode 100644 index 0000000..cb95e9f --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDisplayNodeCornerLayerDelegate.h @@ -0,0 +1,13 @@ +// +// ASDisplayNodeCornerLayerDelegate.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +@interface ASDisplayNodeCornerLayerDelegate : NSObject +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDisplayNodeCornerLayerDelegate.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDisplayNodeCornerLayerDelegate.mm new file mode 100644 index 0000000..42838ba --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDisplayNodeCornerLayerDelegate.mm @@ -0,0 +1,19 @@ +// +// ASDisplayNodeCornerLayerDelegate.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import "ASDisplayNodeCornerLayerDelegate.h" + +@implementation ASDisplayNodeCornerLayerDelegate + +- (id)actionForLayer:(CALayer *)layer forKey:(NSString *)event +{ + return (id)kCFNull; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDisplayNodeInternal.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDisplayNodeInternal.h new file mode 100644 index 0000000..22f3648 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDisplayNodeInternal.h @@ -0,0 +1,400 @@ +// +// ASDisplayNodeInternal.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +// +// The following methods are ONLY for use by _ASDisplayLayer, _ASDisplayView, and ASDisplayNode. +// These methods must never be called or overridden by other classes. +// + +#import +#import +#import +#import +#import +#import +#import +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@protocol _ASDisplayLayerDelegate; +@class _ASDisplayLayer; +@class _ASPendingState; +@class ASNodeController; +struct ASDisplayNodeFlags; + +BOOL ASDisplayNodeSubclassOverridesSelector(Class subclass, SEL selector); +BOOL ASDisplayNodeNeedsSpecialPropertiesHandling(BOOL isSynchronous, BOOL isLayerBacked); + +/// Get the pending view state for the node, creating one if needed. +_ASPendingState * ASDisplayNodeGetPendingState(ASDisplayNode * node); + +typedef NS_OPTIONS(unsigned short, ASDisplayNodeMethodOverrides) +{ + ASDisplayNodeMethodOverrideNone = 0, + ASDisplayNodeMethodOverrideTouchesBegan = 1 << 0, + ASDisplayNodeMethodOverrideTouchesCancelled = 1 << 1, + ASDisplayNodeMethodOverrideTouchesEnded = 1 << 2, + ASDisplayNodeMethodOverrideTouchesMoved = 1 << 3, + ASDisplayNodeMethodOverrideLayoutSpecThatFits = 1 << 4, + ASDisplayNodeMethodOverrideCalcLayoutThatFits = 1 << 5, + ASDisplayNodeMethodOverrideCalcSizeThatFits = 1 << 6, +}; + +typedef NS_OPTIONS(uint_least32_t, ASDisplayNodeAtomicFlags) +{ + Synchronous = 1 << 0, + YogaLayoutInProgress = 1 << 1, +}; + +// Can be called without the node's lock. Client is responsible for thread safety. +#define _loaded(node) (node->_layer != nil) + +#define checkFlag(flag) ((_atomicFlags.load() & flag) != 0) +// Returns the old value of the flag as a BOOL. +#define setFlag(flag, x) (((x ? _atomicFlags.fetch_or(flag) \ + : _atomicFlags.fetch_and(~flag)) & flag) != 0) + +ASDK_EXTERN NSString * const ASRenderingEngineDidDisplayScheduledNodesNotification; +ASDK_EXTERN NSString * const ASRenderingEngineDidDisplayNodesScheduledBeforeTimestamp; + +// Allow 2^n increments of begin disabling hierarchy notifications +#define VISIBILITY_NOTIFICATIONS_DISABLED_BITS 4 + +#define TIME_DISPLAYNODE_OPS 0 // If you're using this information frequently, try: (DEBUG || PROFILE) +static constexpr CACornerMask kASCACornerAllCorners = + kCALayerMinXMinYCorner | kCALayerMaxXMinYCorner | kCALayerMinXMaxYCorner | kCALayerMaxXMaxYCorner; + +#define NUM_CLIP_CORNER_LAYERS 4 + +@interface ASDisplayNode () <_ASTransitionContextCompletionDelegate, CALayerDelegate> +{ +@package + AS::RecursiveMutex __instanceLock__; + + _ASPendingState *_pendingViewState; + + UIView *_view; + CALayer *_layer; + + std::atomic _atomicFlags; + + struct ASDisplayNodeFlags { + // public properties + unsigned viewEverHadAGestureRecognizerAttached:1; + unsigned layerBacked:1; + unsigned displaysAsynchronously:1; + unsigned rasterizesSubtree:1; + unsigned shouldBypassEnsureDisplay:1; + unsigned displaySuspended:1; + unsigned shouldAnimateSizeChanges:1; + + // Wrapped view handling + + // The layer contents should not be cleared in case the node is wrapping a UIImageView.UIImageView is specifically + // optimized for performance and does not use the usual way to provide the contents of the CALayer via the + // CALayerDelegate method that backs the UIImageView. + unsigned canClearContentsOfLayer:1; + + // Prevent calling setNeedsDisplay on a layer that backs a UIImageView. Usually calling setNeedsDisplay on a CALayer + // triggers a recreation of the contents of layer unfortunately calling it on a CALayer that backs a UIImageView + // it goes through the normal flow to assign the contents to a layer via the CALayerDelegate methods. Unfortunately + // UIImageView does not do recreate the layer contents the usual way, it actually does not implement some of the + // methods at all instead it throws away the contents of the layer and nothing will show up. + unsigned canCallSetNeedsDisplayOfLayer:1; + + unsigned implementsDrawRect:1; + unsigned implementsImageDisplay:1; + unsigned implementsDrawParameters:1; + + // internal state + unsigned isEnteringHierarchy:1; + unsigned isExitingHierarchy:1; + unsigned isInHierarchy:1; + unsigned visibilityNotificationsDisabled:VISIBILITY_NOTIFICATIONS_DISABLED_BITS; + unsigned isDeallocating:1; + +#if YOGA + unsigned willApplyNextYogaCalculatedLayout:1; +#endif + // Automatically manages subnodes + unsigned automaticallyManagesSubnodes:1; // Main thread only + unsigned placeholderEnabled:1; + // Accessibility support + unsigned isAccessibilityElement:1; + unsigned accessibilityElementsHidden:1; + unsigned accessibilityViewIsModal:1; + unsigned shouldGroupAccessibilityChildren:1; + unsigned isAccessibilityContainer:1; + unsigned fallbackInsetsLayoutMarginsFromSafeArea:1; + unsigned automaticallyRelayoutOnSafeAreaChanges:1; + unsigned automaticallyRelayoutOnLayoutMarginsChanges:1; + unsigned isViewControllerRoot:1; + unsigned hasHadInterfaceStateDelegates:1; + } _flags; + + ASInterfaceState _interfaceState; + ASHierarchyState _hierarchyState; + ASInterfaceState _pendingInterfaceState; + ASInterfaceState _preExitingInterfaceState; + ASCornerRoundingType _cornerRoundingType; + ASDisplayNodePerformanceMeasurementOptions _measurementOptions; + ASDisplayNodeMethodOverrides _methodOverrides; + // Tinting support + UIColor *_tintColor; + + // Dynamic colors support + UIColor *_backgroundColor; + +@protected + ASDisplayNode * __weak _supernode; + NSMutableArray *_subnodes; + + ASNodeController *_strongNodeController; + __weak ASNodeController *_weakNodeController; + + // Set this to nil whenever you modify _subnodes + NSArray *_cachedSubnodes; + + std::atomic_uint _displaySentinel; + + // This is the desired contentsScale, not the scale at which the layer's contents should be displayed + CGFloat _contentsScaleForDisplay; + + UIEdgeInsets _hitTestSlop; + + // Layout support + ASLayoutElementStyle *_style; + ASPrimitiveTraitCollection _primitiveTraitCollection; + + // Layout Spec + ASLayoutSpecBlock _layoutSpecBlock; + NSString *_debugName; + +#if YOGA + // Only ASDisplayNodes are supported in _yogaChildren currently. This means that it is necessary to + // create ASDisplayNodes to make a stack layout when using Yoga. + // However, the implementation is mostly ready for id , with a few areas requiring updates. + NSMutableArray *_yogaChildren; + __weak ASDisplayNode *_yogaParent; + ASLayout *_yogaCalculatedLayout; +#endif + + // Layout Transition + _ASTransitionContext *_pendingLayoutTransitionContext; + NSTimeInterval _defaultLayoutTransitionDuration; + NSTimeInterval _defaultLayoutTransitionDelay; + UIViewAnimationOptions _defaultLayoutTransitionOptions; + + std::atomic _transitionID; + std::atomic _pendingTransitionID; + ASLayoutTransition *_pendingLayoutTransition; + ASDisplayNodeLayout _calculatedDisplayNodeLayout; + ASDisplayNodeLayout _pendingDisplayNodeLayout; + + /// Sentinel for layout data. Incremented when we get -setNeedsLayout / -invalidateCalculatedLayout. + /// Starts at 1. + std::atomic _layoutVersion; + + + // Layout Spec performance measurement + NSTimeInterval _layoutSpecTotalTime; + NSInteger _layoutSpecNumberOfPasses; + NSTimeInterval _layoutComputationTotalTime; + NSInteger _layoutComputationNumberOfPasses; + + + // View Loading + ASDisplayNodeViewBlock _viewBlock; + ASDisplayNodeLayerBlock _layerBlock; + NSMutableArray *_onDidLoadBlocks; + Class _viewClass; // nil -> _ASDisplayView + Class _layerClass; // nil -> _ASDisplayLayer + + + // Placeholder support + UIImage *_placeholderImage; + CALayer *_placeholderLayer; + + // keeps track of nodes/subnodes that have not finished display, used with placeholders + ASWeakSet *_pendingDisplayNodes; + + + // Corner Radius support + CGFloat _cornerRadius; + CALayer *_clipCornerLayers[NUM_CLIP_CORNER_LAYERS]; + CACornerMask _maskedCorners; + + ASDisplayNodeContextModifier _willDisplayNodeContentWithRenderingContext; + ASDisplayNodeContextModifier _didDisplayNodeContentWithRenderingContext; + + + // Accessibility support + NSString *_accessibilityLabel; + NSAttributedString *_accessibilityAttributedLabel; + NSString *_accessibilityHint; + NSAttributedString *_accessibilityAttributedHint; + NSString *_accessibilityValue; + NSAttributedString *_accessibilityAttributedValue; + UIAccessibilityTraits _accessibilityTraits; + CGRect _accessibilityFrame; + NSString *_accessibilityLanguage; + NSString *_accessibilityIdentifier; + UIAccessibilityNavigationStyle _accessibilityNavigationStyle; + NSArray *_accessibilityCustomActions; + NSArray *_accessibilityHeaderElements; + CGPoint _accessibilityActivationPoint; + UIBezierPath *_accessibilityPath; + + + // Safe Area support + // These properties are used on iOS 10 and lower, where safe area is not supported by UIKit. + UIEdgeInsets _fallbackSafeAreaInsets; + + + +#pragma mark - ASDisplayNode (Debugging) + ASLayout *_unflattenedLayout; + +#if TIME_DISPLAYNODE_OPS +@public + NSTimeInterval _debugTimeToCreateView; + NSTimeInterval _debugTimeToApplyPendingState; + NSTimeInterval _debugTimeToAddSubnodeViews; + NSTimeInterval _debugTimeForDidLoad; +#endif + + /// Fast path: tells whether we've ever had an interface state delegate before. + __weak id _interfaceStateDelegates[AS_MAX_INTERFACE_STATE_DELEGATES]; +} + ++ (void)scheduleNodeForRecursiveDisplay:(ASDisplayNode *)node; + +/// The _ASDisplayLayer backing the node, if any. +@property (nullable, nonatomic, readonly) _ASDisplayLayer *asyncLayer; + +/// Bitmask to check which methods an object overrides. +- (ASDisplayNodeMethodOverrides)methodOverrides; + +/** + * Invoked before a call to setNeedsLayout to the underlying view + */ +- (void)__setNeedsLayout; + +/** + * Invoked after a call to setNeedsDisplay to the underlying view + */ +- (void)__setNeedsDisplay; + +/** + * Setup the node -> controller reference. Strong or weak is based on + * the "shouldInvertStrongReference" property of the controller. + * + * Note: To prevent lock-ordering deadlocks, this method does not take the node's lock. + * In practice, changing the node controller of a node multiple times is not + * supported behavior. + */ +- (void)__setNodeController:(ASNodeController *)controller; + +/** + * Called whenever the node needs to layout its subnodes and, if it's already loaded, its subviews. Executes the layout pass for the node + * + * This method is thread-safe but asserts thread affinity. + */ +- (void)__layout; + +/** + * Internal tree modification methods. + */ +- (void)_removeFromSupernodeIfEqualTo:(ASDisplayNode *)supernode; + +// Private API for helper functions / unit tests. Use ASDisplayNodeDisableHierarchyNotifications() to control this. +- (BOOL)__visibilityNotificationsDisabled; +- (BOOL)__selfOrParentHasVisibilityNotificationsDisabled; +- (void)__incrementVisibilityNotificationsDisabled; +- (void)__decrementVisibilityNotificationsDisabled; + +/// Helper method to summarize whether or not the node run through the display process +- (BOOL)_implementsDisplay; + +/// Display the node's view/layer immediately on the current thread, bypassing the background thread rendering. Will be deprecated. +- (void)displayImmediately; + +/// Refreshes any precomposited or drawn clip corners, setting up state as required to transition corner config. +- (void)updateCornerRoundingWithType:(ASCornerRoundingType)newRoundingType + cornerRadius:(CGFloat)newCornerRadius + maskedCorners:(CACornerMask)newMaskedCorners; + +/// Alternative initialiser for backing with a custom view class. Supports asynchronous display with _ASDisplayView subclasses. +- (instancetype)initWithViewClass:(Class)viewClass; + +/// Alternative initialiser for backing with a custom layer class. Supports asynchronous display with _ASDisplayLayer subclasses. +- (instancetype)initWithLayerClass:(Class)layerClass; + +@property (nonatomic) CGFloat contentsScaleForDisplay; + +- (void)applyPendingViewState; + +/** + * Makes a local copy of the interface state delegates then calls the block on each. + * + * Lock is not held during block invocation. Method must not be called with the lock held. + */ +- (void)enumerateInterfaceStateDelegates:(void(NS_NOESCAPE ^)(id delegate))block; + +/** + * This method has proven helpful in a few rare scenarios, similar to a category extension on UIView, + * but it's considered private API for now and its use should not be encouraged. + * @param checkViewHierarchy If YES, and no supernode can be found, method will walk up from `self.view` to find a supernode. + * If YES, this method must be called on the main thread and the node must not be layer-backed. + */ +- (nullable ASDisplayNode *)_supernodeWithClass:(Class)supernodeClass checkViewHierarchy:(BOOL)checkViewHierarchy; + +/** + * Whether this node rasterizes its descendants. See -enableSubtreeRasterization. + */ +@property (readonly) BOOL rasterizesSubtree; + +/** + * Called if a gesture recognizer was attached to an _ASDisplayView + */ +- (void)nodeViewDidAddGestureRecognizer; + +// Recalculates fallbackSafeAreaInsets for the subnodes +- (void)_fallbackUpdateSafeAreaOnChildren; + +@end + +@interface ASDisplayNode (InternalPropertyBridge) + +@property (nonatomic) CGFloat layerCornerRadius; + +/// NOTE: Changing this to non-default under iOS < 11 will make an assertion (for the end user to see.) +@property (nonatomic) CACornerMask layerMaskedCorners; + +- (BOOL)_locked_insetsLayoutMarginsFromSafeArea; + +@end + +@interface ASDisplayNode (ASLayoutElementPrivate) + +/** + * Returns the internal style object or creates a new if no exists. Need to be called with lock held. + */ +- (ASLayoutElementStyle *)_locked_style; + +/** + * Returns the current layout element. Need to be called with lock held. + */ +- (id)_locked_layoutElementThatFits:(ASSizeRange)constrainedSize; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDisplayNodeLayout.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDisplayNodeLayout.h new file mode 100644 index 0000000..ba8d9c2 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDisplayNodeLayout.h @@ -0,0 +1,58 @@ +// +// ASDisplayNodeLayout.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#pragma once + +#import + +@class ASLayout; + +/* + * Represents a connection between an ASLayout and a ASDisplayNode + * ASDisplayNode uses this to store additional information that are necessary besides the layout + */ +struct ASDisplayNodeLayout { + ASLayout *layout; + ASSizeRange constrainedSize; + CGSize parentSize; + BOOL requestedLayoutFromAbove; + NSUInteger version; + + /* + * Create a new display node layout with + * @param layout The layout to associate, usually returned from a call to -layoutThatFits:parentSize: + * @param constrainedSize Constrained size used to create the layout + * @param parentSize Parent size used to create the layout + * @param version The version of the source layout data – see ASDisplayNode's _layoutVersion. + */ + ASDisplayNodeLayout(ASLayout *layout, ASSizeRange constrainedSize, CGSize parentSize, NSUInteger version) + : layout(layout), constrainedSize(constrainedSize), parentSize(parentSize), requestedLayoutFromAbove(NO), version(version) {}; + + /* + * Creates a layout without any layout associated. By default this display node layout is dirty. + */ + ASDisplayNodeLayout() + : layout(nil), constrainedSize({{0, 0}, {0, 0}}), parentSize({0, 0}), requestedLayoutFromAbove(NO), version(0) {}; + + /** + * Returns whether this is valid for a given version + */ + BOOL isValid(NSUInteger versionArg) { + return layout != nil && version >= versionArg; + } + + /** + * Returns whether this is valid for a given constrained size, parent size, and version + */ + BOOL isValid(ASSizeRange theConstrainedSize, CGSize theParentSize, NSUInteger versionArg) { + return isValid(versionArg) + && CGSizeEqualToSize(parentSize, theParentSize) + && ASSizeRangeEqualToSizeRange(constrainedSize, theConstrainedSize); + } +}; diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDisplayNodeTipState.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDisplayNodeTipState.h new file mode 100644 index 0000000..1e1ef20 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDisplayNodeTipState.h @@ -0,0 +1,32 @@ +// +// ASDisplayNodeTipState.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +@class ASDisplayNode, ASTipNode; + +NS_ASSUME_NONNULL_BEGIN + +AS_SUBCLASSING_RESTRICTED +@interface ASDisplayNodeTipState : NSObject + +- (instancetype)initWithNode:(ASDisplayNode *)node NS_DESIGNATED_INITIALIZER; + +- (instancetype)init NS_UNAVAILABLE; + +/// Unsafe because once the node is deallocated, we will not be able to access the tip state. +@property (nonatomic, unsafe_unretained, readonly) ASDisplayNode *node; + +/// Main-thread-only. +@property (nonatomic, nullable) ASTipNode *tipNode; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDisplayNodeTipState.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDisplayNodeTipState.mm new file mode 100644 index 0000000..b5a4231 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASDisplayNodeTipState.mm @@ -0,0 +1,25 @@ +// +// ASDisplayNodeTipState.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import "ASDisplayNodeTipState.h" + +@interface ASDisplayNodeTipState () +@end + +@implementation ASDisplayNodeTipState + +- (instancetype)initWithNode:(ASDisplayNode *)node +{ + if (self = [super init]) { + _node = node; + } + return self; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASIGListAdapterBasedDataSource.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASIGListAdapterBasedDataSource.h new file mode 100644 index 0000000..5be2185 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASIGListAdapterBasedDataSource.h @@ -0,0 +1,29 @@ +// +// ASIGListAdapterBasedDataSource.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#if AS_IG_LIST_KIT + +#import +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +AS_SUBCLASSING_RESTRICTED +@interface ASIGListAdapterBasedDataSource : NSObject + +- (instancetype)initWithListAdapter:(IGListAdapter *)listAdapter collectionDelegate:(nullable id)collectionDelegate; + +@end + +#endif + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASIGListAdapterBasedDataSource.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASIGListAdapterBasedDataSource.mm new file mode 100644 index 0000000..10f51f3 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASIGListAdapterBasedDataSource.mm @@ -0,0 +1,405 @@ +// +// ASIGListAdapterBasedDataSource.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#if AS_IG_LIST_KIT + +#import "ASIGListAdapterBasedDataSource.h" +#import +#import + +typedef IGListSectionController ASIGSectionController; + +/// The optional methods that a class implements from ASSectionController. +/// Note: Bitfields are not supported by NSValue so we can't use them. +typedef struct { + BOOL sizeRangeForItem; + BOOL shouldBatchFetch; + BOOL beginBatchFetchWithContext; +} ASSectionControllerOverrides; + +/// The optional methods that a class implements from ASSupplementaryNodeSource. +/// Note: Bitfields are not supported by NSValue so we can't use them. +typedef struct { + BOOL sizeRangeForSupplementary; +} ASSupplementarySourceOverrides; + +@protocol ASIGSupplementaryNodeSource +@end + +@interface ASIGListAdapterBasedDataSource () +@property (nonatomic, weak, readonly) IGListAdapter *listAdapter; +@property (nonatomic, readonly) id delegate; +@property (nonatomic, readonly) id dataSource; +@property (nonatomic, weak, readonly) id collectionDelegate; + +/** + * The section controller that we will forward beginBatchFetchWithContext: to. + * Since shouldBatchFetch: is called on main, we capture the last section controller in there, + * and then we use it and clear it in beginBatchFetchWithContext: (on default queue). + * + * It is safe to use it without a lock in this limited way, since those two methods will + * never execute in parallel. + */ +@property (nonatomic, weak) ASIGSectionController *sectionControllerForBatchFetching; +@end + +@implementation ASIGListAdapterBasedDataSource + +- (instancetype)initWithListAdapter:(IGListAdapter *)listAdapter collectionDelegate:(nullable id)collectionDelegate +{ + if (self = [super init]) { +#if IG_LIST_COLLECTION_VIEW + [ASIGListAdapterBasedDataSource setASCollectionViewSuperclass]; +#endif + [ASIGListAdapterBasedDataSource configureUpdater:listAdapter.updater]; + + ASDisplayNodeAssert([listAdapter conformsToProtocol:@protocol(UICollectionViewDataSource)], @"Expected IGListAdapter to conform to UICollectionViewDataSource."); + ASDisplayNodeAssert([listAdapter conformsToProtocol:@protocol(UICollectionViewDelegateFlowLayout)], @"Expected IGListAdapter to conform to UICollectionViewDelegateFlowLayout."); + _listAdapter = listAdapter; + _collectionDelegate = collectionDelegate; + } + return self; +} + +- (id)dataSource +{ + return (id)_listAdapter; +} + +- (id)delegate +{ + return (id)_listAdapter; +} + +#pragma mark - ASCollectionDelegate + +- (void)collectionNode:(ASCollectionNode *)collectionNode didSelectItemAtIndexPath:(NSIndexPath *)indexPath +{ + [self.delegate collectionView:collectionNode.view didSelectItemAtIndexPath:indexPath]; +} + +- (void)collectionNode:(ASCollectionNode *)collectionNode didDeselectItemAtIndexPath:(NSIndexPath *)indexPath +{ + [self.delegate collectionView:collectionNode.view didDeselectItemAtIndexPath:indexPath]; +} + +- (void)collectionNode:(ASCollectionNode *)collectionNode didHighlightItemAtIndexPath:(NSIndexPath *)indexPath +{ + [self.delegate collectionView:collectionNode.view didHighlightItemAtIndexPath:indexPath]; +} + +- (void)collectionNode:(ASCollectionNode *)collectionNode didUnhighlightItemAtIndexPath:(NSIndexPath *)indexPath +{ + [self.delegate collectionView:collectionNode.view didUnhighlightItemAtIndexPath:indexPath]; +} + +- (void)scrollViewDidScroll:(UIScrollView *)scrollView +{ + [self.delegate scrollViewDidScroll:scrollView]; +} + +- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView +{ + [self.delegate scrollViewWillBeginDragging:scrollView]; +} + +- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset +{ + // IGListAdapter doesn't implement scrollViewWillEndDragging yet (pending pull request), so we need this check for now. Doesn't hurt to have it anyways :) + if ([self.delegate respondsToSelector:@selector(scrollViewWillEndDragging:withVelocity:targetContentOffset:)]) { + [self.delegate scrollViewWillEndDragging:scrollView withVelocity:velocity targetContentOffset:targetContentOffset]; + } +} + +- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate +{ + [self.delegate scrollViewDidEndDragging:scrollView willDecelerate:decelerate]; +} + +- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView +{ + [self.delegate scrollViewDidEndDecelerating:scrollView]; +} + +- (BOOL)shouldBatchFetchForCollectionNode:(ASCollectionNode *)collectionNode +{ + if ([_collectionDelegate respondsToSelector:@selector(shouldBatchFetchForCollectionNode:)]) { + return [_collectionDelegate shouldBatchFetchForCollectionNode:collectionNode]; + } + + NSInteger sectionCount = [self numberOfSectionsInCollectionNode:collectionNode]; + if (sectionCount == 0) { + return NO; + } + + // If they implement shouldBatchFetch, call it. Otherwise, just say YES if they implement beginBatchFetch. + ASIGSectionController *ctrl = [self sectionControllerForSection:sectionCount - 1]; + ASSectionControllerOverrides o = [ASIGListAdapterBasedDataSource overridesForSectionControllerClass:ctrl.class]; + BOOL result = (o.shouldBatchFetch ? [ctrl shouldBatchFetch] : o.beginBatchFetchWithContext); + if (result) { + self.sectionControllerForBatchFetching = ctrl; + } + return result; +} + +- (void)collectionNode:(ASCollectionNode *)collectionNode willBeginBatchFetchWithContext:(ASBatchContext *)context +{ + if ([_collectionDelegate respondsToSelector:@selector(collectionNode:willBeginBatchFetchWithContext:)]) { + [_collectionDelegate collectionNode:collectionNode willBeginBatchFetchWithContext:context]; + return; + } + + ASIGSectionController *ctrl = self.sectionControllerForBatchFetching; + self.sectionControllerForBatchFetching = nil; + [ctrl beginBatchFetchWithContext:context]; +} + +- (void)collectionNode:(ASCollectionNode *)collectionNode willDisplayItemWithNode:(ASCellNode *)node +{ + NSIndexPath *indexPath = [collectionNode.view indexPathForNode:node]; + UIView *contentView = node.view.superview; + UICollectionViewCell *cell = (UICollectionViewCell *)contentView.superview; + + if (cell == nil || indexPath == nil) { + return; + } + + [self.delegate collectionView:collectionNode.view willDisplayCell:cell forItemAtIndexPath:indexPath]; +} + +- (void)collectionNode:(ASCollectionNode *)collectionNode didEndDisplayingItemWithNode:(ASCellNode *)node +{ + NSIndexPath *indexPath = [collectionNode.view indexPathForNode:node]; + UIView *contentView = node.view.superview; + UICollectionViewCell *cell = (UICollectionViewCell *)contentView.superview; + + if (cell == nil || indexPath == nil) { + return; + } + + [self.delegate collectionView:collectionNode.view didEndDisplayingCell:cell forItemAtIndexPath:indexPath]; +} + +/** + * Note: It is not documented that ASCollectionNode will forward these UIKit delegate calls if they are implemented. + * It is not considered harmful to do so, and adding them to documentation will confuse most users, who should + * instead using the ASCollectionDelegate callbacks. + */ +#pragma mark - ASCollectionDelegateInterop + +- (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath +{ + [self.delegate collectionView:collectionView willDisplayCell:cell forItemAtIndexPath:indexPath]; +} + +- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath +{ + [self.delegate collectionView:collectionView didEndDisplayingCell:cell forItemAtIndexPath:indexPath]; +} + +#pragma mark - ASCollectionDelegateFlowLayout + +- (ASSizeRange)collectionNode:(ASCollectionNode *)collectionNode sizeRangeForHeaderInSection:(NSInteger)section +{ + id src = [self supplementaryElementSourceForSection:section]; + if ([ASIGListAdapterBasedDataSource overridesForSupplementarySourceClass:[src class]].sizeRangeForSupplementary) { + return [src sizeRangeForSupplementaryElementOfKind:UICollectionElementKindSectionHeader atIndex:0]; + } else { + return ASSizeRangeZero; + } +} + +- (ASSizeRange)collectionNode:(ASCollectionNode *)collectionNode sizeRangeForFooterInSection:(NSInteger)section +{ + id src = [self supplementaryElementSourceForSection:section]; + if ([ASIGListAdapterBasedDataSource overridesForSupplementarySourceClass:[src class]].sizeRangeForSupplementary) { + return [src sizeRangeForSupplementaryElementOfKind:UICollectionElementKindSectionFooter atIndex:0]; + } else { + return ASSizeRangeZero; + } +} + +- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section +{ + return [self.delegate collectionView:collectionView layout:collectionViewLayout insetForSectionAtIndex:section]; +} + +- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section +{ + return [self.delegate collectionView:collectionView layout:collectionViewLayout minimumLineSpacingForSectionAtIndex:section]; +} + +- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section +{ + return [self.delegate collectionView:collectionView layout:collectionViewLayout minimumInteritemSpacingForSectionAtIndex:section]; +} + +#pragma mark - ASCollectionDataSource + +- (NSInteger)collectionNode:(ASCollectionNode *)collectionNode numberOfItemsInSection:(NSInteger)section +{ + return [self.dataSource collectionView:collectionNode.view numberOfItemsInSection:section]; +} + +- (NSInteger)numberOfSectionsInCollectionNode:(ASCollectionNode *)collectionNode +{ + return [self.dataSource numberOfSectionsInCollectionView:collectionNode.view]; +} + +- (ASCellNodeBlock)collectionNode:(ASCollectionNode *)collectionNode nodeBlockForItemAtIndexPath:(NSIndexPath *)indexPath +{ + ASIGSectionController *ctrl = [self sectionControllerForSection:indexPath.section]; + ASDisplayNodeAssert([ctrl respondsToSelector:@selector(nodeBlockForItemAtIndex:)], @"Expected section controller to respond to to %@. Controller: %@", NSStringFromSelector(@selector(nodeBlockForItemAtIndex:)), ctrl); + return [ctrl nodeBlockForItemAtIndex:indexPath.item]; +} + +- (ASCellNode *)collectionNode:(ASCollectionNode *)collectionNode nodeForItemAtIndexPath:(NSIndexPath *)indexPath +{ + ASIGSectionController *ctrl = [self sectionControllerForSection:indexPath.section]; + ASDisplayNodeAssert([ctrl respondsToSelector:@selector(nodeForItemAtIndex:)], @"Expected section controller to respond to to %@. Controller: %@", NSStringFromSelector(@selector(nodeForItemAtIndex:)), ctrl); + return [ctrl nodeForItemAtIndex:indexPath.item]; +} + +- (ASSizeRange)collectionNode:(ASCollectionNode *)collectionNode constrainedSizeForItemAtIndexPath:(NSIndexPath *)indexPath +{ + ASIGSectionController *ctrl = [self sectionControllerForSection:indexPath.section]; + if ([ASIGListAdapterBasedDataSource overridesForSectionControllerClass:ctrl.class].sizeRangeForItem) { + return [ctrl sizeRangeForItemAtIndex:indexPath.item]; + } else { + return ASSizeRangeUnconstrained; + } +} + +- (ASCellNodeBlock)collectionNode:(ASCollectionNode *)collectionNode nodeBlockForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath +{ + id ctrl = [self supplementaryElementSourceForSection:indexPath.section]; + ASDisplayNodeAssert([ctrl respondsToSelector:@selector(nodeBlockForSupplementaryElementOfKind:atIndex:)], @"Expected section controller to respond to to %@. Controller: %@", NSStringFromSelector(@selector(nodeBlockForSupplementaryElementOfKind:atIndex:)), ctrl); + return [ctrl nodeBlockForSupplementaryElementOfKind:kind atIndex:indexPath.item]; +} + +- (ASCellNode *)collectionNode:(ASCollectionNode *)collectionNode nodeForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath +{ + id ctrl = [self supplementaryElementSourceForSection:indexPath.section]; + ASDisplayNodeAssert([ctrl respondsToSelector:@selector(nodeForSupplementaryElementOfKind:atIndex:)], @"Expected section controller to respond to to %@. Controller: %@", NSStringFromSelector(@selector(nodeForSupplementaryElementOfKind:atIndex:)), ctrl); + return [ctrl nodeForSupplementaryElementOfKind:kind atIndex:indexPath.item]; +} + +- (NSArray *)collectionNode:(ASCollectionNode *)collectionNode supplementaryElementKindsInSection:(NSInteger)section +{ + return [[self supplementaryElementSourceForSection:section] supportedElementKinds]; +} + +#pragma mark - ASCollectionDataSourceInterop + +- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(nonnull NSIndexPath *)indexPath +{ + return [self.dataSource collectionView:collectionView cellForItemAtIndexPath:indexPath]; +} + +- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath +{ + return [self.dataSource collectionView:collectionView viewForSupplementaryElementOfKind:kind atIndexPath:indexPath]; +} + ++ (BOOL)dequeuesCellsForNodeBackedItems +{ + return YES; +} + +#pragma mark - Helpers + +- (id)supplementaryElementSourceForSection:(NSInteger)section +{ + ASIGSectionController *ctrl = [self sectionControllerForSection:section]; + id src = (id)ctrl.supplementaryViewSource; + ASDisplayNodeAssert(src == nil || [src conformsToProtocol:@protocol(ASSupplementaryNodeSource)], @"Supplementary view source should conform to %@", NSStringFromProtocol(@protocol(ASSupplementaryNodeSource))); + return src; +} + +- (ASIGSectionController *)sectionControllerForSection:(NSInteger)section +{ + id object = [_listAdapter objectAtSection:section]; + ASIGSectionController *ctrl = (ASIGSectionController *)[_listAdapter sectionControllerForObject:object]; + ASDisplayNodeAssert([ctrl conformsToProtocol:@protocol(ASSectionController)], @"Expected section controller to conform to %@. Controller: %@", NSStringFromProtocol(@protocol(ASSectionController)), ctrl); + return ctrl; +} + +/// If needed, set ASCollectionView's superclass to IGListCollectionView (IGListKit < 3.0). +#if IG_LIST_COLLECTION_VIEW ++ (void)setASCollectionViewSuperclass +{ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + class_setSuperclass([ASCollectionView class], [IGListCollectionView class]); + }); +#pragma clang diagnostic pop +} +#endif + +/// Ensure updater won't call reloadData on us. ++ (void)configureUpdater:(id)updater +{ + // Cast to NSObject will be removed after https://github.com/Instagram/IGListKit/pull/435 + if ([(id)updater isKindOfClass:[IGListAdapterUpdater class]]) { + [(IGListAdapterUpdater *)updater setAllowsBackgroundReloading:NO]; + } else { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + NSLog(@"WARNING: Use of non-%@ updater with AsyncDisplayKit is discouraged. Updater: %@", NSStringFromClass([IGListAdapterUpdater class]), updater); + }); + } +} + ++ (ASSupplementarySourceOverrides)overridesForSupplementarySourceClass:(Class)c +{ + static NSCache *cache; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + cache = [[NSCache alloc] init]; + }); + NSValue *obj = [cache objectForKey:c]; + ASSupplementarySourceOverrides o; + if (obj == nil) { + o.sizeRangeForSupplementary = [c instancesRespondToSelector:@selector(sizeRangeForSupplementaryElementOfKind:atIndex:)]; + obj = [NSValue valueWithBytes:&o objCType:@encode(ASSupplementarySourceOverrides)]; + [cache setObject:obj forKey:c]; + } else { + [obj getValue:&o]; + } + return o; +} + ++ (ASSectionControllerOverrides)overridesForSectionControllerClass:(Class)c +{ + static NSCache *cache; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + cache = [[NSCache alloc] init]; + }); + NSValue *obj = [cache objectForKey:c]; + ASSectionControllerOverrides o; + if (obj == nil) { + o.sizeRangeForItem = [c instancesRespondToSelector:@selector(sizeRangeForItemAtIndex:)]; + o.beginBatchFetchWithContext = [c instancesRespondToSelector:@selector(beginBatchFetchWithContext:)]; + o.shouldBatchFetch = [c instancesRespondToSelector:@selector(shouldBatchFetch)]; + obj = [NSValue valueWithBytes:&o objCType:@encode(ASSectionControllerOverrides)]; + [cache setObject:obj forKey:c]; + } else { + [obj getValue:&o]; + } + return o; +} + +@end + +#endif // AS_IG_LIST_KIT diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASImageNode+AnimatedImagePrivate.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASImageNode+AnimatedImagePrivate.h new file mode 100644 index 0000000..917eb6f --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASImageNode+AnimatedImagePrivate.h @@ -0,0 +1,50 @@ +// +// ASImageNode+AnimatedImagePrivate.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#define ASAnimatedImageDefaultRunLoopMode NSRunLoopCommonModes + +@interface ASImageNode () +{ + AS::Mutex _displayLinkLock; + id _animatedImage; + NSString *_animatedImageRunLoopMode; + CADisplayLink *_displayLink; + NSUInteger _lastSuccessfulFrameIndex; + + //accessed on main thread only + CFTimeInterval _playHead; + NSUInteger _playedLoops; + + // Group the BOOLs into a bitfield struct to save memory. + struct { + unsigned int animatedImagePaused:1; + unsigned int cropEnabled:1; // Defaults to YES. + unsigned int forceUpscaling:1; //Defaults to NO. + unsigned int regenerateFromImageAsset:1; //Defaults to NO. + } _imageNodeFlags; +} + +@property (nonatomic) CFTimeInterval lastDisplayLinkFire; + +@end + +@interface ASImageNode (AnimatedImagePrivate) + +- (void)_locked_setAnimatedImage:(id )animatedImage; + +@end + + +@interface ASImageNode (AnimatedImageInvalidation) + +- (void)invalidateAnimatedImage; + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASImageNode+CGExtras.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASImageNode+CGExtras.h new file mode 100644 index 0000000..dd529a3 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASImageNode+CGExtras.h @@ -0,0 +1,31 @@ +// +// ASImageNode+CGExtras.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +/** + @abstract Decides how to scale and crop an image to fit in the provided size, while not wasting memory by upscaling images + @param sourceImageSize The size of the encoded image. + @param boundsSize The bounds in which the image will be displayed. + @param contentMode The mode that defines how image will be scaled and cropped to fit. Supported values are UIViewContentModeScaleToAspectFill and UIViewContentModeScaleToAspectFit. + @param cropRect A rectangle that is to be featured by the cropped image. The rectangle is specified as a "unit rectangle," using fractions of the source image's width and height, e.g. CGRectMake(0.5, 0, 0.5, 1.0) will feature the full right half a photo. If the cropRect is empty, the contentMode will be used to determine the drawRect's size, and only the cropRect's origin will be used for positioning. + @param forceUpscaling A boolean that indicates you would *not* like the backing size to be downscaled if the image is smaller than the destination size. Setting this to YES will result in higher memory usage when images are smaller than their destination. + @param forcedSize A CGSize, that if non-CGSizeZero, indicates that the backing size should be forcedSize and not calculated based on boundsSize. + @discussion If the image is smaller than the size and UIViewContentModeScaleToAspectFill is specified, we suggest the input size so it will be efficiently upscaled on the GPU by the displaying layer at composite time. + */ +ASDK_EXTERN void ASCroppedImageBackingSizeAndDrawRectInBounds(CGSize sourceImageSize, + CGSize boundsSize, + UIViewContentMode contentMode, + CGRect cropRect, + BOOL forceUpscaling, + CGSize forcedSize, + CGSize *outBackingSize, + CGRect *outDrawRect + ); diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASImageNode+CGExtras.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASImageNode+CGExtras.mm new file mode 100644 index 0000000..b7550c5 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASImageNode+CGExtras.mm @@ -0,0 +1,122 @@ +// +// ASImageNode+CGExtras.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +// TODO rewrite these to be closer to the intended use -- take UIViewContentMode as param, CGRect destinationBounds, CGSize sourceSize. +static CGSize _ASSizeFillWithAspectRatio(CGFloat aspectRatio, CGSize constraints); +static CGSize _ASSizeFitWithAspectRatio(CGFloat aspectRatio, CGSize constraints); + +static CGSize _ASSizeFillWithAspectRatio(CGFloat sizeToScaleAspectRatio, CGSize destinationSize) +{ + CGFloat destinationAspectRatio = destinationSize.width / destinationSize.height; + if (sizeToScaleAspectRatio > destinationAspectRatio) { + return CGSizeMake(destinationSize.height * sizeToScaleAspectRatio, destinationSize.height); + } else { + return CGSizeMake(destinationSize.width, round(destinationSize.width / sizeToScaleAspectRatio)); + } +} + +static CGSize _ASSizeFitWithAspectRatio(CGFloat aspectRatio, CGSize constraints) +{ + CGFloat constraintAspectRatio = constraints.width / constraints.height; + if (aspectRatio > constraintAspectRatio) { + return CGSizeMake(constraints.width, constraints.width / aspectRatio); + } else { + return CGSizeMake(constraints.height * aspectRatio, constraints.height); + } +} + +void ASCroppedImageBackingSizeAndDrawRectInBounds(CGSize sourceImageSize, + CGSize boundsSize, + UIViewContentMode contentMode, + CGRect cropRect, + BOOL forceUpscaling, + CGSize forcedSize, + CGSize *outBackingSize, + CGRect *outDrawRect + ) +{ + + size_t destinationWidth = boundsSize.width; + size_t destinationHeight = boundsSize.height; + + // Often, an image is too low resolution to completely fill the width and height provided. + // Per the API contract as commented in the header, we will adjust input parameters (destinationWidth, destinationHeight) to ensure that the image is not upscaled on the CPU. + CGFloat boundsAspectRatio = (CGFloat)destinationWidth / (CGFloat)destinationHeight; + + CGSize minimumDestinationSize = sourceImageSize; + BOOL cropToRectDimensions = !CGRectIsEmpty(cropRect); + + // Given image size and container ratio, calculate minimum container size for the image under different contentMode + if (cropToRectDimensions) { + minimumDestinationSize = CGSizeMake(boundsSize.width / cropRect.size.width, boundsSize.height / cropRect.size.height); + } else { + if (contentMode == UIViewContentModeScaleAspectFill) + minimumDestinationSize = _ASSizeFitWithAspectRatio(boundsAspectRatio, sourceImageSize); + else if (contentMode == UIViewContentModeScaleAspectFit) + minimumDestinationSize = _ASSizeFillWithAspectRatio(boundsAspectRatio, sourceImageSize); + } + + // If fitting the desired aspect ratio to the image size actually results in a larger buffer, use the input values. + // However, if there is a pixel savings (e.g. we would have to upscale the image), override the function arguments. + if (CGSizeEqualToSize(CGSizeZero, forcedSize) == NO) { + destinationWidth = (size_t)round(forcedSize.width); + destinationHeight = (size_t)round(forcedSize.height); + } else if (forceUpscaling == NO && (minimumDestinationSize.width * minimumDestinationSize.height) < (destinationWidth * destinationHeight)) { + destinationWidth = (size_t)round(minimumDestinationSize.width); + destinationHeight = (size_t)round(minimumDestinationSize.height); + if (destinationWidth == 0 || destinationHeight == 0) { + *outBackingSize = CGSizeZero; + *outDrawRect = CGRectZero; + return; + } + } + + // Figure out the scaled size within the destination bounds. + CGFloat sourceImageAspectRatio = sourceImageSize.width / sourceImageSize.height; + CGSize scaledSizeForImage = CGSizeMake(destinationWidth, destinationHeight); + + if (cropToRectDimensions) { + scaledSizeForImage = CGSizeMake(boundsSize.width / cropRect.size.width, boundsSize.height / cropRect.size.height); + } else { + if (contentMode == UIViewContentModeScaleAspectFill) + scaledSizeForImage = _ASSizeFillWithAspectRatio(sourceImageAspectRatio, scaledSizeForImage); + else if (contentMode == UIViewContentModeScaleAspectFit) + scaledSizeForImage = _ASSizeFitWithAspectRatio(sourceImageAspectRatio, scaledSizeForImage); + } + + // Figure out the rectangle into which to draw the image. + CGRect drawRect = CGRectZero; + if (cropToRectDimensions) { + drawRect = CGRectMake(-cropRect.origin.x * scaledSizeForImage.width, + -cropRect.origin.y * scaledSizeForImage.height, + scaledSizeForImage.width, + scaledSizeForImage.height); + } else { + // We want to obey the origin of cropRect in aspect-fill mode. + if (contentMode == UIViewContentModeScaleAspectFill) { + drawRect = CGRectMake(((destinationWidth - scaledSizeForImage.width) * cropRect.origin.x), + ((destinationHeight - scaledSizeForImage.height) * cropRect.origin.y), + scaledSizeForImage.width, + scaledSizeForImage.height); + + } + // And otherwise just center it. + else { + drawRect = CGRectMake(((destinationWidth - scaledSizeForImage.width) / 2.0), + ((destinationHeight - scaledSizeForImage.height) / 2.0), + scaledSizeForImage.width, + scaledSizeForImage.height); + } + } + + *outDrawRect = drawRect; + *outBackingSize = CGSizeMake(destinationWidth, destinationHeight); +} diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASImageNode+Private.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASImageNode+Private.h new file mode 100644 index 0000000..8de78c8 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASImageNode+Private.h @@ -0,0 +1,17 @@ +// +// ASImageNode+Private.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#pragma once + +@interface ASImageNode (Private) + +- (void)_locked_setImage:(UIImage *)image; +- (UIImage *)_locked_Image; + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASInternalHelpers.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASInternalHelpers.h new file mode 100644 index 0000000..69a8eab --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASInternalHelpers.h @@ -0,0 +1,129 @@ +// +// ASInternalHelpers.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import "ASAvailability.h" + +#import + +#import +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +ASDK_EXTERN void ASInitializeFrameworkMainThread(void); + +ASDK_EXTERN BOOL ASDefaultAllowsGroupOpacity(void); +ASDK_EXTERN BOOL ASDefaultAllowsEdgeAntialiasing(void); + +ASDK_EXTERN BOOL ASSubclassOverridesSelector(Class superclass, Class subclass, SEL selector); +ASDK_EXTERN BOOL ASSubclassOverridesClassSelector(Class superclass, Class subclass, SEL selector); + +/// Replace a method from the given class with a block and returns the original method IMP +ASDK_EXTERN IMP ASReplaceMethodWithBlock(Class c, SEL origSEL, id block); + +/// Dispatches the given block to the main queue if not already running on the main thread +ASDK_EXTERN void ASPerformBlockOnMainThread(void (^block)(void)); + +/// Dispatches the given block to a background queue with priority of DISPATCH_QUEUE_PRIORITY_DEFAULT if not already run on a background queue +ASDK_EXTERN void ASPerformBlockOnBackgroundThread(void (^block)(void)); // DISPATCH_QUEUE_PRIORITY_DEFAULT + +/// For deallocation of objects on a background thread without GCD overhead / thread explosion +ASDK_EXTERN void ASPerformBackgroundDeallocation(id __strong _Nullable * _Nonnull object); + +ASDK_EXTERN CGFloat ASScreenScale(void); + +ASDK_EXTERN CGSize ASFloorSizeValues(CGSize s); + +ASDK_EXTERN CGFloat ASFloorPixelValue(CGFloat f); + +ASDK_EXTERN CGPoint ASCeilPointValues(CGPoint p); + +ASDK_EXTERN CGSize ASCeilSizeValues(CGSize s); + +ASDK_EXTERN CGFloat ASCeilPixelValue(CGFloat f); + +ASDK_EXTERN CGFloat ASRoundPixelValue(CGFloat f); + +ASDK_EXTERN Class _Nullable ASGetClassFromType(const char * _Nullable type); + +ASDISPLAYNODE_INLINE BOOL ASImageAlphaInfoIsOpaque(CGImageAlphaInfo info) { + switch (info) { + case kCGImageAlphaNone: + case kCGImageAlphaNoneSkipLast: + case kCGImageAlphaNoneSkipFirst: + return YES; + default: + return NO; + } +} + +/** + @summary Conditionally performs UIView geometry changes in the given block without animation. + + Used primarily to circumvent UITableView forcing insertion animations when explicitly told not to via + `UITableViewRowAnimationNone`. More info: https://github.com/facebook/AsyncDisplayKit/pull/445 + + @param withoutAnimation Set to `YES` to perform given block without animation + @param block Perform UIView geometry changes within the passed block + */ +ASDISPLAYNODE_INLINE void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)(void)) { + if (withoutAnimation) { + [UIView performWithoutAnimation:block]; + } else { + block(); + } +} + +ASDISPLAYNODE_INLINE void ASBoundsAndPositionForFrame(CGRect rect, CGPoint origin, CGPoint anchorPoint, CGRect *bounds, CGPoint *position) +{ + *bounds = (CGRect){ origin, rect.size }; + *position = CGPointMake(rect.origin.x + rect.size.width * anchorPoint.x, + rect.origin.y + rect.size.height * anchorPoint.y); +} + +ASDISPLAYNODE_INLINE UIEdgeInsets ASConcatInsets(UIEdgeInsets insetsA, UIEdgeInsets insetsB) +{ + insetsA.top += insetsB.top; + insetsA.left += insetsB.left; + insetsA.bottom += insetsB.bottom; + insetsA.right += insetsB.right; + return insetsA; +} + +ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT ASImageDownloaderPriority ASImageDownloaderPriorityWithInterfaceState(ASInterfaceState interfaceState) { + if (ASInterfaceStateIncludesVisible(interfaceState)) { + return ASImageDownloaderPriorityVisible; + } + + if (ASInterfaceStateIncludesDisplay(interfaceState)) { + return ASImageDownloaderPriorityImminent; + } + + if (ASInterfaceStateIncludesPreload(interfaceState)) { + return ASImageDownloaderPriorityPreload; + } + + return ASImageDownloaderPriorityPreload; +} + +@interface NSIndexPath (ASInverseComparison) +- (NSComparisonResult)asdk_inverseCompare:(NSIndexPath *)otherIndexPath; +@end + +/** + * Create an NSMutableSet that uses pointers for hash & equality. + */ +ASDK_EXTERN NSMutableSet *ASCreatePointerBasedMutableSet(void); + +NS_ASSUME_NONNULL_END + +#ifndef AS_INITIALIZE_FRAMEWORK_MANUALLY +#define AS_INITIALIZE_FRAMEWORK_MANUALLY 0 +#endif diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASInternalHelpers.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASInternalHelpers.mm new file mode 100644 index 0000000..7811c3b --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASInternalHelpers.mm @@ -0,0 +1,266 @@ +// +// ASInternalHelpers.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import +#import +#import +#import + +static NSNumber *allowsGroupOpacityFromUIKitOrNil; +static NSNumber *allowsEdgeAntialiasingFromUIKitOrNil; + +BOOL ASDefaultAllowsGroupOpacity() +{ + static BOOL groupOpacity; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + NSNumber *groupOpacityObj = allowsGroupOpacityFromUIKitOrNil ?: [NSBundle.mainBundle objectForInfoDictionaryKey:@"UIViewGroupOpacity"]; + groupOpacity = groupOpacityObj ? groupOpacityObj.boolValue : YES; + }); + return groupOpacity; +} + +BOOL ASDefaultAllowsEdgeAntialiasing() +{ + static BOOL edgeAntialiasing; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + NSNumber *antialiasingObj = allowsEdgeAntialiasingFromUIKitOrNil ?: [NSBundle.mainBundle objectForInfoDictionaryKey:@"UIViewEdgeAntialiasing"]; + edgeAntialiasing = antialiasingObj ? antialiasingObj.boolValue : NO; + }); + return edgeAntialiasing; +} + +#if AS_SIGNPOST_ENABLE +void _ASInitializeSignpostObservers(void) +{ + // Orientation changes. Unavailable on tvOS. +#if !TARGET_OS_TV + [NSNotificationCenter.defaultCenter addObserverForName:UIApplicationWillChangeStatusBarOrientationNotification object:nil queue:nil usingBlock:^(NSNotification *note) { + UIInterfaceOrientation orientation = (UIInterfaceOrientation)[note.userInfo[UIApplicationStatusBarOrientationUserInfoKey] integerValue]; + ASSignpostStart(OrientationChange, (id)nil, "from %s", UIInterfaceOrientationIsPortrait(orientation) ? "portrait" : "landscape"); + [CATransaction begin]; + }]; + [NSNotificationCenter.defaultCenter addObserverForName:UIApplicationDidChangeStatusBarOrientationNotification object:nil queue:nil usingBlock:^(NSNotification *note) { + // When profiling, go ahead and commit the transaction early so that it happens as part of our interval. + UIInterfaceOrientation orientation = (UIInterfaceOrientation)[note.userInfo[UIApplicationStatusBarOrientationUserInfoKey] integerValue]; + [CATransaction setCompletionBlock:^{ + ASSignpostEnd(OrientationChange, (id)nil, "to %s", UIInterfaceOrientationIsPortrait(orientation) ? "portrait" : "landscape"); + }]; + [CATransaction commit]; + }]; +#endif // TARGET_OS_TV +} +#endif // AS_SIGNPOST_ENABLE + +void ASInitializeFrameworkMainThread(void) +{ + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + ASDisplayNodeCAssertMainThread(); + // Ensure these values are cached on the main thread before needed in the background. + if (ASActivateExperimentalFeature(ASExperimentalLayerDefaults)) { + // Nop. We will gather default values on-demand in ASDefaultAllowsGroupOpacity and ASDefaultAllowsEdgeAntialiasing + } else { + CALayer *layer = [[[UIView alloc] init] layer]; + allowsGroupOpacityFromUIKitOrNil = @(layer.allowsGroupOpacity); + allowsEdgeAntialiasingFromUIKitOrNil = @(layer.allowsEdgeAntialiasing); + } + ASNotifyInitialized(); +#if AS_SIGNPOST_ENABLE + _ASInitializeSignpostObservers(); +#endif + }); +} + +BOOL ASSubclassOverridesSelector(Class superclass, Class subclass, SEL selector) +{ + if (superclass == subclass) return NO; // Even if the class implements the selector, it doesn't override itself. + Method superclassMethod = class_getInstanceMethod(superclass, selector); + Method subclassMethod = class_getInstanceMethod(subclass, selector); + return (superclassMethod != subclassMethod); +} + +BOOL ASSubclassOverridesClassSelector(Class superclass, Class subclass, SEL selector) +{ + if (superclass == subclass) return NO; // Even if the class implements the selector, it doesn't override itself. + Method superclassMethod = class_getClassMethod(superclass, selector); + Method subclassMethod = class_getClassMethod(subclass, selector); + return (superclassMethod != subclassMethod); +} + +IMP ASReplaceMethodWithBlock(Class c, SEL origSEL, id block) +{ + NSCParameterAssert(block); + + // Get original method + Method origMethod = class_getInstanceMethod(c, origSEL); + NSCParameterAssert(origMethod); + + // Convert block to IMP trampoline and replace method implementation + IMP newIMP = imp_implementationWithBlock(block); + + // Try adding the method if not yet in the current class + if (!class_addMethod(c, origSEL, newIMP, method_getTypeEncoding(origMethod))) { + return method_setImplementation(origMethod, newIMP); + } else { + return method_getImplementation(origMethod); + } +} + +void ASPerformBlockOnMainThread(void (^block)(void)) +{ + if (block == nil){ + return; + } + if (ASDisplayNodeThreadIsMain()) { + block(); + } else { + dispatch_async(dispatch_get_main_queue(), block); + } +} + +void ASPerformBlockOnBackgroundThread(void (^block)(void)) +{ + if (block == nil){ + return; + } + if (ASDisplayNodeThreadIsMain()) { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block); + } else { + block(); + } +} + +void ASPerformBackgroundDeallocation(id __strong _Nullable * _Nonnull object) +{ + [[ASDeallocQueue sharedDeallocationQueue] releaseObjectInBackground:object]; +} + +Class _Nullable ASGetClassFromType(const char * _Nullable type) +{ + // Class types all start with @" + if (type == NULL || strncmp(type, "@\"", 2) != 0) { + return Nil; + } + + // Ensure length >= 3 + size_t typeLength = strlen(type); + if (typeLength < 3) { + ASDisplayNodeCFailAssert(@"Got invalid type-encoding: %s", type); + return Nil; + } + + // Copy type[2..(end-1)]. So @"UIImage" -> UIImage + size_t resultLength = typeLength - 3; + char className[resultLength + 1]; + strncpy(className, type + 2, resultLength); + className[resultLength] = '\0'; + return objc_getClass(className); +} + +CGFloat ASScreenScale() +{ + static CGFloat __scale = 0.0; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + UIGraphicsBeginImageContextWithOptions(CGSizeMake(1, 1), YES, 0); + __scale = CGContextGetCTM(UIGraphicsGetCurrentContext()).a; + UIGraphicsEndImageContext(); + }); + return __scale; +} + +CGSize ASFloorSizeValues(CGSize s) +{ + return CGSizeMake(ASFloorPixelValue(s.width), ASFloorPixelValue(s.height)); +} + +// See ASCeilPixelValue for a more thoroguh explanation of (f + FLT_EPSILON), +// but here is some quick math: +// +// Imagine a layout that comes back with a height of 100.66666666663 +// for a 3x deice: +// 100.66666666663 * 3 = 301.99999999988995 +// floor(301.99999999988995) = 301 +// 301 / 3 = 100.333333333 +// +// If we add FLT_EPSILON to normalize the garbage at the end we get: +// po (100.66666666663 + FLT_EPSILON) * 3 = 302.00000035751782 +// floor(302.00000035751782) = 302 +// 302/3 = 100.66666666 +CGFloat ASFloorPixelValue(CGFloat f) +{ + CGFloat scale = ASScreenScale(); + return floor((f + FLT_EPSILON) * scale) / scale; +} + +CGPoint ASCeilPointValues(CGPoint p) +{ + return CGPointMake(ASCeilPixelValue(p.x), ASCeilPixelValue(p.y)); +} + +CGSize ASCeilSizeValues(CGSize s) +{ + return CGSizeMake(ASCeilPixelValue(s.width), ASCeilPixelValue(s.height)); +} + +// With 3x devices layouts will often to compute to pixel bounds but +// include garbage values beyond the precision of a float/double. +// This garbage can result in a pixel value being rounded up when it isn't +// necessary. +// +// For example, imagine a layout that comes back with a height of 100.666666666669 +// for a 3x device: +// 100.666666666669 * 3 = 302.00000000000699 +// ceil(302.00000000000699) = 303 +// 303/3 = 101 +// +// If we use FLT_EPSILON to get rid of the garbage at the end of the value, +// things work as expected: +// (100.666666666669 - FLT_EPSILON) * 3 = 301.99999964237912 +// ceil(301.99999964237912) = 302 +// 302/3 = 100.666666666 +// +// For even more conversation around this, see: +// https://github.com/TextureGroup/Texture/issues/838 +CGFloat ASCeilPixelValue(CGFloat f) +{ + CGFloat scale = ASScreenScale(); + return ceil((f - FLT_EPSILON) * scale) / scale; +} + +CGFloat ASRoundPixelValue(CGFloat f) +{ + CGFloat scale = ASScreenScale(); + return round(f * scale) / scale; +} + +@implementation NSIndexPath (ASInverseComparison) + +- (NSComparisonResult)asdk_inverseCompare:(NSIndexPath *)otherIndexPath +{ + return [otherIndexPath compare:self]; +} + +@end + +NSMutableSet *ASCreatePointerBasedMutableSet() +{ + static CFSetCallBacks callbacks; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + callbacks = kCFTypeSetCallBacks; + callbacks.equal = nullptr; + callbacks.hash = nullptr; + }); + return (__bridge_transfer NSMutableSet *)CFSetCreateMutable(NULL, 0, &callbacks); +} diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASLayerBackingTipProvider.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASLayerBackingTipProvider.h new file mode 100644 index 0000000..0cc3d03 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASLayerBackingTipProvider.h @@ -0,0 +1,24 @@ +// +// ASLayerBackingTipProvider.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import "ASTipProvider.h" +#import + +#if AS_ENABLE_TIPS + +NS_ASSUME_NONNULL_BEGIN + +AS_SUBCLASSING_RESTRICTED +@interface ASLayerBackingTipProvider : ASTipProvider + +@end + +NS_ASSUME_NONNULL_END + +#endif // AS_ENABLE_TIPS diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASLayerBackingTipProvider.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASLayerBackingTipProvider.mm new file mode 100644 index 0000000..19d1850 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASLayerBackingTipProvider.mm @@ -0,0 +1,45 @@ +// +// ASLayerBackingTipProvider.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import "ASLayerBackingTipProvider.h" + +#if AS_ENABLE_TIPS + +#import +#import +#import +#import +#import + +@implementation ASLayerBackingTipProvider + +- (ASTip *)tipForNode:(ASDisplayNode *)node +{ + // Already layer-backed. + if (node.layerBacked) { + return nil; + } + + // TODO: Avoid revisiting nodes we already visited + ASDisplayNode *failNode = ASDisplayNodeFindFirstNode(node, ^BOOL(ASDisplayNode * _Nonnull node) { + return !node.supportsLayerBacking; + }); + if (failNode != nil) { + return nil; + } + + ASTip *result = [[ASTip alloc] initWithNode:node + kind:ASTipKindEnableLayerBacking + format:@"Enable layer backing to improve performance"]; + return result; +} + +@end + +#endif // AS_ENABLE_TIPS diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASLayoutTransition.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASLayoutTransition.h new file mode 100644 index 0000000..f2bcf9b --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASLayoutTransition.h @@ -0,0 +1,93 @@ +// +// ASLayoutTransition.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import +#import + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +#pragma mark - ASLayoutElementTransition + +/** + * Objects conform to this project returns if it's possible to layout asynchronous + */ +@protocol ASLayoutElementTransition + +/** + * @abstract Returns if the layoutElement can be used to layout in an asynchronous way on a background thread. + */ +@property (nonatomic, readonly) BOOL canLayoutAsynchronous; + +@end + +@interface ASDisplayNode () +@end +@interface ASLayoutSpec () +@end + + +#pragma mark - ASLayoutTransition + +AS_SUBCLASSING_RESTRICTED +@interface ASLayoutTransition : NSObject <_ASTransitionContextLayoutDelegate> + +/** + * Node to apply layout transition on + */ +@property (nonatomic, weak, readonly) ASDisplayNode *node; + +/** + * Previous layout to transition from + */ +@property (nonatomic, readonly) const ASDisplayNodeLayout &previousLayout NS_RETURNS_INNER_POINTER; + +/** + * Pending layout to transition to + */ +@property (nonatomic, readonly) const ASDisplayNodeLayout &pendingLayout NS_RETURNS_INNER_POINTER; + +/** + * Returns if the layout transition needs to happen synchronously + */ +@property (nonatomic, readonly) BOOL isSynchronous; + +/** + * Returns a newly initialized layout transition + */ +- (instancetype)initWithNode:(ASDisplayNode *)node + pendingLayout:(const ASDisplayNodeLayout &)pendingLayout + previousLayout:(const ASDisplayNodeLayout &)previousLayout NS_DESIGNATED_INITIALIZER; + +/** + * Insert and remove subnodes that were added or removed between the previousLayout and the pendingLayout + */ +- (void)commitTransition; + +/** + * Insert all new subnodes that were added and move the subnodes that moved between the previous layout and + * the pending layout. + */ +- (void)applySubnodeInsertionsAndMoves; + +/** + * Remove all subnodes that are removed between the previous layout and the pending layout + */ +- (void)applySubnodeRemovals; + +- (instancetype)init NS_UNAVAILABLE; +- (instancetype)new NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASLayoutTransition.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASLayoutTransition.mm new file mode 100644 index 0000000..588b275 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASLayoutTransition.mm @@ -0,0 +1,298 @@ +// +// ASLayoutTransition.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import + +#import +#import // Required for _removeFromSupernodeIfEqualTo: + +#import + +#if AS_IG_LIST_DIFF_KIT +#import +#endif + +using AS::MutexLocker; + +/** + * Search the whole layout stack if at least one layout has a layoutElement object that can not be layed out asynchronous. + * This can be the case for example if a node was already loaded + */ +static inline BOOL ASLayoutCanTransitionAsynchronous(ASLayout *layout) { + // Queue used to keep track of sublayouts while traversing this layout in a BFS fashion. + std::queue queue; + queue.push(layout); + + while (!queue.empty()) { + layout = queue.front(); + queue.pop(); + +#if DEBUG + ASDisplayNodeCAssert([layout.layoutElement conformsToProtocol:@protocol(ASLayoutElementTransition)], @"ASLayoutElement in a layout transition needs to conforms to the ASLayoutElementTransition protocol."); +#endif + if (((id)layout.layoutElement).canLayoutAsynchronous == NO) { + return NO; + } + + // Add all sublayouts to process in next step + for (ASLayout *sublayout in layout.sublayouts) { + queue.push(sublayout); + } + } + + return YES; +} + +@implementation ASLayoutTransition { + std::shared_ptr __instanceLock__; + + BOOL _calculatedSubnodeOperations; + NSArray *_insertedSubnodes; + NSArray *_removedSubnodes; + std::vector _insertedSubnodePositions; + std::vector> _subnodeMoves; + ASDisplayNodeLayout _pendingLayout; + ASDisplayNodeLayout _previousLayout; +} + +- (instancetype)initWithNode:(ASDisplayNode *)node + pendingLayout:(const ASDisplayNodeLayout &)pendingLayout + previousLayout:(const ASDisplayNodeLayout &)previousLayout +{ + self = [super init]; + if (self) { + __instanceLock__ = std::make_shared(); + + _node = node; + _pendingLayout = pendingLayout; + _previousLayout = previousLayout; + } + return self; +} + +- (BOOL)isSynchronous +{ + MutexLocker l(*__instanceLock__); + return !ASLayoutCanTransitionAsynchronous(_pendingLayout.layout); +} + +- (void)commitTransition +{ + [self applySubnodeRemovals]; + [self applySubnodeInsertionsAndMoves]; +} + +- (void)applySubnodeInsertionsAndMoves +{ + MutexLocker l(*__instanceLock__); + [self calculateSubnodeOperationsIfNeeded]; + + // Create an activity even if no subnodes affected. + as_activity_create_for_scope("Apply subnode insertions and moves"); + if (_insertedSubnodePositions.size() == 0 && _subnodeMoves.size() == 0) { + return; + } + + NSUInteger i = 0; + NSUInteger j = 0; + for (auto const &move : _subnodeMoves) { + [move.first _removeFromSupernodeIfEqualTo:_node]; + } + j = 0; + while (i < _insertedSubnodePositions.size() && j < _subnodeMoves.size()) { + NSUInteger p = _insertedSubnodePositions[i]; + NSUInteger q = _subnodeMoves[j].second; + if (p < q) { + [_node insertSubnode:_insertedSubnodes[i] atIndex:p]; + i++; + } else { + [_node insertSubnode:_subnodeMoves[j].first atIndex:q]; + j++; + } + } + for (; i < _insertedSubnodePositions.size(); ++i) { + [_node insertSubnode:_insertedSubnodes[i] atIndex:_insertedSubnodePositions[i]]; + } + for (; j < _subnodeMoves.size(); ++j) { + [_node insertSubnode:_subnodeMoves[j].first atIndex:_subnodeMoves[j].second]; + } +} + +- (void)applySubnodeRemovals +{ + as_activity_scope(as_activity_create("Apply subnode removals", AS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT)); + MutexLocker l(*__instanceLock__); + [self calculateSubnodeOperationsIfNeeded]; + + if (_removedSubnodes.count == 0) { + return; + } + + for (ASDisplayNode *subnode in _removedSubnodes) { + // In this case we should only remove the subnode if it's still a subnode of the _node that executes a layout transition. + // It can happen that a node already did a layout transition and added this subnode, in this case the subnode + // would be removed from the new node instead of _node + if (_node.automaticallyManagesSubnodes) { + [subnode _removeFromSupernodeIfEqualTo:_node]; + } + } +} + +- (void)calculateSubnodeOperationsIfNeeded +{ + MutexLocker l(*__instanceLock__); + if (_calculatedSubnodeOperations) { + return; + } + + // Create an activity even if no subnodes affected. + as_activity_create_for_scope("Calculate subnode operations"); + ASLayout *previousLayout = _previousLayout.layout; + ASLayout *pendingLayout = _pendingLayout.layout; + + if (previousLayout) { +#if AS_IG_LIST_DIFF_KIT + // IGListDiff completes in linear time O(m+n), so use it if we have it: + IGListIndexSetResult *result = IGListDiff(previousLayout.sublayouts, pendingLayout.sublayouts, IGListDiffEquality); + _insertedSubnodePositions = findNodesInLayoutAtIndexes(pendingLayout, result.inserts, &_insertedSubnodes); + findNodesInLayoutAtIndexes(previousLayout, result.deletes, &_removedSubnodes); + for (IGListMoveIndex *move in result.moves) { + _subnodeMoves.emplace_back(previousLayout.sublayouts[move.from].layoutElement, move.to); + } + + // Sort by ascending order of move destinations, this will allow easy loop of `insertSubnode:AtIndex` later. + std::sort(_subnodeMoves.begin(), _subnodeMoves.end(), [](std::pair, NSUInteger> a, + std::pair b) { + return a.second < b.second; + }); +#else + NSIndexSet *insertions, *deletions; + NSArray *moves; + NSArray *previousNodes = [previousLayout.sublayouts valueForKey:@"layoutElement"]; + NSArray *pendingNodes = [pendingLayout.sublayouts valueForKey:@"layoutElement"]; + [previousNodes asdk_diffWithArray:pendingNodes + insertions:&insertions + deletions:&deletions + moves:&moves]; + + _insertedSubnodePositions = findNodesInLayoutAtIndexes(pendingLayout, insertions, &_insertedSubnodes); + _removedSubnodes = [previousNodes objectsAtIndexes:deletions]; + // These should arrive sorted in ascending order of move destinations. + for (NSIndexPath *move in moves) { + _subnodeMoves.emplace_back(previousLayout.sublayouts[([move indexAtPosition:0])].layoutElement, + [move indexAtPosition:1]); + } +#endif + } else { + NSIndexSet *indexes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [pendingLayout.sublayouts count])]; + _insertedSubnodePositions = findNodesInLayoutAtIndexes(pendingLayout, indexes, &_insertedSubnodes); + _removedSubnodes = nil; + } + _calculatedSubnodeOperations = YES; +} + +#pragma mark - _ASTransitionContextDelegate + +- (NSArray *)currentSubnodesWithTransitionContext:(_ASTransitionContext *)context +{ + MutexLocker l(*__instanceLock__); + return _node.subnodes; +} + +- (NSArray *)insertedSubnodesWithTransitionContext:(_ASTransitionContext *)context +{ + MutexLocker l(*__instanceLock__); + [self calculateSubnodeOperationsIfNeeded]; + return _insertedSubnodes; +} + +- (NSArray *)removedSubnodesWithTransitionContext:(_ASTransitionContext *)context +{ + MutexLocker l(*__instanceLock__); + [self calculateSubnodeOperationsIfNeeded]; + return _removedSubnodes; +} + +- (ASLayout *)transitionContext:(_ASTransitionContext *)context layoutForKey:(NSString *)key +{ + MutexLocker l(*__instanceLock__); + if ([key isEqualToString:ASTransitionContextFromLayoutKey]) { + return _previousLayout.layout; + } else if ([key isEqualToString:ASTransitionContextToLayoutKey]) { + return _pendingLayout.layout; + } else { + return nil; + } +} + +- (ASSizeRange)transitionContext:(_ASTransitionContext *)context constrainedSizeForKey:(NSString *)key +{ + MutexLocker l(*__instanceLock__); + if ([key isEqualToString:ASTransitionContextFromLayoutKey]) { + return _previousLayout.constrainedSize; + } else if ([key isEqualToString:ASTransitionContextToLayoutKey]) { + return _pendingLayout.constrainedSize; + } else { + return ASSizeRangeMake(CGSizeZero, CGSizeZero); + } +} + +#pragma mark - Filter helpers + +/** + * @abstract Stores the nodes at the given indexes in the `storedNodes` array, storing indexes in a `storedPositions` c++ vector. + */ +static inline std::vector findNodesInLayoutAtIndexes(ASLayout *layout, + NSIndexSet *indexes, + NSArray * __strong *storedNodes) +{ + return findNodesInLayoutAtIndexesWithFilteredNodes(layout, indexes, nil, storedNodes); +} + +/** + * @abstract Stores the nodes at the given indexes in the `storedNodes` array, storing indexes in a `storedPositions` c++ vector. + * Call only with a flattened layout. + * @discussion If the node exists in the `filteredNodes` array, the node is not added to `storedNodes`. + */ +static inline std::vector findNodesInLayoutAtIndexesWithFilteredNodes(ASLayout *layout, + NSIndexSet *indexes, + NSArray *filteredNodes, + NSArray * __strong *storedNodes) +{ + NSMutableArray *nodes = [NSMutableArray arrayWithCapacity:indexes.count]; + std::vector positions = std::vector(); + + // From inspection, this is how enumerateObjectsAtIndexes: works under the hood + NSUInteger firstIndex = indexes.firstIndex; + NSUInteger lastIndex = indexes.lastIndex; + NSUInteger idx = 0; + for (ASLayout *sublayout in layout.sublayouts) { + if (idx > lastIndex) { break; } + if (idx >= firstIndex && [indexes containsIndex:idx]) { + ASDisplayNode *node = (ASDisplayNode *)(sublayout.layoutElement); + ASDisplayNodeCAssert(node, @"ASDisplayNode was deallocated before it was added to a subnode. It's likely the case that you use automatically manages subnodes and allocate a ASDisplayNode in layoutSpecThatFits: and don't have any strong reference to it."); + ASDisplayNodeCAssert([node isKindOfClass:[ASDisplayNode class]], @"sublayout is an ASLayout, but not an ASDisplayNode - only call findNodesInLayoutAtIndexesWithFilteredNodes with a flattened layout (all sublayouts are ASDisplayNodes)."); + if (node != nil) { + BOOL notFiltered = (filteredNodes == nil || [filteredNodes indexOfObjectIdenticalTo:node] == NSNotFound); + if (notFiltered) { + [nodes addObject:node]; + positions.push_back(idx); + } + } + } + idx += 1; + } + *storedNodes = nodes; + + return positions; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASMutableElementMap.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASMutableElementMap.h new file mode 100644 index 0000000..1ca1829 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASMutableElementMap.h @@ -0,0 +1,62 @@ +// +// ASMutableElementMap.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@class ASSection, ASCollectionElement, _ASHierarchyChangeSet; + +/** + * This mutable version will be removed in the future. It's only here now to keep the diff small + * as we port data controller to use ASElementMap. + */ +AS_SUBCLASSING_RESTRICTED +@interface ASMutableElementMap : NSObject + +- (instancetype)init __unavailable; + +- (instancetype)initWithSections:(NSArray *)sections items:(ASCollectionElementTwoDimensionalArray *)items supplementaryElements:(ASSupplementaryElementDictionary *)supplementaryElements; + +- (void)insertSection:(ASSection *)section atIndex:(NSInteger)index; + +- (void)removeAllSections; + +/// Only modifies the array of ASSection * objects +- (void)removeSectionsAtIndexes:(NSIndexSet *)indexes; + +- (void)removeAllElements; + +- (void)removeItemsAtIndexPaths:(NSArray *)indexPaths; + +- (void)removeSectionsOfItems:(NSIndexSet *)itemSections; + +- (void)removeSupplementaryElementsAtIndexPaths:(NSArray *)indexPaths kind:(NSString *)kind; + +- (void)insertEmptySectionsOfItemsAtIndexes:(NSIndexSet *)sections; + +- (void)insertElement:(ASCollectionElement *)element atIndexPath:(NSIndexPath *)indexPath; + +/** + * Update the index paths for all supplementary elements to account for section-level + * deletes, moves, inserts. This must be called before adding new supplementary elements. + * + * This also deletes any supplementary elements in deleted sections. + */ +- (void)migrateSupplementaryElementsWithSectionMapping:(ASIntegerMap *)mapping; + +@end + +@interface ASElementMap (MutableCopying) +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASMutableElementMap.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASMutableElementMap.mm new file mode 100644 index 0000000..f7bf210 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASMutableElementMap.mm @@ -0,0 +1,143 @@ +// +// ASMutableElementMap.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import +#import +#import + +typedef NSMutableArray *> ASMutableCollectionElementTwoDimensionalArray; + +typedef NSMutableDictionary *> ASMutableSupplementaryElementDictionary; + +@implementation ASMutableElementMap { + ASMutableSupplementaryElementDictionary *_supplementaryElements; + NSMutableArray *_sections; + ASMutableCollectionElementTwoDimensionalArray *_sectionsOfItems; +} + +- (instancetype)initWithSections:(NSArray *)sections items:(ASCollectionElementTwoDimensionalArray *)items supplementaryElements:(ASSupplementaryElementDictionary *)supplementaryElements +{ + if (self = [super init]) { + _sections = [sections mutableCopy]; + _sectionsOfItems = (ASMutableCollectionElementTwoDimensionalArray *)ASTwoDimensionalArrayDeepMutableCopy(items); + _supplementaryElements = [ASMutableElementMap deepMutableCopyOfElementsDictionary:supplementaryElements]; + } + return self; +} + +- (id)copyWithZone:(NSZone *)zone +{ + return [[ASElementMap alloc] initWithSections:_sections items:_sectionsOfItems supplementaryElements:_supplementaryElements]; +} + +- (void)removeAllSections +{ + [_sections removeAllObjects]; +} + +- (void)insertSection:(ASSection *)section atIndex:(NSInteger)index +{ + [_sections insertObject:section atIndex:index]; +} + +- (void)removeItemsAtIndexPaths:(NSArray *)indexPaths +{ + ASDeleteElementsInTwoDimensionalArrayAtIndexPaths(_sectionsOfItems, indexPaths); +} + +- (void)removeSectionsAtIndexes:(NSIndexSet *)indexes +{ + [_sections removeObjectsAtIndexes:indexes]; +} + +- (void)removeSupplementaryElementsAtIndexPaths:(NSArray *)indexPaths kind:(NSString *)kind +{ + [_supplementaryElements[kind] removeObjectsForKeys:indexPaths]; +} + +- (void)removeAllElements +{ + [_sectionsOfItems removeAllObjects]; + [_supplementaryElements removeAllObjects]; +} + +- (void)removeSectionsOfItems:(NSIndexSet *)itemSections +{ + [_sectionsOfItems removeObjectsAtIndexes:itemSections]; +} + +- (void)insertEmptySectionsOfItemsAtIndexes:(NSIndexSet *)sections +{ + [sections enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL * _Nonnull stop) { + [_sectionsOfItems insertObject:[[NSMutableArray alloc] init] atIndex:idx]; + }]; +} + +- (void)insertElement:(ASCollectionElement *)element atIndexPath:(NSIndexPath *)indexPath +{ + NSString *kind = element.supplementaryElementKind; + if (kind == nil) { + [_sectionsOfItems[indexPath.section] insertObject:element atIndex:indexPath.item]; + } else { + NSMutableDictionary *supplementariesForKind = _supplementaryElements[kind]; + if (supplementariesForKind == nil) { + supplementariesForKind = [[NSMutableDictionary alloc] init]; + _supplementaryElements[kind] = supplementariesForKind; + } + supplementariesForKind[indexPath] = element; + } +} + +- (void)migrateSupplementaryElementsWithSectionMapping:(ASIntegerMap *)mapping +{ + // Fast-path, no section changes. + if (mapping == ASIntegerMap.identityMap) { + return; + } + + // For each element kind, + [_supplementaryElements enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, NSMutableDictionary * _Nonnull supps, BOOL * _Nonnull stop) { + + // For each index path of that kind, move entries into a new dictionary. + // Note: it's tempting to update the dictionary in-place but because of the likely collision between old and new index paths, + // subtle bugs are possible. Note that this process is rare (only on section-level updates), + // that this work is done off-main, and that the typical supplementary element use case is just 1-per-section (header). + NSMutableDictionary *newSupps = [[NSMutableDictionary alloc] init]; + [supps enumerateKeysAndObjectsUsingBlock:^(NSIndexPath * _Nonnull oldIndexPath, ASCollectionElement * _Nonnull obj, BOOL * _Nonnull stop) { + NSInteger oldSection = oldIndexPath.section; + NSInteger newSection = [mapping integerForKey:oldSection]; + + if (oldSection == newSection) { + // Index path stayed the same, just copy it over. + newSupps[oldIndexPath] = obj; + } else if (newSection != NSNotFound) { + // Section index changed, move it. + NSIndexPath *newIndexPath = [NSIndexPath indexPathForItem:oldIndexPath.item inSection:newSection]; + newSupps[newIndexPath] = obj; + } + }]; + [supps setDictionary:newSupps]; + }]; +} + +#pragma mark - Helpers + ++ (ASMutableSupplementaryElementDictionary *)deepMutableCopyOfElementsDictionary:(ASSupplementaryElementDictionary *)originalDict +{ + NSMutableDictionary *deepCopy = [[NSMutableDictionary alloc] initWithCapacity:originalDict.count]; + [originalDict enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, NSDictionary * _Nonnull obj, BOOL * _Nonnull stop) { + deepCopy[key] = [obj mutableCopy]; + }]; + + return deepCopy; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASNetworkImageLoadInfo+Private.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASNetworkImageLoadInfo+Private.h new file mode 100644 index 0000000..36e423b --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASNetworkImageLoadInfo+Private.h @@ -0,0 +1,22 @@ +// +// ASNetworkImageLoadInfo+Private.h +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface ASNetworkImageLoadInfo () + +- (instancetype)initWithURL:(NSURL *)url + sourceType:(ASNetworkImageSourceType)sourceType + downloadIdentifier:(nullable id)downloadIdentifier + userInfo:(nullable id)userInfo; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASPendingStateController.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASPendingStateController.h new file mode 100644 index 0000000..b50a7c1 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASPendingStateController.h @@ -0,0 +1,50 @@ +// +// ASPendingStateController.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +@class ASDisplayNode; + +NS_ASSUME_NONNULL_BEGIN + +/** + A singleton that is responsible for applying changes to + UIView/CALayer properties of display nodes when they + have been set on background threads. + + This controller will enqueue run-loop events to flush changes + but if you need them flushed now you can call `flush` from the main thread. + */ +AS_SUBCLASSING_RESTRICTED +@interface ASPendingStateController : NSObject + ++ (ASPendingStateController *)sharedInstance; + +@property (nonatomic, readonly) BOOL hasChanges; + +/** + Flush all pending states for nodes now. Any UIView/CALayer properties + that have been set in the background will be applied to their + corresponding views/layers before this method returns. + + You must call this method on the main thread. + */ +- (void)flush; + +/** + Register this node as having pending state that needs to be copied + over to the view/layer. This is called automatically by display nodes + when their view/layer properties are set post-load on background threads. + */ +- (void)registerNode:(ASDisplayNode *)node; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASPendingStateController.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASPendingStateController.mm new file mode 100644 index 0000000..269b37e --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASPendingStateController.mm @@ -0,0 +1,102 @@ +// +// ASPendingStateController.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import +#import // Required for -applyPendingViewState; consider moving this to +FrameworkPrivate + +@interface ASPendingStateController() +{ + AS::Mutex _lock; + + struct ASPendingStateControllerFlags { + unsigned pendingFlush:1; + } _flags; +} + +@property (nonatomic, readonly) ASWeakSet *dirtyNodes; +@end + +@implementation ASPendingStateController + +#pragma mark Lifecycle & Singleton + +- (instancetype)init +{ + self = [super init]; + if (self) { + _dirtyNodes = [[ASWeakSet alloc] init]; + } + return self; +} + ++ (ASPendingStateController *)sharedInstance +{ + static dispatch_once_t onceToken; + static ASPendingStateController *controller = nil; + dispatch_once(&onceToken, ^{ + controller = [[ASPendingStateController alloc] init]; + }); + return controller; +} + +#pragma mark External API + +- (void)registerNode:(ASDisplayNode *)node +{ + ASDisplayNodeAssert(node.nodeLoaded, @"Expected display node to be loaded before it was registered with ASPendingStateController. Node: %@", node); + AS::MutexLocker l(_lock); + [_dirtyNodes addObject:node]; + + [self scheduleFlushIfNeeded]; +} + +- (void)flush +{ + ASDisplayNodeAssertMainThread(); + _lock.lock(); + ASWeakSet *dirtyNodes = _dirtyNodes; + _dirtyNodes = [[ASWeakSet alloc] init]; + _flags.pendingFlush = NO; + _lock.unlock(); + + for (ASDisplayNode *node in dirtyNodes) { + [node applyPendingViewState]; + } +} + + +#pragma mark Private Methods + +/** + This method is assumed to be called with the lock held. + */ +- (void)scheduleFlushIfNeeded +{ + if (_flags.pendingFlush) { + return; + } + + _flags.pendingFlush = YES; + dispatch_async(dispatch_get_main_queue(), ^{ + [self flush]; + }); +} + +@end + +@implementation ASPendingStateController (Testing) + +- (BOOL)test_isFlushScheduled +{ + return _flags.pendingFlush; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASResponderChainEnumerator.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASResponderChainEnumerator.h new file mode 100644 index 0000000..dcd2e1c --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASResponderChainEnumerator.h @@ -0,0 +1,29 @@ +// +// ASResponderChainEnumerator.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +AS_SUBCLASSING_RESTRICTED +@interface ASResponderChainEnumerator : NSEnumerator + +- (instancetype)initWithResponder:(UIResponder *)responder; + +@end + +@interface UIResponder (ASResponderChainEnumerator) + +- (ASResponderChainEnumerator *)asdk_responderChainEnumerator; + +@end + + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASResponderChainEnumerator.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASResponderChainEnumerator.mm new file mode 100644 index 0000000..bb16e0f --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASResponderChainEnumerator.mm @@ -0,0 +1,45 @@ +// +// ASResponderChainEnumerator.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +@implementation ASResponderChainEnumerator { + UIResponder *_currentResponder; +} + +- (instancetype)initWithResponder:(UIResponder *)responder +{ + ASDisplayNodeAssertMainThread(); + if (self = [super init]) { + _currentResponder = responder; + } + return self; +} + +#pragma mark - NSEnumerator + +- (id)nextObject +{ + ASDisplayNodeAssertMainThread(); + id result = [_currentResponder nextResponder]; + _currentResponder = result; + return result; +} + +@end + +@implementation UIResponder (ASResponderChainEnumerator) + +- (NSEnumerator *)asdk_responderChainEnumerator +{ + return [[ASResponderChainEnumerator alloc] initWithResponder:self]; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASSection.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASSection.h new file mode 100644 index 0000000..6058ff4 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASSection.h @@ -0,0 +1,36 @@ +// +// ASSection.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +@protocol ASSectionContext; + +NS_ASSUME_NONNULL_BEGIN + +/** + * An object representing the metadata for a section of elements in a collection. + * + * Its sectionID is namespaced to the data controller that created the section. + * + * These are useful for tracking the movement & lifetime of sections, independent of + * their contents. + */ +AS_SUBCLASSING_RESTRICTED +@interface ASSection : NSObject + +@property (readonly) NSInteger sectionID; +@property (nullable, readonly) id context; + +- (instancetype)init NS_UNAVAILABLE; +- (instancetype)initWithSectionID:(NSInteger)sectionID context:(nullable id)context NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASSection.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASSection.mm new file mode 100644 index 0000000..16fd73f --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASSection.mm @@ -0,0 +1,25 @@ +// +// ASSection.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +@implementation ASSection + +- (instancetype)initWithSectionID:(NSInteger)sectionID context:(id)context +{ + self = [super init]; + if (self) { + _sectionID = sectionID; + _context = context; + } + return self; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASTableView+Undeprecated.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASTableView+Undeprecated.h new file mode 100644 index 0000000..46c5436 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASTableView+Undeprecated.h @@ -0,0 +1,298 @@ +// +// ASTableView+Undeprecated.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * Currently our public table API is on @c ASTableNode and the @c ASTableView + * API is deprecated, but the implementations still live in the view. + * + * This category lets us avoid deprecation warnings everywhere internally. + * In the future, the ASTableView public API will be eliminated and so will this file. + */ +@interface ASTableView (Undeprecated) + +@property (nullable, nonatomic, weak) id asyncDelegate; +@property (nullable, nonatomic, weak) id asyncDataSource; +@property (nonatomic) UIEdgeInsets contentInset; +@property (nonatomic) CGPoint contentOffset; +@property (nonatomic) BOOL automaticallyAdjustsContentOffset; +@property (nonatomic) BOOL inverted; +@property (nullable, nonatomic, readonly) NSArray *indexPathsForVisibleRows; +@property (nullable, nonatomic, readonly) NSArray *indexPathsForSelectedRows; +@property (nullable, nonatomic, readonly) NSIndexPath *indexPathForSelectedRow; + +/** + * The number of screens left to scroll before the delegate -tableView:beginBatchFetchingWithContext: is called. + * + * Defaults to two screenfuls. + */ +@property (nonatomic) CGFloat leadingScreensForBatching; + +/** + * Initializer. + * + * @param frame A rectangle specifying the initial location and size of the table view in its superview’€™s coordinates. + * The frame of the table view changes as table cells are added and deleted. + * + * @param style A constant that specifies the style of the table view. See UITableViewStyle for descriptions of valid constants. + */ +- (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style; + +/** + * Tuning parameters for a range type in full mode. + * + * @param rangeType The range type to get the tuning parameters for. + * + * @return A tuning parameter value for the given range type in full mode. + * + * @see ASLayoutRangeMode + * @see ASLayoutRangeType + */ +- (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType AS_WARN_UNUSED_RESULT; + +/** + * Set the tuning parameters for a range type in full mode. + * + * @param tuningParameters The tuning parameters to store for a range type. + * @param rangeType The range type to set the tuning parameters for. + * + * @see ASLayoutRangeMode + * @see ASLayoutRangeType + */ +- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType; + +/** + * Tuning parameters for a range type in the specified mode. + * + * @param rangeMode The range mode to get the running parameters for. + * @param rangeType The range type to get the tuning parameters for. + * + * @return A tuning parameter value for the given range type in the given mode. + * + * @see ASLayoutRangeMode + * @see ASLayoutRangeType + */ +- (ASRangeTuningParameters)tuningParametersForRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType AS_WARN_UNUSED_RESULT; + +/** + * Set the tuning parameters for a range type in the specified mode. + * + * @param tuningParameters The tuning parameters to store for a range type. + * @param rangeMode The range mode to set the running parameters for. + * @param rangeType The range type to set the tuning parameters for. + * + * @see ASLayoutRangeMode + * @see ASLayoutRangeType + */ +- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType; + +- (nullable __kindof UITableViewCell *)cellForRowAtIndexPath:(NSIndexPath *)indexPath; + +/** + * Scrolls the table to the given row. + * + * @param indexPath The index path of the row. + * @param scrollPosition Where the row should end up after the scroll. + * @param animated Whether the scroll should be animated or not. + */ +- (void)scrollToRowAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UITableViewScrollPosition)scrollPosition animated:(BOOL)animated; + +- (void)selectRowAtIndexPath:(NSIndexPath *)indexPath animated:(BOOL)animated scrollPosition:(UITableViewScrollPosition)scrollPosition; + +- (nullable NSIndexPath *)indexPathForRowAtPoint:(CGPoint)point; + +- (nullable NSArray *)indexPathsForRowsInRect:(CGRect)rect; + +/** + * Similar to -visibleCells. + * + * @return an array containing the cell nodes being displayed on screen. + */ +- (NSArray *)visibleNodes AS_WARN_UNUSED_RESULT; + +/** + * Similar to -indexPathForCell:. + * + * @param cellNode a cellNode part of the table view + * + * @return an indexPath for this cellNode + */ +- (nullable NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode AS_WARN_UNUSED_RESULT; + +/** + * Reload everything from scratch, destroying the working range and all cached nodes. + * + * @param completion block to run on completion of asynchronous loading or nil. If supplied, the block is run on + * the main thread. + * @warning This method is substantially more expensive than UITableView's version. + */ +-(void)reloadDataWithCompletion:(void (^ _Nullable)(void))completion; + +/** + * Reload everything from scratch, destroying the working range and all cached nodes. + * + * @warning This method is substantially more expensive than UITableView's version. + */ +- (void)reloadData; + +/** + * Triggers a relayout of all nodes. + * + * @discussion This method invalidates and lays out every cell node in the table view. + */ +- (void)relayoutItems; + +/** + * Begins a series of method calls that insert, delete, select, or reload rows and sections of the table view, with animation enabled and no completion block. + * + * @discussion You call this method to bracket a series of method calls that ends with endUpdates and that consists of operations + * to insert, delete, select, and reload rows and sections of the table view. When you call endUpdates, ASTableView begins animating + * the operations simultaneously. It's important to remember that the ASTableView will be processing the updates asynchronously after this call is completed. + * + * @warning This method must be called from the main thread. + */ +- (void)beginUpdates; + +/** + * Concludes a series of method calls that insert, delete, select, or reload rows and sections of the table view, with animation enabled and no completion block. + * + * @discussion You call this method to bracket a series of method calls that begins with beginUpdates and that consists of operations + * to insert, delete, select, and reload rows and sections of the table view. When you call endUpdates, ASTableView begins animating + * the operations simultaneously. It's important to remember that the ASTableView will be processing the updates asynchronously after this call is completed. + * + * @warning This method is must be called from the main thread. + */ +- (void)endUpdates; + +/** + * Concludes a series of method calls that insert, delete, select, or reload rows and sections of the table view. + * You call this method to bracket a series of method calls that begins with beginUpdates and that consists of operations + * to insert, delete, select, and reload rows and sections of the table view. When you call endUpdates, ASTableView begins animating + * the operations simultaneously. This method is must be called from the main thread. It's important to remember that the ASTableView will + * be processing the updates asynchronously after this call and are not guaranteed to be reflected in the ASTableView until + * the completion block is executed. + * + * @param animated NO to disable all animations. + * @param completion A completion handler block to execute when all of the operations are finished. This block takes a single + * Boolean parameter that contains the value YES if all of the related animations completed successfully or + * NO if they were interrupted. This parameter may be nil. If supplied, the block is run on the main thread. + */ +- (void)endUpdatesAnimated:(BOOL)animated completion:(void (^ _Nullable)(BOOL completed))completion; + +/** + * Blocks execution of the main thread until all section and row updates are committed. This method must be called from the main thread. + */ +- (void)waitUntilAllUpdatesAreCommitted; + +/** + * Inserts one or more sections, with an option to animate the insertion. + * + * @param sections An index set that specifies the sections to insert. + * + * @param animation A constant that indicates how the insertion is to be animated. See UITableViewRowAnimation. + * + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes + * before this method is called. + */ +- (void)insertSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation; + +/** + * Deletes one or more sections, with an option to animate the deletion. + * + * @param sections An index set that specifies the sections to delete. + * + * @param animation A constant that indicates how the deletion is to be animated. See UITableViewRowAnimation. + * + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes + * before this method is called. + */ +- (void)deleteSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation; + +/** + * Reloads the specified sections using a given animation effect. + * + * @param sections An index set that specifies the sections to reload. + * + * @param animation A constant that indicates how the reloading is to be animated. See UITableViewRowAnimation. + * + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes + * before this method is called. + */ +- (void)reloadSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation; + +/** + * Moves a section to a new location. + * + * @param section The index of the section to move. + * + * @param newSection The index that is the destination of the move for the section. + * + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes + * before this method is called. + */ +- (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection; + +/** + * Inserts rows at the locations identified by an array of index paths, with an option to animate the insertion. + * + * @param indexPaths An array of NSIndexPath objects, each representing a row index and section index that together identify a row. + * + * @param animation A constant that indicates how the insertion is to be animated. See UITableViewRowAnimation. + * + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes + * before this method is called. + */ +- (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation; + +/** + * Deletes the rows specified by an array of index paths, with an option to animate the deletion. + * + * @param indexPaths An array of NSIndexPath objects identifying the rows to delete. + * + * @param animation A constant that indicates how the deletion is to be animated. See UITableViewRowAnimation. + * + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes + * before this method is called. + */ +- (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation; + +/** + * Reloads the specified rows using a given animation effect. + * + * @param indexPaths An array of NSIndexPath objects identifying the rows to reload. + * + * @param animation A constant that indicates how the reloading is to be animated. See UITableViewRowAnimation. + * + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes + * before this method is called. + */ +- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation; + +/** + * Moves the row at a specified location to a destination location. + * + * @param indexPath The index path identifying the row to move. + * + * @param newIndexPath The index path that is the destination of the move for the row. + * + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes + * before this method is called. + */ +- (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath; + +- (void)setContentOffset:(CGPoint)contentOffset animated:(BOOL)animated; + +@end +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASTip.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASTip.h new file mode 100644 index 0000000..5ac6ac1 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASTip.h @@ -0,0 +1,49 @@ +// +// ASTip.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +#if AS_ENABLE_TIPS + +NS_ASSUME_NONNULL_BEGIN + +@class ASDisplayNode; + +typedef NS_ENUM (NSInteger, ASTipKind) { + ASTipKindEnableLayerBacking +}; + +AS_SUBCLASSING_RESTRICTED +@interface ASTip : NSObject + +- (instancetype)initWithNode:(ASDisplayNode *)node + kind:(ASTipKind)kind + format:(NSString *)format, ... NS_FORMAT_FUNCTION(3, 4); + +/** + * The kind of tip this is. + */ +@property (nonatomic, readonly) ASTipKind kind; + +/** + * The node that this tip applies to. + */ +@property (nonatomic, readonly) ASDisplayNode *node; + +/** + * The text to show the user. + */ +@property (nonatomic, readonly) NSString *text; + +@end + +NS_ASSUME_NONNULL_END + +#endif // AS_ENABLE_TIPS diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASTip.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASTip.mm new file mode 100644 index 0000000..af1d299 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASTip.mm @@ -0,0 +1,35 @@ +// +// ASTip.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#if AS_ENABLE_TIPS + +#import + +@implementation ASTip + +- (instancetype)initWithNode:(ASDisplayNode *)node + kind:(ASTipKind)kind + format:(NSString *)format, ... +{ + if (self = [super init]) { + _node = node; + _kind = kind; + va_list args; + va_start(args, format); + _text = [[NSString alloc] initWithFormat:format arguments:args]; + va_end(args); + } + return self; +} + +@end + +#endif // AS_ENABLE_TIPS diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASTipNode.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASTipNode.h new file mode 100644 index 0000000..d01637d --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASTipNode.h @@ -0,0 +1,39 @@ +// +// ASTipNode.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +#if AS_ENABLE_TIPS + +@class ASTip; + +NS_ASSUME_NONNULL_BEGIN + +/** + * ASTipNode will send these up the responder chain. + */ +@protocol ASTipNodeActions +- (void)didTapTipNode:(id)sender; +@end + +AS_SUBCLASSING_RESTRICTED +@interface ASTipNode : ASControlNode + +- (instancetype)initWithTip:(ASTip *)tip NS_DESIGNATED_INITIALIZER; + +- (instancetype)init NS_UNAVAILABLE; + +@property (nonatomic, readonly) ASTip *tip; + +@end + +NS_ASSUME_NONNULL_END + +#endif // AS_ENABLE_TIPS diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASTipNode.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASTipNode.mm new file mode 100644 index 0000000..dcca908 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASTipNode.mm @@ -0,0 +1,28 @@ +// +// ASTipNode.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import "ASTipNode.h" + +#if AS_ENABLE_TIPS + +@implementation ASTipNode + +- (instancetype)initWithTip:(ASTip *)tip +{ + if (self = [super init]) { + self.backgroundColor = [UIColor colorWithRed:0 green:0.7 blue:0.2 alpha:0.3]; + _tip = tip; + [self addTarget:nil action:@selector(didTapTipNode:) forControlEvents:ASControlNodeEventTouchUpInside]; + } + return self; +} + +@end + +#endif // AS_ENABLE_TIPS diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASTipProvider.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASTipProvider.h new file mode 100644 index 0000000..e2aba6c --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASTipProvider.h @@ -0,0 +1,42 @@ +// +// ASTipProvider.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +#if AS_ENABLE_TIPS + +@class ASDisplayNode, ASTip; + +NS_ASSUME_NONNULL_BEGIN + +/** + * An abstract superclass for all tip providers. + */ +@interface ASTipProvider : NSObject + +/** + * The provider looks at the node's current situation and + * generates a tip, if any, to add to the node. + * + * Subclasses must override this. + */ +- (nullable ASTip *)tipForNode:(ASDisplayNode *)node; + +@end + +@interface ASTipProvider (Lookup) + +@property (class, nonatomic, copy, readonly) NSArray<__kindof ASTipProvider *> *all; + +@end + +NS_ASSUME_NONNULL_END + +#endif // AS_ENABLE_TIPS diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASTipProvider.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASTipProvider.mm new file mode 100644 index 0000000..237e83c --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASTipProvider.mm @@ -0,0 +1,43 @@ +// +// ASTipProvider.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#if AS_ENABLE_TIPS + +#import + +// Concrete classes +#import + +@implementation ASTipProvider + +- (ASTip *)tipForNode:(ASDisplayNode *)node +{ + ASDisplayNodeFailAssert(@"Subclasses must override %@", NSStringFromSelector(_cmd)); + return nil; +} + +@end + +@implementation ASTipProvider (Lookup) + ++ (NSArray *)all +{ + static NSArray *providers; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + providers = @[ [ASLayerBackingTipProvider new] ]; + }); + return providers; +} + +@end + +#endif // AS_ENABLE_TIPS diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASTipsController.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASTipsController.h new file mode 100644 index 0000000..fceeb92 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASTipsController.h @@ -0,0 +1,40 @@ +// +// ASTipsController.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +#if AS_ENABLE_TIPS + +@class ASDisplayNode; + +NS_ASSUME_NONNULL_BEGIN + +AS_SUBCLASSING_RESTRICTED +@interface ASTipsController : NSObject + +/** + * The shared tip controller instance. + */ +@property (class, readonly) ASTipsController *shared; + +#pragma mark - Node Event Hooks + +/** + * Informs the controller that the sender did enter the visible range. + * + * The controller will run a pass with its tip providers, adding tips as needed. + */ +- (void)nodeDidAppear:(ASDisplayNode *)node; + +@end + +NS_ASSUME_NONNULL_END + +#endif // AS_ENABLE_TIPS diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASTipsController.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASTipsController.mm new file mode 100644 index 0000000..e30a064 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASTipsController.mm @@ -0,0 +1,185 @@ +// +// ASTipsController.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#if AS_ENABLE_TIPS + +#import +#import +#import +#import +#import +#import + +@interface ASTipsController () + +/// Nil on init, updates to most recent visible window. +@property (nonatomic) UIWindow *appVisibleWindow; + +/// Nil until an application window has become visible. +@property (nonatomic) ASTipsWindow *tipWindow; + +/// Main-thread-only. +@property (nonatomic, readonly) NSMapTable *nodeToTipStates; + +@property (nonatomic) NSMutableArray *nodesThatAppearedDuringRunLoop; + +@end + +@implementation ASTipsController + +#pragma mark - Singleton + +__attribute__((constructor)) static void ASLoadTipsControllerNotification(void) +{ + [NSNotificationCenter.defaultCenter addObserver:ASTipsController.shared + selector:@selector(windowDidBecomeVisibleWithNotification:) + name:UIWindowDidBecomeVisibleNotification + object:nil]; +} + ++ (ASTipsController *)shared +{ + static ASTipsController *ctrl; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + ctrl = [[ASTipsController alloc] init]; + }); + return ctrl; +} + +#pragma mark - Lifecycle + +- (instancetype)init +{ + ASDisplayNodeAssertMainThread(); + if (self = [super init]) { + _nodeToTipStates = [NSMapTable mapTableWithKeyOptions:(NSPointerFunctionsWeakMemory | NSPointerFunctionsObjectPointerPersonality) valueOptions:NSPointerFunctionsStrongMemory]; + _nodesThatAppearedDuringRunLoop = [NSMutableArray array]; + } + return self; +} + +#pragma mark - Event Handling + +- (void)nodeDidAppear:(ASDisplayNode *)node +{ + ASDisplayNodeAssertMainThread(); + // If they disabled tips on this class, bail. + if (![[node class] enableTips]) { + return; + } + + // If this node appeared in some other window (like our tips window) ignore it. + if (ASFindWindowOfLayer(node.layer) != self.appVisibleWindow) { + return; + } + + [_nodesThatAppearedDuringRunLoop addObject:node]; +} + +// If this is a main window, start watching it and clear out our tip window. +- (void)windowDidBecomeVisibleWithNotification:(NSNotification *)notification +{ + ASDisplayNodeAssertMainThread(); + UIWindow *window = notification.object; + + // If this is the same window we're already watching, bail. + if (window == self.appVisibleWindow) { + return; + } + + // Ignore windows that are not at the normal level or have empty bounds + if (window.windowLevel != UIWindowLevelNormal || CGRectIsEmpty(window.bounds)) { + return; + } + + self.appVisibleWindow = window; + + // Create the tip window if needed. + [self createTipWindowIfNeededWithFrame:window.bounds]; + + // Clear out our tip window and reset our states. + self.tipWindow.mainWindow = window; + [_nodeToTipStates removeAllObjects]; +} + +- (void)runLoopDidTick +{ + NSArray *nodes = [_nodesThatAppearedDuringRunLoop copy]; + [_nodesThatAppearedDuringRunLoop removeAllObjects]; + + // Go through the old array, removing any that have tips but aren't still visible. + for (ASDisplayNode *node in [_nodeToTipStates copy]) { + if (!node.visible) { + [_nodeToTipStates removeObjectForKey:node]; + } + } + + for (ASDisplayNode *node in nodes) { + // Get the tip state for the node. + ASDisplayNodeTipState *tipState = [_nodeToTipStates objectForKey:node]; + + // If the node already has a tip, bail. This could change. + if (tipState.tipNode != nil) { + return; + } + + for (ASTipProvider *provider in ASTipProvider.all) { + ASTip *tip = [provider tipForNode:node]; + if (!tip) { continue; } + + if (!tipState) { + tipState = [self createTipStateForNode:node]; + } + tipState.tipNode = [[ASTipNode alloc] initWithTip:tip]; + } + } + self.tipWindow.nodeToTipStates = _nodeToTipStates; + [self.tipWindow setNeedsLayout]; +} + +#pragma mark - Internal + +- (void)createTipWindowIfNeededWithFrame:(CGRect)tipWindowFrame +{ + // Lots of property accesses, but simple safe code, only run once. + if (self.tipWindow == nil) { + self.tipWindow = [[ASTipsWindow alloc] initWithFrame:tipWindowFrame]; + self.tipWindow.hidden = NO; + [self setupRunLoopObserver]; + } +} + +/** + * In order to keep the UI updated, the tips controller registers a run loop observer. + * Before the transaction commit happens, the tips controller calls -setNeedsLayout + * on the view controller's view. It will then layout the main window, and then update the frames + * for tip nodes accordingly. + */ +- (void)setupRunLoopObserver +{ + CFRunLoopObserverRef o = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopBeforeWaiting, true, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) { + [self runLoopDidTick]; + }); + CFRunLoopAddObserver(CFRunLoopGetMain(), o, kCFRunLoopCommonModes); +} + +- (ASDisplayNodeTipState *)createTipStateForNode:(ASDisplayNode *)node +{ + ASDisplayNodeAssertMainThread(); + ASDisplayNodeTipState *tipState = [[ASDisplayNodeTipState alloc] initWithNode:node]; + [_nodeToTipStates setObject:tipState forKey:node]; + return tipState; +} + +@end + +#endif // AS_ENABLE_TIPS diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASTipsWindow.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASTipsWindow.h new file mode 100644 index 0000000..5354cc9 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASTipsWindow.h @@ -0,0 +1,36 @@ +// +// ASTipsWindow.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +#if AS_ENABLE_TIPS + +@class ASDisplayNode, ASDisplayNodeTipState; + +NS_ASSUME_NONNULL_BEGIN + +/** + * A window that shows tips. This was originally meant to be a view controller + * but UIKit will not manage view controllers in non-key windows correctly AT ALL + * as of the time of this writing. + */ +AS_SUBCLASSING_RESTRICTED +@interface ASTipsWindow : UIWindow + +/// The main application window that the tips are tracking. +@property (nonatomic, weak) UIWindow *mainWindow; + +@property (nonatomic, copy, nullable) NSMapTable *nodeToTipStates; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASTipsWindow.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASTipsWindow.mm new file mode 100644 index 0000000..0109326 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASTipsWindow.mm @@ -0,0 +1,98 @@ +// +// ASTipsWindow.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#if AS_ENABLE_TIPS + +#import +#import +#import +#import + +@interface ASTipsWindow () +@property (nonatomic, readonly) ASDisplayNode *node; +@end + +@implementation ASTipsWindow + +- (instancetype)initWithFrame:(CGRect)frame +{ + if (self = [super initWithFrame:frame]) { + /** + * UIKit throws an exception if you don't add a root view controller to a window, + * but if the window isn't key, then it doesn't manage the root view controller correctly! + * + * So we set a dummy root view controller and hide it. + */ + self.rootViewController = [UIViewController new]; + self.rootViewController.view.hidden = YES; + + _node = [[ASDisplayNode alloc] init]; + [self addSubnode:_node]; + + self.windowLevel = UIWindowLevelNormal + 1; + self.opaque = NO; + } + return self; +} + +- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event +{ + UIView *result = [super hitTest:point withEvent:event]; + // Ignore touches unless they hit one of my node's subnodes + if (result == _node.view) { + return nil; + } + return result; +} + +- (void)setMainWindow:(UIWindow *)mainWindow +{ + _mainWindow = mainWindow; + for (ASDisplayNode *node in _node.subnodes) { + [node removeFromSupernode]; + } +} + +- (void)didTapTipNode:(ASTipNode *)tipNode +{ + ASDisplayNode.tipDisplayBlock(tipNode.tip.node, tipNode.tip.text); +} + +- (void)layoutSubviews +{ + [super layoutSubviews]; + _node.frame = self.bounds; + + // Ensure the main window is laid out first. + [self.mainWindow layoutIfNeeded]; + + NSMutableSet *tipNodesToRemove = [NSMutableSet setWithArray:_node.subnodes]; + for (ASDisplayNodeTipState *tipState in [_nodeToTipStates objectEnumerator]) { + ASDisplayNode *node = tipState.node; + ASTipNode *tipNode = tipState.tipNode; + [tipNodesToRemove removeObject:tipNode]; + CGRect rect = node.bounds; + rect = [node.view convertRect:rect toView:nil]; + rect = [self convertRect:rect fromView:nil]; + tipNode.frame = rect; + if (tipNode.supernode != _node) { + [_node addSubnode:tipNode]; + } + } + + // Clean up any tip nodes whose target nodes have disappeared. + for (ASTipNode *tipNode in tipNodesToRemove) { + [tipNode removeFromSupernode]; + } +} + +@end + +#endif // AS_ENABLE_TIPS diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASTwoDimensionalArrayUtils.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASTwoDimensionalArrayUtils.h new file mode 100644 index 0000000..8bddf29 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASTwoDimensionalArrayUtils.h @@ -0,0 +1,46 @@ +// +// ASTwoDimensionalArrayUtils.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * Helper functions for two-dimensional array, where the objects of the root array are each arrays. + */ + +/** + * Deep mutable copy of an array that contains arrays, which contain objects. It will go one level deep into the array to copy. + * This method is substantially faster than the generalized version, e.g. about 10x faster, so use it whenever it fits the need. + */ +ASDK_EXTERN NSMutableArray *ASTwoDimensionalArrayDeepMutableCopy(NSArray *array) AS_WARN_UNUSED_RESULT; + +/** + * Delete the elements of the mutable two-dimensional array at given index paths – sorted in descending order! + */ +ASDK_EXTERN void ASDeleteElementsInTwoDimensionalArrayAtIndexPaths(NSMutableArray *mutableArray, NSArray *indexPaths); + +/** + * Return all the index paths of a two-dimensional array, in ascending order. + */ +ASDK_EXTERN NSArray *ASIndexPathsForTwoDimensionalArray(NSArray* twoDimensionalArray) AS_WARN_UNUSED_RESULT; + +/** + * Return all the elements of a two-dimensional array, in ascending order. + */ +ASDK_EXTERN NSArray *ASElementsInTwoDimensionalArray(NSArray* twoDimensionalArray) AS_WARN_UNUSED_RESULT; + +/** + * Attempt to get the object at the given index path. Returns @c nil if the index path is out of bounds. + */ +ASDK_EXTERN id _Nullable ASGetElementInTwoDimensionalArray(NSArray *array, NSIndexPath *indexPath) AS_WARN_UNUSED_RESULT; + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASTwoDimensionalArrayUtils.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASTwoDimensionalArrayUtils.mm new file mode 100644 index 0000000..2a33334 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASTwoDimensionalArrayUtils.mm @@ -0,0 +1,121 @@ +// +// ASTwoDimensionalArrayUtils.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import +#import + +#import + +// Import UIKit to get [NSIndexPath indexPathForItem:inSection:] which uses +// tagged pointers. + +#pragma mark - Public Methods + +NSMutableArray *ASTwoDimensionalArrayDeepMutableCopy(NSArray *array) +{ + NSMutableArray *newArray = [NSMutableArray arrayWithCapacity:array.count]; + NSInteger i = 0; + for (NSArray *subarray in array) { + ASDisplayNodeCAssert([subarray isKindOfClass:[NSArray class]], @"This function expects NSArray *"); + newArray[i++] = [subarray mutableCopy]; + } + return newArray; +} + +void ASDeleteElementsInTwoDimensionalArrayAtIndexPaths(NSMutableArray *mutableArray, NSArray *indexPaths) +{ + if (indexPaths.count == 0) { + return; + } + +#if ASDISPLAYNODE_ASSERTIONS_ENABLED + NSArray *sortedIndexPaths = [indexPaths sortedArrayUsingSelector:@selector(asdk_inverseCompare:)]; + ASDisplayNodeCAssert([sortedIndexPaths isEqualToArray:indexPaths], @"Expected array of index paths to be sorted in descending order."); +#endif + + /** + * It is tempting to do something clever here and collect indexes into ranges or NSIndexSets + * but deep down, __NSArrayM only implements removeObjectAtIndex: and so doing all that extra + * work ends up running the same code. + */ + for (NSIndexPath *indexPath in indexPaths) { + NSInteger section = indexPath.section; + if (section >= mutableArray.count) { + ASDisplayNodeCFailAssert(@"Invalid section index %ld – only %ld sections", (long)section, (long)mutableArray.count); + continue; + } + + NSMutableArray *subarray = mutableArray[section]; + NSInteger item = indexPath.item; + if (item >= subarray.count) { + ASDisplayNodeCFailAssert(@"Invalid item index %ld – only %ld items in section %ld", (long)item, (long)subarray.count, (long)section); + continue; + } + [subarray removeObjectAtIndex:item]; + } +} + +NSArray *ASIndexPathsForTwoDimensionalArray(NSArray * twoDimensionalArray) +{ + NSInteger sectionCount = twoDimensionalArray.count; + NSInteger counts[sectionCount]; + NSInteger totalCount = 0; + NSInteger i = 0; + for (NSArray *subarray in twoDimensionalArray) { + NSInteger count = subarray.count; + counts[i++] = count; + totalCount += count; + } + + // Count could be huge. Use a reserved vector rather than VLA (stack.) + std::vector indexPaths; + indexPaths.reserve(totalCount); + for (NSInteger i = 0; i < sectionCount; i++) { + for (NSInteger j = 0; j < counts[i]; j++) { + indexPaths.push_back([NSIndexPath indexPathForItem:j inSection:i]); + } + } + return [NSArray arrayByTransferring:indexPaths.data() count:totalCount]; +} + +NSArray *ASElementsInTwoDimensionalArray(NSArray * twoDimensionalArray) +{ + NSInteger totalCount = 0; + for (NSArray *subarray in twoDimensionalArray) { + totalCount += subarray.count; + } + + std::vector elements; + elements.reserve(totalCount); + for (NSArray *subarray in twoDimensionalArray) { + for (id object in subarray) { + elements.push_back(object); + } + } + return [NSArray arrayByTransferring:elements.data() count:totalCount]; +} + +id ASGetElementInTwoDimensionalArray(NSArray *array, NSIndexPath *indexPath) +{ + ASDisplayNodeCAssertNotNil(indexPath, @"Expected non-nil index path"); + ASDisplayNodeCAssert(indexPath.length == 2, @"Expected index path of length 2. Index path: %@", indexPath); + NSInteger section = indexPath.section; + if (array.count <= section) { + return nil; + } + + NSArray *innerArray = array[section]; + NSInteger item = indexPath.item; + if (innerArray.count <= item) { + return nil; + } + return innerArray[item]; +} diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASWeakMap.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASWeakMap.h new file mode 100644 index 0000000..1f413ca --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASWeakMap.h @@ -0,0 +1,59 @@ +// +// ASWeakMap.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + + +/** + * This class is used in conjunction with ASWeakMap. Instances of this type are returned by an ASWeakMap, + * must retain this value for as long as they want the entry to exist in the map. + */ +AS_SUBCLASSING_RESTRICTED +@interface ASWeakMapEntry : NSObject + +@property (readonly) Value value; + +@end + + +/** + * This is not a full-featured map. It does not support features like `count` and FastEnumeration because there + * is not currently a need. + * + * This is a map that does not retain keys or values added to it. When both getting and setting, the caller is + * returned a ASWeakMapEntry and must retain it for as long as it wishes the key/value to remain in the map. + * We return a single Entry value to the caller to avoid two potential problems: + * + * 1) It's easier for callers to retain one value (the Entry) and not two (a key and a value). + * 2) Key values are tested for `isEqual` equality. If if a caller asks for a key "A" that is equal to a key "B" + * already in the map, then we need the caller to retain key "B" and not key "A". Returning an Entry simplifies + * the semantics. + * + * The underlying storage is a hash table and the Key type should implement `hash` and `isEqual:`. + */ +AS_SUBCLASSING_RESTRICTED +@interface ASWeakMap<__covariant Key, Value> : NSObject + +/** + * Read from the cache. The Value object is accessible from the returned ASWeakMapEntry. + */ +- (nullable ASWeakMapEntry *)entryForKey:(Key)key AS_WARN_UNUSED_RESULT; + +/** + * Put a value into the cache. If an entry with an equal key already exists, then the value is updated on the existing entry. + */ +- (ASWeakMapEntry *)setObject:(Value)value forKey:(Key)key AS_WARN_UNUSED_RESULT; + +@end + + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASWeakMap.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASWeakMap.mm new file mode 100644 index 0000000..3110b13 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/ASWeakMap.mm @@ -0,0 +1,78 @@ +// +// ASWeakMap.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +@interface ASWeakMapEntry () +@property (nonatomic, readonly) id key; +@property id value; +@end + +@implementation ASWeakMapEntry + +- (instancetype)initWithKey:(id)key value:(id)value +{ + self = [super init]; + if (self) { + _key = key; + _value = value; + } + return self; +} + +@end + + +@interface ASWeakMap () +@property (nonatomic, readonly) NSMapTable *hashTable; +@end + +/** + * Implementation details: + * + * The retained size of our keys is potentially very large (for example, a UIImage is commonly part of a key). + * Unfortunately, NSMapTable does not make guarantees about how quickly it will dispose of entries where + * either the key or the value is weak and has been disposed. So, a NSMapTable with "strong key to weak value" is + * unsuitable for our purpose because the strong keys are retained longer than the value and for an indefininte period of time. + * More details here: http://cocoamine.net/blog/2013/12/13/nsmaptable-and-zeroing-weak-references/ + * + * Our NSMapTable is "weak key to weak value" where each key maps to an Entry. The Entry object is responsible + * for retaining both the key and value. Our convention is that the caller must retain the Entry object + * in order to keep the key and the value in the cache. + */ +@implementation ASWeakMap + +- (instancetype)init +{ + self = [super init]; + if (self) { + _hashTable = [NSMapTable weakToWeakObjectsMapTable]; + } + return self; +} + +- (ASWeakMapEntry *)entryForKey:(id)key +{ + return [self.hashTable objectForKey:key]; +} + +- (ASWeakMapEntry *)setObject:(id)value forKey:(id)key +{ + ASWeakMapEntry *entry = [self.hashTable objectForKey:key]; + if (entry != nil) { + // Update the value in the existing entry. + entry.value = value; + } else { + entry = [[ASWeakMapEntry alloc] initWithKey:key value:value]; + [self.hashTable setObject:entry forKey:key]; + } + return entry; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/Layout/ASLayoutElementStylePrivate.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/Layout/ASLayoutElementStylePrivate.h new file mode 100644 index 0000000..69e2982 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/Layout/ASLayoutElementStylePrivate.h @@ -0,0 +1,31 @@ +// +// ASLayoutElementStylePrivate.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#pragma once + +#import +#import + +@interface ASLayoutElementStyle () + +/** + * @abstract The object that acts as the delegate of the style. + * + * @discussion The delegate must adopt the ASLayoutElementStyleDelegate protocol. The delegate is not retained. + */ +@property (nullable, nonatomic, weak) id delegate; + +/** + * @abstract A size constraint that should apply to this ASLayoutElement. + */ +@property (nonatomic, readonly) ASLayoutElementSize size; + +@property (nonatomic, assign) ASStackLayoutAlignItems parentAlignStyle; + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/Layout/ASLayoutSpecPrivate.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/Layout/ASLayoutSpecPrivate.h new file mode 100644 index 0000000..9302320 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/Layout/ASLayoutSpecPrivate.h @@ -0,0 +1,37 @@ +// +// ASLayoutSpecPrivate.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +#if DEBUG + #define AS_DEDUPE_LAYOUT_SPEC_TREE 1 +#else + #define AS_DEDUPE_LAYOUT_SPEC_TREE 0 +#endif + +NS_ASSUME_NONNULL_BEGIN + +@interface ASLayoutSpec() { + AS::RecursiveMutex __instanceLock__; + std::atomic _primitiveTraitCollection; + ASLayoutElementStyle *_style; + NSMutableArray *_childrenArray; +} + +#if AS_DEDUPE_LAYOUT_SPEC_TREE +/** + * Recursively search the subtree for elements that occur more than once. + */ +- (nullable NSHashTable> *)findDuplicatedElementsInSubtree; +#endif + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/Layout/ASLayoutSpecUtilities.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/Layout/ASLayoutSpecUtilities.h new file mode 100644 index 0000000..82e07df --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/Layout/ASLayoutSpecUtilities.h @@ -0,0 +1,101 @@ +// +// ASLayoutSpecUtilities.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import +#import +#import +#import + +namespace AS { + // adopted from http://stackoverflow.com/questions/14945223/map-function-with-c11-constructs + // Takes an iterable, applies a function to every element, + // and returns a vector of the results + // + template + auto map(const T &iterable, Func &&func) -> std::vector()))> + { + // Some convenience type definitions + typedef decltype(func(std::declval())) value_type; + + // Prepares an output vector of the appropriate size + std::vector res; + res.reserve(iterable.size()); + + // Let std::transform apply `func` to all elements + // (use perfect forwarding for the function object) + std::transform(std::begin(iterable), std::end(iterable), std::back_inserter(res), + std::forward(func)); + + return res; + } + + template + auto map(id collection, Func &&func) -> std::vector()))> + { + std::vector()))> to; + for (id obj in collection) { + to.push_back(func(obj)); + } + return to; + } + + template + auto filter(const T &iterable, Func &&func) -> std::vector + { + std::vector to; + for (auto obj : iterable) { + if (func(obj)) { + to.push_back(obj); + } + } + return to; + } +}; + +inline CGPoint operator+(const CGPoint &p1, const CGPoint &p2) +{ + return { p1.x + p2.x, p1.y + p2.y }; +} + +inline CGPoint operator-(const CGPoint &p1, const CGPoint &p2) +{ + return { p1.x - p2.x, p1.y - p2.y }; +} + +inline CGSize operator+(const CGSize &s1, const CGSize &s2) +{ + return { s1.width + s2.width, s1.height + s2.height }; +} + +inline CGSize operator-(const CGSize &s1, const CGSize &s2) +{ + return { s1.width - s2.width, s1.height - s2.height }; +} + +inline UIEdgeInsets operator+(const UIEdgeInsets &e1, const UIEdgeInsets &e2) +{ + return { e1.top + e2.top, e1.left + e2.left, e1.bottom + e2.bottom, e1.right + e2.right }; +} + +inline UIEdgeInsets operator-(const UIEdgeInsets &e1, const UIEdgeInsets &e2) +{ + return { e1.top - e2.top, e1.left - e2.left, e1.bottom - e2.bottom, e1.right - e2.right }; +} + +inline UIEdgeInsets operator*(const UIEdgeInsets &e1, const UIEdgeInsets &e2) +{ + return { e1.top * e2.top, e1.left * e2.left, e1.bottom * e2.bottom, e1.right * e2.right }; +} + +inline UIEdgeInsets operator-(const UIEdgeInsets &e) +{ + return { -e.top, -e.left, -e.bottom, -e.right }; +} diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/Layout/ASStackLayoutSpecUtilities.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/Layout/ASStackLayoutSpecUtilities.h new file mode 100644 index 0000000..6c708e0 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/Layout/ASStackLayoutSpecUtilities.h @@ -0,0 +1,135 @@ +// +// ASStackLayoutSpecUtilities.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +typedef struct { + ASStackLayoutDirection direction; + CGFloat spacing; + ASStackLayoutJustifyContent justifyContent; + ASStackLayoutAlignItems alignItems; + ASStackLayoutFlexWrap flexWrap; + ASStackLayoutAlignContent alignContent; + CGFloat lineSpacing; +} ASStackLayoutSpecStyle; + +inline CGFloat stackDimension(const ASStackLayoutDirection direction, const CGSize size) +{ + return (direction == ASStackLayoutDirectionVertical) ? size.height : size.width; +} + +inline CGFloat crossDimension(const ASStackLayoutDirection direction, const CGSize size) +{ + return (direction == ASStackLayoutDirectionVertical) ? size.width : size.height; +} + +inline BOOL compareCrossDimension(const ASStackLayoutDirection direction, const CGSize a, const CGSize b) +{ + return crossDimension(direction, a) < crossDimension(direction, b); +} + +inline CGPoint directionPoint(const ASStackLayoutDirection direction, const CGFloat stack, const CGFloat cross) +{ + return (direction == ASStackLayoutDirectionVertical) ? CGPointMake(cross, stack) : CGPointMake(stack, cross); +} + +inline CGSize directionSize(const ASStackLayoutDirection direction, const CGFloat stack, const CGFloat cross) +{ + return (direction == ASStackLayoutDirectionVertical) ? CGSizeMake(cross, stack) : CGSizeMake(stack, cross); +} + +inline void setStackValueToPoint(const ASStackLayoutDirection direction, const CGFloat stack, CGPoint &point) { + (direction == ASStackLayoutDirectionVertical) ? (point.y = stack) : (point.x = stack); +} + +inline ASSizeRange directionSizeRange(const ASStackLayoutDirection direction, + const CGFloat stackMin, + const CGFloat stackMax, + const CGFloat crossMin, + const CGFloat crossMax) +{ + return {directionSize(direction, stackMin, crossMin), directionSize(direction, stackMax, crossMax)}; +} + +inline ASStackLayoutAlignItems alignment(ASStackLayoutAlignSelf childAlignment, ASStackLayoutAlignItems stackAlignment) +{ + switch (childAlignment) { + case ASStackLayoutAlignSelfCenter: + return ASStackLayoutAlignItemsCenter; + case ASStackLayoutAlignSelfEnd: + return ASStackLayoutAlignItemsEnd; + case ASStackLayoutAlignSelfStart: + return ASStackLayoutAlignItemsStart; + case ASStackLayoutAlignSelfStretch: + return ASStackLayoutAlignItemsStretch; + case ASStackLayoutAlignSelfAuto: + default: + return stackAlignment; + } +} + +inline ASStackLayoutAlignItems alignment(ASHorizontalAlignment alignment, ASStackLayoutAlignItems defaultAlignment) +{ + switch (alignment) { + case ASHorizontalAlignmentLeft: + return ASStackLayoutAlignItemsStart; + case ASHorizontalAlignmentMiddle: + return ASStackLayoutAlignItemsCenter; + case ASHorizontalAlignmentRight: + return ASStackLayoutAlignItemsEnd; + case ASHorizontalAlignmentNone: + default: + return defaultAlignment; + } +} + +inline ASStackLayoutAlignItems alignment(ASVerticalAlignment alignment, ASStackLayoutAlignItems defaultAlignment) +{ + switch (alignment) { + case ASVerticalAlignmentTop: + return ASStackLayoutAlignItemsStart; + case ASVerticalAlignmentCenter: + return ASStackLayoutAlignItemsCenter; + case ASVerticalAlignmentBottom: + return ASStackLayoutAlignItemsEnd; + case ASVerticalAlignmentNone: + default: + return defaultAlignment; + } +} + +inline ASStackLayoutJustifyContent justifyContent(ASHorizontalAlignment alignment, ASStackLayoutJustifyContent defaultJustifyContent) +{ + switch (alignment) { + case ASHorizontalAlignmentLeft: + return ASStackLayoutJustifyContentStart; + case ASHorizontalAlignmentMiddle: + return ASStackLayoutJustifyContentCenter; + case ASHorizontalAlignmentRight: + return ASStackLayoutJustifyContentEnd; + case ASHorizontalAlignmentNone: + default: + return defaultJustifyContent; + } +} + +inline ASStackLayoutJustifyContent justifyContent(ASVerticalAlignment alignment, ASStackLayoutJustifyContent defaultJustifyContent) +{ + switch (alignment) { + case ASVerticalAlignmentTop: + return ASStackLayoutJustifyContentStart; + case ASVerticalAlignmentCenter: + return ASStackLayoutJustifyContentCenter; + case ASVerticalAlignmentBottom: + return ASStackLayoutJustifyContentEnd; + case ASVerticalAlignmentNone: + default: + return defaultJustifyContent; + } +} diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/Layout/ASStackPositionedLayout.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/Layout/ASStackPositionedLayout.h new file mode 100644 index 0000000..103ec3a --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/Layout/ASStackPositionedLayout.h @@ -0,0 +1,24 @@ +// +// ASStackPositionedLayout.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import + +/** Represents a set of laid out and positioned stack layout children. */ +struct ASStackPositionedLayout { + const std::vector items; + /** Final size of the stack */ + const CGSize size; + + /** Given an unpositioned layout, computes the positions each child should be placed at. */ + static ASStackPositionedLayout compute(const ASStackUnpositionedLayout &unpositionedLayout, + const ASStackLayoutSpecStyle &style, + const ASSizeRange &constrainedSize); +}; diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/Layout/ASStackPositionedLayout.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/Layout/ASStackPositionedLayout.mm new file mode 100644 index 0000000..0e9d896 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/Layout/ASStackPositionedLayout.mm @@ -0,0 +1,185 @@ +// +// ASStackPositionedLayout.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import +#import + +#import +#import +#import + +static CGFloat crossOffsetForItem(const ASStackLayoutSpecItem &item, + const ASStackLayoutSpecStyle &style, + const CGFloat crossSize, + const CGFloat baseline) +{ + switch (alignment(item.child.style.alignSelf, style.alignItems)) { + case ASStackLayoutAlignItemsEnd: + return crossSize - crossDimension(style.direction, item.layout.size); + case ASStackLayoutAlignItemsCenter: + return ASFloorPixelValue((crossSize - crossDimension(style.direction, item.layout.size)) / 2); + case ASStackLayoutAlignItemsBaselineFirst: + case ASStackLayoutAlignItemsBaselineLast: + return baseline - ASStackUnpositionedLayout::baselineForItem(style, item); + case ASStackLayoutAlignItemsStart: + case ASStackLayoutAlignItemsStretch: + case ASStackLayoutAlignItemsNotSet: + return 0; + } +} + +static void crossOffsetAndSpacingForEachLine(const std::size_t numOfLines, + const CGFloat crossViolation, + ASStackLayoutAlignContent alignContent, + CGFloat &offset, + CGFloat &spacing) +{ + ASDisplayNodeCAssertTrue(numOfLines > 0); + + // Handle edge cases + if (alignContent == ASStackLayoutAlignContentSpaceBetween && (crossViolation < kViolationEpsilon || numOfLines == 1)) { + alignContent = ASStackLayoutAlignContentStart; + } else if (alignContent == ASStackLayoutAlignContentSpaceAround && (crossViolation < kViolationEpsilon || numOfLines == 1)) { + alignContent = ASStackLayoutAlignContentCenter; + } + + offset = 0; + spacing = 0; + + switch (alignContent) { + case ASStackLayoutAlignContentCenter: + offset = crossViolation / 2; + break; + case ASStackLayoutAlignContentEnd: + offset = crossViolation; + break; + case ASStackLayoutAlignContentSpaceBetween: + // Spacing between the items, no spaces at the edges, evenly distributed + spacing = crossViolation / (numOfLines - 1); + break; + case ASStackLayoutAlignContentSpaceAround: { + // Spacing between items are twice the spacing on the edges + CGFloat spacingUnit = crossViolation / (numOfLines * 2); + offset = spacingUnit; + spacing = spacingUnit * 2; + break; + } + case ASStackLayoutAlignContentStart: + case ASStackLayoutAlignContentStretch: + break; + } +} + +static void stackOffsetAndSpacingForEachItem(const std::size_t numOfItems, + const CGFloat stackViolation, + ASStackLayoutJustifyContent justifyContent, + CGFloat &offset, + CGFloat &spacing) +{ + ASDisplayNodeCAssertTrue(numOfItems > 0); + + // Handle edge cases + if (justifyContent == ASStackLayoutJustifyContentSpaceBetween && (stackViolation < kViolationEpsilon || numOfItems == 1)) { + justifyContent = ASStackLayoutJustifyContentStart; + } else if (justifyContent == ASStackLayoutJustifyContentSpaceAround && (stackViolation < kViolationEpsilon || numOfItems == 1)) { + justifyContent = ASStackLayoutJustifyContentCenter; + } + + offset = 0; + spacing = 0; + + switch (justifyContent) { + case ASStackLayoutJustifyContentCenter: + offset = stackViolation / 2; + break; + case ASStackLayoutJustifyContentEnd: + offset = stackViolation; + break; + case ASStackLayoutJustifyContentSpaceBetween: + // Spacing between the items, no spaces at the edges, evenly distributed + spacing = stackViolation / (numOfItems - 1); + break; + case ASStackLayoutJustifyContentSpaceAround: { + // Spacing between items are twice the spacing on the edges + CGFloat spacingUnit = stackViolation / (numOfItems * 2); + offset = spacingUnit; + spacing = spacingUnit * 2; + break; + } + case ASStackLayoutJustifyContentStart: + break; + } +} + +static void positionItemsInLine(const ASStackUnpositionedLine &line, + const ASStackLayoutSpecStyle &style, + const CGPoint &startingPoint, + const CGFloat stackSpacing) +{ + CGPoint p = startingPoint; + BOOL first = YES; + + for (const auto &item : line.items) { + p = p + directionPoint(style.direction, item.child.style.spacingBefore, 0); + if (!first) { + p = p + directionPoint(style.direction, style.spacing + stackSpacing, 0); + } + first = NO; + item.layout.position = p + directionPoint(style.direction, 0, crossOffsetForItem(item, style, line.crossSize, line.baseline)); + + p = p + directionPoint(style.direction, stackDimension(style.direction, item.layout.size) + item.child.style.spacingAfter, 0); + } +} + +ASStackPositionedLayout ASStackPositionedLayout::compute(const ASStackUnpositionedLayout &layout, + const ASStackLayoutSpecStyle &style, + const ASSizeRange &sizeRange) +{ + const auto &lines = layout.lines; + if (lines.empty()) { + return {}; + } + + const auto numOfLines = lines.size(); + const auto direction = style.direction; + const auto alignContent = style.alignContent; + const auto lineSpacing = style.lineSpacing; + const auto justifyContent = style.justifyContent; + const auto crossViolation = ASStackUnpositionedLayout::computeCrossViolation(layout.crossDimensionSum, style, sizeRange); + CGFloat crossOffset; + CGFloat crossSpacing; + crossOffsetAndSpacingForEachLine(numOfLines, crossViolation, alignContent, crossOffset, crossSpacing); + + std::vector positionedItems; + CGPoint p = directionPoint(direction, 0, crossOffset); + BOOL first = YES; + for (const auto &line : lines) { + if (!first) { + p = p + directionPoint(direction, 0, crossSpacing + lineSpacing); + } + first = NO; + + const auto &items = line.items; + const auto stackViolation = ASStackUnpositionedLayout::computeStackViolation(line.stackDimensionSum, style, sizeRange); + CGFloat stackOffset; + CGFloat stackSpacing; + stackOffsetAndSpacingForEachItem(items.size(), stackViolation, justifyContent, stackOffset, stackSpacing); + + setStackValueToPoint(direction, stackOffset, p); + positionItemsInLine(line, style, p, stackSpacing); + std::move(items.begin(), items.end(), std::back_inserter(positionedItems)); + + p = p + directionPoint(direction, -stackOffset, line.crossSize); + } + + const CGSize finalSize = directionSize(direction, layout.stackDimensionSum, layout.crossDimensionSum); + return {std::move(positionedItems), ASSizeRangeClamp(sizeRange, finalSize)}; +} diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/Layout/ASStackUnpositionedLayout.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/Layout/ASStackUnpositionedLayout.h new file mode 100644 index 0000000..68f9421 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/Layout/ASStackUnpositionedLayout.h @@ -0,0 +1,73 @@ +// +// ASStackUnpositionedLayout.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import +#import +#import + +/** The threshold that determines if a violation has actually occurred. */ +ASDK_EXTERN CGFloat const kViolationEpsilon; + +struct ASStackLayoutSpecChild { + /** The original source child. */ + id element; + /** Style object of element. */ + ASLayoutElementStyle *style; + /** Size object of the element */ + ASLayoutElementSize size; +}; + +struct ASStackLayoutSpecItem { + /** The original source child. */ + ASStackLayoutSpecChild child; + /** The proposed layout or nil if no is calculated yet. */ + ASLayout *layout; +}; + +struct ASStackUnpositionedLine { + /** The set of proposed children in this line, each contains child layout, not yet positioned. */ + std::vector items; + /** The total size of the children in the stack dimension, including all spacing. */ + CGFloat stackDimensionSum; + /** The size in the cross dimension */ + CGFloat crossSize; + /** The baseline of the stack which baseline aligned children should align to */ + CGFloat baseline; +}; + +/** Represents a set of stack layout children that have their final layout computed, but are not yet positioned. */ +struct ASStackUnpositionedLayout { + /** The set of proposed lines, each contains child layouts, not yet positioned. */ + const std::vector lines; + /** + * In a single line stack (e.g no wrao), this is the total size of the children in the stack dimension, including all spacing. + * In a multi-line stack, this is the largest stack dimension among lines. + */ + const CGFloat stackDimensionSum; + const CGFloat crossDimensionSum; + + /** Given a set of children, computes the unpositioned layouts for those children. */ + static ASStackUnpositionedLayout compute(const std::vector &children, + const ASStackLayoutSpecStyle &style, + const ASSizeRange &sizeRange, + const BOOL concurrent); + + static CGFloat baselineForItem(const ASStackLayoutSpecStyle &style, + const ASStackLayoutSpecItem &l); + + static CGFloat computeStackViolation(const CGFloat stackDimensionSum, + const ASStackLayoutSpecStyle &style, + const ASSizeRange &sizeRange); + + static CGFloat computeCrossViolation(const CGFloat crossDimensionSum, + const ASStackLayoutSpecStyle &style, + const ASSizeRange &sizeRange); +}; diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/Layout/ASStackUnpositionedLayout.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/Layout/ASStackUnpositionedLayout.mm new file mode 100644 index 0000000..773f8bb --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/Layout/ASStackUnpositionedLayout.mm @@ -0,0 +1,758 @@ +// +// ASStackUnpositionedLayout.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import +#import + +#import +#import +#import + +CGFloat const kViolationEpsilon = 0.01; + +static CGFloat resolveCrossDimensionMaxForStretchChild(const ASStackLayoutSpecStyle &style, + const ASStackLayoutSpecChild &child, + const CGFloat stackMax, + const CGFloat crossMax) +{ + // stretched children may have a cross direction max that is smaller than the minimum size constraint of the parent. + const CGFloat computedMax = (style.direction == ASStackLayoutDirectionVertical ? + ASLayoutElementSizeResolve(child.style.size, ASLayoutElementParentSizeUndefined).max.width : + ASLayoutElementSizeResolve(child.style.size, ASLayoutElementParentSizeUndefined).max.height); + return computedMax == INFINITY ? crossMax : computedMax; +} + +static CGFloat resolveCrossDimensionMinForStretchChild(const ASStackLayoutSpecStyle &style, + const ASStackLayoutSpecChild &child, + const CGFloat stackMax, + const CGFloat crossMin) +{ + // stretched children will have a cross dimension of at least crossMin, unless they explicitly define a child size + // that is smaller than the constraint of the parent. + return (style.direction == ASStackLayoutDirectionVertical ? + ASLayoutElementSizeResolve(child.style.size, ASLayoutElementParentSizeUndefined).min.width : + ASLayoutElementSizeResolve(child.style.size, ASLayoutElementParentSizeUndefined).min.height) ?: crossMin; +} + +/** + Sizes the child given the parameters specified, and returns the computed layout. + */ +static ASLayout *crossChildLayout(const ASStackLayoutSpecChild &child, + const ASStackLayoutSpecStyle &style, + const CGFloat stackMin, + const CGFloat stackMax, + const CGFloat crossMin, + const CGFloat crossMax, + const CGSize parentSize) +{ + const ASStackLayoutAlignItems alignItems = alignment(child.style.alignSelf, style.alignItems); + // stretched children will have a cross dimension of at least crossMin + const CGFloat childCrossMin = (alignItems == ASStackLayoutAlignItemsStretch ? + resolveCrossDimensionMinForStretchChild(style, child, stackMax, crossMin) : + 0); + const CGFloat childCrossMax = (alignItems == ASStackLayoutAlignItemsStretch ? + resolveCrossDimensionMaxForStretchChild(style, child, stackMax, crossMax) : + crossMax); + const ASSizeRange childSizeRange = directionSizeRange(style.direction, stackMin, stackMax, childCrossMin, childCrossMax); + ASLayout *layout = [child.element layoutThatFits:childSizeRange parentSize:parentSize]; + ASDisplayNodeCAssertNotNil(layout, @"ASLayout returned from -layoutThatFits:parentSize: must not be nil: %@", child.element); + return layout ? : [ASLayout layoutWithLayoutElement:child.element size:{0, 0}]; +} + +static void dispatchApplyIfNeeded(size_t iterationCount, BOOL forced, void(^work)(size_t i)) +{ + if (iterationCount == 0) { + return; + } + + if (iterationCount == 1) { + work(0); + return; + } + + // TODO Once the locking situation in ASDisplayNode has improved, always dispatch if on main + if (forced == NO) { + for (size_t i = 0; i < iterationCount; i++) { + work(i); + } + return; + } + + dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + ASDispatchApply(iterationCount, queue, 0, work); +} + +/** + Computes the consumed cross dimension length for the given vector of lines and stacking style. + + Cross Dimension + +---------------------> + +--------+ +--------+ +--------+ +---------+ + Vertical |Vertical| |Vertical| |Vertical| |Vertical | + Stack | Line 1 | | Line 2 | | Line 3 | | Line 4 | + | | | | | | | | + +--------+ +--------+ +--------+ +---------+ + crossDimensionSum + |------------------------------------------| + + @param lines unpositioned lines + */ +static CGFloat computeLinesCrossDimensionSum(const std::vector &lines, + const ASStackLayoutSpecStyle &style) +{ + return std::accumulate(lines.begin(), lines.end(), + // Start from default spacing between each line: + lines.empty() ? 0 : style.lineSpacing * (lines.size() - 1), + [&](CGFloat x, const ASStackUnpositionedLine &l) { + return x + l.crossSize; + }); +} + + +/** + Computes the violation by comparing a cross dimension sum with the overall allowable size range for the stack. + + Violation is the distance you would have to add to the unbounded cross-direction length of the stack spec's + lines in order to bring the stack within its allowed sizeRange. The diagram below shows 3 vertical stacks, each contains 3-5 vertical lines, + with the different types of violation. + + Cross Dimension + +---------------------> + cross size range + |------------| + +--------+ +--------+ +--------+ +---------+ - - - - - - - - + Vertical |Vertical| |Vertical| |Vertical| |Vertical | | ^ + Stack 1 | Line 1 | | Line 2 | | Line 3 | | Line 4 | (zero violation) | stack size range + | | | | | | | | | | v + +--------+ +--------+ +--------+ +---------+ - - - - - - - - + | | + +--------+ +--------+ +--------+ - - - - - - - - - - - - + Vertical | | | | | | | | ^ + Stack 2 | | | | | |<--> (positive violation) | stack size range + | | | | | | | | v + +--------+ +--------+ +--------+ - - - - - - - - - - - - + | |<------> (negative violation) + +--------+ +--------+ +--------+ +---------+ +-----------+ - - - + Vertical | | | | | | | | | | | | ^ + Stack 3 | | | | | | | | | | | stack size range + | | | | | | | | | | | | v + +--------+ +--------+ +--------+ +---------+ +-----------+ - - - + + @param crossDimensionSum the consumed length of the lines in the stack along the cross dimension + @param style layout style to be applied to all children + @param sizeRange the range of allowable sizes for the stack layout spec + */ +CGFloat ASStackUnpositionedLayout::computeCrossViolation(const CGFloat crossDimensionSum, + const ASStackLayoutSpecStyle &style, + const ASSizeRange &sizeRange) +{ + const CGFloat minCrossDimension = crossDimension(style.direction, sizeRange.min); + const CGFloat maxCrossDimension = crossDimension(style.direction, sizeRange.max); + if (crossDimensionSum < minCrossDimension) { + return minCrossDimension - crossDimensionSum; + } else if (crossDimensionSum > maxCrossDimension) { + return maxCrossDimension - crossDimensionSum; + } + return 0; +} + +/** + Stretches children to lay out along the cross axis according to the alignment stretch settings of the children + (child.alignSelf), and the stack layout's alignment settings (style.alignItems). This does not do the actual alignment + of the items once stretched though; ASStackPositionedLayout will do centering etc. + + Finds the maximum cross dimension among child layouts. If that dimension exceeds the minimum cross layout size then + we must stretch any children whose alignItems specify ASStackLayoutAlignItemsStretch. + + The diagram below shows 3 children in a horizontal stack. The second child is larger than the minCrossDimension, so + its height is used as the childCrossMax. Any children that are stretchable (which may be all children if + style.alignItems specifies stretch) like the first child must be stretched to match that maximum. All children must be + at least minCrossDimension in cross dimension size, which is shown by the sizing of the third child. + + Stack Dimension + +---------------------> + + +-+-------------+-+-------------+--+---------------+ + + + + | | child. | | | | | | | | + | | alignSelf | | | | | | | | + Cross | | = stretch | | | +-------+-------+ | | | + Dimension | +-----+-------+ | | | | | | | | + | | | | | | | | | | + | | | | | v | | | | + v +-+- - - - - - -+-+ - - - - - - +--+- - - - - - - -+ | | + minCrossDimension + | | | | | + | v | | | | | + +- - - - - - -+ +-------------+ | + childCrossMax + | + +--------------------------------------------------+ + crossMax + + @param items pre-computed items; modified in-place as needed + @param style the layout style of the overall stack layout + */ +static void stretchItemsAlongCrossDimension(std::vector &items, + const ASStackLayoutSpecStyle &style, + const BOOL concurrent, + const CGSize parentSize, + const CGFloat crossSize) +{ + dispatchApplyIfNeeded(items.size(), concurrent, ^(size_t i) { + auto &item = items[i]; + const ASStackLayoutAlignItems alignItems = alignment(item.child.style.alignSelf, style.alignItems); + if (alignItems == ASStackLayoutAlignItemsStretch) { + const CGFloat cross = crossDimension(style.direction, item.layout.size); + const CGFloat stack = stackDimension(style.direction, item.layout.size); + const CGFloat violation = crossSize - cross; + + // Only stretch if violation is positive. Compare against kViolationEpsilon here to avoid stretching against a tiny violation. + if (violation > kViolationEpsilon) { + item.layout = crossChildLayout(item.child, style, stack, stack, crossSize, crossSize, parentSize); + } + } + }); +} + +/** + * Stretch lines and their items according to alignContent, alignItems and alignSelf. + * https://www.w3.org/TR/css-flexbox-1/#algo-line-stretch + * https://www.w3.org/TR/css-flexbox-1/#algo-stretch + */ +static void stretchLinesAlongCrossDimension(std::vector &lines, + const ASStackLayoutSpecStyle &style, + const BOOL concurrent, + const ASSizeRange &sizeRange, + const CGSize parentSize) +{ + ASDisplayNodeCAssertFalse(lines.empty()); + const std::size_t numOfLines = lines.size(); + const CGFloat violation = ASStackUnpositionedLayout::computeCrossViolation(computeLinesCrossDimensionSum(lines, style), style, sizeRange); + // Don't stretch if the stack is single line, because the line's cross size was clamped against the stack's constrained size. + const BOOL shouldStretchLines = (numOfLines > 1 + && style.alignContent == ASStackLayoutAlignContentStretch + && violation > kViolationEpsilon); + + CGFloat extraCrossSizePerLine = violation / numOfLines; + for (auto &line : lines) { + if (shouldStretchLines) { + line.crossSize += extraCrossSizePerLine; + } + + stretchItemsAlongCrossDimension(line.items, style, concurrent, parentSize, line.crossSize); + } +} + +static BOOL itemIsBaselineAligned(const ASStackLayoutSpecStyle &style, + const ASStackLayoutSpecItem &l) +{ + ASStackLayoutAlignItems alignItems = alignment(l.child.style.alignSelf, style.alignItems); + return alignItems == ASStackLayoutAlignItemsBaselineFirst || alignItems == ASStackLayoutAlignItemsBaselineLast; +} + +CGFloat ASStackUnpositionedLayout::baselineForItem(const ASStackLayoutSpecStyle &style, + const ASStackLayoutSpecItem &item) +{ + switch (alignment(item.child.style.alignSelf, style.alignItems)) { + case ASStackLayoutAlignItemsBaselineFirst: + return item.child.style.ascender; + case ASStackLayoutAlignItemsBaselineLast: + return crossDimension(style.direction, item.layout.size) + item.child.style.descender; + default: + return 0; + } +} + +/** + * Computes cross size and baseline of each line. + * https://www.w3.org/TR/css-flexbox-1/#algo-cross-line + * + * @param lines All items to lay out + * @param style the layout style of the overall stack layout + * @param sizeRange the range of allowable sizes for the stack layout component + */ +static void computeLinesCrossSizeAndBaseline(std::vector &lines, + const ASStackLayoutSpecStyle &style, + const ASSizeRange &sizeRange) +{ + ASDisplayNodeCAssertFalse(lines.empty()); + const BOOL isSingleLine = (lines.size() == 1); + + const auto minCrossSize = crossDimension(style.direction, sizeRange.min); + const auto maxCrossSize = crossDimension(style.direction, sizeRange.max); + const BOOL definiteCrossSize = (minCrossSize == maxCrossSize); + + // If the stack is single-line and has a definite cross size, the cross size of the line is the stack's definite cross size. + if (isSingleLine && definiteCrossSize) { + auto &line = lines[0]; + line.crossSize = minCrossSize; + + // We still need to determine the line's baseline + //TODO unit test + for (const auto &item : line.items) { + if (itemIsBaselineAligned(style, item)) { + CGFloat baseline = ASStackUnpositionedLayout::baselineForItem(style, item); + line.baseline = MAX(line.baseline, baseline); + } + } + + return; + } + + for (auto &line : lines) { + const auto &items = line.items; + CGFloat maxStartToBaselineDistance = 0; + CGFloat maxBaselineToEndDistance = 0; + CGFloat maxItemCrossSize = 0; + + for (const auto &item : items) { + if (itemIsBaselineAligned(style, item)) { + // Step 1. Collect all the items whose align-self is baseline. Find the largest of the distances + // between each item’s baseline and its hypothetical outer cross-start edge (aka. its baseline value), + // and the largest of the distances between each item’s baseline and its hypothetical outer cross-end edge, + // and sum these two values. + CGFloat baseline = ASStackUnpositionedLayout::baselineForItem(style, item); + maxStartToBaselineDistance = MAX(maxStartToBaselineDistance, baseline); + maxBaselineToEndDistance = MAX(maxBaselineToEndDistance, crossDimension(style.direction, item.layout.size) - baseline); + } else { + // Step 2. Among all the items not collected by the previous step, find the largest outer hypothetical cross size. + maxItemCrossSize = MAX(maxItemCrossSize, crossDimension(style.direction, item.layout.size)); + } + } + + // Step 3. The used cross-size of the flex line is the largest of the numbers found in the previous two steps and zero. + line.crossSize = MAX(maxStartToBaselineDistance + maxBaselineToEndDistance, maxItemCrossSize); + if (isSingleLine) { + // If the stack is single-line, then clamp the line’s cross-size to be within the stack's min and max cross-size properties. + line.crossSize = MIN(MAX(minCrossSize, line.crossSize), maxCrossSize); + } + + line.baseline = maxStartToBaselineDistance; + } +} + +/** + Returns a lambda that computes the relevant flex factor based on the given violation. + @param violation The amount that the stack layout violates its size range. See header for sign interpretation. + */ +static std::function flexFactorInViolationDirection(const CGFloat violation) +{ + if (std::fabs(violation) < kViolationEpsilon) { + return [](const ASStackLayoutSpecItem &item) { return 0.0; }; + } else if (violation > 0) { + return [](const ASStackLayoutSpecItem &item) { return item.child.style.flexGrow; }; + } else { + return [](const ASStackLayoutSpecItem &item) { return item.child.style.flexShrink; }; + } +} + +static inline CGFloat scaledFlexShrinkFactor(const ASStackLayoutSpecItem &item, + const ASStackLayoutSpecStyle &style, + const CGFloat flexFactorSum) +{ + return stackDimension(style.direction, item.layout.size) * (item.child.style.flexShrink / flexFactorSum); +} + +/** + Returns a lambda that computes a flex shrink adjustment for a given item based on the provided violation. + @param items The unpositioned items from the original unconstrained layout pass. + @param style The layout style to be applied to all children. + @param violation The amount that the stack layout violates its size range. + @param flexFactorSum The sum of each item's flex factor as determined by the provided violation. + @return A lambda capable of computing the flex shrink adjustment, if any, for a particular item. + */ +static std::function flexShrinkAdjustment(const std::vector &items, + const ASStackLayoutSpecStyle &style, + const CGFloat violation, + const CGFloat flexFactorSum) +{ + const CGFloat scaledFlexShrinkFactorSum = std::accumulate(items.begin(), items.end(), 0.0, [&](CGFloat x, const ASStackLayoutSpecItem &item) { + return x + scaledFlexShrinkFactor(item, style, flexFactorSum); + }); + return [style, scaledFlexShrinkFactorSum, violation, flexFactorSum](const ASStackLayoutSpecItem &item) { + if (scaledFlexShrinkFactorSum == 0.0) { + return (CGFloat)0.0; + } + + const CGFloat scaledFlexShrinkFactorRatio = scaledFlexShrinkFactor(item, style, flexFactorSum) / scaledFlexShrinkFactorSum; + // The item should shrink proportionally to the scaled flex shrink factor ratio computed above. + // Unlike the flex grow adjustment the flex shrink adjustment needs to take the size of each item into account. + return -std::fabs(scaledFlexShrinkFactorRatio * violation); + }; +} + +/** + Returns a lambda that computes a flex grow adjustment for a given item based on the provided violation. + @param items The unpositioned items from the original unconstrained layout pass. + @param violation The amount that the stack layout violates its size range. + @param flexFactorSum The sum of each item's flex factor as determined by the provided violation. + @return A lambda capable of computing the flex grow adjustment, if any, for a particular item. + */ +static std::function flexGrowAdjustment(const std::vector &items, + const CGFloat violation, + const CGFloat flexFactorSum) +{ + // To compute the flex grow adjustment distribute the violation proportionally based on each item's flex grow factor. + return [violation, flexFactorSum](const ASStackLayoutSpecItem &item) { + return std::floor(violation * (item.child.style.flexGrow / flexFactorSum)); + }; +} + +/** + Returns a lambda that computes a flex adjustment for a given item based on the provided violation. + @param items The unpositioned items from the original unconstrained layout pass. + @param style The layout style to be applied to all children. + @param violation The amount that the stack layout violates its size range. + @param flexFactorSum The sum of each item's flex factor as determined by the provided violation. + @return A lambda capable of computing the flex adjustment for a particular item. + */ +static std::function flexAdjustmentInViolationDirection(const std::vector &items, + const ASStackLayoutSpecStyle &style, + const CGFloat violation, + const CGFloat flexFactorSum) +{ + if (violation > 0) { + return flexGrowAdjustment(items, violation, flexFactorSum); + } else { + return flexShrinkAdjustment(items, style, violation, flexFactorSum); + } +} + +ASDISPLAYNODE_INLINE BOOL isFlexibleInBothDirections(const ASStackLayoutSpecChild &child) +{ + return child.style.flexGrow > 0 && child.style.flexShrink > 0; +} + +/** + The flexible children may have been left not laid out in the initial layout pass, so we may have to go through and size + these children at zero size so that the children layouts are at least present. + */ +static void layoutFlexibleChildrenAtZeroSize(std::vector &items, + const ASStackLayoutSpecStyle &style, + const BOOL concurrent, + const ASSizeRange &sizeRange, + const CGSize parentSize) +{ + dispatchApplyIfNeeded(items.size(), concurrent, ^(size_t i) { + auto &item = items[i]; + if (isFlexibleInBothDirections(item.child)) { + item.layout = crossChildLayout(item.child, + style, + 0, + 0, + crossDimension(style.direction, sizeRange.min), + crossDimension(style.direction, sizeRange.max), + parentSize); + } + }); +} + +/** + Computes the consumed stack dimension length for the given vector of items and stacking style. + + stackDimensionSum + <-----------------------> + +-----+ +-------+ +---+ + | | | | | | + | | | | | | + +-----+ | | +---+ + +-------+ + + @param items unpositioned layouts for items + @param style the layout style of the overall stack layout + */ +static CGFloat computeItemsStackDimensionSum(const std::vector &items, + const ASStackLayoutSpecStyle &style) +{ + // Sum up the children's spacing + const CGFloat childSpacingSum = std::accumulate(items.begin(), items.end(), + // Start from default spacing between each child: + items.empty() ? 0 : style.spacing * (items.size() - 1), + [&](CGFloat x, const ASStackLayoutSpecItem &l) { + return x + l.child.style.spacingBefore + l.child.style.spacingAfter; + }); + + // Sum up the children's dimensions (including spacing) in the stack direction. + const CGFloat childStackDimensionSum = std::accumulate(items.begin(), items.end(), + childSpacingSum, + [&](CGFloat x, const ASStackLayoutSpecItem &l) { + return x + stackDimension(style.direction, l.layout.size); + }); + return childStackDimensionSum; +} + +//TODO move this up near computeCrossViolation and make both methods share the same code path, to make sure they share the same concept of "negative" and "positive" violations. +/** + Computes the violation by comparing a stack dimension sum with the overall allowable size range for the stack. + + Violation is the distance you would have to add to the unbounded stack-direction length of the stack spec's + children in order to bring the stack within its allowed sizeRange. The diagram below shows 3 horizontal stacks with + the different types of violation. + + sizeRange + |------------| + +------+ +-------+ +-------+ +---------+ + | | | | | | | | | | + | | | | | | | | (zero violation) + | | | | | | | | | | + +------+ +-------+ +-------+ +---------+ + | | + +------+ +-------+ +-------+ + | | | | | | | | + | | | | | |<--> (positive violation) + | | | | | | | | + +------+ +-------+ +-------+ + | |<------> (negative violation) + +------+ +-------+ +-------+ +---------+ +-----------+ + | | | | | | | | | | | | + | | | | | | | | | | + | | | | | | | | | | | | + +------+ +-------+ +-------+ +---------+ +-----------+ + + @param stackDimensionSum the consumed length of the children in the stack along the stack dimension + @param style layout style to be applied to all children + @param sizeRange the range of allowable sizes for the stack layout spec + */ +CGFloat ASStackUnpositionedLayout::computeStackViolation(const CGFloat stackDimensionSum, + const ASStackLayoutSpecStyle &style, + const ASSizeRange &sizeRange) +{ + const CGFloat minStackDimension = stackDimension(style.direction, sizeRange.min); + const CGFloat maxStackDimension = stackDimension(style.direction, sizeRange.max); + if (stackDimensionSum < minStackDimension) { + return minStackDimension - stackDimensionSum; + } else if (stackDimensionSum > maxStackDimension) { + return maxStackDimension - stackDimensionSum; + } + return 0; +} + +/** + If we have a single flexible (both shrinkable and growable) child, and our allowed size range is set to a specific + number then we may avoid the first "intrinsic" size calculation. + */ +ASDISPLAYNODE_INLINE BOOL useOptimizedFlexing(const std::vector &children, + const ASStackLayoutSpecStyle &style, + const ASSizeRange &sizeRange) +{ + const NSUInteger flexibleChildren = std::count_if(children.begin(), children.end(), isFlexibleInBothDirections); + return ((flexibleChildren == 1) + && (stackDimension(style.direction, sizeRange.min) == + stackDimension(style.direction, sizeRange.max))); +} + +/** + Flexes children in the stack axis to resolve a min or max stack size violation. First, determines which children are + flexible (see computeStackViolation and isFlexibleInViolationDirection). Then computes how much to flex each flexible child + and performs re-layout. Note that there may still be a non-zero violation even after flexing. + + The actual CSS flexbox spec describes an iterative looping algorithm here, which may be adopted in t5837937: + http://www.w3.org/TR/css3-flexbox/#resolve-flexible-lengths + + @param lines reference to unpositioned lines and items from the original, unconstrained layout pass; modified in-place + @param style layout style to be applied to all children + @param sizeRange the range of allowable sizes for the stack layout component + @param parentSize Size of the stack layout component. May be undefined in either or both directions. + */ +static void flexLinesAlongStackDimension(std::vector &lines, + const ASStackLayoutSpecStyle &style, + const BOOL concurrent, + const ASSizeRange &sizeRange, + const CGSize parentSize, + const BOOL useOptimizedFlexing) +{ + for (auto &line : lines) { + auto &items = line.items; + const CGFloat violation = ASStackUnpositionedLayout::computeStackViolation(computeItemsStackDimensionSum(items, style), style, sizeRange); + std::function flexFactor = flexFactorInViolationDirection(violation); + // The flex factor sum is needed to determine if flexing is necessary. + // This value is also needed if the violation is positive and flexible items need to grow, so keep it around. + const CGFloat flexFactorSum = std::accumulate(items.begin(), items.end(), 0.0, [&](CGFloat x, const ASStackLayoutSpecItem &item) { + return x + flexFactor(item); + }); + + // If no items are able to flex then there is nothing left to do with this line. Bail. + if (flexFactorSum == 0) { + // If optimized flexing was used then we have to clean up the unsized items and lay them out at zero size. + if (useOptimizedFlexing) { + layoutFlexibleChildrenAtZeroSize(items, style, concurrent, sizeRange, parentSize); + } + continue; + } + + std::function flexAdjustment = flexAdjustmentInViolationDirection(items, + style, + violation, + flexFactorSum); + // Compute any remaining violation to the first flexible item. + const CGFloat remainingViolation = std::accumulate(items.begin(), items.end(), violation, [&](CGFloat x, const ASStackLayoutSpecItem &item) { + return x - flexAdjustment(item); + }); + + size_t firstFlexItem = -1; + for(size_t i = 0; i < items.size(); i++) { + // Items are consider inflexible if they do not need to make a flex adjustment. + if (flexAdjustment(items[i]) != 0) { + firstFlexItem = i; + break; + } + } + if (firstFlexItem == -1) { + continue; + } + + dispatchApplyIfNeeded(items.size(), concurrent, ^(size_t i) { + auto &item = items[i]; + const CGFloat currentFlexAdjustment = flexAdjustment(item); + // Items are consider inflexible if they do not need to make a flex adjustment. + if (currentFlexAdjustment != 0) { + const CGFloat originalStackSize = stackDimension(style.direction, item.layout.size); + // Only apply the remaining violation for the first flexible item that has a flex grow factor. + const CGFloat flexedStackSize = originalStackSize + currentFlexAdjustment + (i == firstFlexItem && item.child.style.flexGrow > 0 ? remainingViolation : 0); + item.layout = crossChildLayout(item.child, + style, + MAX(flexedStackSize, 0), + MAX(flexedStackSize, 0), + crossDimension(style.direction, sizeRange.min), + crossDimension(style.direction, sizeRange.max), + parentSize); + } + }); + } +} + +/** + https://www.w3.org/TR/css-flexbox-1/#algo-line-break + */ +static std::vector collectChildrenIntoLines(const std::vector &items, + const ASStackLayoutSpecStyle &style, + const ASSizeRange &sizeRange) +{ + //TODO if infinite max stack size, fast path + if (style.flexWrap == ASStackLayoutFlexWrapNoWrap) { + return std::vector (1, {.items = std::move(items)}); + } + + std::vector lines; + std::vector lineItems; + CGFloat lineStackDimensionSum = 0; + CGFloat interitemSpacing = 0; + + for(auto it = items.begin(); it != items.end(); ++it) { + const auto &item = *it; + const CGFloat itemStackDimension = stackDimension(style.direction, item.layout.size); + const CGFloat itemAndSpacingStackDimension = item.child.style.spacingBefore + itemStackDimension + item.child.style.spacingAfter; + const BOOL negativeViolationIfAddItem = (ASStackUnpositionedLayout::computeStackViolation(lineStackDimensionSum + interitemSpacing + itemAndSpacingStackDimension, style, sizeRange) < 0); + const BOOL breakCurrentLine = negativeViolationIfAddItem && !lineItems.empty(); + + if (breakCurrentLine) { + lines.push_back({.items = std::vector (lineItems)}); + lineItems.clear(); + lineStackDimensionSum = 0; + interitemSpacing = 0; + } + + lineItems.push_back(std::move(item)); + lineStackDimensionSum += interitemSpacing + itemAndSpacingStackDimension; + interitemSpacing = style.spacing; + } + + // Handle last line + lines.push_back({.items = std::vector (lineItems)}); + + return lines; +} + +/** + Performs the first unconstrained layout of the children, generating the unpositioned items that are then flexed and + stretched. + */ +static void layoutItemsAlongUnconstrainedStackDimension(std::vector &items, + const ASStackLayoutSpecStyle &style, + const BOOL concurrent, + const ASSizeRange &sizeRange, + const CGSize parentSize, + const BOOL useOptimizedFlexing) +{ + const CGFloat minCrossDimension = crossDimension(style.direction, sizeRange.min); + const CGFloat maxCrossDimension = crossDimension(style.direction, sizeRange.max); + + dispatchApplyIfNeeded(items.size(), concurrent, ^(size_t i) { + auto &item = items[i]; + if (useOptimizedFlexing && isFlexibleInBothDirections(item.child)) { + item.layout = [ASLayout layoutWithLayoutElement:item.child.element size:{0, 0}]; + } else { + item.layout = crossChildLayout(item.child, + style, + ASDimensionResolve(item.child.style.flexBasis, stackDimension(style.direction, parentSize), 0), + ASDimensionResolve(item.child.style.flexBasis, stackDimension(style.direction, parentSize), INFINITY), + minCrossDimension, + maxCrossDimension, + parentSize); + } + }); +} + +ASStackUnpositionedLayout ASStackUnpositionedLayout::compute(const std::vector &children, + const ASStackLayoutSpecStyle &style, + const ASSizeRange &sizeRange, + const BOOL concurrent) +{ + if (children.empty()) { + return {}; + } + + // If we have a fixed size in either dimension, pass it to children so they can resolve percentages against it. + // Otherwise, we pass ASLayoutElementParentDimensionUndefined since it will depend on the content. + const CGSize parentSize = { + (sizeRange.min.width == sizeRange.max.width) ? sizeRange.min.width : ASLayoutElementParentDimensionUndefined, + (sizeRange.min.height == sizeRange.max.height) ? sizeRange.min.height : ASLayoutElementParentDimensionUndefined, + }; + + // We may be able to avoid some redundant layout passes + const BOOL optimizedFlexing = useOptimizedFlexing(children, style, sizeRange); + + std::vector items = AS::map(children, [&](const ASStackLayoutSpecChild &child) -> ASStackLayoutSpecItem { + return {child, nil}; + }); + + // We do a first pass of all the children, generating an unpositioned layout for each with an unbounded range along + // the stack dimension. This allows us to compute the "intrinsic" size of each child and find the available violation + // which determines whether we must grow or shrink the flexible children. + layoutItemsAlongUnconstrainedStackDimension(items, + style, + concurrent, + sizeRange, + parentSize, + optimizedFlexing); + + // Collect items into lines (https://www.w3.org/TR/css-flexbox-1/#algo-line-break) + std::vector lines = collectChildrenIntoLines(items, style, sizeRange); + + // Resolve the flexible lengths (https://www.w3.org/TR/css-flexbox-1/#resolve-flexible-lengths) + flexLinesAlongStackDimension(lines, style, concurrent, sizeRange, parentSize, optimizedFlexing); + + // Calculate the cross size of each flex line (https://www.w3.org/TR/css-flexbox-1/#algo-cross-line) + computeLinesCrossSizeAndBaseline(lines, style, sizeRange); + + // Handle 'align-content: stretch' (https://www.w3.org/TR/css-flexbox-1/#algo-line-stretch) + // Determine the used cross size of each item (https://www.w3.org/TR/css-flexbox-1/#algo-stretch) + stretchLinesAlongCrossDimension(lines, style, concurrent, sizeRange, parentSize); + + // Compute stack dimension sum of each line and the whole stack + CGFloat layoutStackDimensionSum = 0; + for (auto &line : lines) { + line.stackDimensionSum = computeItemsStackDimensionSum(line.items, style); + // layoutStackDimensionSum is the max stackDimensionSum among all lines + layoutStackDimensionSum = MAX(line.stackDimensionSum, layoutStackDimensionSum); + } + // Compute cross dimension sum of the stack. + // This should be done before `lines` are moved to a new ASStackUnpositionedLayout struct (i.e `std::move(lines)`) + CGFloat layoutCrossDimensionSum = computeLinesCrossDimensionSum(lines, style); + + return {.lines = std::move(lines), .stackDimensionSum = layoutStackDimensionSum, .crossDimensionSum = layoutCrossDimensionSum}; +} diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/Component/ASTextDebugOption.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/Component/ASTextDebugOption.h new file mode 100644 index 0000000..efa7808 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/Component/ASTextDebugOption.h @@ -0,0 +1,92 @@ +// +// ASTextDebugOption.h +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +@class ASTextDebugOption; + +NS_ASSUME_NONNULL_BEGIN + +/** + The ASTextDebugTarget protocol defines the method a debug target should implement. + A debug target can be add to the global container to receive the shared debug + option changed notification. + */ +@protocol ASTextDebugTarget + +@required +/** + When the shared debug option changed, this method would be called on main thread. + It should return as quickly as possible. The option's property should not be changed + in this method. + + @param option The shared debug option. + */ +- (void)setDebugOption:(nullable ASTextDebugOption *)option; +@end + + + +/** + The debug option for ASText. + */ +@interface ASTextDebugOption : NSObject +@property (nullable, nonatomic) UIColor *baselineColor; ///< baseline color +@property (nullable, nonatomic) UIColor *CTFrameBorderColor; ///< CTFrame path border color +@property (nullable, nonatomic) UIColor *CTFrameFillColor; ///< CTFrame path fill color +@property (nullable, nonatomic) UIColor *CTLineBorderColor; ///< CTLine bounds border color +@property (nullable, nonatomic) UIColor *CTLineFillColor; ///< CTLine bounds fill color +@property (nullable, nonatomic) UIColor *CTLineNumberColor; ///< CTLine line number color +@property (nullable, nonatomic) UIColor *CTRunBorderColor; ///< CTRun bounds border color +@property (nullable, nonatomic) UIColor *CTRunFillColor; ///< CTRun bounds fill color +@property (nullable, nonatomic) UIColor *CTRunNumberColor; ///< CTRun number color +@property (nullable, nonatomic) UIColor *CGGlyphBorderColor; ///< CGGlyph bounds border color +@property (nullable, nonatomic) UIColor *CGGlyphFillColor; ///< CGGlyph bounds fill color + +- (BOOL)needDrawDebug; ///< `YES`: at least one debug color is visible. `NO`: all debug color is invisible/nil. +- (void)clear; ///< Set all debug color to nil. + +/** + Add a debug target. + + @discussion When `setSharedDebugOption:` is called, all added debug target will + receive `setDebugOption:` in main thread. It maintains an unsafe_unretained + reference to this target. The target must to removed before dealloc. + + @param target A debug target. + */ ++ (void)addDebugTarget:(id)target; + +/** + Remove a debug target which is added by `addDebugTarget:`. + + @param target A debug target. + */ ++ (void)removeDebugTarget:(id)target; + +/** + Returns the shared debug option. + + @return The shared debug option, default is nil. + */ ++ (nullable ASTextDebugOption *)sharedDebugOption; + +/** + Set a debug option as shared debug option. + This method must be called on main thread. + + @discussion When call this method, the new option will set to all debug target + which is added by `addDebugTarget:`. + + @param option A new debug option (nil is valid). + */ ++ (void)setSharedDebugOption:(nullable ASTextDebugOption *)option; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/Component/ASTextDebugOption.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/Component/ASTextDebugOption.mm new file mode 100644 index 0000000..2565b90 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/Component/ASTextDebugOption.mm @@ -0,0 +1,135 @@ +// +// ASTextDebugOption.mm +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import "ASTextDebugOption.h" +#import + +static pthread_mutex_t _sharedDebugLock; +static CFMutableSetRef _sharedDebugTargets = nil; +static ASTextDebugOption *_sharedDebugOption = nil; + +static const void* _as_sharedDebugSetRetain(CFAllocatorRef allocator, const void *value) { + return value; +} + +static void _as_sharedDebugSetRelease(CFAllocatorRef allocator, const void *value) { +} + +void _as_sharedDebugSetFunction(const void *value, void *context) { + id target = (__bridge id)(value); + [target setDebugOption:_sharedDebugOption]; +} + +static void _initSharedDebug() { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + pthread_mutex_init(&_sharedDebugLock, NULL); + CFSetCallBacks callbacks = kCFTypeSetCallBacks; + callbacks.retain = _as_sharedDebugSetRetain; + callbacks.release = _as_sharedDebugSetRelease; + _sharedDebugTargets = CFSetCreateMutable(CFAllocatorGetDefault(), 0, &callbacks); + }); +} + +static void _setSharedDebugOption(ASTextDebugOption *option) { + _initSharedDebug(); + pthread_mutex_lock(&_sharedDebugLock); + _sharedDebugOption = option.copy; + CFSetApplyFunction(_sharedDebugTargets, _as_sharedDebugSetFunction, NULL); + pthread_mutex_unlock(&_sharedDebugLock); +} + +static ASTextDebugOption *_getSharedDebugOption() { + _initSharedDebug(); + pthread_mutex_lock(&_sharedDebugLock); + ASTextDebugOption *op = _sharedDebugOption; + pthread_mutex_unlock(&_sharedDebugLock); + return op; +} + +static void _addDebugTarget(id target) { + _initSharedDebug(); + pthread_mutex_lock(&_sharedDebugLock); + CFSetAddValue(_sharedDebugTargets, (__bridge const void *)(target)); + pthread_mutex_unlock(&_sharedDebugLock); +} + +static void _removeDebugTarget(id target) { + _initSharedDebug(); + pthread_mutex_lock(&_sharedDebugLock); + CFSetRemoveValue(_sharedDebugTargets, (__bridge const void *)(target)); + pthread_mutex_unlock(&_sharedDebugLock); +} + + +@implementation ASTextDebugOption + +- (id)copyWithZone:(NSZone *)zone { + ASTextDebugOption *op = [self.class new]; + op.baselineColor = self.baselineColor; + op.CTFrameBorderColor = self.CTFrameBorderColor; + op.CTFrameFillColor = self.CTFrameFillColor; + op.CTLineBorderColor = self.CTLineBorderColor; + op.CTLineFillColor = self.CTLineFillColor; + op.CTLineNumberColor = self.CTLineNumberColor; + op.CTRunBorderColor = self.CTRunBorderColor; + op.CTRunFillColor = self.CTRunFillColor; + op.CTRunNumberColor = self.CTRunNumberColor; + op.CGGlyphBorderColor = self.CGGlyphBorderColor; + op.CGGlyphFillColor = self.CGGlyphFillColor; + return op; +} + +- (BOOL)needDrawDebug { + if (self.baselineColor || + self.CTFrameBorderColor || + self.CTFrameFillColor || + self.CTLineBorderColor || + self.CTLineFillColor || + self.CTLineNumberColor || + self.CTRunBorderColor || + self.CTRunFillColor || + self.CTRunNumberColor || + self.CGGlyphBorderColor || + self.CGGlyphFillColor) return YES; + return NO; +} + +- (void)clear { + self.baselineColor = nil; + self.CTFrameBorderColor = nil; + self.CTFrameFillColor = nil; + self.CTLineBorderColor = nil; + self.CTLineFillColor = nil; + self.CTLineNumberColor = nil; + self.CTRunBorderColor = nil; + self.CTRunFillColor = nil; + self.CTRunNumberColor = nil; + self.CGGlyphBorderColor = nil; + self.CGGlyphFillColor = nil; +} + ++ (void)addDebugTarget:(id)target { + if (target) _addDebugTarget(target); +} + ++ (void)removeDebugTarget:(id)target { + if (target) _removeDebugTarget(target); +} + ++ (ASTextDebugOption *)sharedDebugOption { + return _getSharedDebugOption(); +} + ++ (void)setSharedDebugOption:(ASTextDebugOption *)option { + NSAssert([NSThread isMainThread], @"This method must be called on the main thread"); + _setSharedDebugOption(option); +} + +@end + diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/Component/ASTextInput.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/Component/ASTextInput.h new file mode 100644 index 0000000..9a6cbd1 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/Component/ASTextInput.h @@ -0,0 +1,85 @@ +// +// ASTextInput.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + Text position affinity. For example, the offset appears after the last + character on a line is backward affinity, before the first character on + the following line is forward affinity. + */ +typedef NS_ENUM(NSInteger, ASTextAffinity) { + ASTextAffinityForward = 0, ///< offset appears before the character + ASTextAffinityBackward = 1, ///< offset appears after the character +}; + + +/** + A ASTextPosition object represents a position in a text container; in other words, + it is an index into the backing string in a text-displaying view. + + ASTextPosition has the same API as Apple's implementation in UITextView/UITextField, + so you can alse use it to interact with UITextView/UITextField. + */ +@interface ASTextPosition : UITextPosition + +@property (nonatomic, readonly) NSInteger offset; +@property (nonatomic, readonly) ASTextAffinity affinity; + ++ (instancetype)positionWithOffset:(NSInteger)offset NS_RETURNS_RETAINED; ++ (instancetype)positionWithOffset:(NSInteger)offset affinity:(ASTextAffinity) affinity NS_RETURNS_RETAINED; + +- (NSComparisonResult)compare:(id)otherPosition; + +@end + + +/** + A ASTextRange object represents a range of characters in a text container; in other words, + it identifies a starting index and an ending index in string backing a text-displaying view. + + ASTextRange has the same API as Apple's implementation in UITextView/UITextField, + so you can alse use it to interact with UITextView/UITextField. + */ +@interface ASTextRange : UITextRange + +@property (nonatomic, readonly) ASTextPosition *start; +@property (nonatomic, readonly) ASTextPosition *end; +@property (nonatomic, readonly, getter=isEmpty) BOOL empty; + ++ (instancetype)rangeWithRange:(NSRange)range NS_RETURNS_RETAINED; ++ (instancetype)rangeWithRange:(NSRange)range affinity:(ASTextAffinity) affinity NS_RETURNS_RETAINED; ++ (instancetype)rangeWithStart:(ASTextPosition *)start end:(ASTextPosition *)end NS_RETURNS_RETAINED; ++ (instancetype)defaultRange NS_RETURNS_RETAINED; ///< <{0,0} Forward> + +- (NSRange)asRange; + +@end + + +/** + A ASTextSelectionRect object encapsulates information about a selected range of + text in a text-displaying view. + + ASTextSelectionRect has the same API as Apple's implementation in UITextView/UITextField, + so you can alse use it to interact with UITextView/UITextField. + */ +@interface ASTextSelectionRect : UITextSelectionRect + +@property (nonatomic) CGRect rect; +@property (nonatomic) UITextWritingDirection writingDirection; +@property (nonatomic) BOOL containsStart; +@property (nonatomic) BOOL containsEnd; +@property (nonatomic) BOOL isVertical; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/Component/ASTextInput.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/Component/ASTextInput.mm new file mode 100644 index 0000000..1cdfe73 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/Component/ASTextInput.mm @@ -0,0 +1,150 @@ +// +// ASTextInput.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + + +@implementation ASTextPosition + ++ (instancetype)positionWithOffset:(NSInteger)offset NS_RETURNS_RETAINED { + return [self positionWithOffset:offset affinity:ASTextAffinityForward]; +} + ++ (instancetype)positionWithOffset:(NSInteger)offset affinity:(ASTextAffinity)affinity NS_RETURNS_RETAINED { + ASTextPosition *p = [self new]; + p->_offset = offset; + p->_affinity = affinity; + return p; +} + +- (instancetype)copyWithZone:(NSZone *)zone { + return [self.class positionWithOffset:_offset affinity:_affinity]; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"<%@: %p> (%@%@)", self.class, self, @(_offset), _affinity == ASTextAffinityForward ? @"F":@"B"]; +} + +- (NSUInteger)hash { + return _offset * 2 + (_affinity == ASTextAffinityForward ? 1 : 0); +} + +- (BOOL)isEqual:(ASTextPosition *)object { + if (!object) return NO; + return _offset == object.offset && _affinity == object.affinity; +} + +- (NSComparisonResult)compare:(ASTextPosition *)otherPosition { + if (!otherPosition) return NSOrderedAscending; + if (_offset < otherPosition.offset) return NSOrderedAscending; + if (_offset > otherPosition.offset) return NSOrderedDescending; + if (_affinity == ASTextAffinityBackward && otherPosition.affinity == ASTextAffinityForward) return NSOrderedAscending; + if (_affinity == ASTextAffinityForward && otherPosition.affinity == ASTextAffinityBackward) return NSOrderedDescending; + return NSOrderedSame; +} + +@end + + + +@implementation ASTextRange { + ASTextPosition *_start; + ASTextPosition *_end; +} + +- (instancetype)init { + self = [super init]; + if (!self) return nil; + _start = [ASTextPosition positionWithOffset:0]; + _end = [ASTextPosition positionWithOffset:0]; + return self; +} + +- (ASTextPosition *)start { + return _start; +} + +- (ASTextPosition *)end { + return _end; +} + +- (BOOL)isEmpty { + return _start.offset == _end.offset; +} + +- (NSRange)asRange { + return NSMakeRange(_start.offset, _end.offset - _start.offset); +} + ++ (instancetype)rangeWithRange:(NSRange)range NS_RETURNS_RETAINED { + return [self rangeWithRange:range affinity:ASTextAffinityForward]; +} + ++ (instancetype)rangeWithRange:(NSRange)range affinity:(ASTextAffinity)affinity NS_RETURNS_RETAINED { + ASTextPosition *start = [ASTextPosition positionWithOffset:range.location affinity:affinity]; + ASTextPosition *end = [ASTextPosition positionWithOffset:range.location + range.length affinity:affinity]; + return [self rangeWithStart:start end:end]; +} + ++ (instancetype)rangeWithStart:(ASTextPosition *)start end:(ASTextPosition *)end NS_RETURNS_RETAINED { + if (!start || !end) return nil; + if ([start compare:end] == NSOrderedDescending) { + ASTEXT_SWAP(start, end); + } + ASTextRange *range = [ASTextRange new]; + range->_start = start; + range->_end = end; + return range; +} + ++ (instancetype)defaultRange NS_RETURNS_RETAINED { + return [self new]; +} + +- (instancetype)copyWithZone:(NSZone *)zone { + return [self.class rangeWithStart:_start end:_end]; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"<%@: %p> (%@, %@)%@", self.class, self, @(_start.offset), @(_end.offset - _start.offset), _end.affinity == ASTextAffinityForward ? @"F":@"B"]; +} + +- (NSUInteger)hash { + return (sizeof(NSUInteger) == 8 ? OSSwapInt64(_start.hash) : OSSwapInt32(_start.hash)) + _end.hash; +} + +- (BOOL)isEqual:(ASTextRange *)object { + if (!object) return NO; + return [_start isEqual:object.start] && [_end isEqual:object.end]; +} + +@end + + + +@implementation ASTextSelectionRect + +@synthesize rect = _rect; +@synthesize writingDirection = _writingDirection; +@synthesize containsStart = _containsStart; +@synthesize containsEnd = _containsEnd; +@synthesize isVertical = _isVertical; + +- (id)copyWithZone:(NSZone *)zone { + ASTextSelectionRect *one = [self.class new]; + one.rect = _rect; + one.writingDirection = _writingDirection; + one.containsStart = _containsStart; + one.containsEnd = _containsEnd; + one.isVertical = _isVertical; + return one; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/Component/ASTextLayout.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/Component/ASTextLayout.h new file mode 100644 index 0000000..55ac417 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/Component/ASTextLayout.h @@ -0,0 +1,547 @@ +// +// ASTextLayout.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +#import "ASTextDebugOption.h" +#import "ASTextLine.h" +#import "ASTextInput.h" + +@protocol ASTextLinePositionModifier; + +NS_ASSUME_NONNULL_BEGIN + +/** + The max text container size in layout. + */ +ASDK_EXTERN const CGSize ASTextContainerMaxSize; + +/** + The ASTextContainer class defines a region in which text is laid out. + ASTextLayout class uses one or more ASTextContainer objects to generate layouts. + + A ASTextContainer defines rectangular regions (`size` and `insets`) or + nonrectangular shapes (`path`), and you can define exclusion paths inside the + text container's bounding rectangle so that text flows around the exclusion + path as it is laid out. + + All methods in this class is thread-safe. + + Example: + + ┌─────────────────────────────┐ <------- container + │ │ + │ asdfasdfasdfasdfasdfa <------------ container insets + │ asdfasdfa asdfasdfa │ + │ asdfas asdasd │ + │ asdfa <----------------------- container exclusion path + │ asdfas adfasd │ + │ asdfasdfa asdfasdfa │ + │ asdfasdfasdfasdfasdfa │ + │ │ + └─────────────────────────────┘ + */ +@interface ASTextContainer : NSObject + +/// Creates a container with the specified size. @param size The size. ++ (instancetype)containerWithSize:(CGSize)size NS_RETURNS_RETAINED; + +/// Creates a container with the specified size and insets. @param size The size. @param insets The text insets. ++ (instancetype)containerWithSize:(CGSize)size insets:(UIEdgeInsets)insets NS_RETURNS_RETAINED; + +/// Creates a container with the specified path. @param path The path. ++ (instancetype)containerWithPath:(nullable UIBezierPath *)path NS_RETURNS_RETAINED; + +/// Mark this immutable, so you get free copies going forward. +- (void)makeImmutable; + +/// The constrained size. (if the size is larger than ASTextContainerMaxSize, it will be clipped) +@property CGSize size; + +/// The insets for constrained size. The inset value should not be negative. Default is UIEdgeInsetsZero. +@property UIEdgeInsets insets; + +/// Custom constrained path. Set this property to ignore `size` and `insets`. Default is nil. +@property (nullable, copy) UIBezierPath *path; + +/// An array of `UIBezierPath` for path exclusion. Default is nil. +@property (nullable, copy) NSArray *exclusionPaths; + +/// Path line width. Default is 0; +@property CGFloat pathLineWidth; + +/// YES:(PathFillEvenOdd) Text is filled in the area that would be painted if the path were given to CGContextEOFillPath. +/// NO: (PathFillWindingNumber) Text is fill in the area that would be painted if the path were given to CGContextFillPath. +/// Default is YES; +@property (getter=isPathFillEvenOdd) BOOL pathFillEvenOdd; + +/// Whether the text is vertical form (may used for CJK text layout). Default is NO. +@property (getter=isVerticalForm) BOOL verticalForm; + +/// Maximum number of rows, 0 means no limit. Default is 0. +@property NSUInteger maximumNumberOfRows; + +/// The line truncation type, default is none. +@property ASTextTruncationType truncationType; + +/// The truncation token. If nil, the layout will use "…" instead. Default is nil. +@property (nullable, copy) NSAttributedString *truncationToken; + +/// This modifier is applied to the lines before the layout is completed, +/// give you a chance to modify the line position. Default is nil. +@property (nullable, copy) id linePositionModifier; +@end + + +/** + The ASTextLinePositionModifier protocol declares the required method to modify + the line position in text layout progress. See `ASTextLinePositionSimpleModifier` for example. + */ +@protocol ASTextLinePositionModifier +@required +/** + This method will called before layout is completed. The method should be thread-safe. + @param lines An array of ASTextLine. + @param text The full text. + @param container The layout container. + */ +- (void)modifyLines:(NSArray *)lines fromText:(NSAttributedString *)text inContainer:(ASTextContainer *)container; +@end + + +/** + A simple implementation of `ASTextLinePositionModifier`. It can fix each line's position + to a specified value, lets each line of height be the same. + */ +@interface ASTextLinePositionSimpleModifier : NSObject +@property CGFloat fixedLineHeight; ///< The fixed line height (distance between two baseline). +@end + + + +/** + ASTextLayout class is a readonly class stores text layout result. + All the property in this class is readonly, and should not be changed. + The methods in this class is thread-safe (except some of the draw methods). + + example: (layout with a circle exclusion path) + + ┌──────────────────────────┐ <------ container + │ [--------Line0--------] │ <- Row0 + │ [--------Line1--------] │ <- Row1 + │ [-Line2-] [-Line3-] │ <- Row2 + │ [-Line4] [Line5-] │ <- Row3 + │ [-Line6-] [-Line7-] │ <- Row4 + │ [--------Line8--------] │ <- Row5 + │ [--------Line9--------] │ <- Row6 + └──────────────────────────┘ + */ +@interface ASTextLayout : NSObject + + +#pragma mark - Generate text layout +///============================================================================= +/// @name Generate text layout +///============================================================================= + +/** + Generate a layout with the given container size and text. + + @param size The text container's size + @param text The text (if nil, returns nil). + @return A new layout, or nil when an error occurs. + */ ++ (nullable ASTextLayout *)layoutWithContainerSize:(CGSize)size text:(NSAttributedString *)text; + +/** + Generate a layout with the given container and text. + + @param container The text container (if nil, returns nil). + @param text The text (if nil, returns nil). + @return A new layout, or nil when an error occurs. + */ ++ (nullable ASTextLayout *)layoutWithContainer:(ASTextContainer *)container text:(NSAttributedString *)text; + +/** + Generate a layout with the given container and text. + + @param container The text container (if nil, returns nil). + @param text The text (if nil, returns nil). + @param range The text range (if out of range, returns nil). If the + length of the range is 0, it means the length is no limit. + @return A new layout, or nil when an error occurs. + */ ++ (nullable ASTextLayout *)layoutWithContainer:(ASTextContainer *)container text:(NSAttributedString *)text range:(NSRange)range; + +/** + Generate layouts with the given containers and text. + + @param containers An array of ASTextContainer object (if nil, returns nil). + @param text The text (if nil, returns nil). + @return An array of ASTextLayout object (the count is same as containers), + or nil when an error occurs. + */ ++ (nullable NSArray *)layoutWithContainers:(NSArray *)containers + text:(NSAttributedString *)text; + +/** + Generate layouts with the given containers and text. + + @param containers An array of ASTextContainer object (if nil, returns nil). + @param text The text (if nil, returns nil). + @param range The text range (if out of range, returns nil). If the + length of the range is 0, it means the length is no limit. + @return An array of ASTextLayout object (the count is same as containers), + or nil when an error occurs. + */ ++ (nullable NSArray *)layoutWithContainers:(NSArray *)containers + text:(NSAttributedString *)text + range:(NSRange)range; + +- (instancetype)init UNAVAILABLE_ATTRIBUTE; ++ (instancetype)new UNAVAILABLE_ATTRIBUTE; + + +#pragma mark - Text layout attributes +///============================================================================= +/// @name Text layout attributes +///============================================================================= + +///< The text container +@property (nonatomic, readonly) ASTextContainer *container; +///< The full text +@property (nonatomic, readonly) NSAttributedString *text; +///< The text range in full text +@property (nonatomic, readonly) NSRange range; +///< CTFrame +@property (nonatomic, readonly) CTFrameRef frame; +///< Array of `ASTextLine`, no truncated +@property (nonatomic, readonly) NSArray *lines; +///< ASTextLine with truncated token, or nil +@property (nullable, nonatomic, readonly) ASTextLine *truncatedLine; +///< Array of `ASTextAttachment` +@property (nullable, nonatomic, readonly) NSArray *attachments; +///< Array of NSRange(wrapped by NSValue) in text +@property (nullable, nonatomic, readonly) NSArray *attachmentRanges; +///< Array of CGRect(wrapped by NSValue) in container +@property (nullable, nonatomic, readonly) NSArray *attachmentRects; +///< Set of Attachment (UIImage/UIView/CALayer) +@property (nullable, nonatomic, readonly) NSSet *attachmentContentsSet; +///< Number of rows +@property (nonatomic, readonly) NSUInteger rowCount; +///< Visible text range +@property (nonatomic, readonly) NSRange visibleRange; +///< Bounding rect (glyphs) +@property (nonatomic, readonly) CGRect textBoundingRect; +///< Bounding size (glyphs and insets, ceil to pixel) +@property (nonatomic, readonly) CGSize textBoundingSize; +///< Has highlight attribute +@property (nonatomic, readonly) BOOL containsHighlight; +///< Has block border attribute +@property (nonatomic, readonly) BOOL needDrawBlockBorder; +///< Has background border attribute +@property (nonatomic, readonly) BOOL needDrawBackgroundBorder; +///< Has shadow attribute +@property (nonatomic, readonly) BOOL needDrawShadow; +///< Has underline attribute +@property (nonatomic, readonly) BOOL needDrawUnderline; +///< Has visible text +@property (nonatomic, readonly) BOOL needDrawText; +///< Has attachment attribute +@property (nonatomic, readonly) BOOL needDrawAttachment; +///< Has inner shadow attribute +@property (nonatomic, readonly) BOOL needDrawInnerShadow; +///< Has strickthrough attribute +@property (nonatomic, readonly) BOOL needDrawStrikethrough; +///< Has border attribute +@property (nonatomic, readonly) BOOL needDrawBorder; + + +#pragma mark - Query information from text layout +///============================================================================= +/// @name Query information from text layout +///============================================================================= + +/** + The first line index for row. + + @param row A row index. + @return The line index, or NSNotFound if not found. + */ +- (NSUInteger)lineIndexForRow:(NSUInteger)row; + +/** + The number of lines for row. + + @param row A row index. + @return The number of lines, or NSNotFound when an error occurs. + */ +- (NSUInteger)lineCountForRow:(NSUInteger)row; + +/** + The row index for line. + + @param line A row index. + + @return The row index, or NSNotFound if not found. + */ +- (NSUInteger)rowIndexForLine:(NSUInteger)line; + +/** + The line index for a specified point. + + @discussion It returns NSNotFound if there's no text at the point. + + @param point A point in the container. + @return The line index, or NSNotFound if not found. + */ +- (NSUInteger)lineIndexForPoint:(CGPoint)point; + +/** + The line index closest to a specified point. + + @param point A point in the container. + @return The line index, or NSNotFound if no line exist in layout. + */ +- (NSUInteger)closestLineIndexForPoint:(CGPoint)point; + +/** + The offset in container for a text position in a specified line. + + @discussion The offset is the text position's baseline point.x. + If the container is vertical form, the offset is the baseline point.y; + + @param position The text position in string. + @param lineIndex The line index. + @return The offset in container, or CGFLOAT_MAX if not found. + */ +- (CGFloat)offsetForTextPosition:(NSUInteger)position lineIndex:(NSUInteger)lineIndex; + +/** + The text position for a point in a specified line. + + @discussion This method just call CTLineGetStringIndexForPosition() and does + NOT consider the emoji, line break character, binding text... + + @param point A point in the container. + @param lineIndex The line index. + @return The text position, or NSNotFound if not found. + */ +- (NSUInteger)textPositionForPoint:(CGPoint)point lineIndex:(NSUInteger)lineIndex; + +/** + The closest text position to a specified point. + + @discussion This method takes into account the restrict of emoji, line break + character, binding text and text affinity. + + @param point A point in the container. + @return A text position, or nil if not found. + */ +- (nullable ASTextPosition *)closestPositionToPoint:(CGPoint)point; + +/** + Returns the new position when moving selection grabber in text view. + + @discussion There are two grabber in the text selection period, user can only + move one grabber at the same time. + + @param point A point in the container. + @param oldPosition The old text position for the moving grabber. + @param otherPosition The other position in text selection view. + + @return A text position, or nil if not found. + */ +- (nullable ASTextPosition *)positionForPoint:(CGPoint)point + oldPosition:(ASTextPosition *)oldPosition + otherPosition:(ASTextPosition *)otherPosition; + +/** + Returns the character or range of characters that is at a given point in the container. + If there is no text at the point, returns nil. + + @discussion This method takes into account the restrict of emoji, line break + character, binding text and text affinity. + + @param point A point in the container. + @return An object representing a range that encloses a character (or characters) + at point. Or nil if not found. + */ +- (nullable ASTextRange *)textRangeAtPoint:(CGPoint)point; + +/** + Returns the closest character or range of characters that is at a given point in + the container. + + @discussion This method takes into account the restrict of emoji, line break + character, binding text and text affinity. + + @param point A point in the container. + @return An object representing a range that encloses a character (or characters) + at point. Or nil if not found. + */ +- (nullable ASTextRange *)closestTextRangeAtPoint:(CGPoint)point; + +/** + If the position is inside an emoji, composed character sequences, line break '\\r\\n' + or custom binding range, then returns the range by extend the position. Otherwise, + returns a zero length range from the position. + + @param position A text-position object that identifies a location in layout. + + @return A text-range object that extend the position. Or nil if an error occurs + */ +- (nullable ASTextRange *)textRangeByExtendingPosition:(ASTextPosition *)position; + +/** + Returns a text range at a given offset in a specified direction from another + text position to its farthest extent in a certain direction of layout. + + @param position A text-position object that identifies a location in layout. + @param direction A constant that indicates a direction of layout (right, left, up, down). + @param offset A character offset from position. + + @return A text-range object that represents the distance from position to the + farthest extent in direction. Or nil if an error occurs. + */ +- (nullable ASTextRange *)textRangeByExtendingPosition:(ASTextPosition *)position + inDirection:(UITextLayoutDirection)direction + offset:(NSInteger)offset; + +/** + Returns the line index for a given text position. + + @discussion This method takes into account the text affinity. + + @param position A text-position object that identifies a location in layout. + @return The line index, or NSNotFound if not found. + */ +- (NSUInteger)lineIndexForPosition:(ASTextPosition *)position; + +/** + Returns the baseline position for a given text position. + + @param position An object that identifies a location in the layout. + @return The baseline position for text, or CGPointZero if not found. + */ +- (CGPoint)linePositionForPosition:(ASTextPosition *)position; + +/** + Returns a rectangle used to draw the caret at a given insertion point. + + @param position An object that identifies a location in the layout. + @return A rectangle that defines the area for drawing the caret. The width is + always zero in normal container, the height is always zero in vertical form container. + If not found, it returns CGRectNull. + */ +- (CGRect)caretRectForPosition:(ASTextPosition *)position; + +/** + Returns the first rectangle that encloses a range of text in the layout. + + @param range An object that represents a range of text in layout. + + @return The first rectangle in a range of text. You might use this rectangle to + draw a correction rectangle. The "first" in the name refers the rectangle + enclosing the first line when the range encompasses multiple lines of text. + If not found, it returns CGRectNull. + */ +- (CGRect)firstRectForRange:(ASTextRange *)range; + +/** + Returns the rectangle union that encloses a range of text in the layout. + + @param range An object that represents a range of text in layout. + + @return A rectangle that defines the area than encloses the range. + If not found, it returns CGRectNull. + */ +- (CGRect)rectForRange:(ASTextRange *)range; + +/** + Returns an array of selection rects corresponding to the range of text. + The start and end rect can be used to show grabber. + + @param range An object representing a range in text. + @return An array of `ASTextSelectionRect` objects that encompass the selection. + If not found, the array is empty. + */ +- (NSArray *)selectionRectsForRange:(ASTextRange *)range; + +/** + Returns an array of selection rects corresponding to the range of text. + + @param range An object representing a range in text. + @return An array of `ASTextSelectionRect` objects that encompass the selection. + If not found, the array is empty. + */ +- (NSArray *)selectionRectsWithoutStartAndEndForRange:(ASTextRange *)range; + +/** + Returns the start and end selection rects corresponding to the range of text. + The start and end rect can be used to show grabber. + + @param range An object representing a range in text. + @return An array of `ASTextSelectionRect` objects contains the start and end to + the selection. If not found, the array is empty. + */ +- (NSArray *)selectionRectsWithOnlyStartAndEndForRange:(ASTextRange *)range; + + +#pragma mark - Draw text layout +///============================================================================= +/// @name Draw text layout +///============================================================================= + +/** + Draw the layout and show the attachments. + + @discussion If the `view` parameter is not nil, then the attachment views will + add to this `view`, and if the `layer` parameter is not nil, then the attachment + layers will add to this `layer`. + + @warning This method should be called on main thread if `view` or `layer` parameter + is not nil and there's UIView or CALayer attachments in layout. + Otherwise, it can be called on any thread. + + @param context The draw context. Pass nil to avoid text and image drawing. + @param size The context size. + @param point The point at which to draw the layout. + @param view The attachment views will add to this view. + @param layer The attachment layers will add to this layer. + @param debug The debug option. Pass nil to avoid debug drawing. + @param cancel The cancel checker block. It will be called in drawing progress. + If it returns YES, the further draw progress will be canceled. + Pass nil to ignore this feature. + */ +- (void)drawInContext:(nullable CGContextRef)context + size:(CGSize)size + point:(CGPoint)point + view:(nullable UIView *)view + layer:(nullable CALayer *)layer + debug:(nullable ASTextDebugOption *)debug + cancel:(nullable BOOL (^)(void))cancel; + +/** + Draw the layout text and image (without view or layer attachments). + + @discussion This method is thread safe and can be called on any thread. + + @param context The draw context. Pass nil to avoid text and image drawing. + @param size The context size. + @param debug The debug option. Pass nil to avoid debug drawing. + */ +- (void)drawInContext:(nullable CGContextRef)context + size:(CGSize)size + debug:(nullable ASTextDebugOption *)debug; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/Component/ASTextLayout.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/Component/ASTextLayout.mm new file mode 100644 index 0000000..1567322 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/Component/ASTextLayout.mm @@ -0,0 +1,3483 @@ +// +// ASTextLayout.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import +#import +#import +#import +#import +#import + +const CGSize ASTextContainerMaxSize = (CGSize){0x100000, 0x100000}; + +typedef struct { + CGFloat head; + CGFloat foot; +} ASRowEdge; + +static inline CGSize ASTextClipCGSize(CGSize size) { + if (size.width > ASTextContainerMaxSize.width) size.width = ASTextContainerMaxSize.width; + if (size.height > ASTextContainerMaxSize.height) size.height = ASTextContainerMaxSize.height; + return size; +} + +static inline UIEdgeInsets UIEdgeInsetRotateVertical(UIEdgeInsets insets) { + UIEdgeInsets one; + one.top = insets.left; + one.left = insets.bottom; + one.bottom = insets.right; + one.right = insets.top; + return one; +} + +/** + Sometimes CoreText may convert CGColor to UIColor for `kCTForegroundColorAttributeName` + attribute in iOS7. This should be a bug of CoreText, and may cause crash. Here's a workaround. + */ +static CGColorRef ASTextGetCGColor(CGColorRef color) { + static UIColor *defaultColor; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + defaultColor = [UIColor blackColor]; + }); + if (!color) return defaultColor.CGColor; + if ([((__bridge NSObject *)color) respondsToSelector:@selector(CGColor)]) { + return ((__bridge UIColor *)color).CGColor; + } + return color; +} + +@implementation ASTextLinePositionSimpleModifier +- (void)modifyLines:(NSArray *)lines fromText:(NSAttributedString *)text inContainer:(ASTextContainer *)container { + if (container.verticalForm) { + for (NSUInteger i = 0, max = lines.count; i < max; i++) { + ASTextLine *line = lines[i]; + CGPoint pos = line.position; + pos.x = container.size.width - container.insets.right - line.row * _fixedLineHeight - _fixedLineHeight * 0.9; + line.position = pos; + } + } else { + for (NSUInteger i = 0, max = lines.count; i < max; i++) { + ASTextLine *line = lines[i]; + CGPoint pos = line.position; + pos.y = line.row * _fixedLineHeight + _fixedLineHeight * 0.9 + container.insets.top; + line.position = pos; + } + } +} + +- (id)copyWithZone:(NSZone *)zone { + ASTextLinePositionSimpleModifier *one = [self.class new]; + one.fixedLineHeight = _fixedLineHeight; + return one; +} +@end + + +@implementation ASTextContainer { + @package + BOOL _readonly; ///< used only in ASTextLayout.implementation + dispatch_semaphore_t _lock; + + CGSize _size; + UIEdgeInsets _insets; + UIBezierPath *_path; + NSArray *_exclusionPaths; + BOOL _pathFillEvenOdd; + CGFloat _pathLineWidth; + BOOL _verticalForm; + NSUInteger _maximumNumberOfRows; + ASTextTruncationType _truncationType; + NSAttributedString *_truncationToken; + id _linePositionModifier; +} + +- (NSString *)description +{ + return [NSString + stringWithFormat:@"immutable: %@, insets: %@, size: %@", self->_readonly ? @"YES" : @"NO", + NSStringFromUIEdgeInsets(self->_insets), NSStringFromCGSize(self->_size)]; +} + ++ (instancetype)containerWithSize:(CGSize)size NS_RETURNS_RETAINED { + return [self containerWithSize:size insets:UIEdgeInsetsZero]; +} + ++ (instancetype)containerWithSize:(CGSize)size insets:(UIEdgeInsets)insets NS_RETURNS_RETAINED { + ASTextContainer *one = [self new]; + one.size = ASTextClipCGSize(size); + one.insets = insets; + return one; +} + ++ (instancetype)containerWithPath:(UIBezierPath *)path NS_RETURNS_RETAINED { + ASTextContainer *one = [self new]; + one.path = path; + return one; +} + +- (instancetype)init { + self = [super init]; + if (!self) return nil; + _lock = dispatch_semaphore_create(1); + _pathFillEvenOdd = YES; + return self; +} + +- (id)copyForced:(BOOL)forceCopy +{ + dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER); + if (_readonly && !forceCopy) { + dispatch_semaphore_signal(_lock); + return self; + } + + ASTextContainer *one = [self.class new]; + one->_size = _size; + one->_insets = _insets; + one->_path = _path; + one->_exclusionPaths = [_exclusionPaths copy]; + one->_pathFillEvenOdd = _pathFillEvenOdd; + one->_pathLineWidth = _pathLineWidth; + one->_verticalForm = _verticalForm; + one->_maximumNumberOfRows = _maximumNumberOfRows; + one->_truncationType = _truncationType; + one->_truncationToken = [_truncationToken copy]; + one->_linePositionModifier = [(NSObject *)_linePositionModifier copy]; + dispatch_semaphore_signal(_lock); + return one; +} + +- (id)copyWithZone:(NSZone *)zone { + return [self copyForced:NO]; +} + +- (id)mutableCopyWithZone:(NSZone *)zone { + return [self copyForced:YES]; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:[NSValue valueWithCGSize:_size] forKey:@"size"]; + [aCoder encodeObject:[NSValue valueWithUIEdgeInsets:_insets] forKey:@"insets"]; + [aCoder encodeObject:_path forKey:@"path"]; + [aCoder encodeObject:_exclusionPaths forKey:@"exclusionPaths"]; + [aCoder encodeBool:_pathFillEvenOdd forKey:@"pathFillEvenOdd"]; + [aCoder encodeDouble:_pathLineWidth forKey:@"pathLineWidth"]; + [aCoder encodeBool:_verticalForm forKey:@"verticalForm"]; + [aCoder encodeInteger:_maximumNumberOfRows forKey:@"maximumNumberOfRows"]; + [aCoder encodeInteger:_truncationType forKey:@"truncationType"]; + [aCoder encodeObject:_truncationToken forKey:@"truncationToken"]; + if ([_linePositionModifier respondsToSelector:@selector(encodeWithCoder:)] && + [_linePositionModifier respondsToSelector:@selector(initWithCoder:)]) { + [aCoder encodeObject:_linePositionModifier forKey:@"linePositionModifier"]; + } +} + +- (id)initWithCoder:(NSCoder *)aDecoder { + self = [self init]; + _size = ((NSValue *)[aDecoder decodeObjectForKey:@"size"]).CGSizeValue; + _insets = ((NSValue *)[aDecoder decodeObjectForKey:@"insets"]).UIEdgeInsetsValue; + _path = [aDecoder decodeObjectForKey:@"path"]; + _exclusionPaths = [aDecoder decodeObjectForKey:@"exclusionPaths"]; + _pathFillEvenOdd = [aDecoder decodeBoolForKey:@"pathFillEvenOdd"]; + _pathLineWidth = [aDecoder decodeDoubleForKey:@"pathLineWidth"]; + _verticalForm = [aDecoder decodeBoolForKey:@"verticalForm"]; + _maximumNumberOfRows = [aDecoder decodeIntegerForKey:@"maximumNumberOfRows"]; + _truncationType = (ASTextTruncationType)[aDecoder decodeIntegerForKey:@"truncationType"]; + _truncationToken = [aDecoder decodeObjectForKey:@"truncationToken"]; + _linePositionModifier = [aDecoder decodeObjectForKey:@"linePositionModifier"]; + return self; +} + +- (void)makeImmutable +{ + dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER); + _readonly = YES; + dispatch_semaphore_signal(_lock); +} + +#define Getter(...) \ +dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER); \ +__VA_ARGS__; \ +dispatch_semaphore_signal(_lock); + +#define Setter(...) \ +dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER); \ +if (__builtin_expect(_readonly, NO)) { \ + ASDisplayNodeFailAssert(@"Attempt to modify immutable text container."); \ + dispatch_semaphore_signal(_lock); \ + return; \ +} \ +__VA_ARGS__; \ +dispatch_semaphore_signal(_lock); + +- (CGSize)size { + Getter(CGSize size = _size) return size; +} + +- (void)setSize:(CGSize)size { + Setter(if(!_path) _size = ASTextClipCGSize(size)); +} + +- (UIEdgeInsets)insets { + Getter(UIEdgeInsets insets = _insets) return insets; +} + +- (void)setInsets:(UIEdgeInsets)insets { + Setter(if(!_path){ + if (insets.top < 0) insets.top = 0; + if (insets.left < 0) insets.left = 0; + if (insets.bottom < 0) insets.bottom = 0; + if (insets.right < 0) insets.right = 0; + _insets = insets; + }); +} + +- (UIBezierPath *)path { + Getter(UIBezierPath *path = _path) return path; +} + +- (void)setPath:(UIBezierPath *)path { + Setter( + _path = path.copy; + if (_path) { + CGRect bounds = _path.bounds; + CGSize size = bounds.size; + UIEdgeInsets insets = UIEdgeInsetsZero; + if (bounds.origin.x < 0) size.width += bounds.origin.x; + if (bounds.origin.x > 0) insets.left = bounds.origin.x; + if (bounds.origin.y < 0) size.height += bounds.origin.y; + if (bounds.origin.y > 0) insets.top = bounds.origin.y; + _size = size; + _insets = insets; + } + ); +} + +- (NSArray *)exclusionPaths { + Getter(NSArray *paths = _exclusionPaths) return paths; +} + +- (void)setExclusionPaths:(NSArray *)exclusionPaths { + Setter(_exclusionPaths = exclusionPaths.copy); +} + +- (BOOL)isPathFillEvenOdd { + Getter(BOOL is = _pathFillEvenOdd) return is; +} + +- (void)setPathFillEvenOdd:(BOOL)pathFillEvenOdd { + Setter(_pathFillEvenOdd = pathFillEvenOdd); +} + +- (CGFloat)pathLineWidth { + Getter(CGFloat width = _pathLineWidth) return width; +} + +- (void)setPathLineWidth:(CGFloat)pathLineWidth { + Setter(_pathLineWidth = pathLineWidth); +} + +- (BOOL)isVerticalForm { + Getter(BOOL v = _verticalForm) return v; +} + +- (void)setVerticalForm:(BOOL)verticalForm { + Setter(_verticalForm = verticalForm); +} + +- (NSUInteger)maximumNumberOfRows { + Getter(NSUInteger num = _maximumNumberOfRows) return num; +} + +- (void)setMaximumNumberOfRows:(NSUInteger)maximumNumberOfRows { + Setter(_maximumNumberOfRows = maximumNumberOfRows); +} + +- (ASTextTruncationType)truncationType { + Getter(ASTextTruncationType type = _truncationType) return type; +} + +- (void)setTruncationType:(ASTextTruncationType)truncationType { + Setter(_truncationType = truncationType); +} + +- (NSAttributedString *)truncationToken { + Getter(NSAttributedString *token = _truncationToken) return token; +} + +- (void)setTruncationToken:(NSAttributedString *)truncationToken { + Setter(_truncationToken = truncationToken.copy); +} + +- (void)setLinePositionModifier:(id)linePositionModifier { + Setter(_linePositionModifier = [(NSObject *)linePositionModifier copy]); +} + +- (id)linePositionModifier { + Getter(id m = _linePositionModifier) return m; +} + +#undef Getter +#undef Setter +@end + + + + +@interface ASTextLayout () + +@property (nonatomic) ASTextContainer *container; +@property (nonatomic) NSAttributedString *text; +@property (nonatomic) NSRange range; + +@property (nonatomic) CTFrameRef frame; +@property (nonatomic) NSArray *lines; +@property (nonatomic) ASTextLine *truncatedLine; +@property (nonatomic) NSArray *attachments; +@property (nonatomic) NSArray *attachmentRanges; +@property (nonatomic) NSArray *attachmentRects; +@property (nonatomic) NSSet *attachmentContentsSet; +@property (nonatomic) NSUInteger rowCount; +@property (nonatomic) NSRange visibleRange; +@property (nonatomic) CGRect textBoundingRect; +@property (nonatomic) CGSize textBoundingSize; + +@property (nonatomic) BOOL containsHighlight; +@property (nonatomic) BOOL needDrawBlockBorder; +@property (nonatomic) BOOL needDrawBackgroundBorder; +@property (nonatomic) BOOL needDrawShadow; +@property (nonatomic) BOOL needDrawUnderline; +@property (nonatomic) BOOL needDrawText; +@property (nonatomic) BOOL needDrawAttachment; +@property (nonatomic) BOOL needDrawInnerShadow; +@property (nonatomic) BOOL needDrawStrikethrough; +@property (nonatomic) BOOL needDrawBorder; + +@property (nonatomic) NSUInteger *lineRowsIndex; +@property (nonatomic) ASRowEdge *lineRowsEdge; ///< top-left origin + +@end + + + +@implementation ASTextLayout + +#pragma mark - Layout + +- (instancetype)_init { + self = [super init]; + return self; +} + +- (NSString *)description +{ + return [NSString stringWithFormat:@"lines: %ld, visibleRange:%@, textBoundingRect:%@", + (long)[self.lines count], + NSStringFromRange(self.visibleRange), + NSStringFromCGRect(self.textBoundingRect)]; +} + ++ (ASTextLayout *)layoutWithContainerSize:(CGSize)size text:(NSAttributedString *)text { + ASTextContainer *container = [ASTextContainer containerWithSize:size]; + return [self layoutWithContainer:container text:text]; +} + ++ (ASTextLayout *)layoutWithContainer:(ASTextContainer *)container text:(NSAttributedString *)text { + return [self layoutWithContainer:container text:text range:NSMakeRange(0, text.length)]; +} + ++ (ASTextLayout *)layoutWithContainer:(ASTextContainer *)container text:(NSAttributedString *)text range:(NSRange)range { + ASTextLayout *layout = NULL; + CGPathRef cgPath = nil; + CGRect cgPathBox = {0}; + BOOL isVerticalForm = NO; + BOOL rowMaySeparated = NO; + NSMutableDictionary *frameAttrs = nil; + CTFramesetterRef ctSetter = NULL; + CTFrameRef ctFrame = NULL; + CFArrayRef ctLines = nil; + CGPoint *lineOrigins = NULL; + NSUInteger lineCount = 0; + NSMutableArray *lines = nil; + NSMutableArray *attachments = nil; + NSMutableArray *attachmentRanges = nil; + NSMutableArray *attachmentRects = nil; + NSMutableSet *attachmentContentsSet = nil; + BOOL needTruncation = NO; + NSAttributedString *truncationToken = nil; + ASTextLine *truncatedLine = nil; + ASRowEdge *lineRowsEdge = NULL; + NSUInteger *lineRowsIndex = NULL; + NSRange visibleRange; + NSUInteger maximumNumberOfRows = 0; + BOOL constraintSizeIsExtended = NO; + CGRect constraintRectBeforeExtended = {0}; +#define FAIL_AND_RETURN {\ + if (cgPath) CFRelease(cgPath); \ + if (ctSetter) CFRelease(ctSetter); \ + if (ctFrame) CFRelease(ctFrame); \ + if (lineOrigins) free(lineOrigins); \ + if (lineRowsEdge) free(lineRowsEdge); \ + if (lineRowsIndex) free(lineRowsIndex); \ + return nil; } + + container = [container copy]; + if (!text || !container) return nil; + if (range.location + range.length > text.length) return nil; + [container makeImmutable]; + maximumNumberOfRows = container.maximumNumberOfRows; + + // It may use larger constraint size when create CTFrame with + // CTFramesetterCreateFrame in iOS 10. + BOOL needFixLayoutSizeBug = AS_AT_LEAST_IOS10; + + layout = [[ASTextLayout alloc] _init]; + layout.text = text; + layout.container = container; + layout.range = range; + isVerticalForm = container.verticalForm; + + // set cgPath and cgPathBox + if (container.path == nil && container.exclusionPaths.count == 0) { + if (container.size.width <= 0 || container.size.height <= 0) FAIL_AND_RETURN + CGRect rect = (CGRect) {CGPointZero, container.size }; + if (needFixLayoutSizeBug) { + constraintSizeIsExtended = YES; + constraintRectBeforeExtended = UIEdgeInsetsInsetRect(rect, container.insets); + constraintRectBeforeExtended = CGRectStandardize(constraintRectBeforeExtended); + if (container.isVerticalForm) { + rect.size.width = ASTextContainerMaxSize.width; + } else { + rect.size.height = ASTextContainerMaxSize.height; + } + } + rect = UIEdgeInsetsInsetRect(rect, container.insets); + rect = CGRectStandardize(rect); + cgPathBox = rect; + rect = CGRectApplyAffineTransform(rect, CGAffineTransformMakeScale(1, -1)); + cgPath = CGPathCreateWithRect(rect, NULL); // let CGPathIsRect() returns true + } else if (container.path && CGPathIsRect(container.path.CGPath, &cgPathBox) && container.exclusionPaths.count == 0) { + CGRect rect = CGRectApplyAffineTransform(cgPathBox, CGAffineTransformMakeScale(1, -1)); + cgPath = CGPathCreateWithRect(rect, NULL); // let CGPathIsRect() returns true + } else { + rowMaySeparated = YES; + CGMutablePathRef path = NULL; + if (container.path) { + path = CGPathCreateMutableCopy(container.path.CGPath); + } else { + CGRect rect = (CGRect) {CGPointZero, container.size }; + rect = UIEdgeInsetsInsetRect(rect, container.insets); + CGPathRef rectPath = CGPathCreateWithRect(rect, NULL); + if (rectPath) { + path = CGPathCreateMutableCopy(rectPath); + CGPathRelease(rectPath); + } + } + if (path) { + [layout.container.exclusionPaths enumerateObjectsUsingBlock: ^(UIBezierPath *onePath, NSUInteger idx, BOOL *stop) { + CGPathAddPath(path, NULL, onePath.CGPath); + }]; + + cgPathBox = CGPathGetPathBoundingBox(path); + CGAffineTransform trans = CGAffineTransformMakeScale(1, -1); + CGMutablePathRef transPath = CGPathCreateMutableCopyByTransformingPath(path, &trans); + CGPathRelease(path); + path = transPath; + } + cgPath = path; + } + if (!cgPath) FAIL_AND_RETURN + + // frame setter config + frameAttrs = [[NSMutableDictionary alloc] init]; + if (container.isPathFillEvenOdd == NO) { + frameAttrs[(id)kCTFramePathFillRuleAttributeName] = @(kCTFramePathFillWindingNumber); + } + if (container.pathLineWidth > 0) { + frameAttrs[(id)kCTFramePathWidthAttributeName] = @(container.pathLineWidth); + } + if (container.isVerticalForm == YES) { + frameAttrs[(id)kCTFrameProgressionAttributeName] = @(kCTFrameProgressionRightToLeft); + } + + /* + * Framesetter cache. + * Framesetters can only be used by one thread at a time. + * Create a CFSet with no callbacks (raw pointers) to keep track of which + * framesetters are in use on other threads. If the one for our string is already in use, + * just create a new one. This should be pretty rare. + */ + static pthread_mutex_t busyFramesettersLock = PTHREAD_MUTEX_INITIALIZER; + static NSCache *framesetterCache; + static CFMutableSetRef busyFramesetters; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + if (ASActivateExperimentalFeature(ASExperimentalFramesetterCache)) { + framesetterCache = [[NSCache alloc] init]; + framesetterCache.name = @"org.TextureGroup.Texture.framesetterCache"; + busyFramesetters = CFSetCreateMutable(NULL, 0, NULL); + } + }); + + BOOL haveCached = NO, useCached = NO; + if (framesetterCache) { + // Check if there's one in the cache. + ctSetter = (__bridge_retained CTFramesetterRef)[framesetterCache objectForKey:text]; + + if (ctSetter) { + haveCached = YES; + + // Check-and-set busy on the cached one. + pthread_mutex_lock(&busyFramesettersLock); + BOOL busy = CFSetContainsValue(busyFramesetters, ctSetter); + if (!busy) { + CFSetAddValue(busyFramesetters, ctSetter); + useCached = YES; + } + pthread_mutex_unlock(&busyFramesettersLock); + + // Release if it was busy. + if (busy) { + CFRelease(ctSetter); + ctSetter = NULL; + } + } + } + + // Create a framesetter if needed. + if (!ctSetter) { + ctSetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)text); + } + + if (!ctSetter) FAIL_AND_RETURN + ctFrame = CTFramesetterCreateFrame(ctSetter, ASTextCFRangeFromNSRange(range), cgPath, (CFDictionaryRef)frameAttrs); + + // Return to cache. + if (framesetterCache) { + if (useCached) { + // If reused: mark available. + pthread_mutex_lock(&busyFramesettersLock); + CFSetRemoveValue(busyFramesetters, ctSetter); + pthread_mutex_unlock(&busyFramesettersLock); + } else if (!haveCached) { + // If first framesetter, add to cache. + [framesetterCache setObject:(__bridge id)ctSetter forKey:text]; + } + } + + if (!ctFrame) FAIL_AND_RETURN + lines = [NSMutableArray new]; + ctLines = CTFrameGetLines(ctFrame); + lineCount = CFArrayGetCount(ctLines); + if (lineCount > 0) { + lineOrigins = (CGPoint *)malloc(lineCount * sizeof(CGPoint)); + if (lineOrigins == NULL) FAIL_AND_RETURN + CTFrameGetLineOrigins(ctFrame, CFRangeMake(0, lineCount), lineOrigins); + } + + CGRect textBoundingRect = CGRectZero; + CGSize textBoundingSize = CGSizeZero; + NSInteger rowIdx = -1; + NSUInteger rowCount = 0; + CGRect lastRect = CGRectMake(0, -FLT_MAX, 0, 0); + CGPoint lastPosition = CGPointMake(0, -FLT_MAX); + if (isVerticalForm) { + lastRect = CGRectMake(FLT_MAX, 0, 0, 0); + lastPosition = CGPointMake(FLT_MAX, 0); + } + + // calculate line frame + NSUInteger lineCurrentIdx = 0; + BOOL measuringBeyondConstraints = NO; + for (NSUInteger i = 0; i < lineCount; i++) { + CTLineRef ctLine = (CTLineRef)CFArrayGetValueAtIndex(ctLines, i); + CFArrayRef ctRuns = CTLineGetGlyphRuns(ctLine); + if (!ctRuns || CFArrayGetCount(ctRuns) == 0) continue; + + // CoreText coordinate system + CGPoint ctLineOrigin = lineOrigins[i]; + + // UIKit coordinate system + CGPoint position; + position.x = cgPathBox.origin.x + ctLineOrigin.x; + position.y = cgPathBox.size.height + cgPathBox.origin.y - ctLineOrigin.y; + + ASTextLine *line = [ASTextLine lineWithCTLine:ctLine position:position vertical:isVerticalForm]; + + [lines addObject:line]; + } + + // Give user a chance to modify the line's position. + [container.linePositionModifier modifyLines:lines fromText:text inContainer:container]; + + BOOL first = YES; + for (ASTextLine *line in lines) { + CGPoint position = line.position; + CGRect rect = line.bounds; + if (constraintSizeIsExtended) { + if (isVerticalForm) { + if (rect.origin.x + rect.size.width > + constraintRectBeforeExtended.origin.x + + constraintRectBeforeExtended.size.width) { + measuringBeyondConstraints = YES; + } + } else { + if (rect.origin.y + rect.size.height > + constraintRectBeforeExtended.origin.y + + constraintRectBeforeExtended.size.height) { + measuringBeyondConstraints = YES; + } + } + } + + BOOL newRow = !measuringBeyondConstraints; + if (newRow && rowMaySeparated && position.x != lastPosition.x) { + if (isVerticalForm) { + if (rect.size.width > lastRect.size.width) { + if (rect.origin.x > lastPosition.x && lastPosition.x > rect.origin.x - rect.size.width) newRow = NO; + } else { + if (lastRect.origin.x > position.x && position.x > lastRect.origin.x - lastRect.size.width) newRow = NO; + } + } else { + if (rect.size.height > lastRect.size.height) { + if (rect.origin.y < lastPosition.y && lastPosition.y < rect.origin.y + rect.size.height) newRow = NO; + } else { + if (lastRect.origin.y < position.y && position.y < lastRect.origin.y + lastRect.size.height) newRow = NO; + } + } + } + + if (newRow) rowIdx++; + lastRect = rect; + lastPosition = position; + + line.index = lineCurrentIdx; + line.row = rowIdx; + + rowCount = rowIdx + 1; + lineCurrentIdx ++; + + if (first) { + first = NO; + textBoundingRect = rect; + } else if (!measuringBeyondConstraints) { + if (maximumNumberOfRows == 0 || rowIdx < maximumNumberOfRows) { + textBoundingRect = CGRectUnion(textBoundingRect, rect); + } + } + } + + { + NSMutableArray *removedLines = [NSMutableArray new]; + if (rowCount > 0) { + if (maximumNumberOfRows > 0) { + if (rowCount > maximumNumberOfRows) { + needTruncation = YES; + rowCount = maximumNumberOfRows; + do { + ASTextLine *line = lines.lastObject; + if (!line) break; + if (line.row < rowCount) break; // we have removed down to an allowed # of lines now + [lines removeLastObject]; + [removedLines addObject:line]; + } while (1); + } + } + ASTextLine *lastLine = rowCount < lines.count ? lines[rowCount - 1] : lines.lastObject; + if (!needTruncation && lastLine.range.location + lastLine.range.length < text.length) { + needTruncation = YES; + while (lines.count > rowCount) { + ASTextLine *line = lines.lastObject; + [lines removeLastObject]; + [removedLines addObject:line]; + } + } + + lineRowsEdge = (ASRowEdge *) calloc(rowCount, sizeof(ASRowEdge)); + if (lineRowsEdge == NULL) FAIL_AND_RETURN + lineRowsIndex = (NSUInteger *) calloc(rowCount, sizeof(NSUInteger)); + if (lineRowsIndex == NULL) FAIL_AND_RETURN + NSInteger lastRowIdx = -1; + CGFloat lastHead = 0; + CGFloat lastFoot = 0; + for (NSUInteger i = 0, max = lines.count; i < max; i++) { + ASTextLine *line = lines[i]; + CGRect rect = line.bounds; + if ((NSInteger) line.row != lastRowIdx) { + if (lastRowIdx >= 0) { + lineRowsEdge[lastRowIdx] = (ASRowEdge) {.head = lastHead, .foot = lastFoot}; + } + lastRowIdx = line.row; + lineRowsIndex[lastRowIdx] = i; + if (isVerticalForm) { + lastHead = rect.origin.x + rect.size.width; + lastFoot = lastHead - rect.size.width; + } else { + lastHead = rect.origin.y; + lastFoot = lastHead + rect.size.height; + } + } else { + if (isVerticalForm) { + lastHead = MAX(lastHead, rect.origin.x + rect.size.width); + lastFoot = MIN(lastFoot, rect.origin.x); + } else { + lastHead = MIN(lastHead, rect.origin.y); + lastFoot = MAX(lastFoot, rect.origin.y + rect.size.height); + } + } + } + lineRowsEdge[lastRowIdx] = (ASRowEdge) {.head = lastHead, .foot = lastFoot}; + + for (NSUInteger i = 1; i < rowCount; i++) { + ASRowEdge v0 = lineRowsEdge[i - 1]; + ASRowEdge v1 = lineRowsEdge[i]; + lineRowsEdge[i - 1].foot = lineRowsEdge[i].head = (v0.foot + v1.head) * 0.5; + } + } + + { // calculate bounding size + CGRect rect = textBoundingRect; + if (container.path) { + if (container.pathLineWidth > 0) { + CGFloat inset = container.pathLineWidth / 2; + rect = CGRectInset(rect, -inset, -inset); + } + } else { + rect = UIEdgeInsetsInsetRect(rect, ASTextUIEdgeInsetsInvert(container.insets)); + } + rect = CGRectStandardize(rect); + CGSize size = rect.size; + if (container.verticalForm) { + size.width += container.size.width - (rect.origin.x + rect.size.width); + } else { + size.width += rect.origin.x; + } + size.height += rect.origin.y; + if (size.width < 0) size.width = 0; + if (size.height < 0) size.height = 0; + size.width = ceil(size.width); + size.height = ceil(size.height); + textBoundingSize = size; + } + + visibleRange = ASTextNSRangeFromCFRange(CTFrameGetVisibleStringRange(ctFrame)); + if (needTruncation) { + ASTextLine *lastLine = lines.lastObject; + NSRange lastRange = lastLine.range; + visibleRange.length = lastRange.location + lastRange.length - visibleRange.location; + + // create truncated line + if (container.truncationType != ASTextTruncationTypeNone) { + CTLineRef truncationTokenLine = NULL; + if (container.truncationToken) { + truncationToken = container.truncationToken; + truncationTokenLine = CTLineCreateWithAttributedString((CFAttributedStringRef) truncationToken); + } else { + CFArrayRef runs = CTLineGetGlyphRuns(lastLine.CTLine); + NSUInteger runCount = CFArrayGetCount(runs); + NSMutableDictionary *attrs = nil; + if (runCount > 0) { + CTRunRef run = (CTRunRef) CFArrayGetValueAtIndex(runs, runCount - 1); + attrs = (id) CTRunGetAttributes(run); + attrs = attrs ? attrs.mutableCopy : [NSMutableArray new]; + [attrs removeObjectsForKeys:[NSMutableAttributedString as_allDiscontinuousAttributeKeys]]; + CTFontRef font = (__bridge CTFontRef) attrs[(id) kCTFontAttributeName]; + CGFloat fontSize = font ? CTFontGetSize(font) : 12.0; + UIFont *uiFont = [UIFont systemFontOfSize:fontSize * 0.9]; + if (uiFont) { + font = CTFontCreateWithName((__bridge CFStringRef) uiFont.fontName, uiFont.pointSize, NULL); + } else { + font = NULL; + } + if (font) { + attrs[(id) kCTFontAttributeName] = (__bridge id) (font); + uiFont = nil; + CFRelease(font); + } + CGColorRef color = (__bridge CGColorRef) (attrs[(id) kCTForegroundColorAttributeName]); + if (color && CFGetTypeID(color) == CGColorGetTypeID() && CGColorGetAlpha(color) == 0) { + // ignore clear color + [attrs removeObjectForKey:(id) kCTForegroundColorAttributeName]; + } + if (!attrs) attrs = [NSMutableDictionary new]; + } + truncationToken = [[NSAttributedString alloc] initWithString:ASTextTruncationToken attributes:attrs]; + truncationTokenLine = CTLineCreateWithAttributedString((CFAttributedStringRef) truncationToken); + } + if (truncationTokenLine) { + CTLineTruncationType type = kCTLineTruncationEnd; + if (container.truncationType == ASTextTruncationTypeStart) { + type = kCTLineTruncationStart; + } else if (container.truncationType == ASTextTruncationTypeMiddle) { + type = kCTLineTruncationMiddle; + } + NSMutableAttributedString *lastLineText = [text attributedSubstringFromRange:lastLine.range].mutableCopy; + CGFloat truncatedWidth = lastLine.width; + CGFloat atLeastOneLine = lastLine.width; + CGRect cgPathRect = CGRectZero; + if (CGPathIsRect(cgPath, &cgPathRect)) { + if (isVerticalForm) { + truncatedWidth = cgPathRect.size.height; + } else { + truncatedWidth = cgPathRect.size.width; + } + } + int i = 0; + if (type != kCTLineTruncationStart) { // Middle or End/Tail wants to collect some text (at least one line's + // worth) preceding the truncated content, with which to construct a "truncated line". + i = (int)removedLines.count - 1; + while (atLeastOneLine < truncatedWidth && i >= 0) { + if (lastLineText.length > 0 && [lastLineText.string characterAtIndex:lastLineText.string.length - 1] == '\n') { // Explicit newlines are always "long enough". + [lastLineText deleteCharactersInRange:NSMakeRange(lastLineText.string.length - 1, 1)]; + break; + } + [lastLineText appendAttributedString:[text attributedSubstringFromRange:removedLines[i].range]]; + atLeastOneLine += removedLines[i--].width; + } + [lastLineText appendAttributedString:truncationToken]; + } + if (type != kCTLineTruncationEnd && removedLines.count > 0) { // Middle or Start/Head wants to collect some + // text following the truncated content. + i = 0; + atLeastOneLine = removedLines[i].width; + while (atLeastOneLine < truncatedWidth && i < removedLines.count) { + atLeastOneLine += removedLines[i++].width; + } + for (i--; i >= 0; i--) { + NSAttributedString *nextLine = [text attributedSubstringFromRange:removedLines[i].range]; + if ([nextLine.string characterAtIndex:nextLine.string.length - 1] == '\n') { // Explicit newlines are always "long enough". + lastLineText = [NSMutableAttributedString new]; + } else { + [lastLineText appendAttributedString:nextLine]; + } + } + if (type == kCTLineTruncationStart) { + [lastLineText insertAttributedString:truncationToken atIndex:0]; + } + } + + CTLineRef ctLastLineExtend = CTLineCreateWithAttributedString((CFAttributedStringRef) lastLineText); + if (ctLastLineExtend) { + CTLineRef ctTruncatedLine = CTLineCreateTruncatedLine(ctLastLineExtend, truncatedWidth, type, truncationTokenLine); + CFRelease(ctLastLineExtend); + if (ctTruncatedLine) { + truncatedLine = [ASTextLine lineWithCTLine:ctTruncatedLine position:lastLine.position vertical:isVerticalForm]; + truncatedLine.index = lastLine.index; + truncatedLine.row = lastLine.row; + CFRelease(ctTruncatedLine); + } + } + CFRelease(truncationTokenLine); + } + } + } + } + + if (isVerticalForm) { + NSCharacterSet *rotateCharset = ASTextVerticalFormRotateCharacterSet(); + NSCharacterSet *rotateMoveCharset = ASTextVerticalFormRotateAndMoveCharacterSet(); + + void (^lineBlock)(ASTextLine *) = ^(ASTextLine *line){ + CFArrayRef runs = CTLineGetGlyphRuns(line.CTLine); + if (!runs) return; + NSUInteger runCount = CFArrayGetCount(runs); + if (runCount == 0) return; + NSMutableArray *lineRunRanges = [NSMutableArray new]; + line.verticalRotateRange = lineRunRanges; + for (NSUInteger r = 0; r < runCount; r++) { + CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runs, r); + NSMutableArray *runRanges = [NSMutableArray new]; + [lineRunRanges addObject:runRanges]; + NSUInteger glyphCount = CTRunGetGlyphCount(run); + if (glyphCount == 0) continue; + + CFIndex runStrIdx[glyphCount + 1]; + CTRunGetStringIndices(run, CFRangeMake(0, 0), runStrIdx); + CFRange runStrRange = CTRunGetStringRange(run); + runStrIdx[glyphCount] = runStrRange.location + runStrRange.length; + CFDictionaryRef runAttrs = CTRunGetAttributes(run); + CTFontRef font = (CTFontRef)CFDictionaryGetValue(runAttrs, kCTFontAttributeName); + BOOL isColorGlyph = ASTextCTFontContainsColorBitmapGlyphs(font); + + NSUInteger prevIdx = 0; + ASTextRunGlyphDrawMode prevMode = ASTextRunGlyphDrawModeHorizontal; + NSString *layoutStr = layout.text.string; + for (NSUInteger g = 0; g < glyphCount; g++) { + BOOL glyphRotate = 0, glyphRotateMove = NO; + CFIndex runStrLen = runStrIdx[g + 1] - runStrIdx[g]; + if (isColorGlyph) { + glyphRotate = YES; + } else if (runStrLen == 1) { + unichar c = [layoutStr characterAtIndex:runStrIdx[g]]; + glyphRotate = [rotateCharset characterIsMember:c]; + if (glyphRotate) glyphRotateMove = [rotateMoveCharset characterIsMember:c]; + } else if (runStrLen > 1){ + NSString *glyphStr = [layoutStr substringWithRange:NSMakeRange(runStrIdx[g], runStrLen)]; + BOOL glyphRotate = [glyphStr rangeOfCharacterFromSet:rotateCharset].location != NSNotFound; + if (glyphRotate) glyphRotateMove = [glyphStr rangeOfCharacterFromSet:rotateMoveCharset].location != NSNotFound; + } + + ASTextRunGlyphDrawMode mode = glyphRotateMove ? ASTextRunGlyphDrawModeVerticalRotateMove : (glyphRotate ? ASTextRunGlyphDrawModeVerticalRotate : ASTextRunGlyphDrawModeHorizontal); + if (g == 0) { + prevMode = mode; + } else if (mode != prevMode) { + ASTextRunGlyphRange *aRange = [ASTextRunGlyphRange rangeWithRange:NSMakeRange(prevIdx, g - prevIdx) drawMode:prevMode]; + [runRanges addObject:aRange]; + prevIdx = g; + prevMode = mode; + } + } + if (prevIdx < glyphCount) { + ASTextRunGlyphRange *aRange = [ASTextRunGlyphRange rangeWithRange:NSMakeRange(prevIdx, glyphCount - prevIdx) drawMode:prevMode]; + [runRanges addObject:aRange]; + } + + } + }; + for (ASTextLine *line in lines) { + lineBlock(line); + } + if (truncatedLine) lineBlock(truncatedLine); + } + + if (visibleRange.length > 0) { + layout.needDrawText = YES; + + void (^block)(NSDictionary *attrs, NSRange range, BOOL *stop) = ^(NSDictionary *attrs, NSRange range, BOOL *stop) { + if (attrs[ASTextHighlightAttributeName]) layout.containsHighlight = YES; + if (attrs[ASTextBlockBorderAttributeName]) layout.needDrawBlockBorder = YES; + if (attrs[ASTextBackgroundBorderAttributeName]) layout.needDrawBackgroundBorder = YES; + if (attrs[ASTextShadowAttributeName] || attrs[NSShadowAttributeName]) layout.needDrawShadow = YES; + if (attrs[ASTextUnderlineAttributeName]) layout.needDrawUnderline = YES; + if (attrs[ASTextAttachmentAttributeName]) layout.needDrawAttachment = YES; + if (attrs[ASTextInnerShadowAttributeName]) layout.needDrawInnerShadow = YES; + if (attrs[ASTextStrikethroughAttributeName]) layout.needDrawStrikethrough = YES; + if (attrs[ASTextBorderAttributeName]) layout.needDrawBorder = YES; + }; + + [layout.text enumerateAttributesInRange:visibleRange options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired usingBlock:block]; + if (truncatedLine) { + [truncationToken enumerateAttributesInRange:NSMakeRange(0, truncationToken.length) options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired usingBlock:block]; + } + } + for (NSUInteger i = 0, max = lines.count; i < max; i++) { + ASTextLine *line = lines[i]; + if (truncatedLine && line.index == truncatedLine.index) line = truncatedLine; + if (line.attachments.count > 0) { + if (!attachments) { + attachments = [[NSMutableArray alloc] init]; + attachmentRanges = [[NSMutableArray alloc] init]; + attachmentRects = [[NSMutableArray alloc] init]; + attachmentContentsSet = [[NSMutableSet alloc] init]; + } + [attachments addObjectsFromArray:line.attachments]; + [attachmentRanges addObjectsFromArray:line.attachmentRanges]; + [attachmentRects addObjectsFromArray:line.attachmentRects]; + for (ASTextAttachment *attachment in line.attachments) { + if (attachment.content) { + [attachmentContentsSet addObject:attachment.content]; + } + } + } + } + + layout.frame = ctFrame; + layout.lines = lines; + layout.truncatedLine = truncatedLine; + layout.attachments = attachments; + layout.attachmentRanges = attachmentRanges; + layout.attachmentRects = attachmentRects; + layout.attachmentContentsSet = attachmentContentsSet; + layout.rowCount = rowCount; + layout.visibleRange = visibleRange; + layout.textBoundingRect = textBoundingRect; + layout.textBoundingSize = textBoundingSize; + layout.lineRowsEdge = lineRowsEdge; + layout.lineRowsIndex = lineRowsIndex; + CFRelease(cgPath); + CFRelease(ctSetter); + CFRelease(ctFrame); + if (lineOrigins) free(lineOrigins); + return layout; +} + ++ (NSArray *)layoutWithContainers:(NSArray *)containers text:(NSAttributedString *)text { + return [self layoutWithContainers:containers text:text range:NSMakeRange(0, text.length)]; +} + ++ (NSArray *)layoutWithContainers:(NSArray *)containers text:(NSAttributedString *)text range:(NSRange)range { + if (!containers || !text) return nil; + if (range.location + range.length > text.length) return nil; + NSMutableArray *layouts = [[NSMutableArray alloc] init]; + for (NSUInteger i = 0, max = containers.count; i < max; i++) { + ASTextContainer *container = containers[i]; + ASTextLayout *layout = [self layoutWithContainer:container text:text range:range]; + if (!layout) return nil; + NSInteger length = (NSInteger)range.length - (NSInteger)layout.visibleRange.length; + if (length <= 0) { + range.length = 0; + range.location = text.length; + } else { + range.length = length; + range.location += layout.visibleRange.length; + } + } + return layouts; +} + +- (void)setFrame:(CTFrameRef)frame { + if (_frame != frame) { + if (frame) CFRetain(frame); + if (_frame) CFRelease(_frame); + _frame = frame; + } +} + +- (void)dealloc { + if (_frame) CFRelease(_frame); + if (_lineRowsIndex) free(_lineRowsIndex); + if (_lineRowsEdge) free(_lineRowsEdge); +} + +#pragma mark - Copying + +- (id)copyWithZone:(NSZone *)zone { + return self; // readonly object +} + + +#pragma mark - Query + +/** + Get the row index with 'edge' distance. + + @param edge The distance from edge to the point. + If vertical form, the edge is left edge, otherwise the edge is top edge. + + @return Returns NSNotFound if there's no row at the point. + */ +- (NSUInteger)_rowIndexForEdge:(CGFloat)edge { + if (_rowCount == 0) return NSNotFound; + BOOL isVertical = _container.verticalForm; + NSUInteger lo = 0, hi = _rowCount - 1, mid = 0; + NSUInteger rowIdx = NSNotFound; + while (lo <= hi) { + mid = (lo + hi) / 2; + ASRowEdge oneEdge = _lineRowsEdge[mid]; + if (isVertical ? + (oneEdge.foot <= edge && edge <= oneEdge.head) : + (oneEdge.head <= edge && edge <= oneEdge.foot)) { + rowIdx = mid; + break; + } + if ((isVertical ? (edge > oneEdge.head) : (edge < oneEdge.head))) { + if (mid == 0) break; + hi = mid - 1; + } else { + lo = mid + 1; + } + } + return rowIdx; +} + +/** + Get the closest row index with 'edge' distance. + + @param edge The distance from edge to the point. + If vertical form, the edge is left edge, otherwise the edge is top edge. + + @return Returns NSNotFound if there's no line. + */ +- (NSUInteger)_closestRowIndexForEdge:(CGFloat)edge { + if (_rowCount == 0) return NSNotFound; + NSUInteger rowIdx = [self _rowIndexForEdge:edge]; + if (rowIdx == NSNotFound) { + if (_container.verticalForm) { + if (edge > _lineRowsEdge[0].head) { + rowIdx = 0; + } else if (edge < _lineRowsEdge[_rowCount - 1].foot) { + rowIdx = _rowCount - 1; + } + } else { + if (edge < _lineRowsEdge[0].head) { + rowIdx = 0; + } else if (edge > _lineRowsEdge[_rowCount - 1].foot) { + rowIdx = _rowCount - 1; + } + } + } + return rowIdx; +} + +/** + Get a CTRun from a line position. + + @param line The text line. + @param position The position in the whole text. + + @return Returns NULL if not found (no CTRun at the position). + */ +- (CTRunRef)_runForLine:(ASTextLine *)line position:(ASTextPosition *)position { + if (!line || !position) return NULL; + CFArrayRef runs = CTLineGetGlyphRuns(line.CTLine); + for (NSUInteger i = 0, max = CFArrayGetCount(runs); i < max; i++) { + CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runs, i); + CFRange range = CTRunGetStringRange(run); + if (position.affinity == ASTextAffinityBackward) { + if (range.location < position.offset && position.offset <= range.location + range.length) { + return run; + } + } else { + if (range.location <= position.offset && position.offset < range.location + range.length) { + return run; + } + } + } + return NULL; +} + +/** + Whether the position is inside a composed character sequence. + + @param line The text line. + @param position Text text position in whole text. + @param block The block to be executed before returns YES. + left: left X offset + right: right X offset + prev: left position + next: right position + */ +- (BOOL)_insideComposedCharacterSequences:(ASTextLine *)line position:(NSUInteger)position block:(void (^)(CGFloat left, CGFloat right, NSUInteger prev, NSUInteger next))block { + NSRange range = line.range; + if (range.length == 0) return NO; + __block BOOL inside = NO; + __block NSUInteger _prev, _next; + [_text.string enumerateSubstringsInRange:range options:NSStringEnumerationByComposedCharacterSequences usingBlock: ^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) { + NSUInteger prev = substringRange.location; + NSUInteger next = substringRange.location + substringRange.length; + if (prev == position || next == position) { + *stop = YES; + } + if (prev < position && position < next) { + inside = YES; + _prev = prev; + _next = next; + *stop = YES; + } + }]; + if (inside && block) { + CGFloat left = [self offsetForTextPosition:_prev lineIndex:line.index]; + CGFloat right = [self offsetForTextPosition:_next lineIndex:line.index]; + block(left, right, _prev, _next); + } + return inside; +} + +/** + Whether the position is inside an emoji (such as National Flag Emoji). + + @param line The text line. + @param position Text text position in whole text. + @param block Yhe block to be executed before returns YES. + left: emoji's left X offset + right: emoji's right X offset + prev: emoji's left position + next: emoji's right position + */ +- (BOOL)_insideEmoji:(ASTextLine *)line position:(NSUInteger)position block:(void (^)(CGFloat left, CGFloat right, NSUInteger prev, NSUInteger next))block { + if (!line) return NO; + CFArrayRef runs = CTLineGetGlyphRuns(line.CTLine); + for (NSUInteger r = 0, rMax = CFArrayGetCount(runs); r < rMax; r++) { + CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runs, r); + NSUInteger glyphCount = CTRunGetGlyphCount(run); + if (glyphCount == 0) continue; + CFRange range = CTRunGetStringRange(run); + if (range.length <= 1) continue; + if (position <= range.location || position >= range.location + range.length) continue; + CFDictionaryRef attrs = CTRunGetAttributes(run); + CTFontRef font = (CTFontRef)CFDictionaryGetValue(attrs, kCTFontAttributeName); + if (!ASTextCTFontContainsColorBitmapGlyphs(font)) continue; + + // Here's Emoji runs (larger than 1 unichar), and position is inside the range. + CFIndex indices[glyphCount]; + CTRunGetStringIndices(run, CFRangeMake(0, glyphCount), indices); + for (NSUInteger g = 0; g < glyphCount; g++) { + CFIndex prev = indices[g]; + CFIndex next = g + 1 < glyphCount ? indices[g + 1] : range.location + range.length; + if (position == prev) break; // Emoji edge + if (prev < position && position < next) { // inside an emoji (such as National Flag Emoji) + CGPoint pos = CGPointZero; + CGSize adv = CGSizeZero; + CTRunGetPositions(run, CFRangeMake(g, 1), &pos); + CTRunGetAdvances(run, CFRangeMake(g, 1), &adv); + if (block) { + block(line.position.x + pos.x, + line.position.x + pos.x + adv.width, + prev, next); + } + return YES; + } + } + } + return NO; +} +/** + Whether the write direction is RTL at the specified point + + @param line The text line + @param point The point in layout. + + @return YES if RTL. + */ +- (BOOL)_isRightToLeftInLine:(ASTextLine *)line atPoint:(CGPoint)point { + if (!line) return NO; + // get write direction + BOOL RTL = NO; + CFArrayRef runs = CTLineGetGlyphRuns(line.CTLine); + for (NSUInteger r = 0, max = CFArrayGetCount(runs); r < max; r++) { + CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runs, r); + CGPoint glyphPosition; + CTRunGetPositions(run, CFRangeMake(0, 1), &glyphPosition); + if (_container.verticalForm) { + CGFloat runX = glyphPosition.x; + runX += line.position.y; + CGFloat runWidth = CTRunGetTypographicBounds(run, CFRangeMake(0, 0), NULL, NULL, NULL); + if (runX <= point.y && point.y <= runX + runWidth) { + if (CTRunGetStatus(run) & kCTRunStatusRightToLeft) RTL = YES; + break; + } + } else { + CGFloat runX = glyphPosition.x; + runX += line.position.x; + CGFloat runWidth = CTRunGetTypographicBounds(run, CFRangeMake(0, 0), NULL, NULL, NULL); + if (runX <= point.x && point.x <= runX + runWidth) { + if (CTRunGetStatus(run) & kCTRunStatusRightToLeft) RTL = YES; + break; + } + } + } + return RTL; +} + +/** + Correct the range's edge. + */ +- (ASTextRange *)_correctedRangeWithEdge:(ASTextRange *)range { + NSRange visibleRange = self.visibleRange; + ASTextPosition *start = range.start; + ASTextPosition *end = range.end; + + if (start.offset == visibleRange.location && start.affinity == ASTextAffinityBackward) { + start = [ASTextPosition positionWithOffset:start.offset affinity:ASTextAffinityForward]; + } + + if (end.offset == visibleRange.location + visibleRange.length && start.affinity == ASTextAffinityForward) { + end = [ASTextPosition positionWithOffset:end.offset affinity:ASTextAffinityBackward]; + } + + if (start != range.start || end != range.end) { + range = [ASTextRange rangeWithStart:start end:end]; + } + return range; +} + +- (NSUInteger)lineIndexForRow:(NSUInteger)row { + if (row >= _rowCount) return NSNotFound; + return _lineRowsIndex[row]; +} + +- (NSUInteger)lineCountForRow:(NSUInteger)row { + if (row >= _rowCount) return NSNotFound; + if (row == _rowCount - 1) { + return _lines.count - _lineRowsIndex[row]; + } else { + return _lineRowsIndex[row + 1] - _lineRowsIndex[row]; + } +} + +- (NSUInteger)rowIndexForLine:(NSUInteger)line { + if (line >= _lines.count) return NSNotFound; + return ((ASTextLine *)_lines[line]).row; +} + +- (NSUInteger)lineIndexForPoint:(CGPoint)point { + if (_lines.count == 0 || _rowCount == 0) return NSNotFound; + NSUInteger rowIdx = [self _rowIndexForEdge:_container.verticalForm ? point.x : point.y]; + if (rowIdx == NSNotFound) return NSNotFound; + + NSUInteger lineIdx0 = _lineRowsIndex[rowIdx]; + NSUInteger lineIdx1 = rowIdx == _rowCount - 1 ? _lines.count - 1 : _lineRowsIndex[rowIdx + 1] - 1; + for (NSUInteger i = lineIdx0; i <= lineIdx1; i++) { + CGRect bounds = ((ASTextLine *)_lines[i]).bounds; + if (CGRectContainsPoint(bounds, point)) return i; + } + + return NSNotFound; +} + +- (NSUInteger)closestLineIndexForPoint:(CGPoint)point { + BOOL isVertical = _container.verticalForm; + if (_lines.count == 0 || _rowCount == 0) return NSNotFound; + NSUInteger rowIdx = [self _closestRowIndexForEdge:isVertical ? point.x : point.y]; + if (rowIdx == NSNotFound) return NSNotFound; + + NSUInteger lineIdx0 = _lineRowsIndex[rowIdx]; + NSUInteger lineIdx1 = rowIdx == _rowCount - 1 ? _lines.count - 1 : _lineRowsIndex[rowIdx + 1] - 1; + if (lineIdx0 == lineIdx1) return lineIdx0; + + CGFloat minDistance = CGFLOAT_MAX; + NSUInteger minIndex = lineIdx0; + for (NSUInteger i = lineIdx0; i <= lineIdx1; i++) { + CGRect bounds = ((ASTextLine *)_lines[i]).bounds; + if (isVertical) { + if (bounds.origin.y <= point.y && point.y <= bounds.origin.y + bounds.size.height) return i; + CGFloat distance; + if (point.y < bounds.origin.y) { + distance = bounds.origin.y - point.y; + } else { + distance = point.y - (bounds.origin.y + bounds.size.height); + } + if (distance < minDistance) { + minDistance = distance; + minIndex = i; + } + } else { + if (bounds.origin.x <= point.x && point.x <= bounds.origin.x + bounds.size.width) return i; + CGFloat distance; + if (point.x < bounds.origin.x) { + distance = bounds.origin.x - point.x; + } else { + distance = point.x - (bounds.origin.x + bounds.size.width); + } + if (distance < minDistance) { + minDistance = distance; + minIndex = i; + } + } + } + return minIndex; +} + +- (CGFloat)offsetForTextPosition:(NSUInteger)position lineIndex:(NSUInteger)lineIndex { + if (lineIndex >= _lines.count) return CGFLOAT_MAX; + ASTextLine *line = _lines[lineIndex]; + CFRange range = CTLineGetStringRange(line.CTLine); + if (position < range.location || position > range.location + range.length) return CGFLOAT_MAX; + + CGFloat offset = CTLineGetOffsetForStringIndex(line.CTLine, position, NULL); + return _container.verticalForm ? (offset + line.position.y) : (offset + line.position.x); +} + +- (NSUInteger)textPositionForPoint:(CGPoint)point lineIndex:(NSUInteger)lineIndex { + if (lineIndex >= _lines.count) return NSNotFound; + ASTextLine *line = _lines[lineIndex]; + if (_container.verticalForm) { + point.x = point.y - line.position.y; + point.y = 0; + } else { + point.x -= line.position.x; + point.y = 0; + } + CFIndex idx = CTLineGetStringIndexForPosition(line.CTLine, point); + if (idx == kCFNotFound) return NSNotFound; + + /* + If the emoji contains one or more variant form (such as ☔️ "\u2614\uFE0F") + and the font size is smaller than 379/15, then each variant form ("\uFE0F") + will rendered as a single blank glyph behind the emoji glyph. Maybe it's a + bug in CoreText? Seems iOS8.3 fixes this problem. + + If the point hit the blank glyph, the CTLineGetStringIndexForPosition() + returns the position before the emoji glyph, but it should returns the + position after the emoji and variant form. + + Here's a workaround. + */ + CFArrayRef runs = CTLineGetGlyphRuns(line.CTLine); + for (NSUInteger r = 0, max = CFArrayGetCount(runs); r < max; r++) { + CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runs, r); + CFRange range = CTRunGetStringRange(run); + if (range.location <= idx && idx < range.location + range.length) { + NSUInteger glyphCount = CTRunGetGlyphCount(run); + if (glyphCount == 0) break; + CFDictionaryRef attrs = CTRunGetAttributes(run); + CTFontRef font = (CTFontRef)CFDictionaryGetValue(attrs, kCTFontAttributeName); + if (!ASTextCTFontContainsColorBitmapGlyphs(font)) break; + + CFIndex indices[glyphCount]; + CGPoint positions[glyphCount]; + CTRunGetStringIndices(run, CFRangeMake(0, glyphCount), indices); + CTRunGetPositions(run, CFRangeMake(0, glyphCount), positions); + for (NSUInteger g = 0; g < glyphCount; g++) { + NSUInteger gIdx = indices[g]; + if (gIdx == idx && g + 1 < glyphCount) { + CGFloat right = positions[g + 1].x; + if (point.x < right) break; + NSUInteger next = indices[g + 1]; + do { + if (next == range.location + range.length) break; + unichar c = [_text.string characterAtIndex:next]; + if ((c == 0xFE0E || c == 0xFE0F)) { // unicode variant form for emoji style + next++; + } else break; + } + while (1); + if (next != indices[g + 1]) idx = next; + break; + } + } + break; + } + } + return idx; +} + +- (ASTextPosition *)closestPositionToPoint:(CGPoint)point { + BOOL isVertical = _container.verticalForm; + // When call CTLineGetStringIndexForPosition() on ligature such as 'fi', + // and the point `hit` the glyph's left edge, it may get the ligature inside offset. + // I don't know why, maybe it's a bug of CoreText. Try to avoid it. + if (isVertical) point.y += 0.00001234; + else point.x += 0.00001234; + + NSUInteger lineIndex = [self closestLineIndexForPoint:point]; + if (lineIndex == NSNotFound) return nil; + ASTextLine *line = _lines[lineIndex]; + __block NSUInteger position = [self textPositionForPoint:point lineIndex:lineIndex]; + if (position == NSNotFound) position = line.range.location; + if (position <= _visibleRange.location) { + return [ASTextPosition positionWithOffset:_visibleRange.location affinity:ASTextAffinityForward]; + } else if (position >= _visibleRange.location + _visibleRange.length) { + return [ASTextPosition positionWithOffset:_visibleRange.location + _visibleRange.length affinity:ASTextAffinityBackward]; + } + + ASTextAffinity finalAffinity = ASTextAffinityForward; + BOOL finalAffinityDetected = NO; + + // binding range + NSRange bindingRange; + ASTextBinding *binding = [_text attribute:ASTextBindingAttributeName atIndex:position longestEffectiveRange:&bindingRange inRange:NSMakeRange(0, _text.length)]; + if (binding && bindingRange.length > 0) { + NSUInteger headLineIdx = [self lineIndexForPosition:[ASTextPosition positionWithOffset:bindingRange.location]]; + NSUInteger tailLineIdx = [self lineIndexForPosition:[ASTextPosition positionWithOffset:bindingRange.location + bindingRange.length affinity:ASTextAffinityBackward]]; + if (headLineIdx == lineIndex && lineIndex == tailLineIdx) { // all in same line + CGFloat left = [self offsetForTextPosition:bindingRange.location lineIndex:lineIndex]; + CGFloat right = [self offsetForTextPosition:bindingRange.location + bindingRange.length lineIndex:lineIndex]; + if (left != CGFLOAT_MAX && right != CGFLOAT_MAX) { + if (_container.isVerticalForm) { + if (fabs(point.y - left) < fabs(point.y - right)) { + position = bindingRange.location; + finalAffinity = ASTextAffinityForward; + } else { + position = bindingRange.location + bindingRange.length; + finalAffinity = ASTextAffinityBackward; + } + } else { + if (fabs(point.x - left) < fabs(point.x - right)) { + position = bindingRange.location; + finalAffinity = ASTextAffinityForward; + } else { + position = bindingRange.location + bindingRange.length; + finalAffinity = ASTextAffinityBackward; + } + } + } else if (left != CGFLOAT_MAX) { + position = left; + finalAffinity = ASTextAffinityForward; + } else if (right != CGFLOAT_MAX) { + position = right; + finalAffinity = ASTextAffinityBackward; + } + finalAffinityDetected = YES; + } else if (headLineIdx == lineIndex) { + CGFloat left = [self offsetForTextPosition:bindingRange.location lineIndex:lineIndex]; + if (left != CGFLOAT_MAX) { + position = bindingRange.location; + finalAffinity = ASTextAffinityForward; + finalAffinityDetected = YES; + } + } else if (tailLineIdx == lineIndex) { + CGFloat right = [self offsetForTextPosition:bindingRange.location + bindingRange.length lineIndex:lineIndex]; + if (right != CGFLOAT_MAX) { + position = bindingRange.location + bindingRange.length; + finalAffinity = ASTextAffinityBackward; + finalAffinityDetected = YES; + } + } else { + BOOL onLeft = NO, onRight = NO; + if (headLineIdx != NSNotFound && tailLineIdx != NSNotFound) { + if (abs((int)headLineIdx - (int)lineIndex) < abs((int)tailLineIdx - (int)lineIndex)) onLeft = YES; + else onRight = YES; + } else if (headLineIdx != NSNotFound) { + onLeft = YES; + } else if (tailLineIdx != NSNotFound) { + onRight = YES; + } + + if (onLeft) { + CGFloat left = [self offsetForTextPosition:bindingRange.location lineIndex:headLineIdx]; + if (left != CGFLOAT_MAX) { + lineIndex = headLineIdx; + line = _lines[headLineIdx]; + position = bindingRange.location; + finalAffinity = ASTextAffinityForward; + finalAffinityDetected = YES; + } + } else if (onRight) { + CGFloat right = [self offsetForTextPosition:bindingRange.location + bindingRange.length lineIndex:tailLineIdx]; + if (right != CGFLOAT_MAX) { + lineIndex = tailLineIdx; + line = _lines[tailLineIdx]; + position = bindingRange.location + bindingRange.length; + finalAffinity = ASTextAffinityBackward; + finalAffinityDetected = YES; + } + } + } + } + + // empty line + if (line.range.length == 0) { + BOOL behind = (_lines.count > 1 && lineIndex == _lines.count - 1); //end line + return [ASTextPosition positionWithOffset:line.range.location affinity:behind ? ASTextAffinityBackward:ASTextAffinityForward]; + } + + // detect weather the line is a linebreak token + if (line.range.length <= 2) { + NSString *str = [_text.string substringWithRange:line.range]; + if (ASTextIsLinebreakString(str)) { // an empty line ("\r", "\n", "\r\n") + return [ASTextPosition positionWithOffset:line.range.location]; + } + } + + // above whole text frame + if (lineIndex == 0 && (isVertical ? (point.x > line.right) : (point.y < line.top))) { + position = 0; + finalAffinity = ASTextAffinityForward; + finalAffinityDetected = YES; + } + // below whole text frame + if (lineIndex == _lines.count - 1 && (isVertical ? (point.x < line.left) : (point.y > line.bottom))) { + position = line.range.location + line.range.length; + finalAffinity = ASTextAffinityBackward; + finalAffinityDetected = YES; + } + + // There must be at least one non-linebreak char, + // ignore the linebreak characters at line end if exists. + if (position >= line.range.location + line.range.length - 1) { + if (position > line.range.location) { + unichar c1 = [_text.string characterAtIndex:position - 1]; + if (ASTextIsLinebreakChar(c1)) { + position--; + if (position > line.range.location) { + unichar c0 = [_text.string characterAtIndex:position - 1]; + if (ASTextIsLinebreakChar(c0)) { + position--; + } + } + } + } + } + if (position == line.range.location) { + return [ASTextPosition positionWithOffset:position]; + } + if (position == line.range.location + line.range.length) { + return [ASTextPosition positionWithOffset:position affinity:ASTextAffinityBackward]; + } + + [self _insideComposedCharacterSequences:line position:position block: ^(CGFloat left, CGFloat right, NSUInteger prev, NSUInteger next) { + if (isVertical) { + position = fabs(left - point.y) < fabs(right - point.y) < (right ? prev : next); + } else { + position = fabs(left - point.x) < fabs(right - point.x) < (right ? prev : next); + } + }]; + + [self _insideEmoji:line position:position block: ^(CGFloat left, CGFloat right, NSUInteger prev, NSUInteger next) { + if (isVertical) { + position = fabs(left - point.y) < fabs(right - point.y) < (right ? prev : next); + } else { + position = fabs(left - point.x) < fabs(right - point.x) < (right ? prev : next); + } + }]; + + if (position < _visibleRange.location) position = _visibleRange.location; + else if (position > _visibleRange.location + _visibleRange.length) position = _visibleRange.location + _visibleRange.length; + + if (!finalAffinityDetected) { + CGFloat ofs = [self offsetForTextPosition:position lineIndex:lineIndex]; + if (ofs != CGFLOAT_MAX) { + BOOL RTL = [self _isRightToLeftInLine:line atPoint:point]; + if (position >= line.range.location + line.range.length) { + finalAffinity = RTL ? ASTextAffinityForward : ASTextAffinityBackward; + } else if (position <= line.range.location) { + finalAffinity = RTL ? ASTextAffinityBackward : ASTextAffinityForward; + } else { + finalAffinity = (ofs < (isVertical ? point.y : point.x) && !RTL) ? ASTextAffinityForward : ASTextAffinityBackward; + } + } + } + + return [ASTextPosition positionWithOffset:position affinity:finalAffinity]; +} + +- (ASTextPosition *)positionForPoint:(CGPoint)point + oldPosition:(ASTextPosition *)oldPosition + otherPosition:(ASTextPosition *)otherPosition { + if (!oldPosition || !otherPosition) { + return oldPosition; + } + ASTextPosition *newPos = [self closestPositionToPoint:point]; + if (!newPos) return oldPosition; + if ([newPos compare:otherPosition] == [oldPosition compare:otherPosition] && + newPos.offset != otherPosition.offset) { + return newPos; + } + NSUInteger lineIndex = [self lineIndexForPosition:otherPosition]; + if (lineIndex == NSNotFound) return oldPosition; + ASTextLine *line = _lines[lineIndex]; + ASRowEdge vertical = _lineRowsEdge[line.row]; + if (_container.verticalForm) { + point.x = (vertical.head + vertical.foot) * 0.5; + } else { + point.y = (vertical.head + vertical.foot) * 0.5; + } + newPos = [self closestPositionToPoint:point]; + if ([newPos compare:otherPosition] == [oldPosition compare:otherPosition] && + newPos.offset != otherPosition.offset) { + return newPos; + } + + if (_container.isVerticalForm) { + if ([oldPosition compare:otherPosition] == NSOrderedAscending) { // search backward + ASTextRange *range = [self textRangeByExtendingPosition:otherPosition inDirection:UITextLayoutDirectionUp offset:1]; + if (range) return range.start; + } else { // search forward + ASTextRange *range = [self textRangeByExtendingPosition:otherPosition inDirection:UITextLayoutDirectionDown offset:1]; + if (range) return range.end; + } + } else { + if ([oldPosition compare:otherPosition] == NSOrderedAscending) { // search backward + ASTextRange *range = [self textRangeByExtendingPosition:otherPosition inDirection:UITextLayoutDirectionLeft offset:1]; + if (range) return range.start; + } else { // search forward + ASTextRange *range = [self textRangeByExtendingPosition:otherPosition inDirection:UITextLayoutDirectionRight offset:1]; + if (range) return range.end; + } + } + + return oldPosition; +} + +- (ASTextRange *)textRangeAtPoint:(CGPoint)point { + NSUInteger lineIndex = [self lineIndexForPoint:point]; + if (lineIndex == NSNotFound) return nil; + NSUInteger textPosition = [self textPositionForPoint:point lineIndex:[self lineIndexForPoint:point]]; + if (textPosition == NSNotFound) return nil; + ASTextPosition *pos = [self closestPositionToPoint:point]; + if (!pos) return nil; + + // get write direction + BOOL RTL = [self _isRightToLeftInLine:_lines[lineIndex] atPoint:point]; + CGRect rect = [self caretRectForPosition:pos]; + if (CGRectIsNull(rect)) return nil; + + if (_container.verticalForm) { + ASTextRange *range = [self textRangeByExtendingPosition:pos inDirection:(rect.origin.y >= point.y && !RTL) ? UITextLayoutDirectionUp:UITextLayoutDirectionDown offset:1]; + return range; + } else { + ASTextRange *range = [self textRangeByExtendingPosition:pos inDirection:(rect.origin.x >= point.x && !RTL) ? UITextLayoutDirectionLeft:UITextLayoutDirectionRight offset:1]; + return range; + } +} + +- (ASTextRange *)closestTextRangeAtPoint:(CGPoint)point { + ASTextPosition *pos = [self closestPositionToPoint:point]; + if (!pos) return nil; + NSUInteger lineIndex = [self lineIndexForPosition:pos]; + if (lineIndex == NSNotFound) return nil; + ASTextLine *line = _lines[lineIndex]; + BOOL RTL = [self _isRightToLeftInLine:line atPoint:point]; + CGRect rect = [self caretRectForPosition:pos]; + if (CGRectIsNull(rect)) return nil; + + UITextLayoutDirection direction = UITextLayoutDirectionRight; + if (pos.offset >= line.range.location + line.range.length) { + if (direction != RTL) { + direction = _container.verticalForm ? UITextLayoutDirectionUp : UITextLayoutDirectionLeft; + } else { + direction = _container.verticalForm ? UITextLayoutDirectionDown : UITextLayoutDirectionRight; + } + } else if (pos.offset <= line.range.location) { + if (direction != RTL) { + direction = _container.verticalForm ? UITextLayoutDirectionDown : UITextLayoutDirectionRight; + } else { + direction = _container.verticalForm ? UITextLayoutDirectionUp : UITextLayoutDirectionLeft; + } + } else { + if (_container.verticalForm) { + direction = (rect.origin.y >= point.y && !RTL) ? UITextLayoutDirectionUp:UITextLayoutDirectionDown; + } else { + direction = (rect.origin.x >= point.x && !RTL) ? UITextLayoutDirectionLeft:UITextLayoutDirectionRight; + } + } + + ASTextRange *range = [self textRangeByExtendingPosition:pos inDirection:direction offset:1]; + return range; +} + +- (ASTextRange *)textRangeByExtendingPosition:(ASTextPosition *)position { + NSUInteger visibleStart = _visibleRange.location; + NSUInteger visibleEnd = _visibleRange.location + _visibleRange.length; + + if (!position) return nil; + if (position.offset < visibleStart || position.offset > visibleEnd) return nil; + + // head or tail, returns immediately + if (position.offset == visibleStart) { + return [ASTextRange rangeWithRange:NSMakeRange(position.offset, 0)]; + } else if (position.offset == visibleEnd) { + return [ASTextRange rangeWithRange:NSMakeRange(position.offset, 0) affinity:ASTextAffinityBackward]; + } + + // binding range + NSRange tRange; + ASTextBinding *binding = [_text attribute:ASTextBindingAttributeName atIndex:position.offset longestEffectiveRange:&tRange inRange:_visibleRange]; + if (binding && tRange.length > 0 && tRange.location < position.offset) { + return [ASTextRange rangeWithRange:tRange]; + } + + // inside emoji or composed character sequences + NSUInteger lineIndex = [self lineIndexForPosition:position]; + if (lineIndex != NSNotFound) { + __block NSUInteger _prev, _next; + BOOL emoji = NO, seq = NO; + + ASTextLine *line = _lines[lineIndex]; + emoji = [self _insideEmoji:line position:position.offset block: ^(CGFloat left, CGFloat right, NSUInteger prev, NSUInteger next) { + _prev = prev; + _next = next; + }]; + if (!emoji) { + seq = [self _insideComposedCharacterSequences:line position:position.offset block: ^(CGFloat left, CGFloat right, NSUInteger prev, NSUInteger next) { + _prev = prev; + _next = next; + }]; + } + if (emoji || seq) { + return [ASTextRange rangeWithRange:NSMakeRange(_prev, _next - _prev)]; + } + } + + // inside linebreak '\r\n' + if (position.offset > visibleStart && position.offset < visibleEnd) { + unichar c0 = [_text.string characterAtIndex:position.offset - 1]; + if ((c0 == '\r') && position.offset < visibleEnd) { + unichar c1 = [_text.string characterAtIndex:position.offset]; + if (c1 == '\n') { + return [ASTextRange rangeWithStart:[ASTextPosition positionWithOffset:position.offset - 1] end:[ASTextPosition positionWithOffset:position.offset + 1]]; + } + } + if (ASTextIsLinebreakChar(c0) && position.affinity == ASTextAffinityBackward) { + NSString *str = [_text.string substringToIndex:position.offset]; + NSUInteger len = ASTextLinebreakTailLength(str); + return [ASTextRange rangeWithStart:[ASTextPosition positionWithOffset:position.offset - len] end:[ASTextPosition positionWithOffset:position.offset]]; + } + } + + return [ASTextRange rangeWithRange:NSMakeRange(position.offset, 0) affinity:position.affinity]; +} + +- (ASTextRange *)textRangeByExtendingPosition:(ASTextPosition *)position + inDirection:(UITextLayoutDirection)direction + offset:(NSInteger)offset { + NSInteger visibleStart = _visibleRange.location; + NSInteger visibleEnd = _visibleRange.location + _visibleRange.length; + + if (!position) return nil; + if (position.offset < visibleStart || position.offset > visibleEnd) return nil; + if (offset == 0) return [self textRangeByExtendingPosition:position]; + + BOOL isVerticalForm = _container.verticalForm; + BOOL verticalMove, forwardMove; + + if (isVerticalForm) { + verticalMove = direction == UITextLayoutDirectionLeft || direction == UITextLayoutDirectionRight; + forwardMove = direction == UITextLayoutDirectionLeft || direction == UITextLayoutDirectionDown; + } else { + verticalMove = direction == UITextLayoutDirectionUp || direction == UITextLayoutDirectionDown; + forwardMove = direction == UITextLayoutDirectionDown || direction == UITextLayoutDirectionRight; + } + + if (offset < 0) { + forwardMove = !forwardMove; + offset = -offset; + } + + // head or tail, returns immediately + if (!forwardMove && position.offset == visibleStart) { + return [ASTextRange rangeWithRange:NSMakeRange(_visibleRange.location, 0)]; + } else if (forwardMove && position.offset == visibleEnd) { + return [ASTextRange rangeWithRange:NSMakeRange(position.offset, 0) affinity:ASTextAffinityBackward]; + } + + // extend from position + ASTextRange *fromRange = [self textRangeByExtendingPosition:position]; + if (!fromRange) return nil; + ASTextRange *allForward = [ASTextRange rangeWithStart:fromRange.start end:[ASTextPosition positionWithOffset:visibleEnd]]; + ASTextRange *allBackward = [ASTextRange rangeWithStart:[ASTextPosition positionWithOffset:visibleStart] end:fromRange.end]; + + if (verticalMove) { // up/down in text layout + NSInteger lineIndex = [self lineIndexForPosition:position]; + if (lineIndex == NSNotFound) return nil; + + ASTextLine *line = _lines[lineIndex]; + NSInteger moveToRowIndex = (NSInteger)line.row + (forwardMove ? offset : -offset); + if (moveToRowIndex < 0) return allBackward; + else if (moveToRowIndex >= (NSInteger)_rowCount) return allForward; + + CGFloat ofs = [self offsetForTextPosition:position.offset lineIndex:lineIndex]; + if (ofs == CGFLOAT_MAX) return nil; + + NSUInteger moveToLineFirstIndex = [self lineIndexForRow:moveToRowIndex]; + NSUInteger moveToLineCount = [self lineCountForRow:moveToRowIndex]; + if (moveToLineFirstIndex == NSNotFound || moveToLineCount == NSNotFound || moveToLineCount == 0) return nil; + CGFloat mostLeft = CGFLOAT_MAX, mostRight = -CGFLOAT_MAX; + ASTextLine *mostLeftLine = nil, *mostRightLine = nil; + NSUInteger insideIndex = NSNotFound; + for (NSUInteger i = 0; i < moveToLineCount; i++) { + NSUInteger lineIndex = moveToLineFirstIndex + i; + ASTextLine *line = _lines[lineIndex]; + if (isVerticalForm) { + if (line.top <= ofs && ofs <= line.bottom) { + insideIndex = line.index; + break; + } + if (line.top < mostLeft) { + mostLeft = line.top; + mostLeftLine = line; + } + if (line.bottom > mostRight) { + mostRight = line.bottom; + mostRightLine = line; + } + } else { + if (line.left <= ofs && ofs <= line.right) { + insideIndex = line.index; + break; + } + if (line.left < mostLeft) { + mostLeft = line.left; + mostLeftLine = line; + } + if (line.right > mostRight) { + mostRight = line.right; + mostRightLine = line; + } + } + } + BOOL afinityEdge = NO; + if (insideIndex == NSNotFound) { + if (ofs <= mostLeft) { + insideIndex = mostLeftLine.index; + } else { + insideIndex = mostRightLine.index; + } + afinityEdge = YES; + } + ASTextLine *insideLine = _lines[insideIndex]; + NSUInteger pos; + if (isVerticalForm) { + pos = [self textPositionForPoint:CGPointMake(insideLine.position.x, ofs) lineIndex:insideIndex]; + } else { + pos = [self textPositionForPoint:CGPointMake(ofs, insideLine.position.y) lineIndex:insideIndex]; + } + if (pos == NSNotFound) return nil; + ASTextPosition *extPos; + if (afinityEdge) { + if (pos == insideLine.range.location + insideLine.range.length) { + NSString *subStr = [_text.string substringWithRange:insideLine.range]; + NSUInteger lineBreakLen = ASTextLinebreakTailLength(subStr); + extPos = [ASTextPosition positionWithOffset:pos - lineBreakLen]; + } else { + extPos = [ASTextPosition positionWithOffset:pos]; + } + } else { + extPos = [ASTextPosition positionWithOffset:pos]; + } + ASTextRange *ext = [self textRangeByExtendingPosition:extPos]; + if (!ext) return nil; + if (forwardMove) { + return [ASTextRange rangeWithStart:fromRange.start end:ext.end]; + } else { + return [ASTextRange rangeWithStart:ext.start end:fromRange.end]; + } + + } else { // left/right in text layout + ASTextPosition *toPosition = [ASTextPosition positionWithOffset:position.offset + (forwardMove ? offset : -offset)]; + if (toPosition.offset <= visibleStart) return allBackward; + else if (toPosition.offset >= visibleEnd) return allForward; + + ASTextRange *toRange = [self textRangeByExtendingPosition:toPosition]; + if (!toRange) return nil; + + NSInteger start = MIN(fromRange.start.offset, toRange.start.offset); + NSInteger end = MAX(fromRange.end.offset, toRange.end.offset); + return [ASTextRange rangeWithRange:NSMakeRange(start, end - start)]; + } +} + +- (NSUInteger)lineIndexForPosition:(ASTextPosition *)position { + if (!position) return NSNotFound; + if (_lines.count == 0) return NSNotFound; + NSUInteger location = position.offset; + NSInteger lo = 0, hi = _lines.count - 1, mid = 0; + if (position.affinity == ASTextAffinityBackward) { + while (lo <= hi) { + mid = (lo + hi) / 2; + ASTextLine *line = _lines[mid]; + NSRange range = line.range; + if (range.location < location && location <= range.location + range.length) { + return mid; + } + if (location <= range.location) { + hi = mid - 1; + } else { + lo = mid + 1; + } + } + } else { + while (lo <= hi) { + mid = (lo + hi) / 2; + ASTextLine *line = _lines[mid]; + NSRange range = line.range; + if (range.location <= location && location < range.location + range.length) { + return mid; + } + if (location < range.location) { + hi = mid - 1; + } else { + lo = mid + 1; + } + } + } + return NSNotFound; +} + +- (CGPoint)linePositionForPosition:(ASTextPosition *)position { + NSUInteger lineIndex = [self lineIndexForPosition:position]; + if (lineIndex == NSNotFound) return CGPointZero; + ASTextLine *line = _lines[lineIndex]; + CGFloat offset = [self offsetForTextPosition:position.offset lineIndex:lineIndex]; + if (offset == CGFLOAT_MAX) return CGPointZero; + if (_container.verticalForm) { + return CGPointMake(line.position.x, offset); + } else { + return CGPointMake(offset, line.position.y); + } +} + +- (CGRect)caretRectForPosition:(ASTextPosition *)position { + NSUInteger lineIndex = [self lineIndexForPosition:position]; + if (lineIndex == NSNotFound) return CGRectNull; + ASTextLine *line = _lines[lineIndex]; + CGFloat offset = [self offsetForTextPosition:position.offset lineIndex:lineIndex]; + if (offset == CGFLOAT_MAX) return CGRectNull; + if (_container.verticalForm) { + return CGRectMake(line.bounds.origin.x, offset, line.bounds.size.width, 0); + } else { + return CGRectMake(offset, line.bounds.origin.y, 0, line.bounds.size.height); + } +} + +- (CGRect)firstRectForRange:(ASTextRange *)range { + range = [self _correctedRangeWithEdge:range]; + + NSUInteger startLineIndex = [self lineIndexForPosition:range.start]; + NSUInteger endLineIndex = [self lineIndexForPosition:range.end]; + if (startLineIndex == NSNotFound || endLineIndex == NSNotFound) return CGRectNull; + if (startLineIndex > endLineIndex) return CGRectNull; + ASTextLine *startLine = _lines[startLineIndex]; + ASTextLine *endLine = _lines[endLineIndex]; + NSMutableArray *lines = [NSMutableArray new]; + for (NSUInteger i = startLineIndex; i <= startLineIndex; i++) { + ASTextLine *line = _lines[i]; + if (line.row != startLine.row) break; + [lines addObject:line]; + } + if (_container.verticalForm) { + if (lines.count == 1) { + CGFloat top = [self offsetForTextPosition:range.start.offset lineIndex:startLineIndex]; + CGFloat bottom; + if (startLine == endLine) { + bottom = [self offsetForTextPosition:range.end.offset lineIndex:startLineIndex]; + } else { + bottom = startLine.bottom; + } + if (top == CGFLOAT_MAX || bottom == CGFLOAT_MAX) return CGRectNull; + if (top > bottom) ASTEXT_SWAP(top, bottom); + return CGRectMake(startLine.left, top, startLine.width, bottom - top); + } else { + CGFloat top = [self offsetForTextPosition:range.start.offset lineIndex:startLineIndex]; + CGFloat bottom = startLine.bottom; + if (top == CGFLOAT_MAX || bottom == CGFLOAT_MAX) return CGRectNull; + if (top > bottom) ASTEXT_SWAP(top, bottom); + CGRect rect = CGRectMake(startLine.left, top, startLine.width, bottom - top); + for (NSUInteger i = 1; i < lines.count; i++) { + ASTextLine *line = lines[i]; + rect = CGRectUnion(rect, line.bounds); + } + return rect; + } + } else { + if (lines.count == 1) { + CGFloat left = [self offsetForTextPosition:range.start.offset lineIndex:startLineIndex]; + CGFloat right; + if (startLine == endLine) { + right = [self offsetForTextPosition:range.end.offset lineIndex:startLineIndex]; + } else { + right = startLine.right; + } + if (left == CGFLOAT_MAX || right == CGFLOAT_MAX) return CGRectNull; + if (left > right) ASTEXT_SWAP(left, right); + return CGRectMake(left, startLine.top, right - left, startLine.height); + } else { + CGFloat left = [self offsetForTextPosition:range.start.offset lineIndex:startLineIndex]; + CGFloat right = startLine.right; + if (left == CGFLOAT_MAX || right == CGFLOAT_MAX) return CGRectNull; + if (left > right) ASTEXT_SWAP(left, right); + CGRect rect = CGRectMake(left, startLine.top, right - left, startLine.height); + for (NSUInteger i = 1; i < lines.count; i++) { + ASTextLine *line = lines[i]; + rect = CGRectUnion(rect, line.bounds); + } + return rect; + } + } +} + +- (CGRect)rectForRange:(ASTextRange *)range { + NSArray *rects = [self selectionRectsForRange:range]; + if (rects.count == 0) return CGRectNull; + CGRect rectUnion = ((ASTextSelectionRect *)rects.firstObject).rect; + for (NSUInteger i = 1; i < rects.count; i++) { + ASTextSelectionRect *rect = rects[i]; + rectUnion = CGRectUnion(rectUnion, rect.rect); + } + return rectUnion; +} + +- (NSArray *)selectionRectsForRange:(ASTextRange *)range { + range = [self _correctedRangeWithEdge:range]; + + BOOL isVertical = _container.verticalForm; + NSMutableArray *rects = [[NSMutableArray alloc] init]; + if (!range) return rects; + + NSUInteger startLineIndex = [self lineIndexForPosition:range.start]; + NSUInteger endLineIndex = [self lineIndexForPosition:range.end]; + if (startLineIndex == NSNotFound || endLineIndex == NSNotFound) return rects; + if (startLineIndex > endLineIndex) ASTEXT_SWAP(startLineIndex, endLineIndex); + ASTextLine *startLine = _lines[startLineIndex]; + ASTextLine *endLine = _lines[endLineIndex]; + CGFloat offsetStart = [self offsetForTextPosition:range.start.offset lineIndex:startLineIndex]; + CGFloat offsetEnd = [self offsetForTextPosition:range.end.offset lineIndex:endLineIndex]; + + ASTextSelectionRect *start = [ASTextSelectionRect new]; + if (isVertical) { + start.rect = CGRectMake(startLine.left, offsetStart, startLine.width, 0); + } else { + start.rect = CGRectMake(offsetStart, startLine.top, 0, startLine.height); + } + start.containsStart = YES; + start.isVertical = isVertical; + [rects addObject:start]; + + ASTextSelectionRect *end = [ASTextSelectionRect new]; + if (isVertical) { + end.rect = CGRectMake(endLine.left, offsetEnd, endLine.width, 0); + } else { + end.rect = CGRectMake(offsetEnd, endLine.top, 0, endLine.height); + } + end.containsEnd = YES; + end.isVertical = isVertical; + [rects addObject:end]; + + if (startLine.row == endLine.row) { // same row + if (offsetStart > offsetEnd) ASTEXT_SWAP(offsetStart, offsetEnd); + ASTextSelectionRect *rect = [ASTextSelectionRect new]; + if (isVertical) { + rect.rect = CGRectMake(startLine.bounds.origin.x, offsetStart, MAX(startLine.width, endLine.width), offsetEnd - offsetStart); + } else { + rect.rect = CGRectMake(offsetStart, startLine.bounds.origin.y, offsetEnd - offsetStart, MAX(startLine.height, endLine.height)); + } + rect.isVertical = isVertical; + [rects addObject:rect]; + + } else { // more than one row + + // start line select rect + ASTextSelectionRect *topRect = [ASTextSelectionRect new]; + topRect.isVertical = isVertical; + CGFloat topOffset = [self offsetForTextPosition:range.start.offset lineIndex:startLineIndex]; + CTRunRef topRun = [self _runForLine:startLine position:range.start]; + if (topRun && (CTRunGetStatus(topRun) & kCTRunStatusRightToLeft)) { + if (isVertical) { + topRect.rect = CGRectMake(startLine.left, _container.path ? startLine.top : _container.insets.top, startLine.width, topOffset - startLine.top); + } else { + topRect.rect = CGRectMake(_container.path ? startLine.left : _container.insets.left, startLine.top, topOffset - startLine.left, startLine.height); + } + topRect.writingDirection = UITextWritingDirectionRightToLeft; + } else { + if (isVertical) { + topRect.rect = CGRectMake(startLine.left, topOffset, startLine.width, (_container.path ? startLine.bottom : _container.size.height - _container.insets.bottom) - topOffset); + } else { + // TODO: Fixes highlighting first row only to the end of the text and not highlight + // the while line to the end. Needs to brought over to multiline support + topRect.rect = CGRectMake(topOffset, startLine.top, (_container.path ? startLine.right : _container.size.width - _container.insets.right) - topOffset - (_container.size.width - _container.insets.right - startLine.right), startLine.height); + } + } + [rects addObject:topRect]; + + // end line select rect + ASTextSelectionRect *bottomRect = [ASTextSelectionRect new]; + bottomRect.isVertical = isVertical; + CGFloat bottomOffset = [self offsetForTextPosition:range.end.offset lineIndex:endLineIndex]; + CTRunRef bottomRun = [self _runForLine:endLine position:range.end]; + if (bottomRun && (CTRunGetStatus(bottomRun) & kCTRunStatusRightToLeft)) { + if (isVertical) { + bottomRect.rect = CGRectMake(endLine.left, bottomOffset, endLine.width, (_container.path ? endLine.bottom : _container.size.height - _container.insets.bottom) - bottomOffset); + } else { + bottomRect.rect = CGRectMake(bottomOffset, endLine.top, (_container.path ? endLine.right : _container.size.width - _container.insets.right) - bottomOffset, endLine.height); + } + bottomRect.writingDirection = UITextWritingDirectionRightToLeft; + } else { + if (isVertical) { + CGFloat top = _container.path ? endLine.top : _container.insets.top; + bottomRect.rect = CGRectMake(endLine.left, top, endLine.width, bottomOffset - top); + } else { + CGFloat left = _container.path ? endLine.left : _container.insets.left; + bottomRect.rect = CGRectMake(left, endLine.top, bottomOffset - left, endLine.height); + } + } + [rects addObject:bottomRect]; + + if (endLineIndex - startLineIndex >= 2) { + CGRect r = CGRectZero; + BOOL startLineDetected = NO; + for (NSUInteger l = startLineIndex + 1; l < endLineIndex; l++) { + ASTextLine *line = _lines[l]; + if (line.row == startLine.row || line.row == endLine.row) continue; + if (!startLineDetected) { + r = line.bounds; + startLineDetected = YES; + } else { + r = CGRectUnion(r, line.bounds); + } + } + if (startLineDetected) { + if (isVertical) { + if (!_container.path) { + r.origin.y = _container.insets.top; + r.size.height = _container.size.height - _container.insets.bottom - _container.insets.top; + } + r.size.width = CGRectGetMinX(topRect.rect) - CGRectGetMaxX(bottomRect.rect); + r.origin.x = CGRectGetMaxX(bottomRect.rect); + } else { + if (!_container.path) { + r.origin.x = _container.insets.left; + r.size.width = _container.size.width - _container.insets.right - _container.insets.left; + } + r.origin.y = CGRectGetMaxY(topRect.rect); + r.size.height = bottomRect.rect.origin.y - r.origin.y; + } + + ASTextSelectionRect *rect = [ASTextSelectionRect new]; + rect.rect = r; + rect.isVertical = isVertical; + [rects addObject:rect]; + } + } else { + if (isVertical) { + CGRect r0 = bottomRect.rect; + CGRect r1 = topRect.rect; + CGFloat mid = (CGRectGetMaxX(r0) + CGRectGetMinX(r1)) * 0.5; + r0.size.width = mid - r0.origin.x; + CGFloat r1ofs = r1.origin.x - mid; + r1.origin.x -= r1ofs; + r1.size.width += r1ofs; + topRect.rect = r1; + bottomRect.rect = r0; + } else { + CGRect r0 = topRect.rect; + CGRect r1 = bottomRect.rect; + CGFloat mid = (CGRectGetMaxY(r0) + CGRectGetMinY(r1)) * 0.5; + r0.size.height = mid - r0.origin.y; + CGFloat r1ofs = r1.origin.y - mid; + r1.origin.y -= r1ofs; + r1.size.height += r1ofs; + topRect.rect = r0; + bottomRect.rect = r1; + } + } + } + return rects; +} + +- (NSArray *)selectionRectsWithoutStartAndEndForRange:(ASTextRange *)range { + NSMutableArray *rects = [self selectionRectsForRange:range].mutableCopy; + for (NSInteger i = 0, max = rects.count; i < max; i++) { + ASTextSelectionRect *rect = rects[i]; + if (rect.containsStart || rect.containsEnd) { + [rects removeObjectAtIndex:i]; + i--; + max--; + } + } + return rects; +} + +- (NSArray *)selectionRectsWithOnlyStartAndEndForRange:(ASTextRange *)range { + NSMutableArray *rects = [self selectionRectsForRange:range].mutableCopy; + for (NSInteger i = 0, max = rects.count; i < max; i++) { + ASTextSelectionRect *rect = rects[i]; + if (!rect.containsStart && !rect.containsEnd) { + [rects removeObjectAtIndex:i]; + i--; + max--; + } + } + return rects; +} + + +#pragma mark - Draw + + +typedef NS_OPTIONS(NSUInteger, ASTextDecorationType) { + ASTextDecorationTypeUnderline = 1 << 0, + ASTextDecorationTypeStrikethrough = 1 << 1, +}; + +typedef NS_OPTIONS(NSUInteger, ASTextBorderType) { + ASTextBorderTypeBackgound = 1 << 0, + ASTextBorderTypeNormal = 1 << 1, +}; + +static CGRect ASTextMergeRectInSameLine(CGRect rect1, CGRect rect2, BOOL isVertical) { + if (isVertical) { + CGFloat top = MIN(rect1.origin.y, rect2.origin.y); + CGFloat bottom = MAX(rect1.origin.y + rect1.size.height, rect2.origin.y + rect2.size.height); + CGFloat width = MAX(rect1.size.width, rect2.size.width); + return CGRectMake(rect1.origin.x, top, width, bottom - top); + } else { + CGFloat left = MIN(rect1.origin.x, rect2.origin.x); + CGFloat right = MAX(rect1.origin.x + rect1.size.width, rect2.origin.x + rect2.size.width); + CGFloat height = MAX(rect1.size.height, rect2.size.height); + return CGRectMake(left, rect1.origin.y, right - left, height); + } +} + +static void ASTextGetRunsMaxMetric(CFArrayRef runs, CGFloat *xHeight, CGFloat *underlinePosition, CGFloat *lineThickness) { + CGFloat maxXHeight = 0; + CGFloat maxUnderlinePos = 0; + CGFloat maxLineThickness = 0; + for (NSUInteger i = 0, max = CFArrayGetCount(runs); i < max; i++) { + CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runs, i); + CFDictionaryRef attrs = CTRunGetAttributes(run); + if (attrs) { + CTFontRef font = (CTFontRef)CFDictionaryGetValue(attrs, kCTFontAttributeName); + if (font) { + CGFloat xHeight = CTFontGetXHeight(font); + if (xHeight > maxXHeight) maxXHeight = xHeight; + CGFloat underlinePos = CTFontGetUnderlinePosition(font); + if (underlinePos < maxUnderlinePos) maxUnderlinePos = underlinePos; + CGFloat lineThickness = CTFontGetUnderlineThickness(font); + if (lineThickness > maxLineThickness) maxLineThickness = lineThickness; + } + } + } + if (xHeight) *xHeight = maxXHeight; + if (underlinePosition) *underlinePosition = maxUnderlinePos; + if (lineThickness) *lineThickness = maxLineThickness; +} + +static void ASTextDrawRun(ASTextLine *line, CTRunRef run, CGContextRef context, CGSize size, BOOL isVertical, NSArray *runRanges, CGFloat verticalOffset) { + CGAffineTransform runTextMatrix = CTRunGetTextMatrix(run); + BOOL runTextMatrixIsID = CGAffineTransformIsIdentity(runTextMatrix); + + CFDictionaryRef runAttrs = CTRunGetAttributes(run); + NSValue *glyphTransformValue = (NSValue *)CFDictionaryGetValue(runAttrs, (__bridge const void *)(ASTextGlyphTransformAttributeName)); + if (!isVertical && !glyphTransformValue) { // draw run + if (!runTextMatrixIsID) { + CGContextSaveGState(context); + CGAffineTransform trans = CGContextGetTextMatrix(context); + CGContextSetTextMatrix(context, CGAffineTransformConcat(trans, runTextMatrix)); + } + CTRunDraw(run, context, CFRangeMake(0, 0)); + if (!runTextMatrixIsID) { + CGContextRestoreGState(context); + } + } else { // draw glyph + CTFontRef runFont = (CTFontRef)CFDictionaryGetValue(runAttrs, kCTFontAttributeName); + if (!runFont) return; + NSUInteger glyphCount = CTRunGetGlyphCount(run); + if (glyphCount <= 0) return; + + CGGlyph glyphs[glyphCount]; + CGPoint glyphPositions[glyphCount]; + CTRunGetGlyphs(run, CFRangeMake(0, 0), glyphs); + CTRunGetPositions(run, CFRangeMake(0, 0), glyphPositions); + + CGColorRef fillColor = (CGColorRef)CFDictionaryGetValue(runAttrs, kCTForegroundColorAttributeName); + fillColor = ASTextGetCGColor(fillColor); + NSNumber *strokeWidth = (NSNumber *)CFDictionaryGetValue(runAttrs, kCTStrokeWidthAttributeName); + + CGContextSaveGState(context); { + CGContextSetFillColorWithColor(context, fillColor); + if (!strokeWidth || strokeWidth.floatValue == 0) { + CGContextSetTextDrawingMode(context, kCGTextFill); + } else { + CGColorRef strokeColor = (CGColorRef)CFDictionaryGetValue(runAttrs, kCTStrokeColorAttributeName); + if (!strokeColor) strokeColor = fillColor; + CGContextSetStrokeColorWithColor(context, strokeColor); + CGContextSetLineWidth(context, CTFontGetSize(runFont) * fabs(strokeWidth.floatValue * 0.01)); + if (strokeWidth.floatValue > 0) { + CGContextSetTextDrawingMode(context, kCGTextStroke); + } else { + CGContextSetTextDrawingMode(context, kCGTextFillStroke); + } + } + + if (isVertical) { + CFIndex runStrIdx[glyphCount + 1]; + CTRunGetStringIndices(run, CFRangeMake(0, 0), runStrIdx); + CFRange runStrRange = CTRunGetStringRange(run); + runStrIdx[glyphCount] = runStrRange.location + runStrRange.length; + CGSize glyphAdvances[glyphCount]; + CTRunGetAdvances(run, CFRangeMake(0, 0), glyphAdvances); + CGFloat ascent = CTFontGetAscent(runFont); + CGFloat descent = CTFontGetDescent(runFont); + CGAffineTransform glyphTransform = glyphTransformValue.CGAffineTransformValue; + CGPoint zeroPoint = CGPointZero; + + for (ASTextRunGlyphRange *oneRange in runRanges) { + NSRange range = oneRange.glyphRangeInRun; + NSUInteger rangeMax = range.location + range.length; + ASTextRunGlyphDrawMode mode = oneRange.drawMode; + + for (NSUInteger g = range.location; g < rangeMax; g++) { + CGContextSaveGState(context); { + CGContextSetTextMatrix(context, CGAffineTransformIdentity); + if (glyphTransformValue) { + CGContextSetTextMatrix(context, glyphTransform); + } + if (mode) { // CJK glyph, need rotated + CGFloat ofs = (ascent - descent) * 0.5; + CGFloat w = glyphAdvances[g].width * 0.5; + CGFloat x = x = line.position.x + verticalOffset + glyphPositions[g].y + (ofs - w); + CGFloat y = -line.position.y + size.height - glyphPositions[g].x - (ofs + w); + if (mode == ASTextRunGlyphDrawModeVerticalRotateMove) { + x += w; + y += w; + } + CGContextSetTextPosition(context, x, y); + } else { + CGContextRotateCTM(context, -M_PI_2); + CGContextSetTextPosition(context, + line.position.y - size.height + glyphPositions[g].x, + line.position.x + verticalOffset + glyphPositions[g].y); + } + + if (ASTextCTFontContainsColorBitmapGlyphs(runFont)) { + CTFontDrawGlyphs(runFont, glyphs + g, &zeroPoint, 1, context); + } else { + CGFontRef cgFont = CTFontCopyGraphicsFont(runFont, NULL); + CGContextSetFont(context, cgFont); + CGContextSetFontSize(context, CTFontGetSize(runFont)); + CGContextShowGlyphsAtPositions(context, glyphs + g, &zeroPoint, 1); + CGFontRelease(cgFont); + } + } CGContextRestoreGState(context); + } + } + } else { // not vertical + if (glyphTransformValue) { + CFIndex runStrIdx[glyphCount + 1]; + CTRunGetStringIndices(run, CFRangeMake(0, 0), runStrIdx); + CFRange runStrRange = CTRunGetStringRange(run); + runStrIdx[glyphCount] = runStrRange.location + runStrRange.length; + CGSize glyphAdvances[glyphCount]; + CTRunGetAdvances(run, CFRangeMake(0, 0), glyphAdvances); + CGAffineTransform glyphTransform = glyphTransformValue.CGAffineTransformValue; + CGPoint zeroPoint = CGPointZero; + + for (NSUInteger g = 0; g < glyphCount; g++) { + CGContextSaveGState(context); { + CGContextSetTextMatrix(context, CGAffineTransformIdentity); + CGContextSetTextMatrix(context, glyphTransform); + CGContextSetTextPosition(context, + line.position.x + glyphPositions[g].x, + size.height - (line.position.y + glyphPositions[g].y)); + + if (ASTextCTFontContainsColorBitmapGlyphs(runFont)) { + CTFontDrawGlyphs(runFont, glyphs + g, &zeroPoint, 1, context); + } else { + CGFontRef cgFont = CTFontCopyGraphicsFont(runFont, NULL); + CGContextSetFont(context, cgFont); + CGContextSetFontSize(context, CTFontGetSize(runFont)); + CGContextShowGlyphsAtPositions(context, glyphs + g, &zeroPoint, 1); + CGFontRelease(cgFont); + } + } CGContextRestoreGState(context); + } + } else { + if (ASTextCTFontContainsColorBitmapGlyphs(runFont)) { + CTFontDrawGlyphs(runFont, glyphs, glyphPositions, glyphCount, context); + } else { + CGFontRef cgFont = CTFontCopyGraphicsFont(runFont, NULL); + CGContextSetFont(context, cgFont); + CGContextSetFontSize(context, CTFontGetSize(runFont)); + CGContextShowGlyphsAtPositions(context, glyphs, glyphPositions, glyphCount); + CGFontRelease(cgFont); + } + } + } + + } CGContextRestoreGState(context); + } +} + +static void ASTextSetLinePatternInContext(ASTextLineStyle style, CGFloat width, CGFloat phase, CGContextRef context){ + CGContextSetLineWidth(context, width); + CGContextSetLineCap(context, kCGLineCapButt); + CGContextSetLineJoin(context, kCGLineJoinMiter); + + CGFloat dash = 12, dot = 5, space = 3; + NSUInteger pattern = style & 0xF00; + if (pattern == ASTextLineStylePatternSolid) { + CGContextSetLineDash(context, phase, NULL, 0); + } else if (pattern == ASTextLineStylePatternDot) { + CGFloat lengths[2] = {width * dot, width * space}; + CGContextSetLineDash(context, phase, lengths, 2); + } else if (pattern == ASTextLineStylePatternDash) { + CGFloat lengths[2] = {width * dash, width * space}; + CGContextSetLineDash(context, phase, lengths, 2); + } else if (pattern == ASTextLineStylePatternDashDot) { + CGFloat lengths[4] = {width * dash, width * space, width * dot, width * space}; + CGContextSetLineDash(context, phase, lengths, 4); + } else if (pattern == ASTextLineStylePatternDashDotDot) { + CGFloat lengths[6] = {width * dash, width * space,width * dot, width * space, width * dot, width * space}; + CGContextSetLineDash(context, phase, lengths, 6); + } else if (pattern == ASTextLineStylePatternCircleDot) { + CGFloat lengths[2] = {width * 0, width * 3}; + CGContextSetLineDash(context, phase, lengths, 2); + CGContextSetLineCap(context, kCGLineCapRound); + CGContextSetLineJoin(context, kCGLineJoinRound); + } +} + + +static void ASTextDrawBorderRects(CGContextRef context, CGSize size, ASTextBorder *border, NSArray *rects, BOOL isVertical) { + if (rects.count == 0) return; + + ASTextShadow *shadow = border.shadow; + if (shadow.color) { + CGContextSaveGState(context); + CGContextSetShadowWithColor(context, shadow.offset, shadow.radius, shadow.color.CGColor); + CGContextBeginTransparencyLayer(context, NULL); + } + + NSMutableArray *paths = [NSMutableArray new]; + for (NSValue *value in rects) { + CGRect rect = value.CGRectValue; + if (isVertical) { + rect = UIEdgeInsetsInsetRect(rect, UIEdgeInsetRotateVertical(border.insets)); + } else { + rect = UIEdgeInsetsInsetRect(rect, border.insets); + } + rect = ASTextCGRectPixelRound(rect); + UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:border.cornerRadius]; + [path closePath]; + [paths addObject:path]; + } + + if (border.fillColor) { + CGContextSaveGState(context); + CGContextSetFillColorWithColor(context, border.fillColor.CGColor); + for (UIBezierPath *path in paths) { + CGContextAddPath(context, path.CGPath); + } + CGContextFillPath(context); + CGContextRestoreGState(context); + } + + if (border.strokeColor && border.lineStyle > 0 && border.strokeWidth > 0) { + + //-------------------------- single line ------------------------------// + CGContextSaveGState(context); + for (UIBezierPath *path in paths) { + CGRect bounds = CGRectUnion(path.bounds, (CGRect){CGPointZero, size}); + bounds = CGRectInset(bounds, -2 * border.strokeWidth, -2 * border.strokeWidth); + CGContextAddRect(context, bounds); + CGContextAddPath(context, path.CGPath); + CGContextEOClip(context); + } + [border.strokeColor setStroke]; + ASTextSetLinePatternInContext(border.lineStyle, border.strokeWidth, 0, context); + CGFloat inset = -border.strokeWidth * 0.5; + if ((border.lineStyle & 0xFF) == ASTextLineStyleThick) { + inset *= 2; + CGContextSetLineWidth(context, border.strokeWidth * 2); + } + CGFloat radiusDelta = -inset; + if (border.cornerRadius <= 0) { + radiusDelta = 0; + } + CGContextSetLineJoin(context, border.lineJoin); + for (NSValue *value in rects) { + CGRect rect = value.CGRectValue; + if (isVertical) { + rect = UIEdgeInsetsInsetRect(rect, UIEdgeInsetRotateVertical(border.insets)); + } else { + rect = UIEdgeInsetsInsetRect(rect, border.insets); + } + rect = CGRectInset(rect, inset, inset); + UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:border.cornerRadius + radiusDelta]; + [path closePath]; + CGContextAddPath(context, path.CGPath); + } + CGContextStrokePath(context); + CGContextRestoreGState(context); + + //------------------------- second line ------------------------------// + if ((border.lineStyle & 0xFF) == ASTextLineStyleDouble) { + CGContextSaveGState(context); + CGFloat inset = -border.strokeWidth * 2; + for (NSValue *value in rects) { + CGRect rect = value.CGRectValue; + rect = UIEdgeInsetsInsetRect(rect, border.insets); + rect = CGRectInset(rect, inset, inset); + UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:border.cornerRadius + 2 * border.strokeWidth]; + [path closePath]; + + CGRect bounds = CGRectUnion(path.bounds, (CGRect){CGPointZero, size}); + bounds = CGRectInset(bounds, -2 * border.strokeWidth, -2 * border.strokeWidth); + CGContextAddRect(context, bounds); + CGContextAddPath(context, path.CGPath); + CGContextEOClip(context); + } + CGContextSetStrokeColorWithColor(context, border.strokeColor.CGColor); + ASTextSetLinePatternInContext(border.lineStyle, border.strokeWidth, 0, context); + CGContextSetLineJoin(context, border.lineJoin); + inset = -border.strokeWidth * 2.5; + radiusDelta = border.strokeWidth * 2; + if (border.cornerRadius <= 0) { + radiusDelta = 0; + } + for (NSValue *value in rects) { + CGRect rect = value.CGRectValue; + rect = UIEdgeInsetsInsetRect(rect, border.insets); + rect = CGRectInset(rect, inset, inset); + UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:border.cornerRadius + radiusDelta]; + [path closePath]; + CGContextAddPath(context, path.CGPath); + } + CGContextStrokePath(context); + CGContextRestoreGState(context); + } + } + + if (shadow.color) { + CGContextEndTransparencyLayer(context); + CGContextRestoreGState(context); + } +} + +static void ASTextDrawLineStyle(CGContextRef context, CGFloat length, CGFloat lineWidth, ASTextLineStyle style, CGPoint position, CGColorRef color, BOOL isVertical) { + NSUInteger styleBase = style & 0xFF; + if (styleBase == 0) return; + + CGContextSaveGState(context); { + if (isVertical) { + CGFloat x, y1, y2, w; + y1 = ASRoundPixelValue(position.y); + y2 = ASRoundPixelValue(position.y + length); + w = (styleBase == ASTextLineStyleThick ? lineWidth * 2 : lineWidth); + + CGFloat linePixel = ASTextCGFloatToPixel(w); + if (fabs(linePixel - floor(linePixel)) < 0.1) { + int iPixel = linePixel; + if (iPixel == 0 || (iPixel % 2)) { // odd line pixel + x = ASTextCGFloatPixelHalf(position.x); + } else { + x = ASFloorPixelValue(position.x); + } + } else { + x = position.x; + } + + CGContextSetStrokeColorWithColor(context, color); + ASTextSetLinePatternInContext(style, lineWidth, position.y, context); + CGContextSetLineWidth(context, w); + if (styleBase == ASTextLineStyleSingle) { + CGContextMoveToPoint(context, x, y1); + CGContextAddLineToPoint(context, x, y2); + CGContextStrokePath(context); + } else if (styleBase == ASTextLineStyleThick) { + CGContextMoveToPoint(context, x, y1); + CGContextAddLineToPoint(context, x, y2); + CGContextStrokePath(context); + } else if (styleBase == ASTextLineStyleDouble) { + CGContextMoveToPoint(context, x - w, y1); + CGContextAddLineToPoint(context, x - w, y2); + CGContextStrokePath(context); + CGContextMoveToPoint(context, x + w, y1); + CGContextAddLineToPoint(context, x + w, y2); + CGContextStrokePath(context); + } + } else { + CGFloat x1, x2, y, w; + x1 = ASRoundPixelValue(position.x); + x2 = ASRoundPixelValue(position.x + length); + w = (styleBase == ASTextLineStyleThick ? lineWidth * 2 : lineWidth); + + CGFloat linePixel = ASTextCGFloatToPixel(w); + if (fabs(linePixel - floor(linePixel)) < 0.1) { + int iPixel = linePixel; + if (iPixel == 0 || (iPixel % 2)) { // odd line pixel + y = ASTextCGFloatPixelHalf(position.y); + } else { + y = ASFloorPixelValue(position.y); + } + } else { + y = position.y; + } + + CGContextSetStrokeColorWithColor(context, color); + ASTextSetLinePatternInContext(style, lineWidth, position.x, context); + CGContextSetLineWidth(context, w); + if (styleBase == ASTextLineStyleSingle) { + CGContextMoveToPoint(context, x1, y); + CGContextAddLineToPoint(context, x2, y); + CGContextStrokePath(context); + } else if (styleBase == ASTextLineStyleThick) { + CGContextMoveToPoint(context, x1, y); + CGContextAddLineToPoint(context, x2, y); + CGContextStrokePath(context); + } else if (styleBase == ASTextLineStyleDouble) { + CGContextMoveToPoint(context, x1, y - w); + CGContextAddLineToPoint(context, x2, y - w); + CGContextStrokePath(context); + CGContextMoveToPoint(context, x1, y + w); + CGContextAddLineToPoint(context, x2, y + w); + CGContextStrokePath(context); + } + } + } CGContextRestoreGState(context); +} + +static void ASTextDrawText(ASTextLayout *layout, CGContextRef context, CGSize size, CGPoint point, BOOL (^cancel)(void)) { + CGContextSaveGState(context); { + + CGContextTranslateCTM(context, point.x, point.y); + CGContextTranslateCTM(context, 0, size.height); + CGContextScaleCTM(context, 1, -1); + + BOOL isVertical = layout.container.verticalForm; + CGFloat verticalOffset = isVertical ? (size.width - layout.container.size.width) : 0; + + NSArray *lines = layout.lines; + for (NSUInteger l = 0, lMax = lines.count; l < lMax; l++) { + ASTextLine *line = lines[l]; + if (layout.truncatedLine && layout.truncatedLine.index == line.index) line = layout.truncatedLine; + NSArray *lineRunRanges = line.verticalRotateRange; + CGFloat posX = line.position.x + verticalOffset; + CGFloat posY = size.height - line.position.y; + CFArrayRef runs = CTLineGetGlyphRuns(line.CTLine); + for (NSUInteger r = 0, rMax = CFArrayGetCount(runs); r < rMax; r++) { + CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runs, r); + CGContextSetTextMatrix(context, CGAffineTransformIdentity); + CGContextSetTextPosition(context, posX, posY); + ASTextDrawRun(line, run, context, size, isVertical, lineRunRanges[r], verticalOffset); + } + if (cancel && cancel()) break; + } + + // Use this to draw frame for test/debug. + // CGContextTranslateCTM(context, verticalOffset, size.height); + // CTFrameDraw(layout.frame, context); + + } CGContextRestoreGState(context); +} + +static void ASTextDrawBlockBorder(ASTextLayout *layout, CGContextRef context, CGSize size, CGPoint point, BOOL (^cancel)(void)) { + CGContextSaveGState(context); + CGContextTranslateCTM(context, point.x, point.y); + + BOOL isVertical = layout.container.verticalForm; + CGFloat verticalOffset = isVertical ? (size.width - layout.container.size.width) : 0; + + NSArray *lines = layout.lines; + for (NSInteger l = 0, lMax = lines.count; l < lMax; l++) { + if (cancel && cancel()) break; + + ASTextLine *line = lines[l]; + if (layout.truncatedLine && layout.truncatedLine.index == line.index) line = layout.truncatedLine; + CFArrayRef runs = CTLineGetGlyphRuns(line.CTLine); + for (NSInteger r = 0, rMax = CFArrayGetCount(runs); r < rMax; r++) { + CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runs, r); + CFIndex glyphCount = CTRunGetGlyphCount(run); + if (glyphCount == 0) continue; + NSDictionary *attrs = (id)CTRunGetAttributes(run); + ASTextBorder *border = attrs[ASTextBlockBorderAttributeName]; + if (!border) continue; + + NSUInteger lineStartIndex = line.index; + while (lineStartIndex > 0) { + if (((ASTextLine *)lines[lineStartIndex - 1]).row == line.row) lineStartIndex--; + else break; + } + + CGRect unionRect = CGRectZero; + NSUInteger lineStartRow = ((ASTextLine *)lines[lineStartIndex]).row; + NSUInteger lineContinueIndex = lineStartIndex; + NSUInteger lineContinueRow = lineStartRow; + do { + ASTextLine *one = lines[lineContinueIndex]; + if (lineContinueIndex == lineStartIndex) { + unionRect = one.bounds; + } else { + unionRect = CGRectUnion(unionRect, one.bounds); + } + if (lineContinueIndex + 1 == lMax) break; + ASTextLine *next = lines[lineContinueIndex + 1]; + if (next.row != lineContinueRow) { + ASTextBorder *nextBorder = [layout.text as_attribute:ASTextBlockBorderAttributeName atIndex:next.range.location]; + if ([nextBorder isEqual:border]) { + lineContinueRow++; + } else { + break; + } + } + lineContinueIndex++; + } while (true); + + if (isVertical) { + UIEdgeInsets insets = layout.container.insets; + unionRect.origin.y = insets.top; + unionRect.size.height = layout.container.size.height -insets.top - insets.bottom; + } else { + UIEdgeInsets insets = layout.container.insets; + unionRect.origin.x = insets.left; + unionRect.size.width = layout.container.size.width -insets.left - insets.right; + } + unionRect.origin.x += verticalOffset; + ASTextDrawBorderRects(context, size, border, @[[NSValue valueWithCGRect:unionRect]], isVertical); + + l = lineContinueIndex; + break; + } + } + + + CGContextRestoreGState(context); +} + +static void ASTextDrawBorder(ASTextLayout *layout, CGContextRef context, CGSize size, CGPoint point, ASTextBorderType type, BOOL (^cancel)(void)) { + CGContextSaveGState(context); + CGContextTranslateCTM(context, point.x, point.y); + + BOOL isVertical = layout.container.verticalForm; + CGFloat verticalOffset = isVertical ? (size.width - layout.container.size.width) : 0; + + NSArray *lines = layout.lines; + NSString *borderKey = (type == ASTextBorderTypeNormal ? ASTextBorderAttributeName : ASTextBackgroundBorderAttributeName); + + BOOL needJumpRun = NO; + NSUInteger jumpRunIndex = 0; + + for (NSInteger l = 0, lMax = lines.count; l < lMax; l++) { + if (cancel && cancel()) break; + + ASTextLine *line = lines[l]; + if (layout.truncatedLine && layout.truncatedLine.index == line.index) line = layout.truncatedLine; + CFArrayRef runs = CTLineGetGlyphRuns(line.CTLine); + for (NSInteger r = 0, rMax = CFArrayGetCount(runs); r < rMax; r++) { + if (needJumpRun) { + needJumpRun = NO; + r = jumpRunIndex + 1; + if (r >= rMax) break; + } + + CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runs, r); + CFIndex glyphCount = CTRunGetGlyphCount(run); + if (glyphCount == 0) continue; + + NSDictionary *attrs = (id)CTRunGetAttributes(run); + ASTextBorder *border = attrs[borderKey]; + if (!border) continue; + + CFRange runRange = CTRunGetStringRange(run); + if (runRange.location == kCFNotFound || runRange.length == 0) continue; + if (runRange.location + runRange.length > layout.text.length) continue; + + NSMutableArray *runRects = [NSMutableArray new]; + NSInteger endLineIndex = l; + NSInteger endRunIndex = r; + BOOL endFound = NO; + for (NSInteger ll = l; ll < lMax; ll++) { + if (endFound) break; + ASTextLine *iLine = lines[ll]; + CFArrayRef iRuns = CTLineGetGlyphRuns(iLine.CTLine); + + CGRect extLineRect = CGRectNull; + for (NSInteger rr = (ll == l) ? r : 0, rrMax = CFArrayGetCount(iRuns); rr < rrMax; rr++) { + CTRunRef iRun = (CTRunRef)CFArrayGetValueAtIndex(iRuns, rr); + NSDictionary *iAttrs = (id)CTRunGetAttributes(iRun); + ASTextBorder *iBorder = iAttrs[borderKey]; + if (![border isEqual:iBorder]) { + endFound = YES; + break; + } + endLineIndex = ll; + endRunIndex = rr; + + CGPoint iRunPosition = CGPointZero; + CTRunGetPositions(iRun, CFRangeMake(0, 1), &iRunPosition); + CGFloat ascent, descent; + CGFloat iRunWidth = CTRunGetTypographicBounds(iRun, CFRangeMake(0, 0), &ascent, &descent, NULL); + + if (isVertical) { + ASTEXT_SWAP(iRunPosition.x, iRunPosition.y); + iRunPosition.y += iLine.position.y; + CGRect iRect = CGRectMake(verticalOffset + line.position.x - descent, iRunPosition.y, ascent + descent, iRunWidth); + if (CGRectIsNull(extLineRect)) { + extLineRect = iRect; + } else { + extLineRect = CGRectUnion(extLineRect, iRect); + } + } else { + iRunPosition.x += iLine.position.x; + CGRect iRect = CGRectMake(iRunPosition.x, iLine.position.y - ascent, iRunWidth, ascent + descent); + if (CGRectIsNull(extLineRect)) { + extLineRect = iRect; + } else { + extLineRect = CGRectUnion(extLineRect, iRect); + } + } + } + + if (!CGRectIsNull(extLineRect)) { + [runRects addObject:[NSValue valueWithCGRect:extLineRect]]; + } + } + + NSMutableArray *drawRects = [NSMutableArray new]; + CGRect curRect= ((NSValue *)[runRects firstObject]).CGRectValue; + for (NSInteger re = 0, reMax = runRects.count; re < reMax; re++) { + CGRect rect = ((NSValue *)runRects[re]).CGRectValue; + if (isVertical) { + if (fabs(rect.origin.x - curRect.origin.x) < 1) { + curRect = ASTextMergeRectInSameLine(rect, curRect, isVertical); + } else { + [drawRects addObject:[NSValue valueWithCGRect:curRect]]; + curRect = rect; + } + } else { + if (fabs(rect.origin.y - curRect.origin.y) < 1) { + curRect = ASTextMergeRectInSameLine(rect, curRect, isVertical); + } else { + [drawRects addObject:[NSValue valueWithCGRect:curRect]]; + curRect = rect; + } + } + } + if (!CGRectEqualToRect(curRect, CGRectZero)) { + [drawRects addObject:[NSValue valueWithCGRect:curRect]]; + } + + ASTextDrawBorderRects(context, size, border, drawRects, isVertical); + + if (l == endLineIndex) { + r = endRunIndex; + } else { + l = endLineIndex - 1; + needJumpRun = YES; + jumpRunIndex = endRunIndex; + break; + } + + } + } + + CGContextRestoreGState(context); +} + +static void ASTextDrawDecoration(ASTextLayout *layout, CGContextRef context, CGSize size, CGPoint point, ASTextDecorationType type, BOOL (^cancel)(void)) { + NSArray *lines = layout.lines; + + CGContextSaveGState(context); + CGContextTranslateCTM(context, point.x, point.y); + + BOOL isVertical = layout.container.verticalForm; + CGFloat verticalOffset = isVertical ? (size.width - layout.container.size.width) : 0; + CGContextTranslateCTM(context, verticalOffset, 0); + + for (NSUInteger l = 0, lMax = layout.lines.count; l < lMax; l++) { + if (cancel && cancel()) break; + + ASTextLine *line = lines[l]; + if (layout.truncatedLine && layout.truncatedLine.index == line.index) line = layout.truncatedLine; + CFArrayRef runs = CTLineGetGlyphRuns(line.CTLine); + for (NSUInteger r = 0, rMax = CFArrayGetCount(runs); r < rMax; r++) { + CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runs, r); + CFIndex glyphCount = CTRunGetGlyphCount(run); + if (glyphCount == 0) continue; + + NSDictionary *attrs = (id)CTRunGetAttributes(run); + ASTextDecoration *underline = attrs[ASTextUnderlineAttributeName]; + ASTextDecoration *strikethrough = attrs[ASTextStrikethroughAttributeName]; + + BOOL needDrawUnderline = NO, needDrawStrikethrough = NO; + if ((type & ASTextDecorationTypeUnderline) && underline.style > 0) { + needDrawUnderline = YES; + } + if ((type & ASTextDecorationTypeStrikethrough) && strikethrough.style > 0) { + needDrawStrikethrough = YES; + } + if (!needDrawUnderline && !needDrawStrikethrough) continue; + + CFRange runRange = CTRunGetStringRange(run); + if (runRange.location == kCFNotFound || runRange.length == 0) continue; + if (runRange.location + runRange.length > layout.text.length) continue; + NSString *runStr = [layout.text attributedSubstringFromRange:NSMakeRange(runRange.location, runRange.length)].string; + if (ASTextIsLinebreakString(runStr)) continue; // may need more checks... + + CGFloat xHeight, underlinePosition, lineThickness; + ASTextGetRunsMaxMetric(runs, &xHeight, &underlinePosition, &lineThickness); + + CGPoint underlineStart, strikethroughStart; + CGFloat length; + + if (isVertical) { + underlineStart.x = line.position.x + underlinePosition; + strikethroughStart.x = line.position.x + xHeight / 2; + + CGPoint runPosition = CGPointZero; + CTRunGetPositions(run, CFRangeMake(0, 1), &runPosition); + underlineStart.y = strikethroughStart.y = runPosition.x + line.position.y; + length = CTRunGetTypographicBounds(run, CFRangeMake(0, 0), NULL, NULL, NULL); + + } else { + underlineStart.y = line.position.y - underlinePosition; + strikethroughStart.y = line.position.y - xHeight / 2; + + CGPoint runPosition = CGPointZero; + CTRunGetPositions(run, CFRangeMake(0, 1), &runPosition); + underlineStart.x = strikethroughStart.x = runPosition.x + line.position.x; + length = CTRunGetTypographicBounds(run, CFRangeMake(0, 0), NULL, NULL, NULL); + } + + if (needDrawUnderline) { + CGColorRef color = underline.color.CGColor; + if (!color) { + color = (__bridge CGColorRef)(attrs[(id)kCTForegroundColorAttributeName]); + color = ASTextGetCGColor(color); + } + CGFloat thickness = underline.width ? underline.width.floatValue : lineThickness; + ASTextShadow *shadow = underline.shadow; + while (shadow) { + if (!shadow.color) { + shadow = shadow.subShadow; + continue; + } + CGFloat offsetAlterX = size.width + 0xFFFF; + CGContextSaveGState(context); { + CGSize offset = shadow.offset; + offset.width -= offsetAlterX; + CGContextSaveGState(context); { + CGContextSetShadowWithColor(context, offset, shadow.radius, shadow.color.CGColor); + CGContextSetBlendMode(context, shadow.blendMode); + CGContextTranslateCTM(context, offsetAlterX, 0); + ASTextDrawLineStyle(context, length, thickness, underline.style, underlineStart, color, isVertical); + } CGContextRestoreGState(context); + } CGContextRestoreGState(context); + shadow = shadow.subShadow; + } + ASTextDrawLineStyle(context, length, thickness, underline.style, underlineStart, color, isVertical); + } + + if (needDrawStrikethrough) { + CGColorRef color = strikethrough.color.CGColor; + if (!color) { + color = (__bridge CGColorRef)(attrs[(id)kCTForegroundColorAttributeName]); + color = ASTextGetCGColor(color); + } + CGFloat thickness = strikethrough.width ? strikethrough.width.floatValue : lineThickness; + ASTextShadow *shadow = underline.shadow; + while (shadow) { + if (!shadow.color) { + shadow = shadow.subShadow; + continue; + } + CGFloat offsetAlterX = size.width + 0xFFFF; + CGContextSaveGState(context); { + CGSize offset = shadow.offset; + offset.width -= offsetAlterX; + CGContextSaveGState(context); { + CGContextSetShadowWithColor(context, offset, shadow.radius, shadow.color.CGColor); + CGContextSetBlendMode(context, shadow.blendMode); + CGContextTranslateCTM(context, offsetAlterX, 0); + ASTextDrawLineStyle(context, length, thickness, underline.style, underlineStart, color, isVertical); + } CGContextRestoreGState(context); + } CGContextRestoreGState(context); + shadow = shadow.subShadow; + } + ASTextDrawLineStyle(context, length, thickness, strikethrough.style, strikethroughStart, color, isVertical); + } + } + } + CGContextRestoreGState(context); +} + +static void ASTextDrawAttachment(ASTextLayout *layout, CGContextRef context, CGSize size, CGPoint point, UIView *targetView, CALayer *targetLayer, BOOL (^cancel)(void)) { + + BOOL isVertical = layout.container.verticalForm; + CGFloat verticalOffset = isVertical ? (size.width - layout.container.size.width) : 0; + + for (NSUInteger i = 0, max = layout.attachments.count; i < max; i++) { + ASTextAttachment *a = layout.attachments[i]; + if (!a.content) continue; + + UIImage *image = nil; + UIView *view = nil; + CALayer *layer = nil; + if ([a.content isKindOfClass:[UIImage class]]) { + image = a.content; + } else if ([a.content isKindOfClass:[UIView class]]) { + view = a.content; + } else if ([a.content isKindOfClass:[CALayer class]]) { + layer = a.content; + } + if (!image && !view && !layer) continue; + if (image && !context) continue; + if (view && !targetView) continue; + if (layer && !targetLayer) continue; + if (cancel && cancel()) break; + + CGSize asize = image ? image.size : view ? view.frame.size : layer.frame.size; + CGRect rect = ((NSValue *)layout.attachmentRects[i]).CGRectValue; + if (isVertical) { + rect = UIEdgeInsetsInsetRect(rect, UIEdgeInsetRotateVertical(a.contentInsets)); + } else { + rect = UIEdgeInsetsInsetRect(rect, a.contentInsets); + } + rect = ASTextCGRectFitWithContentMode(rect, asize, a.contentMode); + rect = ASTextCGRectPixelRound(rect); + rect = CGRectStandardize(rect); + rect.origin.x += point.x + verticalOffset; + rect.origin.y += point.y; + if (image) { + CGImageRef ref = image.CGImage; + if (ref) { + CGContextSaveGState(context); + CGContextTranslateCTM(context, 0, CGRectGetMaxY(rect) + CGRectGetMinY(rect)); + CGContextScaleCTM(context, 1, -1); + CGContextDrawImage(context, rect, ref); + CGContextRestoreGState(context); + } + } else if (view) { + view.frame = rect; + [targetView addSubview:view]; + } else if (layer) { + layer.frame = rect; + [targetLayer addSublayer:layer]; + } + } +} + +static void ASTextDrawShadow(ASTextLayout *layout, CGContextRef context, CGSize size, CGPoint point, BOOL (^cancel)(void)) { + //move out of context. (0xFFFF is just a random large number) + CGFloat offsetAlterX = size.width + 0xFFFF; + + BOOL isVertical = layout.container.verticalForm; + CGFloat verticalOffset = isVertical ? (size.width - layout.container.size.width) : 0; + + CGContextSaveGState(context); { + CGContextTranslateCTM(context, point.x, point.y); + CGContextTranslateCTM(context, 0, size.height); + CGContextScaleCTM(context, 1, -1); + NSArray *lines = layout.lines; + for (NSUInteger l = 0, lMax = layout.lines.count; l < lMax; l++) { + if (cancel && cancel()) break; + ASTextLine *line = lines[l]; + if (layout.truncatedLine && layout.truncatedLine.index == line.index) line = layout.truncatedLine; + NSArray *lineRunRanges = line.verticalRotateRange; + CGFloat linePosX = line.position.x; + CGFloat linePosY = size.height - line.position.y; + CFArrayRef runs = CTLineGetGlyphRuns(line.CTLine); + for (NSUInteger r = 0, rMax = CFArrayGetCount(runs); r < rMax; r++) { + CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runs, r); + CGContextSetTextMatrix(context, CGAffineTransformIdentity); + CGContextSetTextPosition(context, linePosX, linePosY); + NSDictionary *attrs = (id)CTRunGetAttributes(run); + ASTextShadow *shadow = attrs[ASTextShadowAttributeName]; + ASTextShadow *nsShadow = [ASTextShadow shadowWithNSShadow:attrs[NSShadowAttributeName]]; // NSShadow compatible + if (nsShadow) { + nsShadow.subShadow = shadow; + shadow = nsShadow; + } + while (shadow) { + if (!shadow.color) { + shadow = shadow.subShadow; + continue; + } + CGSize offset = shadow.offset; + offset.width -= offsetAlterX; + CGContextSaveGState(context); { + CGContextSetShadowWithColor(context, offset, shadow.radius, shadow.color.CGColor); + CGContextSetBlendMode(context, shadow.blendMode); + CGContextTranslateCTM(context, offsetAlterX, 0); + ASTextDrawRun(line, run, context, size, isVertical, lineRunRanges[r], verticalOffset); + } CGContextRestoreGState(context); + shadow = shadow.subShadow; + } + } + } + } CGContextRestoreGState(context); +} + +static void ASTextDrawInnerShadow(ASTextLayout *layout, CGContextRef context, CGSize size, CGPoint point, BOOL (^cancel)(void)) { + CGContextSaveGState(context); + CGContextTranslateCTM(context, point.x, point.y); + CGContextTranslateCTM(context, 0, size.height); + CGContextScaleCTM(context, 1, -1); + CGContextSetTextMatrix(context, CGAffineTransformIdentity); + + BOOL isVertical = layout.container.verticalForm; + CGFloat verticalOffset = isVertical ? (size.width - layout.container.size.width) : 0; + + NSArray *lines = layout.lines; + for (NSUInteger l = 0, lMax = lines.count; l < lMax; l++) { + if (cancel && cancel()) break; + + ASTextLine *line = lines[l]; + if (layout.truncatedLine && layout.truncatedLine.index == line.index) line = layout.truncatedLine; + NSArray *lineRunRanges = line.verticalRotateRange; + CGFloat linePosX = line.position.x; + CGFloat linePosY = size.height - line.position.y; + CFArrayRef runs = CTLineGetGlyphRuns(line.CTLine); + for (NSUInteger r = 0, rMax = CFArrayGetCount(runs); r < rMax; r++) { + CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runs, r); + if (CTRunGetGlyphCount(run) == 0) continue; + CGContextSetTextMatrix(context, CGAffineTransformIdentity); + CGContextSetTextPosition(context, linePosX, linePosY); + NSDictionary *attrs = (id)CTRunGetAttributes(run); + ASTextShadow *shadow = attrs[ASTextInnerShadowAttributeName]; + while (shadow) { + if (!shadow.color) { + shadow = shadow.subShadow; + continue; + } + CGPoint runPosition = CGPointZero; + CTRunGetPositions(run, CFRangeMake(0, 1), &runPosition); + CGRect runImageBounds = CTRunGetImageBounds(run, context, CFRangeMake(0, 0)); + runImageBounds.origin.x += runPosition.x; + if (runImageBounds.size.width < 0.1 || runImageBounds.size.height < 0.1) continue; + + CFDictionaryRef runAttrs = CTRunGetAttributes(run); + NSValue *glyphTransformValue = (NSValue *)CFDictionaryGetValue(runAttrs, (__bridge const void *)(ASTextGlyphTransformAttributeName)); + if (glyphTransformValue) { + runImageBounds = CGRectMake(0, 0, size.width, size.height); + } + + // text inner shadow + CGContextSaveGState(context); { + CGContextSetBlendMode(context, shadow.blendMode); + CGContextSetShadowWithColor(context, CGSizeZero, 0, NULL); + CGContextSetAlpha(context, CGColorGetAlpha(shadow.color.CGColor)); + CGContextClipToRect(context, runImageBounds); + CGContextBeginTransparencyLayer(context, NULL); { + UIColor *opaqueShadowColor = [shadow.color colorWithAlphaComponent:1]; + CGContextSetShadowWithColor(context, shadow.offset, shadow.radius, opaqueShadowColor.CGColor); + CGContextSetFillColorWithColor(context, opaqueShadowColor.CGColor); + CGContextSetBlendMode(context, kCGBlendModeSourceOut); + CGContextBeginTransparencyLayer(context, NULL); { + CGContextFillRect(context, runImageBounds); + CGContextSetBlendMode(context, kCGBlendModeDestinationIn); + CGContextBeginTransparencyLayer(context, NULL); { + ASTextDrawRun(line, run, context, size, isVertical, lineRunRanges[r], verticalOffset); + } CGContextEndTransparencyLayer(context); + } CGContextEndTransparencyLayer(context); + } CGContextEndTransparencyLayer(context); + } CGContextRestoreGState(context); + shadow = shadow.subShadow; + } + } + } + + CGContextRestoreGState(context); +} + +static void ASTextDrawDebug(ASTextLayout *layout, CGContextRef context, CGSize size, CGPoint point, ASTextDebugOption *op) { + UIGraphicsPushContext(context); + CGContextSaveGState(context); + CGContextTranslateCTM(context, point.x, point.y); + CGContextSetLineWidth(context, 1.0 / ASScreenScale()); + CGContextSetLineDash(context, 0, NULL, 0); + CGContextSetLineJoin(context, kCGLineJoinMiter); + CGContextSetLineCap(context, kCGLineCapButt); + + BOOL isVertical = layout.container.verticalForm; + CGFloat verticalOffset = isVertical ? (size.width - layout.container.size.width) : 0; + CGContextTranslateCTM(context, verticalOffset, 0); + + if (op.CTFrameBorderColor || op.CTFrameFillColor) { + UIBezierPath *path = layout.container.path; + if (!path) { + CGRect rect = (CGRect){CGPointZero, layout.container.size}; + rect = UIEdgeInsetsInsetRect(rect, layout.container.insets); + if (op.CTFrameBorderColor) rect = ASTextCGRectPixelHalf(rect); + else rect = ASTextCGRectPixelRound(rect); + path = [UIBezierPath bezierPathWithRect:rect]; + } + [path closePath]; + + for (UIBezierPath *ex in layout.container.exclusionPaths) { + [path appendPath:ex]; + } + if (op.CTFrameFillColor) { + [op.CTFrameFillColor setFill]; + if (layout.container.pathLineWidth > 0) { + CGContextSaveGState(context); { + CGContextBeginTransparencyLayer(context, NULL); { + CGContextAddPath(context, path.CGPath); + if (layout.container.pathFillEvenOdd) { + CGContextEOFillPath(context); + } else { + CGContextFillPath(context); + } + CGContextSetBlendMode(context, kCGBlendModeDestinationOut); + [[UIColor blackColor] setFill]; + CGPathRef cgPath = CGPathCreateCopyByStrokingPath(path.CGPath, NULL, layout.container.pathLineWidth, kCGLineCapButt, kCGLineJoinMiter, 0); + if (cgPath) { + CGContextAddPath(context, cgPath); + CGContextFillPath(context); + } + CGPathRelease(cgPath); + } CGContextEndTransparencyLayer(context); + } CGContextRestoreGState(context); + } else { + CGContextAddPath(context, path.CGPath); + if (layout.container.pathFillEvenOdd) { + CGContextEOFillPath(context); + } else { + CGContextFillPath(context); + } + } + } + if (op.CTFrameBorderColor) { + CGContextSaveGState(context); { + if (layout.container.pathLineWidth > 0) { + CGContextSetLineWidth(context, layout.container.pathLineWidth); + } + [op.CTFrameBorderColor setStroke]; + CGContextAddPath(context, path.CGPath); + CGContextStrokePath(context); + } CGContextRestoreGState(context); + } + } + + NSArray *lines = layout.lines; + for (NSUInteger l = 0, lMax = lines.count; l < lMax; l++) { + ASTextLine *line = lines[l]; + if (layout.truncatedLine && layout.truncatedLine.index == line.index) line = layout.truncatedLine; + CGRect lineBounds = line.bounds; + if (op.CTLineFillColor) { + [op.CTLineFillColor setFill]; + CGContextAddRect(context, ASTextCGRectPixelRound(lineBounds)); + CGContextFillPath(context); + } + if (op.CTLineBorderColor) { + [op.CTLineBorderColor setStroke]; + CGContextAddRect(context, ASTextCGRectPixelHalf(lineBounds)); + CGContextStrokePath(context); + } + if (op.baselineColor) { + [op.baselineColor setStroke]; + if (isVertical) { + CGFloat x = ASTextCGFloatPixelHalf(line.position.x); + CGFloat y1 = ASTextCGFloatPixelHalf(line.top); + CGFloat y2 = ASTextCGFloatPixelHalf(line.bottom); + CGContextMoveToPoint(context, x, y1); + CGContextAddLineToPoint(context, x, y2); + CGContextStrokePath(context); + } else { + CGFloat x1 = ASTextCGFloatPixelHalf(lineBounds.origin.x); + CGFloat x2 = ASTextCGFloatPixelHalf(lineBounds.origin.x + lineBounds.size.width); + CGFloat y = ASTextCGFloatPixelHalf(line.position.y); + CGContextMoveToPoint(context, x1, y); + CGContextAddLineToPoint(context, x2, y); + CGContextStrokePath(context); + } + } + if (op.CTLineNumberColor) { + [op.CTLineNumberColor set]; + NSMutableAttributedString *num = [[NSMutableAttributedString alloc] initWithString:@(l).description]; + num.as_color = op.CTLineNumberColor; + num.as_font = [UIFont systemFontOfSize:6]; + [num drawAtPoint:CGPointMake(line.position.x, line.position.y - (isVertical ? 1 : 6))]; + } + if (op.CTRunFillColor || op.CTRunBorderColor || op.CTRunNumberColor || op.CGGlyphFillColor || op.CGGlyphBorderColor) { + CFArrayRef runs = CTLineGetGlyphRuns(line.CTLine); + for (NSUInteger r = 0, rMax = CFArrayGetCount(runs); r < rMax; r++) { + CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runs, r); + CFIndex glyphCount = CTRunGetGlyphCount(run); + if (glyphCount == 0) continue; + + CGPoint glyphPositions[glyphCount]; + CTRunGetPositions(run, CFRangeMake(0, glyphCount), glyphPositions); + + CGSize glyphAdvances[glyphCount]; + CTRunGetAdvances(run, CFRangeMake(0, glyphCount), glyphAdvances); + + CGPoint runPosition = glyphPositions[0]; + if (isVertical) { + ASTEXT_SWAP(runPosition.x, runPosition.y); + runPosition.x = line.position.x; + runPosition.y += line.position.y; + } else { + runPosition.x += line.position.x; + runPosition.y = line.position.y - runPosition.y; + } + + CGFloat ascent, descent, leading; + CGFloat width = CTRunGetTypographicBounds(run, CFRangeMake(0, 0), &ascent, &descent, &leading); + CGRect runTypoBounds; + if (isVertical) { + runTypoBounds = CGRectMake(runPosition.x - descent, runPosition.y, ascent + descent, width); + } else { + runTypoBounds = CGRectMake(runPosition.x, line.position.y - ascent, width, ascent + descent); + } + + if (op.CTRunFillColor) { + [op.CTRunFillColor setFill]; + CGContextAddRect(context, ASTextCGRectPixelRound(runTypoBounds)); + CGContextFillPath(context); + } + if (op.CTRunBorderColor) { + [op.CTRunBorderColor setStroke]; + CGContextAddRect(context, ASTextCGRectPixelHalf(runTypoBounds)); + CGContextStrokePath(context); + } + if (op.CTRunNumberColor) { + [op.CTRunNumberColor set]; + NSMutableAttributedString *num = [[NSMutableAttributedString alloc] initWithString:@(r).description]; + num.as_color = op.CTRunNumberColor; + num.as_font = [UIFont systemFontOfSize:6]; + [num drawAtPoint:CGPointMake(runTypoBounds.origin.x, runTypoBounds.origin.y - 1)]; + } + if (op.CGGlyphBorderColor || op.CGGlyphFillColor) { + for (NSUInteger g = 0; g < glyphCount; g++) { + CGPoint pos = glyphPositions[g]; + CGSize adv = glyphAdvances[g]; + CGRect rect; + if (isVertical) { + ASTEXT_SWAP(pos.x, pos.y); + pos.x = runPosition.x; + pos.y += line.position.y; + rect = CGRectMake(pos.x - descent, pos.y, runTypoBounds.size.width, adv.width); + } else { + pos.x += line.position.x; + pos.y = runPosition.y; + rect = CGRectMake(pos.x, pos.y - ascent, adv.width, runTypoBounds.size.height); + } + if (op.CGGlyphFillColor) { + [op.CGGlyphFillColor setFill]; + CGContextAddRect(context, ASTextCGRectPixelRound(rect)); + CGContextFillPath(context); + } + if (op.CGGlyphBorderColor) { + [op.CGGlyphBorderColor setStroke]; + CGContextAddRect(context, ASTextCGRectPixelHalf(rect)); + CGContextStrokePath(context); + } + } + } + } + } + } + CGContextRestoreGState(context); + UIGraphicsPopContext(); +} + + +- (void)drawInContext:(CGContextRef)context + size:(CGSize)size + point:(CGPoint)point + view:(UIView *)view + layer:(CALayer *)layer + debug:(ASTextDebugOption *)debug + cancel:(BOOL (^)(void))cancel{ + @autoreleasepool { + if (self.needDrawBlockBorder && context) { + if (cancel && cancel()) return; + ASTextDrawBlockBorder(self, context, size, point, cancel); + } + if (self.needDrawBackgroundBorder && context) { + if (cancel && cancel()) return; + ASTextDrawBorder(self, context, size, point, ASTextBorderTypeBackgound, cancel); + } + if (self.needDrawShadow && context) { + if (cancel && cancel()) return; + ASTextDrawShadow(self, context, size, point, cancel); + } + if (self.needDrawUnderline && context) { + if (cancel && cancel()) return; + ASTextDrawDecoration(self, context, size, point, ASTextDecorationTypeUnderline, cancel); + } + if (self.needDrawText && context) { + if (cancel && cancel()) return; + ASTextDrawText(self, context, size, point, cancel); + } + if (self.needDrawAttachment && (context || view || layer)) { + if (cancel && cancel()) return; + ASTextDrawAttachment(self, context, size, point, view, layer, cancel); + } + if (self.needDrawInnerShadow && context) { + if (cancel && cancel()) return; + ASTextDrawInnerShadow(self, context, size, point, cancel); + } + if (self.needDrawStrikethrough && context) { + if (cancel && cancel()) return; + ASTextDrawDecoration(self, context, size, point, ASTextDecorationTypeStrikethrough, cancel); + } + if (self.needDrawBorder && context) { + if (cancel && cancel()) return; + ASTextDrawBorder(self, context, size, point, ASTextBorderTypeNormal, cancel); + } + if (debug.needDrawDebug && context) { + if (cancel && cancel()) return; + ASTextDrawDebug(self, context, size, point, debug); + } + } +} + +- (void)drawInContext:(CGContextRef)context + size:(CGSize)size + debug:(ASTextDebugOption *)debug { + [self drawInContext:context size:size point:CGPointZero view:nil layer:nil debug:debug cancel:nil]; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/Component/ASTextLine.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/Component/ASTextLine.h new file mode 100644 index 0000000..acb02e9 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/Component/ASTextLine.h @@ -0,0 +1,77 @@ +// +// ASTextLine.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import + +@class ASTextRunGlyphRange; + +NS_ASSUME_NONNULL_BEGIN + +/** + A text line object wrapped `CTLineRef`, see `ASTextLayout` for more. + */ +@interface ASTextLine : NSObject + ++ (instancetype)lineWithCTLine:(CTLineRef)CTLine position:(CGPoint)position vertical:(BOOL)isVertical NS_RETURNS_RETAINED; + +@property (nonatomic) NSUInteger index; ///< line index +@property (nonatomic) NSUInteger row; ///< line row +@property (nullable, nonatomic) NSArray *> *verticalRotateRange; ///< Run rotate range + +@property (nonatomic, readonly) CTLineRef CTLine; ///< CoreText line +@property (nonatomic, readonly) NSRange range; ///< string range +@property (nonatomic, readonly) BOOL vertical; ///< vertical form + +@property (nonatomic, readonly) CGRect bounds; ///< bounds (ascent + descent) +@property (nonatomic, readonly) CGSize size; ///< bounds.size +@property (nonatomic, readonly) CGFloat width; ///< bounds.size.width +@property (nonatomic, readonly) CGFloat height; ///< bounds.size.height +@property (nonatomic, readonly) CGFloat top; ///< bounds.origin.y +@property (nonatomic, readonly) CGFloat bottom; ///< bounds.origin.y + bounds.size.height +@property (nonatomic, readonly) CGFloat left; ///< bounds.origin.x +@property (nonatomic, readonly) CGFloat right; ///< bounds.origin.x + bounds.size.width + +@property (nonatomic) CGPoint position; ///< baseline position +@property (nonatomic, readonly) CGFloat ascent; ///< line ascent +@property (nonatomic, readonly) CGFloat descent; ///< line descent +@property (nonatomic, readonly) CGFloat leading; ///< line leading +@property (nonatomic, readonly) CGFloat lineWidth; ///< line width +@property (nonatomic, readonly) CGFloat trailingWhitespaceWidth; + +@property (nullable, nonatomic, readonly) NSArray *attachments; ///< ASTextAttachment +@property (nullable, nonatomic, readonly) NSArray *attachmentRanges; ///< NSRange(NSValue) +@property (nullable, nonatomic, readonly) NSArray *attachmentRects; ///< CGRect(NSValue) + +@end + + +typedef NS_ENUM(NSUInteger, ASTextRunGlyphDrawMode) { + /// No rotate. + ASTextRunGlyphDrawModeHorizontal = 0, + + /// Rotate vertical for single glyph. + ASTextRunGlyphDrawModeVerticalRotate = 1, + + /// Rotate vertical for single glyph, and move the glyph to a better position, + /// such as fullwidth punctuation. + ASTextRunGlyphDrawModeVerticalRotateMove = 2, +}; + +/** + A range in CTRun, used for vertical form. + */ +@interface ASTextRunGlyphRange : NSObject +@property (nonatomic) NSRange glyphRangeInRun; +@property (nonatomic) ASTextRunGlyphDrawMode drawMode; ++ (instancetype)rangeWithRange:(NSRange)range drawMode:(ASTextRunGlyphDrawMode)mode NS_RETURNS_RETAINED; +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/Component/ASTextLine.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/Component/ASTextLine.mm new file mode 100644 index 0000000..37c68e0 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/Component/ASTextLine.mm @@ -0,0 +1,169 @@ +// +// ASTextLine.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +@implementation ASTextLine { + CGFloat _firstGlyphPos; // first glyph position for baseline, typically 0. +} + ++ (instancetype)lineWithCTLine:(CTLineRef)CTLine position:(CGPoint)position vertical:(BOOL)isVertical NS_RETURNS_RETAINED { + if (!CTLine) return nil; + ASTextLine *line = [self new]; + line->_position = position; + line->_vertical = isVertical; + [line setCTLine:CTLine]; + return line; +} + +- (void)dealloc { + if (_CTLine) CFRelease(_CTLine); +} + +- (void)setCTLine:(_Nonnull CTLineRef)CTLine { + if (_CTLine != CTLine) { + if (CTLine) CFRetain(CTLine); + if (_CTLine) CFRelease(_CTLine); + _CTLine = CTLine; + if (_CTLine) { + _lineWidth = CTLineGetTypographicBounds(_CTLine, &_ascent, &_descent, &_leading); + CFRange range = CTLineGetStringRange(_CTLine); + _range = NSMakeRange(range.location, range.length); + if (CTLineGetGlyphCount(_CTLine) > 0) { + CFArrayRef runs = CTLineGetGlyphRuns(_CTLine); + CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runs, 0); + CGPoint pos; + CTRunGetPositions(run, CFRangeMake(0, 1), &pos); + _firstGlyphPos = pos.x; + } else { + _firstGlyphPos = 0; + } + _trailingWhitespaceWidth = CTLineGetTrailingWhitespaceWidth(_CTLine); + } else { + _lineWidth = _ascent = _descent = _leading = _firstGlyphPos = _trailingWhitespaceWidth = 0; + _range = NSMakeRange(0, 0); + } + [self reloadBounds]; + } +} + +- (void)setPosition:(CGPoint)position { + _position = position; + [self reloadBounds]; +} + +- (void)reloadBounds { + if (_vertical) { + _bounds = CGRectMake(_position.x - _descent, _position.y, _ascent + _descent, _lineWidth); + _bounds.origin.y += _firstGlyphPos; + } else { + _bounds = CGRectMake(_position.x, _position.y - _ascent, _lineWidth, _ascent + _descent); + _bounds.origin.x += _firstGlyphPos; + } + + _attachments = nil; + _attachmentRanges = nil; + _attachmentRects = nil; + if (!_CTLine) return; + CFArrayRef runs = CTLineGetGlyphRuns(_CTLine); + NSUInteger runCount = CFArrayGetCount(runs); + if (runCount == 0) return; + + NSMutableArray *attachments = nil; + NSMutableArray *attachmentRanges = nil; + NSMutableArray *attachmentRects = nil; + for (NSUInteger r = 0; r < runCount; r++) { + CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runs, r); + CFIndex glyphCount = CTRunGetGlyphCount(run); + if (glyphCount == 0) continue; + NSDictionary *attrs = (id)CTRunGetAttributes(run); + ASTextAttachment *attachment = attrs[ASTextAttachmentAttributeName]; + if (attachment) { + CGPoint runPosition = CGPointZero; + CTRunGetPositions(run, CFRangeMake(0, 1), &runPosition); + + CGFloat ascent, descent, leading, runWidth; + CGRect runTypoBounds; + runWidth = CTRunGetTypographicBounds(run, CFRangeMake(0, 0), &ascent, &descent, &leading); + + if (_vertical) { + ASTEXT_SWAP(runPosition.x, runPosition.y); + runPosition.y = _position.y + runPosition.y; + runTypoBounds = CGRectMake(_position.x + runPosition.x - descent, runPosition.y , ascent + descent, runWidth); + } else { + runPosition.x += _position.x; + runPosition.y = _position.y - runPosition.y; + runTypoBounds = CGRectMake(runPosition.x, runPosition.y - ascent, runWidth, ascent + descent); + } + + NSRange runRange = ASTextNSRangeFromCFRange(CTRunGetStringRange(run)); + if (!attachments) { + attachments = [[NSMutableArray alloc] init]; + attachmentRanges = [[NSMutableArray alloc] init]; + attachmentRects = [[NSMutableArray alloc] init]; + } + [attachments addObject:attachment]; + [attachmentRanges addObject:[NSValue valueWithRange:runRange]]; + [attachmentRects addObject:[NSValue valueWithCGRect:runTypoBounds]]; + } + } + _attachments = attachments; + _attachmentRanges = attachmentRanges; + _attachmentRects = attachmentRects; +} + +- (CGSize)size { + return _bounds.size; +} + +- (CGFloat)width { + return CGRectGetWidth(_bounds); +} + +- (CGFloat)height { + return CGRectGetHeight(_bounds); +} + +- (CGFloat)top { + return CGRectGetMinY(_bounds); +} + +- (CGFloat)bottom { + return CGRectGetMaxY(_bounds); +} + +- (CGFloat)left { + return CGRectGetMinX(_bounds); +} + +- (CGFloat)right { + return CGRectGetMaxX(_bounds); +} + +- (NSString *)description { + NSMutableString *desc = @"".mutableCopy; + NSRange range = self.range; + [desc appendFormat:@" row:%ld range:%tu,%tu", self, (long)self.row, range.location, range.length]; + [desc appendFormat:@" position:%@",NSStringFromCGPoint(self.position)]; + [desc appendFormat:@" bounds:%@",NSStringFromCGRect(self.bounds)]; + return desc; +} + +@end + + +@implementation ASTextRunGlyphRange ++ (instancetype)rangeWithRange:(NSRange)range drawMode:(ASTextRunGlyphDrawMode)mode NS_RETURNS_RETAINED { + ASTextRunGlyphRange *one = [self new]; + one.glyphRangeInRun = range; + one.drawMode = mode; + return one; +} +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/String/ASTextAttribute.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/String/ASTextAttribute.h new file mode 100644 index 0000000..571334e --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/String/ASTextAttribute.h @@ -0,0 +1,346 @@ +// +// ASTextAttribute.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +#pragma mark - Enum Define + +/// The attribute type +typedef NS_OPTIONS(NSInteger, ASTextAttributeType) { + ASTextAttributeTypeNone = 0, + ASTextAttributeTypeUIKit = 1 << 0, ///< UIKit attributes, such as UILabel/UITextField/drawInRect. + ASTextAttributeTypeCoreText = 1 << 1, ///< CoreText attributes, used by CoreText. + ASTextAttributeTypeASText = 1 << 2, ///< ASText attributes, used by ASText. +}; + +/// Get the attribute type from an attribute name. +ASDK_EXTERN ASTextAttributeType ASTextAttributeGetType(NSString *attributeName); + +/** + Line style in ASText (similar to NSUnderlineStyle). + */ +typedef NS_OPTIONS (NSInteger, ASTextLineStyle) { + // basic style (bitmask:0xFF) + ASTextLineStyleNone = 0x00, ///< ( ) Do not draw a line (Default). + ASTextLineStyleSingle = 0x01, ///< (──────) Draw a single line. + ASTextLineStyleThick = 0x02, ///< (━━━━━━━) Draw a thick line. + ASTextLineStyleDouble = 0x09, ///< (══════) Draw a double line. + + // style pattern (bitmask:0xF00) + ASTextLineStylePatternSolid = 0x000, ///< (────────) Draw a solid line (Default). + ASTextLineStylePatternDot = 0x100, ///< (‑ ‑ ‑ ‑ ‑ ‑) Draw a line of dots. + ASTextLineStylePatternDash = 0x200, ///< (— — — —) Draw a line of dashes. + ASTextLineStylePatternDashDot = 0x300, ///< (— ‑ — ‑ — ‑) Draw a line of alternating dashes and dots. + ASTextLineStylePatternDashDotDot = 0x400, ///< (— ‑ ‑ — ‑ ‑) Draw a line of alternating dashes and two dots. + ASTextLineStylePatternCircleDot = 0x900, ///< (••••••••••••) Draw a line of small circle dots. +}; + +/** + Text vertical alignment. + */ +typedef NS_ENUM(NSInteger, ASTextVerticalAlignment) { + ASTextVerticalAlignmentTop = 0, ///< Top alignment. + ASTextVerticalAlignmentCenter = 1, ///< Center alignment. + ASTextVerticalAlignmentBottom = 2, ///< Bottom alignment. +}; + +/** + The direction define in ASText. + */ +typedef NS_OPTIONS(NSUInteger, ASTextDirection) { + ASTextDirectionNone = 0, + ASTextDirectionTop = 1 << 0, + ASTextDirectionRight = 1 << 1, + ASTextDirectionBottom = 1 << 2, + ASTextDirectionLeft = 1 << 3, +}; + +/** + The trunction type, tells the truncation engine which type of truncation is being requested. + */ +typedef NS_ENUM (NSUInteger, ASTextTruncationType) { + /// No truncate. + ASTextTruncationTypeNone = 0, + + /// Truncate at the beginning of the line, leaving the end portion visible. + ASTextTruncationTypeStart = 1, + + /// Truncate at the end of the line, leaving the start portion visible. + ASTextTruncationTypeEnd = 2, + + /// Truncate in the middle of the line, leaving both the start and the end portions visible. + ASTextTruncationTypeMiddle = 3, +}; + + + +#pragma mark - Attribute Name Defined in ASText + +/// The value of this attribute is a `ASTextBackedString` object. +/// Use this attribute to store the original plain text if it is replaced by something else (such as attachment). +UIKIT_EXTERN NSString *const ASTextBackedStringAttributeName; + +/// The value of this attribute is a `ASTextBinding` object. +/// Use this attribute to bind a range of text together, as if it was a single charactor. +UIKIT_EXTERN NSString *const ASTextBindingAttributeName; + +/// The value of this attribute is a `ASTextShadow` object. +/// Use this attribute to add shadow to a range of text. +/// Shadow will be drawn below text glyphs. Use ASTextShadow.subShadow to add multi-shadow. +UIKIT_EXTERN NSString *const ASTextShadowAttributeName; + +/// The value of this attribute is a `ASTextShadow` object. +/// Use this attribute to add inner shadow to a range of text. +/// Inner shadow will be drawn above text glyphs. Use ASTextShadow.subShadow to add multi-shadow. +UIKIT_EXTERN NSString *const ASTextInnerShadowAttributeName; + +/// The value of this attribute is a `ASTextDecoration` object. +/// Use this attribute to add underline to a range of text. +/// The underline will be drawn below text glyphs. +UIKIT_EXTERN NSString *const ASTextUnderlineAttributeName; + +/// The value of this attribute is a `ASTextDecoration` object. +/// Use this attribute to add strikethrough (delete line) to a range of text. +/// The strikethrough will be drawn above text glyphs. +UIKIT_EXTERN NSString *const ASTextStrikethroughAttributeName; + +/// The value of this attribute is a `ASTextBorder` object. +/// Use this attribute to add cover border or cover color to a range of text. +/// The border will be drawn above the text glyphs. +UIKIT_EXTERN NSString *const ASTextBorderAttributeName; + +/// The value of this attribute is a `ASTextBorder` object. +/// Use this attribute to add background border or background color to a range of text. +/// The border will be drawn below the text glyphs. +UIKIT_EXTERN NSString *const ASTextBackgroundBorderAttributeName; + +/// The value of this attribute is a `ASTextBorder` object. +/// Use this attribute to add a code block border to one or more line of text. +/// The border will be drawn below the text glyphs. +UIKIT_EXTERN NSString *const ASTextBlockBorderAttributeName; + +/// The value of this attribute is a `ASTextAttachment` object. +/// Use this attribute to add attachment to text. +/// It should be used in conjunction with a CTRunDelegate. +UIKIT_EXTERN NSString *const ASTextAttachmentAttributeName; + +/// The value of this attribute is a `ASTextHighlight` object. +/// Use this attribute to add a touchable highlight state to a range of text. +UIKIT_EXTERN NSString *const ASTextHighlightAttributeName; + +/// The value of this attribute is a `NSValue` object stores CGAffineTransform. +/// Use this attribute to add transform to each glyph in a range of text. +UIKIT_EXTERN NSString *const ASTextGlyphTransformAttributeName; + + + +#pragma mark - String Token Define + +UIKIT_EXTERN NSString *const ASTextAttachmentToken; ///< Object replacement character (U+FFFC), used for text attachment. +UIKIT_EXTERN NSString *const ASTextTruncationToken; ///< Horizontal ellipsis (U+2026), used for text truncation "…". + + + +#pragma mark - Attribute Value Define + +/** + The tap/long press action callback defined in ASText. + + @param containerView The text container view (such as ASLabel/ASTextView). + @param text The whole text. + @param range The text range in `text` (if no range, the range.location is NSNotFound). + @param rect The text frame in `containerView` (if no data, the rect is CGRectNull). + */ +typedef void(^ASTextAction)(UIView *containerView, NSAttributedString *text, NSRange range, CGRect rect); + + +/** + ASTextBackedString objects are used by the NSAttributedString class cluster + as the values for text backed string attributes (stored in the attributed + string under the key named ASTextBackedStringAttributeName). + + It may used for copy/paste plain text from attributed string. + Example: If :) is replace by a custom emoji (such as😊), the backed string can be set to @":)". + */ +@interface ASTextBackedString : NSObject ++ (instancetype)stringWithString:(nullable NSString *)string NS_RETURNS_RETAINED; +@property (nullable, nonatomic, copy) NSString *string; ///< backed string +@end + + +/** + ASTextBinding objects are used by the NSAttributedString class cluster + as the values for shadow attributes (stored in the attributed string under + the key named ASTextBindingAttributeName). + + Add this to a range of text will make the specified characters 'binding together'. + ASTextView will treat the range of text as a single character during text + selection and edit. + */ +@interface ASTextBinding : NSObject ++ (instancetype)bindingWithDeleteConfirm:(BOOL)deleteConfirm NS_RETURNS_RETAINED; +@property (nonatomic) BOOL deleteConfirm; ///< confirm the range when delete in ASTextView +@end + + +/** + ASTextShadow objects are used by the NSAttributedString class cluster + as the values for shadow attributes (stored in the attributed string under + the key named ASTextShadowAttributeName or ASTextInnerShadowAttributeName). + + It's similar to `NSShadow`, but offers more options. + */ +@interface ASTextShadow : NSObject ++ (instancetype)shadowWithColor:(nullable UIColor *)color offset:(CGSize)offset radius:(CGFloat)radius NS_RETURNS_RETAINED; + +@property (nullable, nonatomic) UIColor *color; ///< shadow color +@property (nonatomic) CGSize offset; ///< shadow offset +@property (nonatomic) CGFloat radius; ///< shadow blur radius +@property (nonatomic) CGBlendMode blendMode; ///< shadow blend mode +@property (nullable, nonatomic) ASTextShadow *subShadow; ///< a sub shadow which will be added above the parent shadow + ++ (instancetype)shadowWithNSShadow:(NSShadow *)nsShadow NS_RETURNS_RETAINED; ///< convert NSShadow to ASTextShadow +- (NSShadow *)nsShadow; ///< convert ASTextShadow to NSShadow +@end + + +/** + ASTextDecorationLine objects are used by the NSAttributedString class cluster + as the values for decoration line attributes (stored in the attributed string under + the key named ASTextUnderlineAttributeName or ASTextStrikethroughAttributeName). + + When it's used as underline, the line is drawn below text glyphs; + when it's used as strikethrough, the line is drawn above text glyphs. + */ +@interface ASTextDecoration : NSObject ++ (instancetype)decorationWithStyle:(ASTextLineStyle)style NS_RETURNS_RETAINED; ++ (instancetype)decorationWithStyle:(ASTextLineStyle)style width:(nullable NSNumber *)width color:(nullable UIColor *)color NS_RETURNS_RETAINED; +@property (nonatomic) ASTextLineStyle style; ///< line style +@property (nullable, nonatomic) NSNumber *width; ///< line width (nil means automatic width) +@property (nullable, nonatomic) UIColor *color; ///< line color (nil means automatic color) +@property (nullable, nonatomic) ASTextShadow *shadow; ///< line shadow +@end + + +/** + ASTextBorder objects are used by the NSAttributedString class cluster + as the values for border attributes (stored in the attributed string under + the key named ASTextBorderAttributeName or ASTextBackgroundBorderAttributeName). + + It can be used to draw a border around a range of text, or draw a background + to a range of text. + + Example: + ╭──────╮ + │ Text │ + ╰──────╯ + */ +@interface ASTextBorder : NSObject ++ (instancetype)borderWithLineStyle:(ASTextLineStyle)lineStyle lineWidth:(CGFloat)width strokeColor:(nullable UIColor *)color NS_RETURNS_RETAINED; ++ (instancetype)borderWithFillColor:(nullable UIColor *)color cornerRadius:(CGFloat)cornerRadius NS_RETURNS_RETAINED; +@property (nonatomic) ASTextLineStyle lineStyle; ///< border line style +@property (nonatomic) CGFloat strokeWidth; ///< border line width +@property (nullable, nonatomic) UIColor *strokeColor; ///< border line color +@property (nonatomic) CGLineJoin lineJoin; ///< border line join +@property (nonatomic) UIEdgeInsets insets; ///< border insets for text bounds +@property (nonatomic) CGFloat cornerRadius; ///< border corder radius +@property (nullable, nonatomic) ASTextShadow *shadow; ///< border shadow +@property (nullable, nonatomic) UIColor *fillColor; ///< inner fill color +@end + + +/** + ASTextAttachment objects are used by the NSAttributedString class cluster + as the values for attachment attributes (stored in the attributed string under + the key named ASTextAttachmentAttributeName). + + When display an attributed string which contains `ASTextAttachment` object, + the content will be placed in text metric. If the content is `UIImage`, + then it will be drawn to CGContext; if the content is `UIView` or `CALayer`, + then it will be added to the text container's view or layer. + */ +@interface ASTextAttachment : NSObject ++ (instancetype)attachmentWithContent:(nullable id)content NS_RETURNS_RETAINED; +@property (nullable, nonatomic) id content; ///< Supported type: UIImage, UIView, CALayer +@property (nonatomic) UIViewContentMode contentMode; ///< Content display mode. +@property (nonatomic) UIEdgeInsets contentInsets; ///< The insets when drawing content. +@property (nullable, nonatomic) NSDictionary *userInfo; ///< The user information dictionary. +@end + + +/** + ASTextHighlight objects are used by the NSAttributedString class cluster + as the values for touchable highlight attributes (stored in the attributed string + under the key named ASTextHighlightAttributeName). + + When display an attributed string in `ASLabel` or `ASTextView`, the range of + highlight text can be toucheds down by users. If a range of text is turned into + highlighted state, the `attributes` in `ASTextHighlight` will be used to modify + (set or remove) the original attributes in the range for display. + */ +@interface ASTextHighlight : NSObject + +/** + Attributes that you can apply to text in an attributed string when highlight. + Key: Same as CoreText/ASText Attribute Name. + Value: Modify attribute value when highlight (NSNull for remove attribute). + */ +@property (nullable, nonatomic, copy) NSDictionary *attributes; + +/** + Creates a highlight object with specified attributes. + + @param attributes The attributes which will replace original attributes when highlight, + If the value is NSNull, it will removed when highlight. + */ ++ (instancetype)highlightWithAttributes:(nullable NSDictionary *)attributes NS_RETURNS_RETAINED; + +/** + Convenience methods to create a default highlight with the specifeid background color. + + @param color The background border color. + */ ++ (instancetype)highlightWithBackgroundColor:(nullable UIColor *)color NS_RETURNS_RETAINED; + +// Convenience methods below to set the `attributes`. +- (void)setFont:(nullable UIFont *)font; +- (void)setColor:(nullable UIColor *)color; +- (void)setStrokeWidth:(nullable NSNumber *)width; +- (void)setStrokeColor:(nullable UIColor *)color; +- (void)setShadow:(nullable ASTextShadow *)shadow; +- (void)setInnerShadow:(nullable ASTextShadow *)shadow; +- (void)setUnderline:(nullable ASTextDecoration *)underline; +- (void)setStrikethrough:(nullable ASTextDecoration *)strikethrough; +- (void)setBackgroundBorder:(nullable ASTextBorder *)border; +- (void)setBorder:(nullable ASTextBorder *)border; +- (void)setAttachment:(nullable ASTextAttachment *)attachment; + +/** + The user information dictionary, default is nil. + */ +@property (nullable, nonatomic, copy) NSDictionary *userInfo; + +/** + Tap action when user tap the highlight, default is nil. + If the value is nil, ASTextView or ASLabel will ask it's delegate to handle the tap action. + */ +@property (nullable, nonatomic) ASTextAction tapAction; + +/** + Long press action when user long press the highlight, default is nil. + If the value is nil, ASTextView or ASLabel will ask it's delegate to handle the long press action. + */ +@property (nullable, nonatomic) ASTextAction longPressAction; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/String/ASTextAttribute.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/String/ASTextAttribute.mm new file mode 100644 index 0000000..8af4322 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/String/ASTextAttribute.mm @@ -0,0 +1,487 @@ +// +// ASTextAttribute.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import "ASTextAttribute.h" +#import +#import + +NSString *const ASTextBackedStringAttributeName = @"ASTextBackedString"; +NSString *const ASTextBindingAttributeName = @"ASTextBinding"; +NSString *const ASTextShadowAttributeName = @"ASTextShadow"; +NSString *const ASTextInnerShadowAttributeName = @"ASTextInnerShadow"; +NSString *const ASTextUnderlineAttributeName = @"ASTextUnderline"; +NSString *const ASTextStrikethroughAttributeName = @"ASTextStrikethrough"; +NSString *const ASTextBorderAttributeName = @"ASTextBorder"; +NSString *const ASTextBackgroundBorderAttributeName = @"ASTextBackgroundBorder"; +NSString *const ASTextBlockBorderAttributeName = @"ASTextBlockBorder"; +NSString *const ASTextAttachmentAttributeName = @"ASTextAttachment"; +NSString *const ASTextHighlightAttributeName = @"ASTextHighlight"; +NSString *const ASTextGlyphTransformAttributeName = @"ASTextGlyphTransform"; + +NSString *const ASTextAttachmentToken = @"\uFFFC"; +NSString *const ASTextTruncationToken = @"\u2026"; + + +ASTextAttributeType ASTextAttributeGetType(NSString *name){ + if (name.length == 0) return ASTextAttributeTypeNone; + + static NSMutableDictionary *dic; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + dic = [NSMutableDictionary new]; + NSNumber *All = @(ASTextAttributeTypeUIKit | ASTextAttributeTypeCoreText | ASTextAttributeTypeASText); + NSNumber *CoreText_ASText = @(ASTextAttributeTypeCoreText | ASTextAttributeTypeASText); + NSNumber *UIKit_ASText = @(ASTextAttributeTypeUIKit | ASTextAttributeTypeASText); + NSNumber *UIKit_CoreText = @(ASTextAttributeTypeUIKit | ASTextAttributeTypeCoreText); + NSNumber *UIKit = @(ASTextAttributeTypeUIKit); + NSNumber *CoreText = @(ASTextAttributeTypeCoreText); + NSNumber *ASText = @(ASTextAttributeTypeASText); + + dic[NSFontAttributeName] = All; + dic[NSKernAttributeName] = All; + dic[NSForegroundColorAttributeName] = UIKit; + dic[(id)kCTForegroundColorAttributeName] = CoreText; + dic[(id)kCTForegroundColorFromContextAttributeName] = CoreText; + dic[NSBackgroundColorAttributeName] = UIKit; + dic[NSStrokeWidthAttributeName] = All; + dic[NSStrokeColorAttributeName] = UIKit; + dic[(id)kCTStrokeColorAttributeName] = CoreText_ASText; + dic[NSShadowAttributeName] = UIKit_ASText; + dic[NSStrikethroughStyleAttributeName] = UIKit; + dic[NSUnderlineStyleAttributeName] = UIKit_CoreText; + dic[(id)kCTUnderlineColorAttributeName] = CoreText; + dic[NSLigatureAttributeName] = All; + dic[(id)kCTSuperscriptAttributeName] = UIKit; //it's a CoreText attrubite, but only supported by UIKit... + dic[NSVerticalGlyphFormAttributeName] = All; + dic[(id)kCTGlyphInfoAttributeName] = CoreText_ASText; +#if TARGET_OS_IOS +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + dic[(id)kCTCharacterShapeAttributeName] = CoreText_ASText; +#pragma clang diagnostic pop +#endif + dic[(id)kCTRunDelegateAttributeName] = CoreText_ASText; + dic[(id)kCTBaselineClassAttributeName] = CoreText_ASText; + dic[(id)kCTBaselineInfoAttributeName] = CoreText_ASText; + dic[(id)kCTBaselineReferenceInfoAttributeName] = CoreText_ASText; + dic[(id)kCTWritingDirectionAttributeName] = CoreText_ASText; + dic[NSParagraphStyleAttributeName] = All; + + dic[NSStrikethroughColorAttributeName] = UIKit; + dic[NSUnderlineColorAttributeName] = UIKit; + dic[NSTextEffectAttributeName] = UIKit; + dic[NSObliquenessAttributeName] = UIKit; + dic[NSExpansionAttributeName] = UIKit; + dic[(id)kCTLanguageAttributeName] = CoreText_ASText; + dic[NSBaselineOffsetAttributeName] = UIKit; + dic[NSWritingDirectionAttributeName] = All; + dic[NSAttachmentAttributeName] = UIKit; + dic[NSLinkAttributeName] = UIKit; + dic[(id)kCTRubyAnnotationAttributeName] = CoreText; + + dic[ASTextBackedStringAttributeName] = ASText; + dic[ASTextBindingAttributeName] = ASText; + dic[ASTextShadowAttributeName] = ASText; + dic[ASTextInnerShadowAttributeName] = ASText; + dic[ASTextUnderlineAttributeName] = ASText; + dic[ASTextStrikethroughAttributeName] = ASText; + dic[ASTextBorderAttributeName] = ASText; + dic[ASTextBackgroundBorderAttributeName] = ASText; + dic[ASTextBlockBorderAttributeName] = ASText; + dic[ASTextAttachmentAttributeName] = ASText; + dic[ASTextHighlightAttributeName] = ASText; + dic[ASTextGlyphTransformAttributeName] = ASText; + }); + NSNumber *num = dic[name]; + if (num) return num.integerValue; + return ASTextAttributeTypeNone; +} + + +@implementation ASTextBackedString + ++ (instancetype)stringWithString:(NSString *)string NS_RETURNS_RETAINED { + ASTextBackedString *one = [self new]; + one.string = string; + return one; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:self.string forKey:@"string"]; +} + +- (id)initWithCoder:(NSCoder *)aDecoder { + self = [super init]; + _string = [aDecoder decodeObjectForKey:@"string"]; + return self; +} + +- (id)copyWithZone:(NSZone *)zone { + __typeof__(self) one = [self.class new]; + one.string = self.string; + return one; +} + +@end + + +@implementation ASTextBinding + ++ (instancetype)bindingWithDeleteConfirm:(BOOL)deleteConfirm NS_RETURNS_RETAINED { + ASTextBinding *one = [self new]; + one.deleteConfirm = deleteConfirm; + return one; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:@(self.deleteConfirm) forKey:@"deleteConfirm"]; +} + +- (id)initWithCoder:(NSCoder *)aDecoder { + self = [super init]; + _deleteConfirm = ((NSNumber *)[aDecoder decodeObjectForKey:@"deleteConfirm"]).boolValue; + return self; +} + +- (id)copyWithZone:(NSZone *)zone { + __typeof__(self) one = [self.class new]; + one.deleteConfirm = self.deleteConfirm; + return one; +} + +@end + + +@implementation ASTextShadow + ++ (instancetype)shadowWithColor:(UIColor *)color offset:(CGSize)offset radius:(CGFloat)radius NS_RETURNS_RETAINED { + ASTextShadow *one = [self new]; + one.color = color; + one.offset = offset; + one.radius = radius; + return one; +} + ++ (instancetype)shadowWithNSShadow:(NSShadow *)nsShadow NS_RETURNS_RETAINED { + if (!nsShadow) return nil; + ASTextShadow *shadow = [self new]; + shadow.offset = nsShadow.shadowOffset; + shadow.radius = nsShadow.shadowBlurRadius; + id color = nsShadow.shadowColor; + if (color) { + if (CGColorGetTypeID() == CFGetTypeID((__bridge CFTypeRef)(color))) { + color = [UIColor colorWithCGColor:(__bridge CGColorRef)(color)]; + } + if ([color isKindOfClass:[UIColor class]]) { + shadow.color = color; + } + } + return shadow; +} + +- (NSShadow *)nsShadow { + NSShadow *shadow = [NSShadow new]; + shadow.shadowOffset = self.offset; + shadow.shadowBlurRadius = self.radius; + shadow.shadowColor = self.color; + return shadow; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:self.color forKey:@"color"]; + [aCoder encodeObject:@(self.radius) forKey:@"radius"]; + [aCoder encodeObject:[NSValue valueWithCGSize:self.offset] forKey:@"offset"]; + [aCoder encodeObject:self.subShadow forKey:@"subShadow"]; +} + +- (id)initWithCoder:(NSCoder *)aDecoder { + self = [super init]; + _color = [aDecoder decodeObjectForKey:@"color"]; + _radius = ((NSNumber *)[aDecoder decodeObjectForKey:@"radius"]).floatValue; + _offset = ((NSValue *)[aDecoder decodeObjectForKey:@"offset"]).CGSizeValue; + _subShadow = [aDecoder decodeObjectForKey:@"subShadow"]; + return self; +} + +- (id)copyWithZone:(NSZone *)zone { + __typeof__(self) one = [self.class new]; + one.color = self.color; + one.radius = self.radius; + one.offset = self.offset; + one.subShadow = self.subShadow.copy; + return one; +} + +@end + + +@implementation ASTextDecoration + +- (instancetype)init { + self = [super init]; + _style = ASTextLineStyleSingle; + return self; +} + ++ (instancetype)decorationWithStyle:(ASTextLineStyle)style NS_RETURNS_RETAINED { + ASTextDecoration *one = [self new]; + one.style = style; + return one; +} ++ (instancetype)decorationWithStyle:(ASTextLineStyle)style width:(NSNumber *)width color:(UIColor *)color NS_RETURNS_RETAINED { + ASTextDecoration *one = [self new]; + one.style = style; + one.width = width; + one.color = color; + return one; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:@(self.style) forKey:@"style"]; + [aCoder encodeObject:self.width forKey:@"width"]; + [aCoder encodeObject:self.color forKey:@"color"]; +} + +- (id)initWithCoder:(NSCoder *)aDecoder { + self = [super init]; + self.style = ((NSNumber *)[aDecoder decodeObjectForKey:@"style"]).unsignedIntegerValue; + self.width = [aDecoder decodeObjectForKey:@"width"]; + self.color = [aDecoder decodeObjectForKey:@"color"]; + return self; +} + +- (id)copyWithZone:(NSZone *)zone { + __typeof__(self) one = [self.class new]; + one.style = self.style; + one.width = self.width; + one.color = self.color; + return one; +} + +@end + + +@implementation ASTextBorder + ++ (instancetype)borderWithLineStyle:(ASTextLineStyle)lineStyle lineWidth:(CGFloat)width strokeColor:(UIColor *)color NS_RETURNS_RETAINED { + ASTextBorder *one = [self new]; + one.lineStyle = lineStyle; + one.strokeWidth = width; + one.strokeColor = color; + return one; +} + ++ (instancetype)borderWithFillColor:(UIColor *)color cornerRadius:(CGFloat)cornerRadius NS_RETURNS_RETAINED { + ASTextBorder *one = [self new]; + one.fillColor = color; + one.cornerRadius = cornerRadius; + one.insets = UIEdgeInsetsMake(-2, 0, 0, -2); + return one; +} + +- (instancetype)init { + self = [super init]; + self.lineStyle = ASTextLineStyleSingle; + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:@(self.lineStyle) forKey:@"lineStyle"]; + [aCoder encodeObject:@(self.strokeWidth) forKey:@"strokeWidth"]; + [aCoder encodeObject:self.strokeColor forKey:@"strokeColor"]; + [aCoder encodeObject:@(self.lineJoin) forKey:@"lineJoin"]; + [aCoder encodeObject:[NSValue valueWithUIEdgeInsets:self.insets] forKey:@"insets"]; + [aCoder encodeObject:@(self.cornerRadius) forKey:@"cornerRadius"]; + [aCoder encodeObject:self.shadow forKey:@"shadow"]; + [aCoder encodeObject:self.fillColor forKey:@"fillColor"]; +} + +- (id)initWithCoder:(NSCoder *)aDecoder { + self = [super init]; + _lineStyle = ((NSNumber *)[aDecoder decodeObjectForKey:@"lineStyle"]).unsignedIntegerValue; + _strokeWidth = ((NSNumber *)[aDecoder decodeObjectForKey:@"strokeWidth"]).doubleValue; + _strokeColor = [aDecoder decodeObjectForKey:@"strokeColor"]; + _lineJoin = (CGLineJoin)((NSNumber *)[aDecoder decodeObjectForKey:@"join"]).unsignedIntegerValue; + _insets = ((NSValue *)[aDecoder decodeObjectForKey:@"insets"]).UIEdgeInsetsValue; + _cornerRadius = ((NSNumber *)[aDecoder decodeObjectForKey:@"cornerRadius"]).doubleValue; + _shadow = [aDecoder decodeObjectForKey:@"shadow"]; + _fillColor = [aDecoder decodeObjectForKey:@"fillColor"]; + return self; +} + +- (id)copyWithZone:(NSZone *)zone { + __typeof__(self) one = [self.class new]; + one.lineStyle = self.lineStyle; + one.strokeWidth = self.strokeWidth; + one.strokeColor = self.strokeColor; + one.lineJoin = self.lineJoin; + one.insets = self.insets; + one.cornerRadius = self.cornerRadius; + one.shadow = self.shadow.copy; + one.fillColor = self.fillColor; + return one; +} + +@end + + +@implementation ASTextAttachment + ++ (instancetype)attachmentWithContent:(id)content NS_RETURNS_RETAINED { + ASTextAttachment *one = [self new]; + one.content = content; + return one; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:self.content forKey:@"content"]; + [aCoder encodeObject:[NSValue valueWithUIEdgeInsets:self.contentInsets] forKey:@"contentInsets"]; + [aCoder encodeObject:self.userInfo forKey:@"userInfo"]; +} + +- (id)initWithCoder:(NSCoder *)aDecoder { + self = [super init]; + _content = [aDecoder decodeObjectForKey:@"content"]; + _contentInsets = ((NSValue *)[aDecoder decodeObjectForKey:@"contentInsets"]).UIEdgeInsetsValue; + _userInfo = [aDecoder decodeObjectForKey:@"userInfo"]; + return self; +} + +- (id)copyWithZone:(NSZone *)zone { + __typeof__(self) one = [self.class new]; + if ([self.content respondsToSelector:@selector(copy)]) { + one.content = [self.content copy]; + } else { + one.content = self.content; + } + one.contentInsets = self.contentInsets; + one.userInfo = self.userInfo.copy; + return one; +} + +@end + + +@implementation ASTextHighlight + ++ (instancetype)highlightWithAttributes:(NSDictionary *)attributes NS_RETURNS_RETAINED { + ASTextHighlight *one = [self new]; + one.attributes = attributes; + return one; +} + ++ (instancetype)highlightWithBackgroundColor:(UIColor *)color NS_RETURNS_RETAINED { + ASTextBorder *highlightBorder = [ASTextBorder new]; + highlightBorder.insets = UIEdgeInsetsMake(-2, -1, -2, -1); + highlightBorder.cornerRadius = 3; + highlightBorder.fillColor = color; + + ASTextHighlight *one = [self new]; + [one setBackgroundBorder:highlightBorder]; + return one; +} + +- (void)setAttributes:(NSDictionary *)attributes { + _attributes = attributes.mutableCopy; +} + +- (id)copyWithZone:(NSZone *)zone { + __typeof__(self) one = [self.class new]; + one.attributes = self.attributes.mutableCopy; + return one; +} + +- (void)_makeMutableAttributes { + if (!_attributes) { + _attributes = [NSMutableDictionary new]; + } else if (![_attributes isKindOfClass:[NSMutableDictionary class]]) { + _attributes = _attributes.mutableCopy; + } +} + +- (void)setFont:(UIFont *)font { + [self _makeMutableAttributes]; + if (font == (id)[NSNull null] || font == nil) { + ((NSMutableDictionary *)_attributes)[(id)kCTFontAttributeName] = [NSNull null]; + } else { + CTFontRef ctFont = CTFontCreateWithName((__bridge CFStringRef)font.fontName, font.pointSize, NULL); + if (ctFont) { + ((NSMutableDictionary *)_attributes)[(id)kCTFontAttributeName] = (__bridge id)(ctFont); + CFRelease(ctFont); + } + } +} + +- (void)setColor:(UIColor *)color { + [self _makeMutableAttributes]; + if (color == (id)[NSNull null] || color == nil) { + ((NSMutableDictionary *)_attributes)[(id)kCTForegroundColorAttributeName] = [NSNull null]; + ((NSMutableDictionary *)_attributes)[NSForegroundColorAttributeName] = [NSNull null]; + } else { + ((NSMutableDictionary *)_attributes)[(id)kCTForegroundColorAttributeName] = (__bridge id)(color.CGColor); + ((NSMutableDictionary *)_attributes)[NSForegroundColorAttributeName] = color; + } +} + +- (void)setStrokeWidth:(NSNumber *)width { + [self _makeMutableAttributes]; + if (width == (id)[NSNull null] || width == nil) { + ((NSMutableDictionary *)_attributes)[(id)kCTStrokeWidthAttributeName] = [NSNull null]; + } else { + ((NSMutableDictionary *)_attributes)[(id)kCTStrokeWidthAttributeName] = width; + } +} + +- (void)setStrokeColor:(UIColor *)color { + [self _makeMutableAttributes]; + if (color == (id)[NSNull null] || color == nil) { + ((NSMutableDictionary *)_attributes)[(id)kCTStrokeColorAttributeName] = [NSNull null]; + ((NSMutableDictionary *)_attributes)[NSStrokeColorAttributeName] = [NSNull null]; + } else { + ((NSMutableDictionary *)_attributes)[(id)kCTStrokeColorAttributeName] = (__bridge id)(color.CGColor); + ((NSMutableDictionary *)_attributes)[NSStrokeColorAttributeName] = color; + } +} + +- (void)setTextAttribute:(NSString *)attribute value:(id)value { + [self _makeMutableAttributes]; + if (value == nil) value = [NSNull null]; + ((NSMutableDictionary *)_attributes)[attribute] = value; +} + +- (void)setShadow:(ASTextShadow *)shadow { + [self setTextAttribute:ASTextShadowAttributeName value:shadow]; +} + +- (void)setInnerShadow:(ASTextShadow *)shadow { + [self setTextAttribute:ASTextInnerShadowAttributeName value:shadow]; +} + +- (void)setUnderline:(ASTextDecoration *)underline { + [self setTextAttribute:ASTextUnderlineAttributeName value:underline]; +} + +- (void)setStrikethrough:(ASTextDecoration *)strikethrough { + [self setTextAttribute:ASTextStrikethroughAttributeName value:strikethrough]; +} + +- (void)setBackgroundBorder:(ASTextBorder *)border { + [self setTextAttribute:ASTextBackgroundBorderAttributeName value:border]; +} + +- (void)setBorder:(ASTextBorder *)border { + [self setTextAttribute:ASTextBorderAttributeName value:border]; +} + +- (void)setAttachment:(ASTextAttachment *)attachment { + [self setTextAttribute:ASTextAttachmentAttributeName value:attachment]; +} + +@end + diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/String/ASTextRunDelegate.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/String/ASTextRunDelegate.h new file mode 100644 index 0000000..973dc5f --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/String/ASTextRunDelegate.h @@ -0,0 +1,65 @@ +// +// ASTextRunDelegate.h +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + Wrapper for CTRunDelegateRef. + + Example: + + ASTextRunDelegate *delegate = [ASTextRunDelegate new]; + delegate.ascent = 20; + delegate.descent = 4; + delegate.width = 20; + CTRunDelegateRef ctRunDelegate = delegate.CTRunDelegate; + if (ctRunDelegate) { + /// add to attributed string + CFRelease(ctRunDelegate); + } + + */ +@interface ASTextRunDelegate : NSObject + +/** + Creates and returns the CTRunDelegate. + + @discussion You need call CFRelease() after used. + The CTRunDelegateRef has a strong reference to this ASTextRunDelegate object. + In CoreText, use CTRunDelegateGetRefCon() to get this ASTextRunDelegate object. + + @return The CTRunDelegate object. + */ +- (nullable CTRunDelegateRef)CTRunDelegate CF_RETURNS_RETAINED; + +/** + Additional information about the the run delegate. + */ +@property (nullable, nonatomic, copy) NSDictionary *userInfo; + +/** + The typographic ascent of glyphs in the run. + */ +@property (nonatomic) CGFloat ascent; + +/** + The typographic descent of glyphs in the run. + */ +@property (nonatomic) CGFloat descent; + +/** + The typographic width of glyphs in the run. + */ +@property (nonatomic) CGFloat width; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/String/ASTextRunDelegate.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/String/ASTextRunDelegate.mm new file mode 100644 index 0000000..1c179b1 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/String/ASTextRunDelegate.mm @@ -0,0 +1,68 @@ +// +// ASTextRunDelegate.mm +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +static void DeallocCallback(void *ref) { + ASTextRunDelegate *self = (__bridge_transfer ASTextRunDelegate *)(ref); + self = nil; // release +} + +static CGFloat GetAscentCallback(void *ref) { + ASTextRunDelegate *self = (__bridge ASTextRunDelegate *)(ref); + return self.ascent; +} + +static CGFloat GetDecentCallback(void *ref) { + ASTextRunDelegate *self = (__bridge ASTextRunDelegate *)(ref); + return self.descent; +} + +static CGFloat GetWidthCallback(void *ref) { + ASTextRunDelegate *self = (__bridge ASTextRunDelegate *)(ref); + return self.width; +} + +@implementation ASTextRunDelegate + +- (CTRunDelegateRef)CTRunDelegate CF_RETURNS_RETAINED { + CTRunDelegateCallbacks callbacks; + callbacks.version = kCTRunDelegateCurrentVersion; + callbacks.dealloc = DeallocCallback; + callbacks.getAscent = GetAscentCallback; + callbacks.getDescent = GetDecentCallback; + callbacks.getWidth = GetWidthCallback; + return CTRunDelegateCreate(&callbacks, (__bridge_retained void *)(self.copy)); +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:@(_ascent) forKey:@"ascent"]; + [aCoder encodeObject:@(_descent) forKey:@"descent"]; + [aCoder encodeObject:@(_width) forKey:@"width"]; + [aCoder encodeObject:_userInfo forKey:@"userInfo"]; +} + +- (id)initWithCoder:(NSCoder *)aDecoder { + self = [super init]; + _ascent = ((NSNumber *)[aDecoder decodeObjectForKey:@"ascent"]).floatValue; + _descent = ((NSNumber *)[aDecoder decodeObjectForKey:@"descent"]).floatValue; + _width = ((NSNumber *)[aDecoder decodeObjectForKey:@"width"]).floatValue; + _userInfo = [aDecoder decodeObjectForKey:@"userInfo"]; + return self; +} + +- (id)copyWithZone:(NSZone *)zone { + __typeof__(self) one = [self.class new]; + one.ascent = self.ascent; + one.descent = self.descent; + one.width = self.width; + one.userInfo = self.userInfo; + return one; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/Utility/ASTextUtilities.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/Utility/ASTextUtilities.h new file mode 100644 index 0000000..2ba0f09 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/Utility/ASTextUtilities.h @@ -0,0 +1,315 @@ +// +// ASTextUtilities.h +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import +#import +#import + + +#ifndef ASTEXT_CLAMP // return the clamped value +#define ASTEXT_CLAMP(_x_, _low_, _high_) (((_x_) > (_high_)) ? (_high_) : (((_x_) < (_low_)) ? (_low_) : (_x_))) +#endif + +#ifndef ASTEXT_SWAP // swap two value +#define ASTEXT_SWAP(_a_, _b_) do { __typeof__(_a_) _tmp_ = (_a_); (_a_) = (_b_); (_b_) = _tmp_; } while (0) +#endif + +NS_ASSUME_NONNULL_BEGIN + +/** + Whether the character is 'line break char': + U+000D (\\r or CR) + U+2028 (Unicode line separator) + U+000A (\\n or LF) + U+2029 (Unicode paragraph separator) + + @param c A character + @return YES or NO. + */ +static inline BOOL ASTextIsLinebreakChar(unichar c) { + switch (c) { + case 0x000D: + case 0x2028: + case 0x000A: + case 0x2029: + return YES; + default: + return NO; + } +} + +/** + Whether the string is a 'line break': + U+000D (\\r or CR) + U+2028 (Unicode line separator) + U+000A (\\n or LF) + U+2029 (Unicode paragraph separator) + \\r\\n, in that order (also known as CRLF) + + @param str A string + @return YES or NO. + */ +static inline BOOL ASTextIsLinebreakString(NSString * _Nullable str) { + if (str.length > 2 || str.length == 0) return NO; + if (str.length == 1) { + unichar c = [str characterAtIndex:0]; + return ASTextIsLinebreakChar(c); + } else { + return ([str characterAtIndex:0] == '\r') && ([str characterAtIndex:1] == '\n'); + } +} + +/** + If the string has a 'line break' suffix, return the 'line break' length. + + @param str A string. + @return The length of the tail line break: 0, 1 or 2. + */ +static inline NSUInteger ASTextLinebreakTailLength(NSString * _Nullable str) { + if (str.length >= 2) { + unichar c2 = [str characterAtIndex:str.length - 1]; + if (ASTextIsLinebreakChar(c2)) { + unichar c1 = [str characterAtIndex:str.length - 2]; + if (c1 == '\r' && c2 == '\n') return 2; + else return 1; + } else { + return 0; + } + } else if (str.length == 1) { + return ASTextIsLinebreakChar([str characterAtIndex:0]) ? 1 : 0; + } else { + return 0; + } +} + +/** + Whether the font contains color bitmap glyphs. + + @discussion Only `AppleColorEmoji` contains color bitmap glyphs in iOS system fonts. + @param font A font. + @return YES: the font contains color bitmap glyphs, NO: the font has no color bitmap glyph. + */ +static inline BOOL ASTextCTFontContainsColorBitmapGlyphs(CTFontRef font) { + return (CTFontGetSymbolicTraits(font) & kCTFontTraitColorGlyphs) != 0; +} + +/** + Get the `AppleColorEmoji` font's ascent with a specified font size. + It may used to create custom emoji. + + @param fontSize The specified font size. + @return The font ascent. + */ +static inline CGFloat ASTextEmojiGetAscentWithFontSize(CGFloat fontSize) { + if (fontSize < 16) { + return 1.25 * fontSize; + } else if (16 <= fontSize && fontSize <= 24) { + return 0.5 * fontSize + 12; + } else { + return fontSize; + } +} + +/** + Get the `AppleColorEmoji` font's descent with a specified font size. + It may used to create custom emoji. + + @param fontSize The specified font size. + @return The font descent. + */ +static inline CGFloat ASTextEmojiGetDescentWithFontSize(CGFloat fontSize) { + if (fontSize < 16) { + return 0.390625 * fontSize; + } else if (16 <= fontSize && fontSize <= 24) { + return 0.15625 * fontSize + 3.75; + } else { + return 0.3125 * fontSize; + } +} + +/** + Get the `AppleColorEmoji` font's glyph bounding rect with a specified font size. + It may used to create custom emoji. + + @param fontSize The specified font size. + @return The font glyph bounding rect. + */ +static inline CGRect ASTextEmojiGetGlyphBoundingRectWithFontSize(CGFloat fontSize) { + CGRect rect; + rect.origin.x = 0.75; + rect.size.width = rect.size.height = ASTextEmojiGetAscentWithFontSize(fontSize); + if (fontSize < 16) { + rect.origin.y = -0.2525 * fontSize; + } else if (16 <= fontSize && fontSize <= 24) { + rect.origin.y = 0.1225 * fontSize -6; + } else { + rect.origin.y = -0.1275 * fontSize; + } + return rect; +} + + +/** + Get the character set which should rotate in vertical form. + @return The shared character set. + */ +NSCharacterSet *ASTextVerticalFormRotateCharacterSet(void); + +/** + Get the character set which should rotate and move in vertical form. + @return The shared character set. + */ +NSCharacterSet *ASTextVerticalFormRotateAndMoveCharacterSet(void); + + +/// Get the transform rotation. +/// @return the rotation in radians [-PI,PI] ([-180°,180°]) +static inline CGFloat ASTextCGAffineTransformGetRotation(CGAffineTransform transform) { + return atan2(transform.b, transform.a); +} + +/// Negates/inverts a UIEdgeInsets. +static inline UIEdgeInsets ASTextUIEdgeInsetsInvert(UIEdgeInsets insets) { + return UIEdgeInsetsMake(-insets.top, -insets.left, -insets.bottom, -insets.right); +} + +/** + Returns a rectangle to fit `rect` with specified content mode. + + @param rect The constraint rect + @param size The content size + @param mode The content mode + @return A rectangle for the given content mode. + @discussion UIViewContentModeRedraw is same as UIViewContentModeScaleToFill. + */ +CGRect ASTextCGRectFitWithContentMode(CGRect rect, CGSize size, UIViewContentMode mode); + +/// Returns the center for the rectangle. +static inline CGPoint ASTextCGRectGetCenter(CGRect rect) { + return CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect)); +} + +/// Returns the area of the rectangle. +static inline CGFloat ASTextCGRectGetArea(CGRect rect) { + if (CGRectIsNull(rect)) return 0; + rect = CGRectStandardize(rect); + return rect.size.width * rect.size.height; +} + +/// Returns the minmium distance between a point to a rectangle. +static inline CGFloat ASTextCGPointGetDistanceToRect(CGPoint p, CGRect r) { + r = CGRectStandardize(r); + if (CGRectContainsPoint(r, p)) return 0; + CGFloat distV, distH; + if (CGRectGetMinY(r) <= p.y && p.y <= CGRectGetMaxY(r)) { + distV = 0; + } else { + distV = p.y < CGRectGetMinY(r) ? CGRectGetMinY(r) - p.y : p.y - CGRectGetMaxY(r); + } + if (CGRectGetMinX(r) <= p.x && p.x <= CGRectGetMaxX(r)) { + distH = 0; + } else { + distH = p.x < CGRectGetMinX(r) ? CGRectGetMinX(r) - p.x : p.x - CGRectGetMaxX(r); + } + return MAX(distV, distH); +} + +/// Convert point to pixel. +static inline CGFloat ASTextCGFloatToPixel(CGFloat value) { + return value * ASScreenScale(); +} + +/// Convert pixel to point. +static inline CGFloat ASTextCGFloatFromPixel(CGFloat value) { + return value / ASScreenScale(); +} + +/// round point value to .5 pixel for path stroke (odd pixel line width pixel-aligned) +static inline CGFloat ASTextCGFloatPixelHalf(CGFloat value) { + CGFloat scale = ASScreenScale(); + return (floor(value * scale) + 0.5) / scale; +} + +/// floor point value for pixel-aligned +static inline CGPoint ASTextCGPointPixelFloor(CGPoint point) { + CGFloat scale = ASScreenScale(); + return CGPointMake(floor(point.x * scale) / scale, + floor(point.y * scale) / scale); +} + +/// round point value for pixel-aligned +static inline CGPoint ASTextCGPointPixelRound(CGPoint point) { + CGFloat scale = ASScreenScale(); + return CGPointMake(round(point.x * scale) / scale, + round(point.y * scale) / scale); +} + +/// ceil point value for pixel-aligned +static inline CGPoint ASTextCGPointPixelCeil(CGPoint point) { + CGFloat scale = ASScreenScale(); + return CGPointMake(ceil(point.x * scale) / scale, + ceil(point.y * scale) / scale); +} + +/// round point value to .5 pixel for path stroke (odd pixel line width pixel-aligned) +static inline CGPoint ASTextCGPointPixelHalf(CGPoint point) { + CGFloat scale = ASScreenScale(); + return CGPointMake((floor(point.x * scale) + 0.5) / scale, + (floor(point.y * scale) + 0.5) / scale); +} + +/// round point value for pixel-aligned +static inline CGRect ASTextCGRectPixelRound(CGRect rect) { + CGPoint origin = ASTextCGPointPixelRound(rect.origin); + CGPoint corner = ASTextCGPointPixelRound(CGPointMake(rect.origin.x + rect.size.width, + rect.origin.y + rect.size.height)); + return CGRectMake(origin.x, origin.y, corner.x - origin.x, corner.y - origin.y); +} + +/// round point value to .5 pixel for path stroke (odd pixel line width pixel-aligned) +static inline CGRect ASTextCGRectPixelHalf(CGRect rect) { + CGPoint origin = ASTextCGPointPixelHalf(rect.origin); + CGPoint corner = ASTextCGPointPixelHalf(CGPointMake(rect.origin.x + rect.size.width, + rect.origin.y + rect.size.height)); + return CGRectMake(origin.x, origin.y, corner.x - origin.x, corner.y - origin.y); +} + + +static inline UIFont * _Nullable ASTextFontWithBold(UIFont *font) { + return [UIFont fontWithDescriptor:[font.fontDescriptor fontDescriptorWithSymbolicTraits:UIFontDescriptorTraitBold] size:font.pointSize]; +} + +static inline UIFont * _Nullable ASTextFontWithItalic(UIFont *font) { + return [UIFont fontWithDescriptor:[font.fontDescriptor fontDescriptorWithSymbolicTraits:UIFontDescriptorTraitItalic] size:font.pointSize]; +} + +static inline UIFont * _Nullable ASTextFontWithBoldItalic(UIFont *font) { + return [UIFont fontWithDescriptor:[font.fontDescriptor fontDescriptorWithSymbolicTraits:UIFontDescriptorTraitBold | UIFontDescriptorTraitItalic] size:font.pointSize]; +} + + + +/** + Convert CFRange to NSRange + @param range CFRange @return NSRange + */ +static inline NSRange ASTextNSRangeFromCFRange(CFRange range) { + return NSMakeRange(range.location, range.length); +} + +/** + Convert NSRange to CFRange + @param range NSRange @return CFRange + */ +static inline CFRange ASTextCFRangeFromNSRange(NSRange range) { + return CFRangeMake(range.location, range.length); +} + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/Utility/ASTextUtilities.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/Utility/ASTextUtilities.mm new file mode 100644 index 0000000..7194dad --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/Utility/ASTextUtilities.mm @@ -0,0 +1,142 @@ +// +// ASTextUtilities.mm +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import "ASTextUtilities.h" + +NSCharacterSet *ASTextVerticalFormRotateCharacterSet() { + static NSMutableCharacterSet *set; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + set = [NSMutableCharacterSet new]; + [set addCharactersInRange:NSMakeRange(0x1100, 256)]; // Hangul Jamo + [set addCharactersInRange:NSMakeRange(0x2460, 160)]; // Enclosed Alphanumerics + [set addCharactersInRange:NSMakeRange(0x2600, 256)]; // Miscellaneous Symbols + [set addCharactersInRange:NSMakeRange(0x2700, 192)]; // Dingbats + [set addCharactersInRange:NSMakeRange(0x2E80, 128)]; // CJK Radicals Supplement + [set addCharactersInRange:NSMakeRange(0x2F00, 224)]; // Kangxi Radicals + [set addCharactersInRange:NSMakeRange(0x2FF0, 16)]; // Ideographic Description Characters + [set addCharactersInRange:NSMakeRange(0x3000, 64)]; // CJK Symbols and Punctuation + [set removeCharactersInRange:NSMakeRange(0x3008, 10)]; + [set removeCharactersInRange:NSMakeRange(0x3014, 12)]; + [set addCharactersInRange:NSMakeRange(0x3040, 96)]; // Hiragana + [set addCharactersInRange:NSMakeRange(0x30A0, 96)]; // Katakana + [set addCharactersInRange:NSMakeRange(0x3100, 48)]; // Bopomofo + [set addCharactersInRange:NSMakeRange(0x3130, 96)]; // Hangul Compatibility Jamo + [set addCharactersInRange:NSMakeRange(0x3190, 16)]; // Kanbun + [set addCharactersInRange:NSMakeRange(0x31A0, 32)]; // Bopomofo Extended + [set addCharactersInRange:NSMakeRange(0x31C0, 48)]; // CJK Strokes + [set addCharactersInRange:NSMakeRange(0x31F0, 16)]; // Katakana Phonetic Extensions + [set addCharactersInRange:NSMakeRange(0x3200, 256)]; // Enclosed CJK Letters and Months + [set addCharactersInRange:NSMakeRange(0x3300, 256)]; // CJK Compatibility + [set addCharactersInRange:NSMakeRange(0x3400, 2582)]; // CJK Unified Ideographs Extension A + [set addCharactersInRange:NSMakeRange(0x4E00, 20941)]; // CJK Unified Ideographs + [set addCharactersInRange:NSMakeRange(0xAC00, 11172)]; // Hangul Syllables + [set addCharactersInRange:NSMakeRange(0xD7B0, 80)]; // Hangul Jamo Extended-B + [set addCharactersInString:@""]; // U+F8FF (Private Use Area) + [set addCharactersInRange:NSMakeRange(0xF900, 512)]; // CJK Compatibility Ideographs + [set addCharactersInRange:NSMakeRange(0xFE10, 16)]; // Vertical Forms + [set addCharactersInRange:NSMakeRange(0xFF00, 240)]; // Halfwidth and Fullwidth Forms + [set addCharactersInRange:NSMakeRange(0x1F200, 256)]; // Enclosed Ideographic Supplement + [set addCharactersInRange:NSMakeRange(0x1F300, 768)]; // Enclosed Ideographic Supplement + [set addCharactersInRange:NSMakeRange(0x1F600, 80)]; // Emoticons (Emoji) + [set addCharactersInRange:NSMakeRange(0x1F680, 128)]; // Transport and Map Symbols + + // See http://unicode-table.com/ for more information. + }); + return set; +} + +NSCharacterSet *ASTextVerticalFormRotateAndMoveCharacterSet() { + static NSMutableCharacterSet *set; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + set = [NSMutableCharacterSet new]; + [set addCharactersInString:@",。、."]; + }); + return set; +} + +CGRect ASTextCGRectFitWithContentMode(CGRect rect, CGSize size, UIViewContentMode mode) { + rect = CGRectStandardize(rect); + size.width = size.width < 0 ? -size.width : size.width; + size.height = size.height < 0 ? -size.height : size.height; + CGPoint center = CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect)); + switch (mode) { + case UIViewContentModeScaleAspectFit: + case UIViewContentModeScaleAspectFill: { + if (rect.size.width < 0.01 || rect.size.height < 0.01 || + size.width < 0.01 || size.height < 0.01) { + rect.origin = center; + rect.size = CGSizeZero; + } else { + CGFloat scale; + if (mode == UIViewContentModeScaleAspectFit) { + if (size.width / size.height < rect.size.width / rect.size.height) { + scale = rect.size.height / size.height; + } else { + scale = rect.size.width / size.width; + } + } else { + if (size.width / size.height < rect.size.width / rect.size.height) { + scale = rect.size.width / size.width; + } else { + scale = rect.size.height / size.height; + } + } + size.width *= scale; + size.height *= scale; + rect.size = size; + rect.origin = CGPointMake(center.x - size.width * 0.5, center.y - size.height * 0.5); + } + } break; + case UIViewContentModeCenter: { + rect.size = size; + rect.origin = CGPointMake(center.x - size.width * 0.5, center.y - size.height * 0.5); + } break; + case UIViewContentModeTop: { + rect.origin.x = center.x - size.width * 0.5; + rect.size = size; + } break; + case UIViewContentModeBottom: { + rect.origin.x = center.x - size.width * 0.5; + rect.origin.y += rect.size.height - size.height; + rect.size = size; + } break; + case UIViewContentModeLeft: { + rect.origin.y = center.y - size.height * 0.5; + rect.size = size; + } break; + case UIViewContentModeRight: { + rect.origin.y = center.y - size.height * 0.5; + rect.origin.x += rect.size.width - size.width; + rect.size = size; + } break; + case UIViewContentModeTopLeft: { + rect.size = size; + } break; + case UIViewContentModeTopRight: { + rect.origin.x += rect.size.width - size.width; + rect.size = size; + } break; + case UIViewContentModeBottomLeft: { + rect.origin.y += rect.size.height - size.height; + rect.size = size; + } break; + case UIViewContentModeBottomRight: { + rect.origin.x += rect.size.width - size.width; + rect.origin.y += rect.size.height - size.height; + rect.size = size; + } break; + case UIViewContentModeScaleToFill: + case UIViewContentModeRedraw: + default: { + rect = rect; + } + } + return rect; +} diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/Utility/NSAttributedString+ASText.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/Utility/NSAttributedString+ASText.h new file mode 100644 index 0000000..ef44fb4 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/Utility/NSAttributedString+ASText.h @@ -0,0 +1,1375 @@ +// +// NSAttributedString+ASText.h +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + Get pre-defined attributes from attributed string. + All properties defined in UIKit, CoreText and ASText are included. + */ +@interface NSAttributedString (ASText) + +#pragma mark - Retrieving character attribute information +///============================================================================= +/// @name Retrieving character attribute information +///============================================================================= + +/** + Returns the attributes at first charactor. + */ +@property (nullable, nonatomic, copy, readonly) NSDictionary *as_attributes; + +/** + Returns the attributes for the character at a given index. + + @discussion Raises an `NSRangeException` if index lies beyond the end of the + receiver's characters. + + @param index The index for which to return attributes. + This value must lie within the bounds of the receiver. + + @return The attributes for the character at index. + */ +- (nullable NSDictionary *)as_attributesAtIndex:(NSUInteger)index; + +/** + Returns the value for an attribute with a given name of the character at a given index. + + @discussion Raises an `NSRangeException` if index lies beyond the end of the + receiver's characters. + + @param attributeName The name of an attribute. + @param index The index for which to return attributes. + This value must not exceed the bounds of the receiver. + + @return The value for the attribute named `attributeName` of the character at + index `index`, or nil if there is no such attribute. + */ +- (nullable id)as_attribute:(NSString *)attributeName atIndex:(NSUInteger)index; + + +#pragma mark - Get character attribute as property +///============================================================================= +/// @name Get character attribute as property +///============================================================================= + +/** + The font of the text. (read-only) + + @discussion Default is Helvetica (Neue) 12. + @discussion Get this property returns the first character's attribute. + @since CoreText:3.2 UIKit:6.0 ASText:6.0 + */ +@property (nullable, nonatomic, readonly) UIFont *as_font; +- (nullable UIFont *)as_fontAtIndex:(NSUInteger)index; + +/** + A kerning adjustment. (read-only) + + @discussion Default is standard kerning. The kerning attribute indicate how many + points the following character should be shifted from its default offset as + defined by the current character's font in points; a positive kern indicates a + shift farther along and a negative kern indicates a shift closer to the current + character. If this attribute is not present, standard kerning will be used. + If this attribute is set to 0.0, no kerning will be done at all. + @discussion Get this property returns the first character's attribute. + @since CoreText:3.2 UIKit:6.0 ASText:6.0 + */ +@property (nullable, nonatomic, readonly) NSNumber *as_kern; +- (nullable NSNumber *)as_kernAtIndex:(NSUInteger)index; + +/** + The foreground color. (read-only) + + @discussion Default is Black. + @discussion Get this property returns the first character's attribute. + @since CoreText:3.2 UIKit:6.0 ASText:6.0 + */ +@property (nullable, nonatomic, readonly) UIColor *as_color; +- (nullable UIColor *)as_colorAtIndex:(NSUInteger)index; + +/** + The background color. (read-only) + + @discussion Default is nil (or no background). + @discussion Get this property returns the first character's attribute. + @since UIKit:6.0 + */ +@property (nullable, nonatomic, readonly) UIColor *as_backgroundColor; +- (nullable UIColor *)as_backgroundColorAtIndex:(NSUInteger)index; + +/** + The stroke width. (read-only) + + @discussion Default value is 0.0 (no stroke). This attribute, interpreted as + a percentage of font point size, controls the text drawing mode: positive + values effect drawing with stroke only; negative values are for stroke and fill. + A typical value for outlined text is 3.0. + @discussion Get this property returns the first character's attribute. + @since CoreText:3.2 UIKit:6.0 + */ +@property (nullable, nonatomic, readonly) NSNumber *as_strokeWidth; +- (nullable NSNumber *)as_strokeWidthAtIndex:(NSUInteger)index; + +/** + The stroke color. (read-only) + + @discussion Default value is nil (same as foreground color). + @discussion Get this property returns the first character's attribute. + @since CoreText:3.2 UIKit:6.0 + */ +@property (nullable, nonatomic, readonly) UIColor *as_strokeColor; +- (nullable UIColor *)as_strokeColorAtIndex:(NSUInteger)index; + +/** + The text shadow. (read-only) + + @discussion Default value is nil (no shadow). + @discussion Get this property returns the first character's attribute. + @since UIKit:6.0 ASText:6.0 + */ +@property (nullable, nonatomic, readonly) NSShadow *as_shadow; +- (nullable NSShadow *)as_shadowAtIndex:(NSUInteger)index; + +/** + The strikethrough style. (read-only) + + @discussion Default value is NSUnderlineStyleNone (no strikethrough). + @discussion Get this property returns the first character's attribute. + @since UIKit:6.0 + */ +@property (nonatomic, readonly) NSUnderlineStyle as_strikethroughStyle; +- (NSUnderlineStyle)as_strikethroughStyleAtIndex:(NSUInteger)index; + +/** + The strikethrough color. (read-only) + + @discussion Default value is nil (same as foreground color). + @discussion Get this property returns the first character's attribute. + @since UIKit:7.0 + */ +@property (nullable, nonatomic, readonly) UIColor *as_strikethroughColor; +- (nullable UIColor *)as_strikethroughColorAtIndex:(NSUInteger)index; + +/** + The underline style. (read-only) + + @discussion Default value is NSUnderlineStyleNone (no underline). + @discussion Get this property returns the first character's attribute. + @since CoreText:3.2 UIKit:6.0 + */ +@property (nonatomic, readonly) NSUnderlineStyle as_underlineStyle; +- (NSUnderlineStyle)as_underlineStyleAtIndex:(NSUInteger)index; + +/** + The underline color. (read-only) + + @discussion Default value is nil (same as foreground color). + @discussion Get this property returns the first character's attribute. + @since CoreText:3.2 UIKit:7.0 + */ +@property (nullable, nonatomic, readonly) UIColor *as_underlineColor; +- (nullable UIColor *)as_underlineColorAtIndex:(NSUInteger)index; + +/** + Ligature formation control. (read-only) + + @discussion Default is int value 1. The ligature attribute determines what kinds + of ligatures should be used when displaying the string. A value of 0 indicates + that only ligatures essential for proper rendering of text should be used, + 1 indicates that standard ligatures should be used, and 2 indicates that all + available ligatures should be used. Which ligatures are standard depends on the + script and possibly the font. + @discussion Get this property returns the first character's attribute. + @since CoreText:3.2 UIKit:6.0 ASText:6.0 + */ +@property (nullable, nonatomic, readonly) NSNumber *as_ligature; +- (nullable NSNumber *)as_ligatureAtIndex:(NSUInteger)index; + +/** + The text effect. (read-only) + + @discussion Default is nil (no effect). The only currently supported value + is NSTextEffectLetterpressStyle. + @discussion Get this property returns the first character's attribute. + @since UIKit:7.0 + */ +@property (nullable, nonatomic, readonly) NSString *as_textEffect; +- (nullable NSString *)as_textEffectAtIndex:(NSUInteger)index; + +/** + The skew to be applied to glyphs. (read-only) + + @discussion Default is 0 (no skew). + @discussion Get this property returns the first character's attribute. + @since UIKit:7.0 + */ +@property (nullable, nonatomic, readonly) NSNumber *as_obliqueness; +- (nullable NSNumber *)as_obliquenessAtIndex:(NSUInteger)index; + +/** + The log of the expansion factor to be applied to glyphs. (read-only) + + @discussion Default is 0 (no expansion). + @discussion Get this property returns the first character's attribute. + @since UIKit:7.0 + */ +@property (nullable, nonatomic, readonly) NSNumber *as_expansion; +- (nullable NSNumber *)as_expansionAtIndex:(NSUInteger)index; + +/** + The character's offset from the baseline, in points. (read-only) + + @discussion Default is 0. + @discussion Get this property returns the first character's attribute. + @since UIKit:7.0 + */ +@property (nullable, nonatomic, readonly) NSNumber *as_baselineOffset; +- (nullable NSNumber *)as_baselineOffsetAtIndex:(NSUInteger)index; + +/** + Glyph orientation control. (read-only) + + @discussion Default is NO. A value of NO indicates that horizontal glyph forms + are to be used, YES indicates that vertical glyph forms are to be used. + @discussion Get this property returns the first character's attribute. + @since CoreText:4.3 ASText:6.0 + */ +@property (nonatomic, readonly) BOOL as_verticalGlyphForm; +- (BOOL)as_verticalGlyphFormAtIndex:(NSUInteger)index; + +/** + Specifies text language. (read-only) + + @discussion Value must be a NSString containing a locale identifier. Default is + unset. When this attribute is set to a valid identifier, it will be used to select + localized glyphs (if supported by the font) and locale-specific line breaking rules. + @discussion Get this property returns the first character's attribute. + @since CoreText:7.0 ASText:7.0 + */ +@property (nullable, nonatomic, readonly) NSString *as_language; +- (nullable NSString *)as_languageAtIndex:(NSUInteger)index; + +/** + Specifies a bidirectional override or embedding. (read-only) + + @discussion See alse NSWritingDirection and NSWritingDirectionAttributeName. + @discussion Get this property returns the first character's attribute. + @since CoreText:6.0 UIKit:7.0 ASText:6.0 + */ +@property (nullable, nonatomic, readonly) NSArray *as_writingDirection; +- (nullable NSArray *)as_writingDirectionAtIndex:(NSUInteger)index; + +/** + An NSParagraphStyle object which is used to specify things like + line alignment, tab rulers, writing direction, etc. (read-only) + + @discussion Default is nil ([NSParagraphStyle defaultParagraphStyle]). + @discussion Get this property returns the first character's attribute. + @since CoreText:6.0 UIKit:6.0 ASText:6.0 + */ +@property (nullable, nonatomic, readonly) NSParagraphStyle *as_paragraphStyle; +- (nullable NSParagraphStyle *)as_paragraphStyleAtIndex:(NSUInteger)index; + +#pragma mark - Get paragraph attribute as property +///============================================================================= +/// @name Get paragraph attribute as property +///============================================================================= + +/** + The text alignment (A wrapper for NSParagraphStyle). (read-only) + + @discussion Natural text alignment is realized as left or right alignment + depending on the line sweep direction of the first script contained in the paragraph. + @discussion Default is NSTextAlignmentNatural. + @discussion Get this property returns the first character's attribute. + @since CoreText:6.0 UIKit:6.0 ASText:6.0 + */ +@property (nonatomic, readonly) NSTextAlignment as_alignment; +- (NSTextAlignment)as_alignmentAtIndex:(NSUInteger)index; + +/** + The mode that should be used to break lines (A wrapper for NSParagraphStyle). (read-only) + + @discussion This property contains the line break mode to be used laying out the paragraph's text. + @discussion Default is NSLineBreakByWordWrapping. + @discussion Get this property returns the first character's attribute. + @since CoreText:6.0 UIKit:6.0 ASText:6.0 + */ +@property (nonatomic, readonly) NSLineBreakMode as_lineBreakMode; +- (NSLineBreakMode)as_lineBreakModeAtIndex:(NSUInteger)index; + +/** + The distance in points between the bottom of one line fragment and the top of the next. + (A wrapper for NSParagraphStyle) (read-only) + + @discussion This value is always nonnegative. This value is included in the line + fragment heights in the layout manager. + @discussion Default is 0. + @discussion Get this property returns the first character's attribute. + @since CoreText:6.0 UIKit:6.0 ASText:6.0 + */ +@property (nonatomic, readonly) CGFloat as_lineSpacing; +- (CGFloat)as_lineSpacingAtIndex:(NSUInteger)index; + +/** + The space after the end of the paragraph (A wrapper for NSParagraphStyle). (read-only) + + @discussion This property contains the space (measured in points) added at the + end of the paragraph to separate it from the following paragraph. This value must + be nonnegative. The space between paragraphs is determined by adding the previous + paragraph's paragraphSpacing and the current paragraph's paragraphSpacingBefore. + @discussion Default is 0. + @discussion Get this property returns the first character's attribute. + @since CoreText:6.0 UIKit:6.0 ASText:6.0 + */ +@property (nonatomic, readonly) CGFloat as_paragraphSpacing; +- (CGFloat)as_paragraphSpacingAtIndex:(NSUInteger)index; + +/** + The distance between the paragraph's top and the beginning of its text content. + (A wrapper for NSParagraphStyle). (read-only) + + @discussion This property contains the space (measured in points) between the + paragraph's top and the beginning of its text content. + @discussion Default is 0. + @discussion Get this property returns the first character's attribute. + @since CoreText:6.0 UIKit:6.0 ASText:6.0 + */ +@property (nonatomic, readonly) CGFloat as_paragraphSpacingBefore; +- (CGFloat)as_paragraphSpacingBeforeAtIndex:(NSUInteger)index; + +/** + The indentation of the first line (A wrapper for NSParagraphStyle). (read-only) + + @discussion This property contains the distance (in points) from the leading margin + of a text container to the beginning of the paragraph's first line. This value + is always nonnegative. + @discussion Default is 0. + @discussion Get this property returns the first character's attribute. + @since CoreText:6.0 UIKit:6.0 ASText:6.0 + */ +@property (nonatomic, readonly) CGFloat as_firstLineHeadIndent; +- (CGFloat)as_firstLineHeadIndentAtIndex:(NSUInteger)index; + +/** + The indentation of the receiver's lines other than the first. (A wrapper for NSParagraphStyle). (read-only) + + @discussion This property contains the distance (in points) from the leading margin + of a text container to the beginning of lines other than the first. This value is + always nonnegative. + @discussion Default is 0. + @discussion Get this property returns the first character's attribute. + @since CoreText:6.0 UIKit:6.0 ASText:6.0 + */ +@property (nonatomic, readonly) CGFloat as_headIndent; +- (CGFloat)as_headIndentAtIndex:(NSUInteger)index; + +/** + The trailing indentation (A wrapper for NSParagraphStyle). (read-only) + + @discussion If positive, this value is the distance from the leading margin + (for example, the left margin in left-to-right text). If 0 or negative, it's the + distance from the trailing margin. + @discussion Default is 0. + @discussion Get this property returns the first character's attribute. + @since CoreText:6.0 UIKit:6.0 ASText:6.0 + */ +@property (nonatomic, readonly) CGFloat as_tailIndent; +- (CGFloat)as_tailIndentAtIndex:(NSUInteger)index; + +/** + The receiver's minimum height (A wrapper for NSParagraphStyle). (read-only) + + @discussion This property contains the minimum height in points that any line in + the receiver will occupy, regardless of the font size or size of any attached graphic. + This value must be nonnegative. + @discussion Default is 0. + @discussion Get this property returns the first character's attribute. + @since CoreText:6.0 UIKit:6.0 ASText:6.0 + */ +@property (nonatomic, readonly) CGFloat as_minimumLineHeight; +- (CGFloat)as_minimumLineHeightAtIndex:(NSUInteger)index; + +/** + The receiver's maximum line height (A wrapper for NSParagraphStyle). (read-only) + + @discussion This property contains the maximum height in points that any line in + the receiver will occupy, regardless of the font size or size of any attached graphic. + This value is always nonnegative. Glyphs and graphics exceeding this height will + overlap neighboring lines; however, a maximum height of 0 implies no line height limit. + Although this limit applies to the line itself, line spacing adds extra space between adjacent lines. + @discussion Default is 0 (no limit). + @discussion Get this property returns the first character's attribute. + @since CoreText:6.0 UIKit:6.0 ASText:6.0 + */ +@property (nonatomic, readonly) CGFloat as_maximumLineHeight; +- (CGFloat)as_maximumLineHeightAtIndex:(NSUInteger)index; + +/** + The line height multiple (A wrapper for NSParagraphStyle). (read-only) + + @discussion This property contains the line break mode to be used laying out the paragraph's text. + @discussion Default is 0 (no multiple). + @discussion Get this property returns the first character's attribute. + @since CoreText:6.0 UIKit:6.0 ASText:6.0 + */ +@property (nonatomic, readonly) CGFloat as_lineHeightMultiple; +- (CGFloat)as_lineHeightMultipleAtIndex:(NSUInteger)index; + +/** + The base writing direction (A wrapper for NSParagraphStyle). (read-only) + + @discussion If you specify NSWritingDirectionNaturalDirection, the receiver resolves + the writing direction to either NSWritingDirectionLeftToRight or NSWritingDirectionRightToLeft, + depending on the direction for the user's `language` preference setting. + @discussion Default is NSWritingDirectionNatural. + @discussion Get this property returns the first character's attribute. + @since CoreText:6.0 UIKit:6.0 ASText:6.0 + */ +@property (nonatomic, readonly) NSWritingDirection as_baseWritingDirection; +- (NSWritingDirection)as_baseWritingDirectionAtIndex:(NSUInteger)index; + +/** + The paragraph's threshold for hyphenation. (A wrapper for NSParagraphStyle). (read-only) + + @discussion Valid values lie between 0.0 and 1.0 inclusive. Hyphenation is attempted + when the ratio of the text width (as broken without hyphenation) to the width of the + line fragment is less than the hyphenation factor. When the paragraph's hyphenation + factor is 0.0, the layout manager's hyphenation factor is used instead. When both + are 0.0, hyphenation is disabled. + @discussion Default is 0. + @discussion Get this property returns the first character's attribute. + @since UIKit:6.0 + */ +@property (nonatomic, readonly) float as_hyphenationFactor; +- (float)as_hyphenationFactorAtIndex:(NSUInteger)index; + +/** + The document-wide default tab interval (A wrapper for NSParagraphStyle). (read-only) + + @discussion This property represents the default tab interval in points. Tabs after the + last specified in tabStops are placed at integer multiples of this distance (if positive). + @discussion Default is 0. + @discussion Get this property returns the first character's attribute. + @since CoreText:7.0 UIKit:7.0 ASText:7.0 + */ +@property (nonatomic, readonly) CGFloat as_defaultTabInterval; +- (CGFloat)as_defaultTabIntervalAtIndex:(NSUInteger)index; + +/** + An array of NSTextTab objects representing the receiver's tab stops. + (A wrapper for NSParagraphStyle). (read-only) + + @discussion The NSTextTab objects, sorted by location, define the tab stops for + the paragraph style. + @discussion Default is 12 TabStops with 28.0 tab interval. + @discussion Get this property returns the first character's attribute. + @since CoreText:7.0 UIKit:7.0 ASText:7.0 + */ +@property (nullable, nonatomic, copy, readonly) NSArray *as_tabStops; +- (nullable NSArray *)as_tabStopsAtIndex:(NSUInteger)index; + +#pragma mark - Get ASText attribute as property +///============================================================================= +/// @name Get ASText attribute as property +///============================================================================= + +/** + The text shadow. (read-only) + + @discussion Default value is nil (no shadow). + @discussion Get this property returns the first character's attribute. + @since ASText:6.0 + */ +@property (nullable, nonatomic, readonly) ASTextShadow *as_textShadow; +- (nullable ASTextShadow *)as_textShadowAtIndex:(NSUInteger)index; + +/** + The text inner shadow. (read-only) + + @discussion Default value is nil (no shadow). + @discussion Get this property returns the first character's attribute. + @since ASText:6.0 + */ +@property (nullable, nonatomic, readonly) ASTextShadow *as_textInnerShadow; +- (nullable ASTextShadow *)as_textInnerShadowAtIndex:(NSUInteger)index; + +/** + The text underline. (read-only) + + @discussion Default value is nil (no underline). + @discussion Get this property returns the first character's attribute. + @since ASText:6.0 + */ +@property (nullable, nonatomic, readonly) ASTextDecoration *as_textUnderline; +- (nullable ASTextDecoration *)as_textUnderlineAtIndex:(NSUInteger)index; + +/** + The text strikethrough. (read-only) + + @discussion Default value is nil (no strikethrough). + @discussion Get this property returns the first character's attribute. + @since ASText:6.0 + */ +@property (nullable, nonatomic, readonly) ASTextDecoration *as_textStrikethrough; +- (nullable ASTextDecoration *)as_textStrikethroughAtIndex:(NSUInteger)index; + +/** + The text border. (read-only) + + @discussion Default value is nil (no border). + @discussion Get this property returns the first character's attribute. + @since ASText:6.0 + */ +@property (nullable, nonatomic, readonly) ASTextBorder *as_textBorder; +- (nullable ASTextBorder *)as_textBorderAtIndex:(NSUInteger)index; + +/** + The text background border. (read-only) + + @discussion Default value is nil (no background border). + @discussion Get this property returns the first character's attribute. + @since ASText:6.0 + */ +@property (nullable, nonatomic, readonly) ASTextBorder *as_textBackgroundBorder; +- (nullable ASTextBorder *)as_textBackgroundBorderAtIndex:(NSUInteger)index; + +/** + The glyph transform. (read-only) + + @discussion Default value is CGAffineTransformIdentity (no transform). + @discussion Get this property returns the first character's attribute. + @since ASText:6.0 + */ +@property (nonatomic, readonly) CGAffineTransform as_textGlyphTransform; +- (CGAffineTransform)as_textGlyphTransformAtIndex:(NSUInteger)index; + + +#pragma mark - Query for ASText +///============================================================================= +/// @name Query for ASText +///============================================================================= + +/** + Returns the plain text from a range. + If there's `ASTextBackedStringAttributeName` attribute, the backed string will + replace the attributed string range. + + @param range A range in receiver. + @return The plain text. + */ +- (nullable NSString *)as_plainTextForRange:(NSRange)range; + + +#pragma mark - Create attachment string for ASText +///============================================================================= +/// @name Create attachment string for ASText +///============================================================================= + +/** + Creates and returns an attachment. + + @param content The attachment (UIImage/UIView/CALayer). + @param contentMode The attachment's content mode. + @param width The attachment's container width in layout. + @param ascent The attachment's container ascent in layout. + @param descent The attachment's container descent in layout. + + @return An attributed string, or nil if an error occurs. + @since ASText:6.0 + */ ++ (NSMutableAttributedString *)as_attachmentStringWithContent:(nullable id)content + contentMode:(UIViewContentMode)contentMode + width:(CGFloat)width + ascent:(CGFloat)ascent + descent:(CGFloat)descent; + +/** + Creates and returns an attachment. + + + Example: ContentMode:bottom Alignment:Top. + + The text The attachment holder + ↓ ↓ + ─────────┌──────────────────────┐─────── + / \ │ │ / ___| + / _ \ │ │| | + / ___ \ │ │| |___ ←── The text line + /_/ \_\│ ██████████████ │ \____| + ─────────│ ██████████████ │─────── + │ ██████████████ │ + │ ██████████████ ←───────────────── The attachment content + │ ██████████████ │ + └──────────────────────┘ + + @param content The attachment (UIImage/UIView/CALayer). + @param contentMode The attachment's content mode in attachment holder + @param attachmentSize The attachment holder's size in text layout. + @param font The attachment will align to this font. + @param alignment The attachment holder's alignment to text line. + + @return An attributed string, or nil if an error occurs. + @since ASText:6.0 + */ ++ (NSMutableAttributedString *)as_attachmentStringWithContent:(nullable id)content + contentMode:(UIViewContentMode)contentMode + attachmentSize:(CGSize)attachmentSize + alignToFont:(UIFont *)font + alignment:(ASTextVerticalAlignment)alignment; + +/** + Creates and returns an attahment from a fourquare image as if it was an emoji. + + @param image A fourquare image. + @param fontSize The font size. + + @return An attributed string, or nil if an error occurs. + @since ASText:6.0 + */ ++ (nullable NSMutableAttributedString *)as_attachmentStringWithEmojiImage:(UIImage *)image + fontSize:(CGFloat)fontSize; + +#pragma mark - Utility +///============================================================================= +/// @name Utility +///============================================================================= + +/** + Returns NSMakeRange(0, self.length). + */ +- (NSRange)as_rangeOfAll; + +/** + If YES, it share the same attribute in entire text range. + */ +- (BOOL)as_isSharedAttributesInAllRange; + +/** + If YES, it can be drawn with the [drawWithRect:options:context:] method or displayed with UIKit. + If NO, it should be drawn with CoreText or ASText. + + @discussion If the method returns NO, it means that there's at least one attribute + which is not supported by UIKit (such as CTParagraphStyleRef). If display this string + in UIKit, it may lose some attribute, or even crash the app. + */ +- (BOOL)as_canDrawWithUIKit; + +@end + + + + +/** + Set pre-defined attributes to attributed string. + All properties defined in UIKit, CoreText and ASText are included. + */ +@interface NSMutableAttributedString (ASText) + +#pragma mark - Set character attribute +///============================================================================= +/// @name Set character attribute +///============================================================================= + +/** + Sets the attributes to the entire text string. + + @discussion The old attributes will be removed. + + @param attributes A dictionary containing the attributes to set, or nil to remove all attributes. + */ +- (void)as_setAttributes:(nullable NSDictionary *)attributes; +- (void)setAs_attributes:(nullable NSDictionary *)attributes; + +/** + Sets an attribute with the given name and value to the entire text string. + + @param name A string specifying the attribute name. + @param value The attribute value associated with name. Pass `nil` or `NSNull` to + remove the attribute. + */ +- (void)as_setAttribute:(NSString *)name value:(nullable id)value; + +/** + Sets an attribute with the given name and value to the characters in the specified range. + + @param name A string specifying the attribute name. + @param value The attribute value associated with name. Pass `nil` or `NSNull` to + remove the attribute. + @param range The range of characters to which the specified attribute/value pair applies. + */ +- (void)as_setAttribute:(NSString *)name value:(nullable id)value range:(NSRange)range; + +/** + Removes all attributes in the specified range. + + @param range The range of characters. + */ +- (void)as_removeAttributesInRange:(NSRange)range; + + +#pragma mark - Set character attribute as property +///============================================================================= +/// @name Set character attribute as property +///============================================================================= + +/** + The font of the text. + + @discussion Default is Helvetica (Neue) 12. + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since CoreText:3.2 UIKit:6.0 ASText:6.0 + */ +@property (nullable, nonatomic) UIFont *as_font; +- (void)as_setFont:(nullable UIFont *)font range:(NSRange)range; + +/** + A kerning adjustment. + + @discussion Default is standard kerning. The kerning attribute indicate how many + points the following character should be shifted from its default offset as + defined by the current character's font in points; a positive kern indicates a + shift farther along and a negative kern indicates a shift closer to the current + character. If this attribute is not present, standard kerning will be used. + If this attribute is set to 0.0, no kerning will be done at all. + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since CoreText:3.2 UIKit:6.0 ASText:6.0 + */ +@property (nullable, nonatomic) NSNumber *as_kern; +- (void)as_setKern:(nullable NSNumber *)kern range:(NSRange)range; + +/** + The foreground color. + + @discussion Default is Black. + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since CoreText:3.2 UIKit:6.0 ASText:6.0 + */ +@property (nullable, nonatomic) UIColor *as_color; +- (void)as_setColor:(nullable UIColor *)color range:(NSRange)range; + +/** + The background color. + + @discussion Default is nil (or no background). + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since UIKit:6.0 + */ +@property (nullable, nonatomic) UIColor *as_backgroundColor; +- (void)as_setBackgroundColor:(nullable UIColor *)backgroundColor range:(NSRange)range; + +/** + The stroke width. + + @discussion Default value is 0.0 (no stroke). This attribute, interpreted as + a percentage of font point size, controls the text drawing mode: positive + values effect drawing with stroke only; negative values are for stroke and fill. + A typical value for outlined text is 3.0. + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since CoreText:3.2 UIKit:6.0 ASText:6.0 + */ +@property (nullable, nonatomic) NSNumber *as_strokeWidth; +- (void)as_setStrokeWidth:(nullable NSNumber *)strokeWidth range:(NSRange)range; + +/** + The stroke color. + + @discussion Default value is nil (same as foreground color). + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since CoreText:3.2 UIKit:6.0 ASText:6.0 + */ +@property (nullable, nonatomic) UIColor *as_strokeColor; +- (void)as_setStrokeColor:(nullable UIColor *)strokeColor range:(NSRange)range; + +/** + The text shadow. + + @discussion Default value is nil (no shadow). + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since UIKit:6.0 ASText:6.0 + */ +@property (nullable, nonatomic) NSShadow *as_shadow; +- (void)as_setShadow:(nullable NSShadow *)shadow range:(NSRange)range; + +/** + The strikethrough style. + + @discussion Default value is NSUnderlineStyleNone (no strikethrough). + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since UIKit:6.0 + */ +@property (nonatomic) NSUnderlineStyle as_strikethroughStyle; +- (void)as_setStrikethroughStyle:(NSUnderlineStyle)strikethroughStyle range:(NSRange)range; + +/** + The strikethrough color. + + @discussion Default value is nil (same as foreground color). + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since UIKit:7.0 + */ +@property (nullable, nonatomic) UIColor *as_strikethroughColor; +- (void)as_setStrikethroughColor:(nullable UIColor *)strikethroughColor range:(NSRange)range NS_AVAILABLE_IOS(7_0); + +/** + The underline style. + + @discussion Default value is NSUnderlineStyleNone (no underline). + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since CoreText:3.2 UIKit:6.0 + */ +@property (nonatomic) NSUnderlineStyle as_underlineStyle; +- (void)as_setUnderlineStyle:(NSUnderlineStyle)underlineStyle range:(NSRange)range; + +/** + The underline color. + + @discussion Default value is nil (same as foreground color). + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since CoreText:3.2 UIKit:7.0 + */ +@property (nullable, nonatomic) UIColor *as_underlineColor; +- (void)as_setUnderlineColor:(nullable UIColor *)underlineColor range:(NSRange)range; + +/** + Ligature formation control. + + @discussion Default is int value 1. The ligature attribute determines what kinds + of ligatures should be used when displaying the string. A value of 0 indicates + that only ligatures essential for proper rendering of text should be used, + 1 indicates that standard ligatures should be used, and 2 indicates that all + available ligatures should be used. Which ligatures are standard depends on the + script and possibly the font. + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since CoreText:3.2 UIKit:6.0 ASText:6.0 + */ +@property (nullable, nonatomic) NSNumber *as_ligature; +- (void)as_setLigature:(nullable NSNumber *)ligature range:(NSRange)range; + +/** + The text effect. + + @discussion Default is nil (no effect). The only currently supported value + is NSTextEffectLetterpressStyle. + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since UIKit:7.0 + */ +@property (nullable, nonatomic) NSString *as_textEffect; +- (void)as_setTextEffect:(nullable NSString *)textEffect range:(NSRange)range NS_AVAILABLE_IOS(7_0); + +/** + The skew to be applied to glyphs. + + @discussion Default is 0 (no skew). + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since UIKit:7.0 + */ +@property (nullable, nonatomic) NSNumber *as_obliqueness; +- (void)as_setObliqueness:(nullable NSNumber *)obliqueness range:(NSRange)range NS_AVAILABLE_IOS(7_0); + +/** + The log of the expansion factor to be applied to glyphs. + + @discussion Default is 0 (no expansion). + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since UIKit:7.0 + */ +@property (nullable, nonatomic) NSNumber *as_expansion; +- (void)as_setExpansion:(nullable NSNumber *)expansion range:(NSRange)range NS_AVAILABLE_IOS(7_0); + +/** + The character's offset from the baseline, in points. + + @discussion Default is 0. + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since UIKit:7.0 + */ +@property (nullable, nonatomic) NSNumber *as_baselineOffset; +- (void)as_setBaselineOffset:(nullable NSNumber *)baselineOffset range:(NSRange)range NS_AVAILABLE_IOS(7_0); + +/** + Glyph orientation control. + + @discussion Default is NO. A value of NO indicates that horizontal glyph forms + are to be used, YES indicates that vertical glyph forms are to be used. + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since CoreText:4.3 ASText:6.0 + */ +@property (nonatomic) BOOL as_verticalGlyphForm; +- (void)as_setVerticalGlyphForm:(BOOL)verticalGlyphForm range:(NSRange)range; + +/** + Specifies text language. + + @discussion Value must be a NSString containing a locale identifier. Default is + unset. When this attribute is set to a valid identifier, it will be used to select + localized glyphs (if supported by the font) and locale-specific line breaking rules. + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since CoreText:7.0 ASText:7.0 + */ +@property (nullable, nonatomic) NSString *as_language; +- (void)as_setLanguage:(nullable NSString *)language range:(NSRange)range NS_AVAILABLE_IOS(7_0); + +/** + Specifies a bidirectional override or embedding. + + @discussion See alse NSWritingDirection and NSWritingDirectionAttributeName. + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since CoreText:6.0 UIKit:7.0 ASText:6.0 + */ +@property (nullable, nonatomic) NSArray *as_writingDirection; +- (void)as_setWritingDirection:(nullable NSArray *)writingDirection range:(NSRange)range; + +/** + An NSParagraphStyle object which is used to specify things like + line alignment, tab rulers, writing direction, etc. + + @discussion Default is nil ([NSParagraphStyle defaultParagraphStyle]). + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since CoreText:6.0 UIKit:6.0 ASText:6.0 + */ +@property (nullable, nonatomic) NSParagraphStyle *as_paragraphStyle; +- (void)as_setParagraphStyle:(nullable NSParagraphStyle *)paragraphStyle range:(NSRange)range; + + +#pragma mark - Set paragraph attribute as property +///============================================================================= +/// @name Set paragraph attribute as property +///============================================================================= + +/** + The text alignment (A wrapper for NSParagraphStyle). + + @discussion Natural text alignment is realized as left or right alignment + depending on the line sweep direction of the first script contained in the paragraph. + @discussion Default is NSTextAlignmentNatural. + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since CoreText:6.0 UIKit:6.0 ASText:6.0 + */ +@property (nonatomic) NSTextAlignment as_alignment; +- (void)as_setAlignment:(NSTextAlignment)alignment range:(NSRange)range; + +/** + The mode that should be used to break lines (A wrapper for NSParagraphStyle). + + @discussion This property contains the line break mode to be used laying out the paragraph's text. + @discussion Default is NSLineBreakByWordWrapping. + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since CoreText:6.0 UIKit:6.0 ASText:6.0 + */ +@property (nonatomic) NSLineBreakMode as_lineBreakMode; +- (void)as_setLineBreakMode:(NSLineBreakMode)lineBreakMode range:(NSRange)range; + +/** + The distance in points between the bottom of one line fragment and the top of the next. + (A wrapper for NSParagraphStyle) + + @discussion This value is always nonnegative. This value is included in the line + fragment heights in the layout manager. + @discussion Default is 0. + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since CoreText:6.0 UIKit:6.0 ASText:6.0 + */ +@property (nonatomic) CGFloat as_lineSpacing; +- (void)as_setLineSpacing:(CGFloat)lineSpacing range:(NSRange)range; + +/** + The space after the end of the paragraph (A wrapper for NSParagraphStyle). + + @discussion This property contains the space (measured in points) added at the + end of the paragraph to separate it from the following paragraph. This value must + be nonnegative. The space between paragraphs is determined by adding the previous + paragraph's paragraphSpacing and the current paragraph's paragraphSpacingBefore. + @discussion Default is 0. + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since CoreText:6.0 UIKit:6.0 ASText:6.0 + */ +@property (nonatomic) CGFloat as_paragraphSpacing; +- (void)as_setParagraphSpacing:(CGFloat)paragraphSpacing range:(NSRange)range; + +/** + The distance between the paragraph's top and the beginning of its text content. + (A wrapper for NSParagraphStyle). + + @discussion This property contains the space (measured in points) between the + paragraph's top and the beginning of its text content. + @discussion Default is 0. + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since CoreText:6.0 UIKit:6.0 ASText:6.0 + */ +@property (nonatomic) CGFloat as_paragraphSpacingBefore; +- (void)as_setParagraphSpacingBefore:(CGFloat)paragraphSpacingBefore range:(NSRange)range; + +/** + The indentation of the first line (A wrapper for NSParagraphStyle). + + @discussion This property contains the distance (in points) from the leading margin + of a text container to the beginning of the paragraph's first line. This value + is always nonnegative. + @discussion Default is 0. + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since CoreText:6.0 UIKit:6.0 ASText:6.0 + */ +@property (nonatomic) CGFloat as_firstLineHeadIndent; +- (void)as_setFirstLineHeadIndent:(CGFloat)firstLineHeadIndent range:(NSRange)range; + +/** + The indentation of the receiver's lines other than the first. (A wrapper for NSParagraphStyle). + + @discussion This property contains the distance (in points) from the leading margin + of a text container to the beginning of lines other than the first. This value is + always nonnegative. + @discussion Default is 0. + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since CoreText:6.0 UIKit:6.0 ASText:6.0 + */ +@property (nonatomic) CGFloat as_headIndent; +- (void)as_setHeadIndent:(CGFloat)headIndent range:(NSRange)range; + +/** + The trailing indentation (A wrapper for NSParagraphStyle). + + @discussion If positive, this value is the distance from the leading margin + (for example, the left margin in left-to-right text). If 0 or negative, it's the + distance from the trailing margin. + @discussion Default is 0. + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since CoreText:6.0 UIKit:6.0 ASText:6.0 + */ +@property (nonatomic) CGFloat as_tailIndent; +- (void)as_setTailIndent:(CGFloat)tailIndent range:(NSRange)range; + +/** + The receiver's minimum height (A wrapper for NSParagraphStyle). + + @discussion This property contains the minimum height in points that any line in + the receiver will occupy, regardless of the font size or size of any attached graphic. + This value must be nonnegative. + @discussion Default is 0. + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since CoreText:6.0 UIKit:6.0 ASText:6.0 + */ +@property (nonatomic) CGFloat as_minimumLineHeight; +- (void)as_setMinimumLineHeight:(CGFloat)minimumLineHeight range:(NSRange)range; + +/** + The receiver's maximum line height (A wrapper for NSParagraphStyle). + + @discussion This property contains the maximum height in points that any line in + the receiver will occupy, regardless of the font size or size of any attached graphic. + This value is always nonnegative. Glyphs and graphics exceeding this height will + overlap neighboring lines; however, a maximum height of 0 implies no line height limit. + Although this limit applies to the line itself, line spacing adds extra space between adjacent lines. + @discussion Default is 0 (no limit). + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since CoreText:6.0 UIKit:6.0 ASText:6.0 + */ +@property (nonatomic) CGFloat as_maximumLineHeight; +- (void)as_setMaximumLineHeight:(CGFloat)maximumLineHeight range:(NSRange)range; + +/** + The line height multiple (A wrapper for NSParagraphStyle). + + @discussion This property contains the line break mode to be used laying out the paragraph's text. + @discussion Default is 0 (no multiple). + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since CoreText:6.0 UIKit:6.0 ASText:6.0 + */ +@property (nonatomic) CGFloat as_lineHeightMultiple; +- (void)as_setLineHeightMultiple:(CGFloat)lineHeightMultiple range:(NSRange)range; + +/** + The base writing direction (A wrapper for NSParagraphStyle). + + @discussion If you specify NSWritingDirectionNaturalDirection, the receiver resolves + the writing direction to either NSWritingDirectionLeftToRight or NSWritingDirectionRightToLeft, + depending on the direction for the user's `language` preference setting. + @discussion Default is NSWritingDirectionNatural. + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since CoreText:6.0 UIKit:6.0 ASText:6.0 + */ +@property (nonatomic) NSWritingDirection as_baseWritingDirection; +- (void)as_setBaseWritingDirection:(NSWritingDirection)baseWritingDirection range:(NSRange)range; + +/** + The paragraph's threshold for hyphenation. (A wrapper for NSParagraphStyle). + + @discussion Valid values lie between 0.0 and 1.0 inclusive. Hyphenation is attempted + when the ratio of the text width (as broken without hyphenation) to the width of the + line fragment is less than the hyphenation factor. When the paragraph's hyphenation + factor is 0.0, the layout manager's hyphenation factor is used instead. When both + are 0.0, hyphenation is disabled. + @discussion Default is 0. + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since UIKit:6.0 + */ +@property (nonatomic) float as_hyphenationFactor; +- (void)as_setHyphenationFactor:(float)hyphenationFactor range:(NSRange)range; + +/** + The document-wide default tab interval (A wrapper for NSParagraphStyle). + + @discussion This property represents the default tab interval in points. Tabs after the + last specified in tabStops are placed at integer multiples of this distance (if positive). + @discussion Default is 0. + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since CoreText:7.0 UIKit:7.0 ASText:7.0 + */ +@property (nonatomic) CGFloat as_defaultTabInterval; +- (void)as_setDefaultTabInterval:(CGFloat)defaultTabInterval range:(NSRange)range NS_AVAILABLE_IOS(7_0); + +/** + An array of NSTextTab objects representing the receiver's tab stops. + (A wrapper for NSParagraphStyle). + + @discussion The NSTextTab objects, sorted by location, define the tab stops for + the paragraph style. + @discussion Default is 12 TabStops with 28.0 tab interval. + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since CoreText:7.0 UIKit:7.0 ASText:7.0 + */ +@property (nullable, nonatomic, copy) NSArray *as_tabStops; +- (void)as_setTabStops:(nullable NSArray *)tabStops range:(NSRange)range NS_AVAILABLE_IOS(7_0); + +#pragma mark - Set ASText attribute as property +///============================================================================= +/// @name Set ASText attribute as property +///============================================================================= + +/** + The text shadow. + + @discussion Default value is nil (no shadow). + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since ASText:6.0 + */ +@property (nullable, nonatomic) ASTextShadow *as_textShadow; +- (void)as_setTextShadow:(nullable ASTextShadow *)textShadow range:(NSRange)range; + +/** + The text inner shadow. + + @discussion Default value is nil (no shadow). + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since ASText:6.0 + */ +@property (nullable, nonatomic) ASTextShadow *as_textInnerShadow; +- (void)as_setTextInnerShadow:(nullable ASTextShadow *)textInnerShadow range:(NSRange)range; + +/** + The text underline. + + @discussion Default value is nil (no underline). + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since ASText:6.0 + */ +@property (nullable, nonatomic) ASTextDecoration *as_textUnderline; +- (void)as_setTextUnderline:(nullable ASTextDecoration *)textUnderline range:(NSRange)range; + +/** + The text strikethrough. + + @discussion Default value is nil (no strikethrough). + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since ASText:6.0 + */ +@property (nullable, nonatomic) ASTextDecoration *as_textStrikethrough; +- (void)as_setTextStrikethrough:(nullable ASTextDecoration *)textStrikethrough range:(NSRange)range; + +/** + The text border. + + @discussion Default value is nil (no border). + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since ASText:6.0 + */ +@property (nullable, nonatomic) ASTextBorder *as_textBorder; +- (void)as_setTextBorder:(nullable ASTextBorder *)textBorder range:(NSRange)range; + +/** + The text background border. + + @discussion Default value is nil (no background border). + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since ASText:6.0 + */ +@property (nullable, nonatomic) ASTextBorder *as_textBackgroundBorder; +- (void)as_setTextBackgroundBorder:(nullable ASTextBorder *)textBackgroundBorder range:(NSRange)range; + +/** + The glyph transform. + + @discussion Default value is CGAffineTransformIdentity (no transform). + @discussion Set this property applies to the entire text string. + Get this property returns the first character's attribute. + @since ASText:6.0 + */ +@property (nonatomic) CGAffineTransform as_textGlyphTransform; +- (void)as_setTextGlyphTransform:(CGAffineTransform)textGlyphTransform range:(NSRange)range; + + +#pragma mark - Set discontinuous attribute for range +///============================================================================= +/// @name Set discontinuous attribute for range +///============================================================================= + +- (void)as_setSuperscript:(nullable NSNumber *)superscript range:(NSRange)range; +- (void)as_setGlyphInfo:(nullable CTGlyphInfoRef)glyphInfo range:(NSRange)range; +- (void)as_setCharacterShape:(nullable NSNumber *)characterShape range:(NSRange)range __TVOS_PROHIBITED; +- (void)as_setRunDelegate:(nullable CTRunDelegateRef)runDelegate range:(NSRange)range; +- (void)as_setBaselineClass:(nullable CFStringRef)baselineClass range:(NSRange)range; +- (void)as_setBaselineInfo:(nullable CFDictionaryRef)baselineInfo range:(NSRange)range; +- (void)as_setBaselineReferenceInfo:(nullable CFDictionaryRef)referenceInfo range:(NSRange)range; +- (void)as_setRubyAnnotation:(nullable CTRubyAnnotationRef)ruby range:(NSRange)range NS_AVAILABLE_IOS(8_0); +- (void)as_setAttachment:(nullable NSTextAttachment *)attachment range:(NSRange)range NS_AVAILABLE_IOS(7_0); +- (void)as_setLink:(nullable id)link range:(NSRange)range NS_AVAILABLE_IOS(7_0); +- (void)as_setTextBackedString:(nullable ASTextBackedString *)textBackedString range:(NSRange)range; +- (void)as_setTextBinding:(nullable ASTextBinding *)textBinding range:(NSRange)range; +- (void)as_setTextAttachment:(nullable ASTextAttachment *)textAttachment range:(NSRange)range; +- (void)as_setTextHighlight:(nullable ASTextHighlight *)textHighlight range:(NSRange)range; +- (void)as_setTextBlockBorder:(nullable ASTextBorder *)textBlockBorder range:(NSRange)range; + + +#pragma mark - Convenience methods for text highlight +///============================================================================= +/// @name Convenience methods for text highlight +///============================================================================= + +/** + Convenience method to set text highlight + + @param range text range + @param color text color (pass nil to ignore) + @param backgroundColor text background color when highlight + @param userInfo user information dictionary (pass nil to ignore) + @param tapAction tap action when user tap the highlight (pass nil to ignore) + @param longPressAction long press action when user long press the highlight (pass nil to ignore) + */ +- (void)as_setTextHighlightRange:(NSRange)range + color:(nullable UIColor *)color + backgroundColor:(nullable UIColor *)backgroundColor + userInfo:(nullable NSDictionary *)userInfo + tapAction:(nullable ASTextAction)tapAction + longPressAction:(nullable ASTextAction)longPressAction; + +/** + Convenience method to set text highlight + + @param range text range + @param color text color (pass nil to ignore) + @param backgroundColor text background color when highlight + @param tapAction tap action when user tap the highlight (pass nil to ignore) + */ +- (void)as_setTextHighlightRange:(NSRange)range + color:(nullable UIColor *)color + backgroundColor:(nullable UIColor *)backgroundColor + tapAction:(nullable ASTextAction)tapAction; + +/** + Convenience method to set text highlight + + @param range text range + @param color text color (pass nil to ignore) + @param backgroundColor text background color when highlight + @param userInfo tap action when user tap the highlight (pass nil to ignore) + */ +- (void)as_setTextHighlightRange:(NSRange)range + color:(nullable UIColor *)color + backgroundColor:(nullable UIColor *)backgroundColor + userInfo:(nullable NSDictionary *)userInfo; + +#pragma mark - Utilities +///============================================================================= +/// @name Utilities +///============================================================================= + +/** + Inserts into the receiver the characters of a given string at a given location. + The new string inherit the attributes of the first replaced character from location. + + @param string The string to insert into the receiver, must not be nil. + @param location The location at which string is inserted. The location must not + exceed the bounds of the receiver. + @throw Raises an NSRangeException if the location out of bounds. + */ +- (void)as_insertString:(NSString *)string atIndex:(NSUInteger)location; + +/** + Adds to the end of the receiver the characters of a given string. + The new string inherit the attributes of the receiver's tail. + + @param string The string to append to the receiver, must not be nil. + */ +- (void)as_appendString:(NSString *)string; + +/** + Removes all discontinuous attributes in a specified range. + See `allDiscontinuousAttributeKeys`. + + @param range A text range. + */ +- (void)as_removeDiscontinuousAttributesInRange:(NSRange)range; + +/** + Returns all discontinuous attribute keys, such as RunDelegate/Attachment/Ruby. + + @discussion These attributes can only set to a specified range of text, and + should not extend to other range when editing text. + */ ++ (NSArray *)as_allDiscontinuousAttributeKeys; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/Utility/NSAttributedString+ASText.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/Utility/NSAttributedString+ASText.mm new file mode 100644 index 0000000..39f6281 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/Utility/NSAttributedString+ASText.mm @@ -0,0 +1,1208 @@ +// +// NSAttributedString+ASText.mm +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import +#import +#import + + +// Dummy class for category +@interface NSAttributedString_ASText : NSObject @end +@implementation NSAttributedString_ASText @end + + +@implementation NSAttributedString (ASText) + +- (NSDictionary *)as_attributesAtIndex:(NSUInteger)index { + if (index > self.length || self.length == 0) return nil; + if (self.length > 0 && index == self.length) index--; + return [self attributesAtIndex:index effectiveRange:NULL]; +} + +- (id)as_attribute:(NSString *)attributeName atIndex:(NSUInteger)index { + if (!attributeName) return nil; + if (index > self.length || self.length == 0) return nil; + if (self.length > 0 && index == self.length) index--; + return [self attribute:attributeName atIndex:index effectiveRange:NULL]; +} + +- (NSDictionary *)as_attributes { + return [self as_attributesAtIndex:0]; +} + +- (UIFont *)as_font { + return [self as_fontAtIndex:0]; +} + +- (UIFont *)as_fontAtIndex:(NSUInteger)index { + return [self as_attribute:NSFontAttributeName atIndex:index]; +} + +- (NSNumber *)as_kern { + return [self as_kernAtIndex:0]; +} + +- (NSNumber *)as_kernAtIndex:(NSUInteger)index { + return [self as_attribute:NSKernAttributeName atIndex:index]; +} + +- (UIColor *)as_color { + return [self as_colorAtIndex:0]; +} + +- (UIColor *)as_colorAtIndex:(NSUInteger)index { + UIColor *color = [self as_attribute:NSForegroundColorAttributeName atIndex:index]; + if (!color) { + CGColorRef ref = (__bridge CGColorRef)([self as_attribute:(NSString *)kCTForegroundColorAttributeName atIndex:index]); + color = [UIColor colorWithCGColor:ref]; + } + if (color && ![color isKindOfClass:[UIColor class]]) { + if (CFGetTypeID((__bridge CFTypeRef)(color)) == CGColorGetTypeID()) { + color = [UIColor colorWithCGColor:(__bridge CGColorRef)(color)]; + } else { + color = nil; + } + } + return color; +} + +- (UIColor *)as_backgroundColor { + return [self as_backgroundColorAtIndex:0]; +} + +- (UIColor *)as_backgroundColorAtIndex:(NSUInteger)index { + return [self as_attribute:NSBackgroundColorAttributeName atIndex:index]; +} + +- (NSNumber *)as_strokeWidth { + return [self as_strokeWidthAtIndex:0]; +} + +- (NSNumber *)as_strokeWidthAtIndex:(NSUInteger)index { + return [self as_attribute:NSStrokeWidthAttributeName atIndex:index]; +} + +- (UIColor *)as_strokeColor { + return [self as_strokeColorAtIndex:0]; +} + +- (UIColor *)as_strokeColorAtIndex:(NSUInteger)index { + UIColor *color = [self as_attribute:NSStrokeColorAttributeName atIndex:index]; + if (!color) { + CGColorRef ref = (__bridge CGColorRef)([self as_attribute:(NSString *)kCTStrokeColorAttributeName atIndex:index]); + color = [UIColor colorWithCGColor:ref]; + } + return color; +} + +- (NSShadow *)as_shadow { + return [self as_shadowAtIndex:0]; +} + +- (NSShadow *)as_shadowAtIndex:(NSUInteger)index { + return [self as_attribute:NSShadowAttributeName atIndex:index]; +} + +- (NSUnderlineStyle)as_strikethroughStyle { + return [self as_strikethroughStyleAtIndex:0]; +} + +- (NSUnderlineStyle)as_strikethroughStyleAtIndex:(NSUInteger)index { + NSNumber *style = [self as_attribute:NSStrikethroughStyleAttributeName atIndex:index]; + return (NSUnderlineStyle)style.integerValue; +} + +- (UIColor *)as_strikethroughColor { + return [self as_strikethroughColorAtIndex:0]; +} + +- (UIColor *)as_strikethroughColorAtIndex:(NSUInteger)index { + return [self as_attribute:NSStrikethroughColorAttributeName atIndex:index]; +} + +- (NSUnderlineStyle)as_underlineStyle { + return [self as_underlineStyleAtIndex:0]; +} + +- (NSUnderlineStyle)as_underlineStyleAtIndex:(NSUInteger)index { + NSNumber *style = [self as_attribute:NSUnderlineStyleAttributeName atIndex:index]; + return (NSUnderlineStyle)style.integerValue; +} + +- (UIColor *)as_underlineColor { + return [self as_underlineColorAtIndex:0]; +} + +- (UIColor *)as_underlineColorAtIndex:(NSUInteger)index { + UIColor *color = [self as_attribute:NSUnderlineColorAttributeName atIndex:index]; + if (!color) { + CGColorRef ref = (__bridge CGColorRef)([self as_attribute:(NSString *)kCTUnderlineColorAttributeName atIndex:index]); + color = [UIColor colorWithCGColor:ref]; + } + return color; +} + +- (NSNumber *)as_ligature { + return [self as_ligatureAtIndex:0]; +} + +- (NSNumber *)as_ligatureAtIndex:(NSUInteger)index { + return [self as_attribute:NSLigatureAttributeName atIndex:index]; +} + +- (NSString *)as_textEffect { + return [self as_textEffectAtIndex:0]; +} + +- (NSString *)as_textEffectAtIndex:(NSUInteger)index { + return [self as_attribute:NSTextEffectAttributeName atIndex:index]; +} + +- (NSNumber *)as_obliqueness { + return [self as_obliquenessAtIndex:0]; +} + +- (NSNumber *)as_obliquenessAtIndex:(NSUInteger)index { + return [self as_attribute:NSObliquenessAttributeName atIndex:index]; +} + +- (NSNumber *)as_expansion { + return [self as_expansionAtIndex:0]; +} + +- (NSNumber *)as_expansionAtIndex:(NSUInteger)index { + return [self as_attribute:NSExpansionAttributeName atIndex:index]; +} + +- (NSNumber *)as_baselineOffset { + return [self as_baselineOffsetAtIndex:0]; +} + +- (NSNumber *)as_baselineOffsetAtIndex:(NSUInteger)index { + return [self as_attribute:NSBaselineOffsetAttributeName atIndex:index]; +} + +- (BOOL)as_verticalGlyphForm { + return [self as_verticalGlyphFormAtIndex:0]; +} + +- (BOOL)as_verticalGlyphFormAtIndex:(NSUInteger)index { + NSNumber *num = [self as_attribute:NSVerticalGlyphFormAttributeName atIndex:index]; + return num.boolValue; +} + +- (NSString *)as_language { + return [self as_languageAtIndex:0]; +} + +- (NSString *)as_languageAtIndex:(NSUInteger)index { + return [self as_attribute:(id)kCTLanguageAttributeName atIndex:index]; +} + +- (NSArray *)as_writingDirection { + return [self as_writingDirectionAtIndex:0]; +} + +- (NSArray *)as_writingDirectionAtIndex:(NSUInteger)index { + return [self as_attribute:(id)kCTWritingDirectionAttributeName atIndex:index]; +} + +- (NSParagraphStyle *)as_paragraphStyle { + return [self as_paragraphStyleAtIndex:0]; +} + +- (NSParagraphStyle *)as_paragraphStyleAtIndex:(NSUInteger)index { + /* + NSParagraphStyle is NOT toll-free bridged to CTParagraphStyleRef. + + CoreText can use both NSParagraphStyle and CTParagraphStyleRef, + but UILabel/UITextView can only use NSParagraphStyle. + + We use NSParagraphStyle in both CoreText and UIKit. + */ + NSParagraphStyle *style = [self as_attribute:NSParagraphStyleAttributeName atIndex:index]; + if (style) { + if (CFGetTypeID((__bridge CFTypeRef)(style)) == CTParagraphStyleGetTypeID()) { \ + style = [NSParagraphStyle as_styleWithCTStyle:(__bridge CTParagraphStyleRef)(style)]; + } + } + return style; +} + +#define ParagraphAttribute(_attr_) \ +NSParagraphStyle *style = self.as_paragraphStyle; \ +if (!style) style = [NSParagraphStyle defaultParagraphStyle]; \ +return style. _attr_; + +#define ParagraphAttributeAtIndex(_attr_) \ +NSParagraphStyle *style = [self as_paragraphStyleAtIndex:index]; \ +if (!style) style = [NSParagraphStyle defaultParagraphStyle]; \ +return style. _attr_; + +- (NSTextAlignment)as_alignment { + ParagraphAttribute(alignment); +} + +- (NSLineBreakMode)as_lineBreakMode { + ParagraphAttribute(lineBreakMode); +} + +- (CGFloat)as_lineSpacing { + ParagraphAttribute(lineSpacing); +} + +- (CGFloat)as_paragraphSpacing { + ParagraphAttribute(paragraphSpacing); +} + +- (CGFloat)as_paragraphSpacingBefore { + ParagraphAttribute(paragraphSpacingBefore); +} + +- (CGFloat)as_firstLineHeadIndent { + ParagraphAttribute(firstLineHeadIndent); +} + +- (CGFloat)as_headIndent { + ParagraphAttribute(headIndent); +} + +- (CGFloat)as_tailIndent { + ParagraphAttribute(tailIndent); +} + +- (CGFloat)as_minimumLineHeight { + ParagraphAttribute(minimumLineHeight); +} + +- (CGFloat)as_maximumLineHeight { + ParagraphAttribute(maximumLineHeight); +} + +- (CGFloat)as_lineHeightMultiple { + ParagraphAttribute(lineHeightMultiple); +} + +- (NSWritingDirection)as_baseWritingDirection { + ParagraphAttribute(baseWritingDirection); +} + +- (float)as_hyphenationFactor { + ParagraphAttribute(hyphenationFactor); +} + +- (CGFloat)as_defaultTabInterval { + ParagraphAttribute(defaultTabInterval); +} + +- (NSArray *)as_tabStops { + ParagraphAttribute(tabStops); +} + +- (NSTextAlignment)as_alignmentAtIndex:(NSUInteger)index { + ParagraphAttributeAtIndex(alignment); +} + +- (NSLineBreakMode)as_lineBreakModeAtIndex:(NSUInteger)index { + ParagraphAttributeAtIndex(lineBreakMode); +} + +- (CGFloat)as_lineSpacingAtIndex:(NSUInteger)index { + ParagraphAttributeAtIndex(lineSpacing); +} + +- (CGFloat)as_paragraphSpacingAtIndex:(NSUInteger)index { + ParagraphAttributeAtIndex(paragraphSpacing); +} + +- (CGFloat)as_paragraphSpacingBeforeAtIndex:(NSUInteger)index { + ParagraphAttributeAtIndex(paragraphSpacingBefore); +} + +- (CGFloat)as_firstLineHeadIndentAtIndex:(NSUInteger)index { + ParagraphAttributeAtIndex(firstLineHeadIndent); +} + +- (CGFloat)as_headIndentAtIndex:(NSUInteger)index { + ParagraphAttributeAtIndex(headIndent); +} + +- (CGFloat)as_tailIndentAtIndex:(NSUInteger)index { + ParagraphAttributeAtIndex(tailIndent); +} + +- (CGFloat)as_minimumLineHeightAtIndex:(NSUInteger)index { + ParagraphAttributeAtIndex(minimumLineHeight); +} + +- (CGFloat)as_maximumLineHeightAtIndex:(NSUInteger)index { + ParagraphAttributeAtIndex(maximumLineHeight); +} + +- (CGFloat)as_lineHeightMultipleAtIndex:(NSUInteger)index { + ParagraphAttributeAtIndex(lineHeightMultiple); +} + +- (NSWritingDirection)as_baseWritingDirectionAtIndex:(NSUInteger)index { + ParagraphAttributeAtIndex(baseWritingDirection); +} + +- (float)as_hyphenationFactorAtIndex:(NSUInteger)index { + ParagraphAttributeAtIndex(hyphenationFactor); +} + +- (CGFloat)as_defaultTabIntervalAtIndex:(NSUInteger)index { + ParagraphAttributeAtIndex(defaultTabInterval); +} + +- (NSArray *)as_tabStopsAtIndex:(NSUInteger)index { + ParagraphAttributeAtIndex(tabStops); +} + +#undef ParagraphAttribute +#undef ParagraphAttributeAtIndex + +- (ASTextShadow *)as_textShadow { + return [self as_textShadowAtIndex:0]; +} + +- (ASTextShadow *)as_textShadowAtIndex:(NSUInteger)index { + return [self as_attribute:ASTextShadowAttributeName atIndex:index]; +} + +- (ASTextShadow *)as_textInnerShadow { + return [self as_textInnerShadowAtIndex:0]; +} + +- (ASTextShadow *)as_textInnerShadowAtIndex:(NSUInteger)index { + return [self as_attribute:ASTextInnerShadowAttributeName atIndex:index]; +} + +- (ASTextDecoration *)as_textUnderline { + return [self as_textUnderlineAtIndex:0]; +} + +- (ASTextDecoration *)as_textUnderlineAtIndex:(NSUInteger)index { + return [self as_attribute:ASTextUnderlineAttributeName atIndex:index]; +} + +- (ASTextDecoration *)as_textStrikethrough { + return [self as_textStrikethroughAtIndex:0]; +} + +- (ASTextDecoration *)as_textStrikethroughAtIndex:(NSUInteger)index { + return [self as_attribute:ASTextStrikethroughAttributeName atIndex:index]; +} + +- (ASTextBorder *)as_textBorder { + return [self as_textBorderAtIndex:0]; +} + +- (ASTextBorder *)as_textBorderAtIndex:(NSUInteger)index { + return [self as_attribute:ASTextBorderAttributeName atIndex:index]; +} + +- (ASTextBorder *)as_textBackgroundBorder { + return [self as_textBackgroundBorderAtIndex:0]; +} + +- (ASTextBorder *)as_textBackgroundBorderAtIndex:(NSUInteger)index { + return [self as_attribute:ASTextBackedStringAttributeName atIndex:index]; +} + +- (CGAffineTransform)as_textGlyphTransform { + return [self as_textGlyphTransformAtIndex:0]; +} + +- (CGAffineTransform)as_textGlyphTransformAtIndex:(NSUInteger)index { + NSValue *value = [self as_attribute:ASTextGlyphTransformAttributeName atIndex:index]; + if (!value) return CGAffineTransformIdentity; + return [value CGAffineTransformValue]; +} + +- (NSString *)as_plainTextForRange:(NSRange)range { + if (range.location == NSNotFound ||range.length == NSNotFound) return nil; + NSMutableString *result = [NSMutableString string]; + if (range.length == 0) return result; + NSString *string = self.string; + [self enumerateAttribute:ASTextBackedStringAttributeName inRange:range options:kNilOptions usingBlock:^(id value, NSRange range, BOOL *stop) { + ASTextBackedString *backed = value; + if (backed && backed.string) { + [result appendString:backed.string]; + } else { + [result appendString:[string substringWithRange:range]]; + } + }]; + return result; +} + ++ (NSMutableAttributedString *)as_attachmentStringWithContent:(id)content + contentMode:(UIViewContentMode)contentMode + width:(CGFloat)width + ascent:(CGFloat)ascent + descent:(CGFloat)descent { + NSMutableAttributedString *atr = [[NSMutableAttributedString alloc] initWithString:ASTextAttachmentToken]; + + ASTextAttachment *attach = [ASTextAttachment new]; + attach.content = content; + attach.contentMode = contentMode; + [atr as_setTextAttachment:attach range:NSMakeRange(0, atr.length)]; + + ASTextRunDelegate *delegate = [ASTextRunDelegate new]; + delegate.width = width; + delegate.ascent = ascent; + delegate.descent = descent; + CTRunDelegateRef delegateRef = delegate.CTRunDelegate; + [atr as_setRunDelegate:delegateRef range:NSMakeRange(0, atr.length)]; + if (delegate) CFRelease(delegateRef); + + return atr; +} + ++ (NSMutableAttributedString *)as_attachmentStringWithContent:(id)content + contentMode:(UIViewContentMode)contentMode + attachmentSize:(CGSize)attachmentSize + alignToFont:(UIFont *)font + alignment:(ASTextVerticalAlignment)alignment { + NSMutableAttributedString *atr = [[NSMutableAttributedString alloc] initWithString:ASTextAttachmentToken]; + + ASTextAttachment *attach = [ASTextAttachment new]; + attach.content = content; + attach.contentMode = contentMode; + [atr as_setTextAttachment:attach range:NSMakeRange(0, atr.length)]; + + ASTextRunDelegate *delegate = [ASTextRunDelegate new]; + delegate.width = attachmentSize.width; + switch (alignment) { + case ASTextVerticalAlignmentTop: { + delegate.ascent = font.ascender; + delegate.descent = attachmentSize.height - font.ascender; + if (delegate.descent < 0) { + delegate.descent = 0; + delegate.ascent = attachmentSize.height; + } + } break; + case ASTextVerticalAlignmentCenter: { + CGFloat fontHeight = font.ascender - font.descender; + CGFloat yOffset = font.ascender - fontHeight * 0.5; + delegate.ascent = attachmentSize.height * 0.5 + yOffset; + delegate.descent = attachmentSize.height - delegate.ascent; + if (delegate.descent < 0) { + delegate.descent = 0; + delegate.ascent = attachmentSize.height; + } + } break; + case ASTextVerticalAlignmentBottom: { + delegate.ascent = attachmentSize.height + font.descender; + delegate.descent = -font.descender; + if (delegate.ascent < 0) { + delegate.ascent = 0; + delegate.descent = attachmentSize.height; + } + } break; + default: { + delegate.ascent = attachmentSize.height; + delegate.descent = 0; + } break; + } + + CTRunDelegateRef delegateRef = delegate.CTRunDelegate; + [atr as_setRunDelegate:delegateRef range:NSMakeRange(0, atr.length)]; + if (delegate) CFRelease(delegateRef); + + return atr; +} + ++ (NSMutableAttributedString *)as_attachmentStringWithEmojiImage:(UIImage *)image + fontSize:(CGFloat)fontSize { + if (!image || fontSize <= 0) return nil; + + BOOL hasAnim = NO; + if (image.images.count > 1) { + hasAnim = YES; + } else if (NSProtocolFromString(@"ASAnimatedImage") && + [image conformsToProtocol:NSProtocolFromString(@"ASAnimatedImage")]) { + NSNumber *frameCount = [image valueForKey:@"animatedImageFrameCount"]; + if (frameCount.intValue > 1) hasAnim = YES; + } + + CGFloat ascent = ASTextEmojiGetAscentWithFontSize(fontSize); + CGFloat descent = ASTextEmojiGetDescentWithFontSize(fontSize); + CGRect bounding = ASTextEmojiGetGlyphBoundingRectWithFontSize(fontSize); + + ASTextRunDelegate *delegate = [ASTextRunDelegate new]; + delegate.ascent = ascent; + delegate.descent = descent; + delegate.width = bounding.size.width + 2 * bounding.origin.x; + + ASTextAttachment *attachment = [ASTextAttachment new]; + attachment.contentMode = UIViewContentModeScaleAspectFit; + attachment.contentInsets = UIEdgeInsetsMake(ascent - (bounding.size.height + bounding.origin.y), bounding.origin.x, descent + bounding.origin.y, bounding.origin.x); + if (hasAnim) { + Class imageClass = NSClassFromString(@"ASAnimatedImageView"); + if (!imageClass) imageClass = [UIImageView class]; + UIImageView *view = (id)[imageClass new]; + view.frame = bounding; + view.image = image; + view.contentMode = UIViewContentModeScaleAspectFit; + attachment.content = view; + } else { + attachment.content = image; + } + + NSMutableAttributedString *atr = [[NSMutableAttributedString alloc] initWithString:ASTextAttachmentToken]; + [atr as_setTextAttachment:attachment range:NSMakeRange(0, atr.length)]; + CTRunDelegateRef ctDelegate = delegate.CTRunDelegate; + [atr as_setRunDelegate:ctDelegate range:NSMakeRange(0, atr.length)]; + if (ctDelegate) CFRelease(ctDelegate); + + return atr; +} + +- (NSRange)as_rangeOfAll { + return NSMakeRange(0, self.length); +} + +- (BOOL)as_isSharedAttributesInAllRange { + __block BOOL shared = YES; + __block NSDictionary *firstAttrs = nil; + [self enumerateAttributesInRange:self.as_rangeOfAll options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired usingBlock:^(NSDictionary *attrs, NSRange range, BOOL *stop) { + if (range.location == 0) { + firstAttrs = attrs; + } else { + if (firstAttrs.count != attrs.count) { + shared = NO; + *stop = YES; + } else if (firstAttrs) { + if (![firstAttrs isEqualToDictionary:attrs]) { + shared = NO; + *stop = YES; + } + } + } + }]; + return shared; +} + +- (BOOL)as_canDrawWithUIKit { + static NSMutableSet *failSet; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + failSet = [NSMutableSet new]; + [failSet addObject:(id)kCTGlyphInfoAttributeName]; +#if TARGET_OS_IOS +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + [failSet addObject:(id)kCTCharacterShapeAttributeName]; +#pragma clang diagnostic pop +#endif + [failSet addObject:(id)kCTLanguageAttributeName]; + [failSet addObject:(id)kCTRunDelegateAttributeName]; + [failSet addObject:(id)kCTBaselineClassAttributeName]; + [failSet addObject:(id)kCTBaselineInfoAttributeName]; + [failSet addObject:(id)kCTBaselineReferenceInfoAttributeName]; + [failSet addObject:(id)kCTRubyAnnotationAttributeName]; + [failSet addObject:ASTextShadowAttributeName]; + [failSet addObject:ASTextInnerShadowAttributeName]; + [failSet addObject:ASTextUnderlineAttributeName]; + [failSet addObject:ASTextStrikethroughAttributeName]; + [failSet addObject:ASTextBorderAttributeName]; + [failSet addObject:ASTextBackgroundBorderAttributeName]; + [failSet addObject:ASTextBlockBorderAttributeName]; + [failSet addObject:ASTextAttachmentAttributeName]; + [failSet addObject:ASTextHighlightAttributeName]; + [failSet addObject:ASTextGlyphTransformAttributeName]; + }); + +#define Fail { result = NO; *stop = YES; return; } + __block BOOL result = YES; + [self enumerateAttributesInRange:self.as_rangeOfAll options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired usingBlock:^(NSDictionary *attrs, NSRange range, BOOL *stop) { + if (attrs.count == 0) return; + for (NSString *str in attrs.allKeys) { + if ([failSet containsObject:str]) Fail; + } + if (attrs[(id)kCTForegroundColorAttributeName] && !attrs[NSForegroundColorAttributeName]) Fail; + if (attrs[(id)kCTStrokeColorAttributeName] && !attrs[NSStrokeColorAttributeName]) Fail; + if (attrs[(id)kCTUnderlineColorAttributeName]) { + if (!attrs[NSUnderlineColorAttributeName]) Fail; + } + NSParagraphStyle *style = attrs[NSParagraphStyleAttributeName]; + if (style && CFGetTypeID((__bridge CFTypeRef)(style)) == CTParagraphStyleGetTypeID()) Fail; + }]; + return result; +#undef Fail +} + +@end + +@implementation NSMutableAttributedString (ASText) + +- (void)as_setAttributes:(NSDictionary *)attributes { + [self setAs_attributes:attributes]; +} + +- (void)setAs_attributes:(NSDictionary *)attributes { + if (attributes == (id)[NSNull null]) attributes = nil; + [self setAttributes:@{} range:NSMakeRange(0, self.length)]; + [attributes enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { + [self as_setAttribute:key value:obj]; + }]; +} + +- (void)as_setAttribute:(NSString *)name value:(id)value { + [self as_setAttribute:name value:value range:NSMakeRange(0, self.length)]; +} + +- (void)as_setAttribute:(NSString *)name value:(id)value range:(NSRange)range { + if (!name || [NSNull isEqual:name]) return; + if (value && ![NSNull isEqual:value]) [self addAttribute:name value:value range:range]; + else [self removeAttribute:name range:range]; +} + +- (void)as_removeAttributesInRange:(NSRange)range { + [self setAttributes:nil range:range]; +} + +#pragma mark - Property Setter + +- (void)setAs_font:(UIFont *)font { + /* + In iOS7 and later, UIFont is toll-free bridged to CTFontRef, + although Apple does not mention it in documentation. + + In iOS6, UIFont is a wrapper for CTFontRef, so CoreText can alse use UIfont, + but UILabel/UITextView cannot use CTFontRef. + + We use UIFont for both CoreText and UIKit. + */ + [self as_setFont:font range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_kern:(NSNumber *)kern { + [self as_setKern:kern range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_color:(UIColor *)color { + [self as_setColor:color range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_backgroundColor:(UIColor *)backgroundColor { + [self as_setBackgroundColor:backgroundColor range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_strokeWidth:(NSNumber *)strokeWidth { + [self as_setStrokeWidth:strokeWidth range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_strokeColor:(UIColor *)strokeColor { + [self as_setStrokeColor:strokeColor range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_shadow:(NSShadow *)shadow { + [self as_setShadow:shadow range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_strikethroughStyle:(NSUnderlineStyle)strikethroughStyle { + [self as_setStrikethroughStyle:strikethroughStyle range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_strikethroughColor:(UIColor *)strikethroughColor { + [self as_setStrikethroughColor:strikethroughColor range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_underlineStyle:(NSUnderlineStyle)underlineStyle { + [self as_setUnderlineStyle:underlineStyle range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_underlineColor:(UIColor *)underlineColor { + [self as_setUnderlineColor:underlineColor range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_ligature:(NSNumber *)ligature { + [self as_setLigature:ligature range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_textEffect:(NSString *)textEffect { + [self as_setTextEffect:textEffect range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_obliqueness:(NSNumber *)obliqueness { + [self as_setObliqueness:obliqueness range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_expansion:(NSNumber *)expansion { + [self as_setExpansion:expansion range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_baselineOffset:(NSNumber *)baselineOffset { + [self as_setBaselineOffset:baselineOffset range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_verticalGlyphForm:(BOOL)verticalGlyphForm { + [self as_setVerticalGlyphForm:verticalGlyphForm range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_language:(NSString *)language { + [self as_setLanguage:language range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_writingDirection:(NSArray *)writingDirection { + [self as_setWritingDirection:writingDirection range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_paragraphStyle:(NSParagraphStyle *)paragraphStyle { + /* + NSParagraphStyle is NOT toll-free bridged to CTParagraphStyleRef. + + CoreText can use both NSParagraphStyle and CTParagraphStyleRef, + but UILabel/UITextView can only use NSParagraphStyle. + + We use NSParagraphStyle in both CoreText and UIKit. + */ + [self as_setParagraphStyle:paragraphStyle range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_alignment:(NSTextAlignment)alignment { + [self as_setAlignment:alignment range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_baseWritingDirection:(NSWritingDirection)baseWritingDirection { + [self as_setBaseWritingDirection:baseWritingDirection range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_lineSpacing:(CGFloat)lineSpacing { + [self as_setLineSpacing:lineSpacing range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_paragraphSpacing:(CGFloat)paragraphSpacing { + [self as_setParagraphSpacing:paragraphSpacing range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_paragraphSpacingBefore:(CGFloat)paragraphSpacingBefore { + [self as_setParagraphSpacing:paragraphSpacingBefore range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_firstLineHeadIndent:(CGFloat)firstLineHeadIndent { + [self as_setFirstLineHeadIndent:firstLineHeadIndent range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_headIndent:(CGFloat)headIndent { + [self as_setHeadIndent:headIndent range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_tailIndent:(CGFloat)tailIndent { + [self as_setTailIndent:tailIndent range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_lineBreakMode:(NSLineBreakMode)lineBreakMode { + [self as_setLineBreakMode:lineBreakMode range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_minimumLineHeight:(CGFloat)minimumLineHeight { + [self as_setMinimumLineHeight:minimumLineHeight range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_maximumLineHeight:(CGFloat)maximumLineHeight { + [self as_setMaximumLineHeight:maximumLineHeight range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_lineHeightMultiple:(CGFloat)lineHeightMultiple { + [self as_setLineHeightMultiple:lineHeightMultiple range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_hyphenationFactor:(float)hyphenationFactor { + [self as_setHyphenationFactor:hyphenationFactor range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_defaultTabInterval:(CGFloat)defaultTabInterval { + [self as_setDefaultTabInterval:defaultTabInterval range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_tabStops:(NSArray *)tabStops { + [self as_setTabStops:tabStops range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_textShadow:(ASTextShadow *)textShadow { + [self as_setTextShadow:textShadow range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_textInnerShadow:(ASTextShadow *)textInnerShadow { + [self as_setTextInnerShadow:textInnerShadow range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_textUnderline:(ASTextDecoration *)textUnderline { + [self as_setTextUnderline:textUnderline range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_textStrikethrough:(ASTextDecoration *)textStrikethrough { + [self as_setTextStrikethrough:textStrikethrough range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_textBorder:(ASTextBorder *)textBorder { + [self as_setTextBorder:textBorder range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_textBackgroundBorder:(ASTextBorder *)textBackgroundBorder { + [self as_setTextBackgroundBorder:textBackgroundBorder range:NSMakeRange(0, self.length)]; +} + +- (void)setAs_textGlyphTransform:(CGAffineTransform)textGlyphTransform { + [self as_setTextGlyphTransform:textGlyphTransform range:NSMakeRange(0, self.length)]; +} + +#pragma mark - Range Setter + +- (void)as_setFont:(UIFont *)font range:(NSRange)range { + [self as_setAttribute:NSFontAttributeName value:font range:range]; +} + +- (void)as_setKern:(NSNumber *)kern range:(NSRange)range { + [self as_setAttribute:NSKernAttributeName value:kern range:range]; +} + +- (void)as_setColor:(UIColor *)color range:(NSRange)range { + [self as_setAttribute:(id)kCTForegroundColorAttributeName value:(id)color.CGColor range:range]; + [self as_setAttribute:NSForegroundColorAttributeName value:color range:range]; +} + +- (void)as_setBackgroundColor:(UIColor *)backgroundColor range:(NSRange)range { + [self as_setAttribute:NSBackgroundColorAttributeName value:backgroundColor range:range]; +} + +- (void)as_setStrokeWidth:(NSNumber *)strokeWidth range:(NSRange)range { + [self as_setAttribute:NSStrokeWidthAttributeName value:strokeWidth range:range]; +} + +- (void)as_setStrokeColor:(UIColor *)strokeColor range:(NSRange)range { + [self as_setAttribute:(id)kCTStrokeColorAttributeName value:(id)strokeColor.CGColor range:range]; + [self as_setAttribute:NSStrokeColorAttributeName value:strokeColor range:range]; +} + +- (void)as_setShadow:(NSShadow *)shadow range:(NSRange)range { + [self as_setAttribute:NSShadowAttributeName value:shadow range:range]; +} + +- (void)as_setStrikethroughStyle:(NSUnderlineStyle)strikethroughStyle range:(NSRange)range { + NSNumber *style = strikethroughStyle == 0 ? nil : @(strikethroughStyle); + [self as_setAttribute:NSStrikethroughStyleAttributeName value:style range:range]; +} + +- (void)as_setStrikethroughColor:(UIColor *)strikethroughColor range:(NSRange)range { + [self as_setAttribute:NSStrikethroughColorAttributeName value:strikethroughColor range:range]; +} + +- (void)as_setUnderlineStyle:(NSUnderlineStyle)underlineStyle range:(NSRange)range { + NSNumber *style = underlineStyle == 0 ? nil : @(underlineStyle); + [self as_setAttribute:NSUnderlineStyleAttributeName value:style range:range]; +} + +- (void)as_setUnderlineColor:(UIColor *)underlineColor range:(NSRange)range { + [self as_setAttribute:(id)kCTUnderlineColorAttributeName value:(id)underlineColor.CGColor range:range]; + [self as_setAttribute:NSUnderlineColorAttributeName value:underlineColor range:range]; +} + +- (void)as_setLigature:(NSNumber *)ligature range:(NSRange)range { + [self as_setAttribute:NSLigatureAttributeName value:ligature range:range]; +} + +- (void)as_setTextEffect:(NSString *)textEffect range:(NSRange)range { + [self as_setAttribute:NSTextEffectAttributeName value:textEffect range:range]; +} + +- (void)as_setObliqueness:(NSNumber *)obliqueness range:(NSRange)range { + [self as_setAttribute:NSObliquenessAttributeName value:obliqueness range:range]; +} + +- (void)as_setExpansion:(NSNumber *)expansion range:(NSRange)range { + [self as_setAttribute:NSExpansionAttributeName value:expansion range:range]; +} + +- (void)as_setBaselineOffset:(NSNumber *)baselineOffset range:(NSRange)range { + [self as_setAttribute:NSBaselineOffsetAttributeName value:baselineOffset range:range]; +} + +- (void)as_setVerticalGlyphForm:(BOOL)verticalGlyphForm range:(NSRange)range { + NSNumber *v = verticalGlyphForm ? @(YES) : nil; + [self as_setAttribute:NSVerticalGlyphFormAttributeName value:v range:range]; +} + +- (void)as_setLanguage:(NSString *)language range:(NSRange)range { + [self as_setAttribute:(id)kCTLanguageAttributeName value:language range:range]; +} + +- (void)as_setWritingDirection:(NSArray *)writingDirection range:(NSRange)range { + [self as_setAttribute:(id)kCTWritingDirectionAttributeName value:writingDirection range:range]; +} + +- (void)as_setParagraphStyle:(NSParagraphStyle *)paragraphStyle range:(NSRange)range { + /* + NSParagraphStyle is NOT toll-free bridged to CTParagraphStyleRef. + + CoreText can use both NSParagraphStyle and CTParagraphStyleRef, + but UILabel/UITextView can only use NSParagraphStyle. + + We use NSParagraphStyle in both CoreText and UIKit. + */ + [self as_setAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:range]; +} + +#define ParagraphStyleSet(_attr_) \ +[self enumerateAttribute:NSParagraphStyleAttributeName \ +inRange:range \ +options:kNilOptions \ +usingBlock: ^(NSParagraphStyle *value, NSRange subRange, BOOL *stop) { \ +NSMutableParagraphStyle *style = nil; \ +if (value) { \ +if (CFGetTypeID((__bridge CFTypeRef)(value)) == CTParagraphStyleGetTypeID()) { \ +value = [NSParagraphStyle as_styleWithCTStyle:(__bridge CTParagraphStyleRef)(value)]; \ +} \ +if (value. _attr_ == _attr_) return; \ +if ([value isKindOfClass:[NSMutableParagraphStyle class]]) { \ +style = (id)value; \ +} else { \ +style = value.mutableCopy; \ +} \ +} else { \ +if ([NSParagraphStyle defaultParagraphStyle]. _attr_ == _attr_) return; \ +style = [NSParagraphStyle defaultParagraphStyle].mutableCopy; \ +} \ +style. _attr_ = _attr_; \ +[self as_setParagraphStyle:style range:subRange]; \ +}]; + +- (void)as_setAlignment:(NSTextAlignment)alignment range:(NSRange)range { + ParagraphStyleSet(alignment); +} + +- (void)as_setBaseWritingDirection:(NSWritingDirection)baseWritingDirection range:(NSRange)range { + ParagraphStyleSet(baseWritingDirection); +} + +- (void)as_setLineSpacing:(CGFloat)lineSpacing range:(NSRange)range { + ParagraphStyleSet(lineSpacing); +} + +- (void)as_setParagraphSpacing:(CGFloat)paragraphSpacing range:(NSRange)range { + ParagraphStyleSet(paragraphSpacing); +} + +- (void)as_setParagraphSpacingBefore:(CGFloat)paragraphSpacingBefore range:(NSRange)range { + ParagraphStyleSet(paragraphSpacingBefore); +} + +- (void)as_setFirstLineHeadIndent:(CGFloat)firstLineHeadIndent range:(NSRange)range { + ParagraphStyleSet(firstLineHeadIndent); +} + +- (void)as_setHeadIndent:(CGFloat)headIndent range:(NSRange)range { + ParagraphStyleSet(headIndent); +} + +- (void)as_setTailIndent:(CGFloat)tailIndent range:(NSRange)range { + ParagraphStyleSet(tailIndent); +} + +- (void)as_setLineBreakMode:(NSLineBreakMode)lineBreakMode range:(NSRange)range { + ParagraphStyleSet(lineBreakMode); +} + +- (void)as_setMinimumLineHeight:(CGFloat)minimumLineHeight range:(NSRange)range { + ParagraphStyleSet(minimumLineHeight); +} + +- (void)as_setMaximumLineHeight:(CGFloat)maximumLineHeight range:(NSRange)range { + ParagraphStyleSet(maximumLineHeight); +} + +- (void)as_setLineHeightMultiple:(CGFloat)lineHeightMultiple range:(NSRange)range { + ParagraphStyleSet(lineHeightMultiple); +} + +- (void)as_setHyphenationFactor:(float)hyphenationFactor range:(NSRange)range { + ParagraphStyleSet(hyphenationFactor); +} + +- (void)as_setDefaultTabInterval:(CGFloat)defaultTabInterval range:(NSRange)range { + ParagraphStyleSet(defaultTabInterval); +} + +- (void)as_setTabStops:(NSArray *)tabStops range:(NSRange)range { + ParagraphStyleSet(tabStops); +} + +#undef ParagraphStyleSet + +- (void)as_setSuperscript:(NSNumber *)superscript range:(NSRange)range { + if ([superscript isEqualToNumber:@(0)]) { + superscript = nil; + } + [self as_setAttribute:(id)kCTSuperscriptAttributeName value:superscript range:range]; +} + +- (void)as_setGlyphInfo:(CTGlyphInfoRef)glyphInfo range:(NSRange)range { + [self as_setAttribute:(id)kCTGlyphInfoAttributeName value:(__bridge id)glyphInfo range:range]; +} + +- (void)as_setCharacterShape:(NSNumber *)characterShape range:(NSRange)range { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + [self as_setAttribute:(id)kCTCharacterShapeAttributeName value:characterShape range:range]; +#pragma clang diagnostic pop +} + +- (void)as_setRunDelegate:(CTRunDelegateRef)runDelegate range:(NSRange)range { + [self as_setAttribute:(id)kCTRunDelegateAttributeName value:(__bridge id)runDelegate range:range]; +} + +- (void)as_setBaselineClass:(CFStringRef)baselineClass range:(NSRange)range { + [self as_setAttribute:(id)kCTBaselineClassAttributeName value:(__bridge id)baselineClass range:range]; +} + +- (void)as_setBaselineInfo:(CFDictionaryRef)baselineInfo range:(NSRange)range { + [self as_setAttribute:(id)kCTBaselineInfoAttributeName value:(__bridge id)baselineInfo range:range]; +} + +- (void)as_setBaselineReferenceInfo:(CFDictionaryRef)referenceInfo range:(NSRange)range { + [self as_setAttribute:(id)kCTBaselineReferenceInfoAttributeName value:(__bridge id)referenceInfo range:range]; +} + +- (void)as_setRubyAnnotation:(CTRubyAnnotationRef)ruby range:(NSRange)range { + [self as_setAttribute:(id)kCTRubyAnnotationAttributeName value:(__bridge id)ruby range:range]; +} + +- (void)as_setAttachment:(NSTextAttachment *)attachment range:(NSRange)range { + [self as_setAttribute:NSAttachmentAttributeName value:attachment range:range]; +} + +- (void)as_setLink:(id)link range:(NSRange)range { + [self as_setAttribute:NSLinkAttributeName value:link range:range]; +} + +- (void)as_setTextBackedString:(ASTextBackedString *)textBackedString range:(NSRange)range { + [self as_setAttribute:ASTextBackedStringAttributeName value:textBackedString range:range]; +} + +- (void)as_setTextBinding:(ASTextBinding *)textBinding range:(NSRange)range { + [self as_setAttribute:ASTextBindingAttributeName value:textBinding range:range]; +} + +- (void)as_setTextShadow:(ASTextShadow *)textShadow range:(NSRange)range { + [self as_setAttribute:ASTextShadowAttributeName value:textShadow range:range]; +} + +- (void)as_setTextInnerShadow:(ASTextShadow *)textInnerShadow range:(NSRange)range { + [self as_setAttribute:ASTextInnerShadowAttributeName value:textInnerShadow range:range]; +} + +- (void)as_setTextUnderline:(ASTextDecoration *)textUnderline range:(NSRange)range { + [self as_setAttribute:ASTextUnderlineAttributeName value:textUnderline range:range]; +} + +- (void)as_setTextStrikethrough:(ASTextDecoration *)textStrikethrough range:(NSRange)range { + [self as_setAttribute:ASTextStrikethroughAttributeName value:textStrikethrough range:range]; +} + +- (void)as_setTextBorder:(ASTextBorder *)textBorder range:(NSRange)range { + [self as_setAttribute:ASTextBorderAttributeName value:textBorder range:range]; +} + +- (void)as_setTextBackgroundBorder:(ASTextBorder *)textBackgroundBorder range:(NSRange)range { + [self as_setAttribute:ASTextBackgroundBorderAttributeName value:textBackgroundBorder range:range]; +} + +- (void)as_setTextAttachment:(ASTextAttachment *)textAttachment range:(NSRange)range { + [self as_setAttribute:ASTextAttachmentAttributeName value:textAttachment range:range]; +} + +- (void)as_setTextHighlight:(ASTextHighlight *)textHighlight range:(NSRange)range { + [self as_setAttribute:ASTextHighlightAttributeName value:textHighlight range:range]; +} + +- (void)as_setTextBlockBorder:(ASTextBorder *)textBlockBorder range:(NSRange)range { + [self as_setAttribute:ASTextBlockBorderAttributeName value:textBlockBorder range:range]; +} + +- (void)as_setTextGlyphTransform:(CGAffineTransform)textGlyphTransform range:(NSRange)range { + NSValue *value = CGAffineTransformIsIdentity(textGlyphTransform) ? nil : [NSValue valueWithCGAffineTransform:textGlyphTransform]; + [self as_setAttribute:ASTextGlyphTransformAttributeName value:value range:range]; +} + +- (void)as_setTextHighlightRange:(NSRange)range + color:(UIColor *)color + backgroundColor:(UIColor *)backgroundColor + userInfo:(NSDictionary *)userInfo + tapAction:(ASTextAction)tapAction + longPressAction:(ASTextAction)longPressAction { + ASTextHighlight *highlight = [ASTextHighlight highlightWithBackgroundColor:backgroundColor]; + highlight.userInfo = userInfo; + highlight.tapAction = tapAction; + highlight.longPressAction = longPressAction; + if (color) [self as_setColor:color range:range]; + [self as_setTextHighlight:highlight range:range]; +} + +- (void)as_setTextHighlightRange:(NSRange)range + color:(UIColor *)color + backgroundColor:(UIColor *)backgroundColor + tapAction:(ASTextAction)tapAction { + [self as_setTextHighlightRange:range + color:color + backgroundColor:backgroundColor + userInfo:nil + tapAction:tapAction + longPressAction:nil]; +} + +- (void)as_setTextHighlightRange:(NSRange)range + color:(UIColor *)color + backgroundColor:(UIColor *)backgroundColor + userInfo:(NSDictionary *)userInfo { + [self as_setTextHighlightRange:range + color:color + backgroundColor:backgroundColor + userInfo:userInfo + tapAction:nil + longPressAction:nil]; +} + +- (void)as_insertString:(NSString *)string atIndex:(NSUInteger)location { + [self replaceCharactersInRange:NSMakeRange(location, 0) withString:string]; + [self as_removeDiscontinuousAttributesInRange:NSMakeRange(location, string.length)]; +} + +- (void)as_appendString:(NSString *)string { + NSUInteger length = self.length; + [self replaceCharactersInRange:NSMakeRange(length, 0) withString:string]; + [self as_removeDiscontinuousAttributesInRange:NSMakeRange(length, string.length)]; +} + +- (void)as_removeDiscontinuousAttributesInRange:(NSRange)range { + NSArray *keys = [NSMutableAttributedString as_allDiscontinuousAttributeKeys]; + for (NSString *key in keys) { + [self removeAttribute:key range:range]; + } +} + ++ (NSArray *)as_allDiscontinuousAttributeKeys { + static NSArray *keys; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + keys = @[(id)kCTSuperscriptAttributeName, + (id)kCTRunDelegateAttributeName, + ASTextBackedStringAttributeName, + ASTextBindingAttributeName, + ASTextAttachmentAttributeName, + (id)kCTRubyAnnotationAttributeName, + NSAttachmentAttributeName]; + }); + return keys; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/Utility/NSParagraphStyle+ASText.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/Utility/NSParagraphStyle+ASText.h new file mode 100644 index 0000000..a7c0aec --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/Utility/NSParagraphStyle+ASText.h @@ -0,0 +1,34 @@ +// +// NSParagraphStyle+ASText.h +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + Provides extensions for `NSParagraphStyle` to work with CoreText. + */ +@interface NSParagraphStyle (ASText) + +/** + Creates a new NSParagraphStyle object from the CoreText Style. + + @param CTStyle CoreText Paragraph Style. + + @return a new NSParagraphStyle + */ ++ (nullable NSParagraphStyle *)as_styleWithCTStyle:(CTParagraphStyleRef)CTStyle; + +/** + Creates and returns a CoreText Paragraph Style. (need call CFRelease() after used) + */ +- (nullable CTParagraphStyleRef)as_CTStyle CF_RETURNS_RETAINED; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/Utility/NSParagraphStyle+ASText.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/Utility/NSParagraphStyle+ASText.mm new file mode 100644 index 0000000..bc19fd2 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/TextExperiment/Utility/NSParagraphStyle+ASText.mm @@ -0,0 +1,219 @@ +// +// NSParagraphStyle+ASText.mm +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import + +// Dummy class for category +@interface NSParagraphStyle_ASText : NSObject @end +@implementation NSParagraphStyle_ASText @end + + +@implementation NSParagraphStyle (ASText) + ++ (NSParagraphStyle *)as_styleWithCTStyle:(CTParagraphStyleRef)CTStyle { + if (CTStyle == NULL) return nil; + + NSMutableParagraphStyle *style = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; + +#if TARGET_OS_IOS +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + CGFloat lineSpacing; + if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierLineSpacing, sizeof(CGFloat), &lineSpacing)) { + style.lineSpacing = lineSpacing; + } +#pragma clang diagnostic pop +#endif + + CGFloat paragraphSpacing; + if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierParagraphSpacing, sizeof(CGFloat), ¶graphSpacing)) { + style.paragraphSpacing = paragraphSpacing; + } + + CTTextAlignment alignment; + if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierAlignment, sizeof(CTTextAlignment), &alignment)) { + style.alignment = NSTextAlignmentFromCTTextAlignment(alignment); + } + + CGFloat firstLineHeadIndent; + if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierFirstLineHeadIndent, sizeof(CGFloat), &firstLineHeadIndent)) { + style.firstLineHeadIndent = firstLineHeadIndent; + } + + CGFloat headIndent; + if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierHeadIndent, sizeof(CGFloat), &headIndent)) { + style.headIndent = headIndent; + } + + CGFloat tailIndent; + if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierTailIndent, sizeof(CGFloat), &tailIndent)) { + style.tailIndent = tailIndent; + } + + CTLineBreakMode lineBreakMode; + if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierLineBreakMode, sizeof(CTLineBreakMode), &lineBreakMode)) { + style.lineBreakMode = (NSLineBreakMode)lineBreakMode; + } + + CGFloat minimumLineHeight; + if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierMinimumLineHeight, sizeof(CGFloat), &minimumLineHeight)) { + style.minimumLineHeight = minimumLineHeight; + } + + CGFloat maximumLineHeight; + if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierMaximumLineHeight, sizeof(CGFloat), &maximumLineHeight)) { + style.maximumLineHeight = maximumLineHeight; + } + + CTWritingDirection baseWritingDirection; + if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierBaseWritingDirection, sizeof(CTWritingDirection), &baseWritingDirection)) { + style.baseWritingDirection = (NSWritingDirection)baseWritingDirection; + } + + CGFloat lineHeightMultiple; + if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierLineHeightMultiple, sizeof(CGFloat), &lineHeightMultiple)) { + style.lineHeightMultiple = lineHeightMultiple; + } + + CGFloat paragraphSpacingBefore; + if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierParagraphSpacingBefore, sizeof(CGFloat), ¶graphSpacingBefore)) { + style.paragraphSpacingBefore = paragraphSpacingBefore; + } + + CFArrayRef tabStops; + if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierTabStops, sizeof(CFArrayRef), &tabStops)) { + NSMutableArray *tabs = [NSMutableArray new]; + [((__bridge NSArray *)(tabStops))enumerateObjectsUsingBlock : ^(id obj, NSUInteger idx, BOOL *stop) { + CTTextTabRef ctTab = (__bridge CTTextTabRef)obj; + + NSTextTab *tab = [[NSTextTab alloc] initWithTextAlignment:NSTextAlignmentFromCTTextAlignment(CTTextTabGetAlignment(ctTab)) location:CTTextTabGetLocation(ctTab) options:(__bridge id)CTTextTabGetOptions(ctTab)]; + [tabs addObject:tab]; + }]; + if (tabs.count) { + style.tabStops = tabs; + } + } + + CGFloat defaultTabInterval; + if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierDefaultTabInterval, sizeof(CGFloat), &defaultTabInterval)) { + style.defaultTabInterval = defaultTabInterval; + } + + return style; +} + +- (CTParagraphStyleRef)as_CTStyle CF_RETURNS_RETAINED { + CTParagraphStyleSetting set[kCTParagraphStyleSpecifierCount] = { }; + int count = 0; + +#if TARGET_OS_IOS +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + CGFloat lineSpacing = self.lineSpacing; + set[count].spec = kCTParagraphStyleSpecifierLineSpacing; + set[count].valueSize = sizeof(CGFloat); + set[count].value = &lineSpacing; + count++; +#pragma clang diagnostic pop +#endif + + CGFloat paragraphSpacing = self.paragraphSpacing; + set[count].spec = kCTParagraphStyleSpecifierParagraphSpacing; + set[count].valueSize = sizeof(CGFloat); + set[count].value = ¶graphSpacing; + count++; + + CTTextAlignment alignment = NSTextAlignmentToCTTextAlignment(self.alignment); + set[count].spec = kCTParagraphStyleSpecifierAlignment; + set[count].valueSize = sizeof(CTTextAlignment); + set[count].value = &alignment; + count++; + + CGFloat firstLineHeadIndent = self.firstLineHeadIndent; + set[count].spec = kCTParagraphStyleSpecifierFirstLineHeadIndent; + set[count].valueSize = sizeof(CGFloat); + set[count].value = &firstLineHeadIndent; + count++; + + CGFloat headIndent = self.headIndent; + set[count].spec = kCTParagraphStyleSpecifierHeadIndent; + set[count].valueSize = sizeof(CGFloat); + set[count].value = &headIndent; + count++; + + CGFloat tailIndent = self.tailIndent; + set[count].spec = kCTParagraphStyleSpecifierTailIndent; + set[count].valueSize = sizeof(CGFloat); + set[count].value = &tailIndent; + count++; + + CTLineBreakMode paraLineBreak = (CTLineBreakMode)self.lineBreakMode; + set[count].spec = kCTParagraphStyleSpecifierLineBreakMode; + set[count].valueSize = sizeof(CTLineBreakMode); + set[count].value = ¶LineBreak; + count++; + + CGFloat minimumLineHeight = self.minimumLineHeight; + set[count].spec = kCTParagraphStyleSpecifierMinimumLineHeight; + set[count].valueSize = sizeof(CGFloat); + set[count].value = &minimumLineHeight; + count++; + + CGFloat maximumLineHeight = self.maximumLineHeight; + set[count].spec = kCTParagraphStyleSpecifierMaximumLineHeight; + set[count].valueSize = sizeof(CGFloat); + set[count].value = &maximumLineHeight; + count++; + + CTWritingDirection paraWritingDirection = (CTWritingDirection)self.baseWritingDirection; + set[count].spec = kCTParagraphStyleSpecifierBaseWritingDirection; + set[count].valueSize = sizeof(CTWritingDirection); + set[count].value = ¶WritingDirection; + count++; + + CGFloat lineHeightMultiple = self.lineHeightMultiple; + set[count].spec = kCTParagraphStyleSpecifierLineHeightMultiple; + set[count].valueSize = sizeof(CGFloat); + set[count].value = &lineHeightMultiple; + count++; + + CGFloat paragraphSpacingBefore = self.paragraphSpacingBefore; + set[count].spec = kCTParagraphStyleSpecifierParagraphSpacingBefore; + set[count].valueSize = sizeof(CGFloat); + set[count].value = ¶graphSpacingBefore; + count++; + + NSMutableArray *tabs = [NSMutableArray array]; + NSInteger numTabs = self.tabStops.count; + if (numTabs) { + [self.tabStops enumerateObjectsUsingBlock: ^(NSTextTab *tab, NSUInteger idx, BOOL *stop) { + CTTextTabRef ctTab = CTTextTabCreate(NSTextAlignmentToCTTextAlignment(tab.alignment), tab.location, (__bridge CFDictionaryRef)tab.options); + [tabs addObject:(__bridge id)ctTab]; + CFRelease(ctTab); + }]; + + CFArrayRef tabStops = (__bridge CFArrayRef)(tabs); + set[count].spec = kCTParagraphStyleSpecifierTabStops; + set[count].valueSize = sizeof(CFArrayRef); + set[count].value = &tabStops; + count++; + } + + CGFloat defaultTabInterval = self.defaultTabInterval; + set[count].spec = kCTParagraphStyleSpecifierDefaultTabInterval; + set[count].valueSize = sizeof(CGFloat); + set[count].value = &defaultTabInterval; + count++; + + CTParagraphStyleRef style = CTParagraphStyleCreate(set, count); + return style; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/_ASCollectionGalleryLayoutInfo.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/_ASCollectionGalleryLayoutInfo.h new file mode 100644 index 0000000..6d7c91e --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/_ASCollectionGalleryLayoutInfo.h @@ -0,0 +1,26 @@ +// +// _ASCollectionGalleryLayoutInfo.h +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +@interface _ASCollectionGalleryLayoutInfo : NSObject + +// Read-only properties +@property (nonatomic, readonly) CGSize itemSize; +@property (nonatomic, readonly) CGFloat minimumLineSpacing; +@property (nonatomic, readonly) CGFloat minimumInteritemSpacing; +@property (nonatomic, readonly) UIEdgeInsets sectionInset; + +- (instancetype)initWithItemSize:(CGSize)itemSize + minimumLineSpacing:(CGFloat)minimumLineSpacing + minimumInteritemSpacing:(CGFloat)minimumInteritemSpacing + sectionInset:(UIEdgeInsets)sectionInset NS_DESIGNATED_INITIALIZER; + +- (instancetype)init NS_UNAVAILABLE; + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/_ASCollectionGalleryLayoutInfo.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/_ASCollectionGalleryLayoutInfo.mm new file mode 100644 index 0000000..fec0b52 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/_ASCollectionGalleryLayoutInfo.mm @@ -0,0 +1,68 @@ +// +// _ASCollectionGalleryLayoutInfo.mm +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +@implementation _ASCollectionGalleryLayoutInfo + +- (instancetype)initWithItemSize:(CGSize)itemSize + minimumLineSpacing:(CGFloat)minimumLineSpacing + minimumInteritemSpacing:(CGFloat)minimumInteritemSpacing + sectionInset:(UIEdgeInsets)sectionInset +{ + self = [super init]; + if (self) { + _itemSize = itemSize; + _minimumLineSpacing = minimumLineSpacing; + _minimumInteritemSpacing = minimumInteritemSpacing; + _sectionInset = sectionInset; + } + return self; +} + +- (BOOL)isEqualToInfo:(_ASCollectionGalleryLayoutInfo *)info +{ + if (info == nil) { + return NO; + } + + return CGSizeEqualToSize(_itemSize, info.itemSize) + && _minimumLineSpacing == info.minimumLineSpacing + && _minimumInteritemSpacing == info.minimumInteritemSpacing + && UIEdgeInsetsEqualToEdgeInsets(_sectionInset, info.sectionInset); +} + +- (BOOL)isEqual:(id)other +{ + if (self == other) { + return YES; + } + if (! [other isKindOfClass:[_ASCollectionGalleryLayoutInfo class]]) { + return NO; + } + return [self isEqualToInfo:other]; +} + +- (NSUInteger)hash +{ + struct { + CGSize itemSize; + CGFloat minimumLineSpacing; + CGFloat minimumInteritemSpacing; + UIEdgeInsets sectionInset; + } data = { + _itemSize, + _minimumLineSpacing, + _minimumInteritemSpacing, + _sectionInset, + }; + return ASHashBytes(&data, sizeof(data)); +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/_ASCollectionGalleryLayoutItem.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/_ASCollectionGalleryLayoutItem.h new file mode 100644 index 0000000..ce071bb --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/_ASCollectionGalleryLayoutItem.h @@ -0,0 +1,34 @@ +// +// _ASCollectionGalleryLayoutItem.h +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import + +@class ASCollectionElement; + +NS_ASSUME_NONNULL_BEGIN + +/** + * A dummy item that represents a collection element to participate in the collection layout calculation process + * without triggering measurement on the actual node of the collection element. + * + * This item always has a fixed size that is the item size passed to it. + */ +AS_SUBCLASSING_RESTRICTED +@interface _ASGalleryLayoutItem : NSObject + +@property (nonatomic, readonly) CGSize itemSize; +@property (nonatomic, weak, readonly) ASCollectionElement *collectionElement; + +- (instancetype)initWithItemSize:(CGSize)itemSize collectionElement:(ASCollectionElement *)collectionElement; +- (instancetype)init __unavailable; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/_ASCollectionGalleryLayoutItem.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/_ASCollectionGalleryLayoutItem.mm new file mode 100644 index 0000000..3e4320e --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/_ASCollectionGalleryLayoutItem.mm @@ -0,0 +1,82 @@ +// +// _ASCollectionGalleryLayoutItem.mm +// Texture +// +// Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import +#import +#import +#import + +@interface _ASGalleryLayoutItem () +@property ASPrimitiveTraitCollection primitiveTraitCollection; + +@end + +@implementation _ASGalleryLayoutItem + +@synthesize style; + +- (instancetype)initWithItemSize:(CGSize)itemSize collectionElement:(ASCollectionElement *)collectionElement +{ + self = [super init]; + if (self) { + ASDisplayNodeAssert(! CGSizeEqualToSize(CGSizeZero, itemSize), @"Item size should not be zero"); + ASDisplayNodeAssertNotNil(collectionElement, @"Collection element should not be nil"); + _itemSize = itemSize; + _collectionElement = collectionElement; + } + return self; +} + +ASLayoutElementStyleExtensibilityForwarding + +- (ASTraitCollection *)asyncTraitCollection +{ + ASDisplayNodeAssertNotSupported(); + return nil; +} + +- (ASLayoutElementType)layoutElementType +{ + return ASLayoutElementTypeLayoutSpec; +} + +- (NSArray> *)sublayoutElements +{ + ASDisplayNodeAssertNotSupported(); + return nil; +} + +- (BOOL)implementsLayoutMethod +{ + return YES; +} + +ASLayoutElementLayoutCalculationDefaults + +- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize +{ + ASDisplayNodeAssert(CGSizeEqualToSize(_itemSize, ASSizeRangeClamp(constrainedSize, _itemSize)), + @"Item size %@ can't fit within the bounds of constrained size %@", NSStringFromCGSize(_itemSize), NSStringFromASSizeRange(constrainedSize)); + return [ASLayout layoutWithLayoutElement:self size:_itemSize]; +} + +#pragma mark - ASLayoutElementAsciiArtProtocol + +- (NSString *)asciiArtString +{ + return [ASLayoutSpec asciiArtStringForChildren:@[] parentName:[self asciiArtName]]; +} + +- (NSString *)asciiArtName +{ + return [NSMutableString stringWithCString:object_getClassName(self) encoding:NSASCIIStringEncoding]; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/_ASCoreAnimationExtras.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/_ASCoreAnimationExtras.h new file mode 100644 index 0000000..182bc35 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/_ASCoreAnimationExtras.h @@ -0,0 +1,84 @@ +// +// _ASCoreAnimationExtras.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import +#import + +// This protocol defines the core properties that ASDisplayNode and CALayer share, for managing contents. +@protocol ASResizableContents +@required +@property id contents; +@property CGRect contentsRect; +@property CGRect contentsCenter; +@property CGFloat contentsScale; +@property CGFloat rasterizationScale; +@property NSString *contentsGravity; +@end + +@interface CALayer (ASResizableContents) +@end +@interface ASDisplayNode (ASResizableContents) +@end + +/** + This function can operate on either an ASDisplayNode (including un-loaded) or CALayer directly. More info about resizing mode: https://github.com/TextureGroup/Texture/issues/439 + + @param obj ASDisplayNode, CALayer or object that conforms to `ASResizableContents` protocol + @param image Image you would like to resize + */ +ASDK_EXTERN void ASDisplayNodeSetResizableContents(id obj, UIImage *image); + +/** + Turns a value of UIViewContentMode to a string for debugging or serialization + @param contentMode Any of the UIViewContentMode constants + @return A human-readable representation of the constant, or the integer value of the constant if not recognized. + */ +ASDK_EXTERN NSString *ASDisplayNodeNSStringFromUIContentMode(UIViewContentMode contentMode); + +/** + Turns a string representing a contentMode into a contentMode + @param string Any of the strings in UIContentModeDescriptionLUT + @return Any of the UIViewContentMode constants, or an int if the string is a number. If the string is not recognized, UIViewContentModeScaleToFill is returned. + */ +ASDK_EXTERN UIViewContentMode ASDisplayNodeUIContentModeFromNSString(NSString *string); + +/** + Maps a value of UIViewContentMode to a corresponding contentsGravity + It is worth noting that UIKit and CA have inverse definitions of "top" and "bottom" on iOS, so the corresponding contentsGravity for UIViewContentModeTopLeft is kCAContentsGravityBottomLeft + @param contentMode A content mode except for UIViewContentModeRedraw, which has no corresponding contentsGravity (it corresponds to needsDisplayOnBoundsChange = YES) + @return An NSString constant from the documentation, eg kCAGravityCenter... or nil if there is no corresponding contentsGravity. Will assert if contentMode is unknown. + */ +ASDK_EXTERN NSString *const ASDisplayNodeCAContentsGravityFromUIContentMode(UIViewContentMode contentMode); + +/** + Maps a value of contentsGravity to a corresponding UIViewContentMode + It is worth noting that UIKit and CA have inverse definitions of "top" and "bottom" on iOS, so the corresponding contentMode for kCAContentsGravityBottomLeft is UIViewContentModeTopLeft + @param contentsGravity A contents gravity + @return A UIViewContentMode constant from UIView.h, eg UIViewContentModeCenter..., or UIViewContentModeScaleToFill if contentsGravity is not one of the CA constants. Will assert if the contentsGravity is unknown. + */ +ASDK_EXTERN UIViewContentMode ASDisplayNodeUIContentModeFromCAContentsGravity(NSString *const contentsGravity); + +/** + Use this to create a stretchable appropriate to approximate a filled rectangle, but with antialiasing on the edges when not pixel-aligned. It's best to keep the layer this image is added to with contentsScale equal to the scale of the final transform to screen space so it is able to antialias appropriately even when you shrink or grow the layer. + @param color the fill color to use in the center of the image + @param innerSize Unfortunately, 4 seems to be the smallest inner size that works if you're applying this stretchable to a larger box, whereas it does not display correctly for larger boxes. Thus some adjustment is necessary for the size of box you're displaying. If you're showing a 1px horizontal line, pass 1 height and at least 4 width. 2px vertical line: 2px wide, 4px high. Passing an innerSize greater that you desire is wasteful + */ +ASDK_EXTERN UIImage *ASDisplayNodeStretchableBoxContentsWithColor(UIColor *color, CGSize innerSize); + +/** + Checks whether a layer has ongoing animations + @param layer A layer to check if animations are ongoing + @return YES if the layer has ongoing animations, otherwise NO + */ +ASDK_EXTERN BOOL ASDisplayNodeLayerHasAnimations(CALayer *layer); + +// This function is a less generalized version of ASDisplayNodeSetResizableContents. +ASDK_EXTERN void ASDisplayNodeSetupLayerContentsWithResizableImage(CALayer *layer, UIImage *image) ASDISPLAYNODE_DEPRECATED; diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/_ASCoreAnimationExtras.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/_ASCoreAnimationExtras.mm new file mode 100644 index 0000000..e73c3ad --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/_ASCoreAnimationExtras.mm @@ -0,0 +1,186 @@ +// +// _ASCoreAnimationExtras.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +void ASDisplayNodeSetupLayerContentsWithResizableImage(CALayer *layer, UIImage *image) +{ + ASDisplayNodeSetResizableContents(layer, image); +} + +void ASDisplayNodeSetResizableContents(id obj, UIImage *image) +{ + // FIXME (https://github.com/TextureGroup/Texture/issues/1046): This method does not currently handle UIImageResizingModeTile, which is the default. + // See also https://developer.apple.com/documentation/uikit/uiimage/1624157-resizingmode?language=objc + // I'm not sure of a way to use CALayer directly to perform such tiling on the GPU, though the stretch is handled by the GPU, + // and CALayer.h documents the fact that contentsCenter is used to stretch the pixels. + + if (image) { + ASDisplayNodeCAssert(image.resizingMode == UIImageResizingModeStretch || UIEdgeInsetsEqualToEdgeInsets(image.capInsets, UIEdgeInsetsZero), + @"Image insets must be all-zero or resizingMode has to be UIImageResizingModeStretch. XCode assets default value is UIImageResizingModeTile which is not supported by Texture because of GPU-accelerated CALayer features."); + + // Image may not actually be stretchable in one or both dimensions; this is handled + obj.contents = (id)[image CGImage]; + obj.contentsScale = [image scale]; + obj.rasterizationScale = [image scale]; + CGSize imageSize = [image size]; + + UIEdgeInsets insets = [image capInsets]; + + // These are lifted from what UIImageView does by experimentation. Without these exact values, the stretching is slightly off. + const CGFloat halfPixelFudge = 0.49f; + const CGFloat otherPixelFudge = 0.02f; + // Convert to unit coordinates for the contentsCenter property. + CGRect contentsCenter = CGRectMake(0.0f, 0.0f, 1.0f, 1.0f); + if (insets.left > 0 || insets.right > 0) { + contentsCenter.origin.x = ((insets.left + halfPixelFudge) / imageSize.width); + contentsCenter.size.width = (imageSize.width - (insets.left + insets.right + 1.f) + otherPixelFudge) / imageSize.width; + } + if (insets.top > 0 || insets.bottom > 0) { + contentsCenter.origin.y = ((insets.top + halfPixelFudge) / imageSize.height); + contentsCenter.size.height = (imageSize.height - (insets.top + insets.bottom + 1.f) + otherPixelFudge) / imageSize.height; + } + obj.contentsGravity = kCAGravityResize; + obj.contentsCenter = contentsCenter; + + } else { + obj.contents = nil; + } +} + + +struct _UIContentModeStringLUTEntry { + UIViewContentMode contentMode; + NSString *const string; +}; + +static const _UIContentModeStringLUTEntry *UIContentModeCAGravityLUT(size_t *count) +{ + // Initialize this in a function (instead of at file level) to avoid + // startup initialization time. + static const _UIContentModeStringLUTEntry sUIContentModeCAGravityLUT[] = { + {UIViewContentModeScaleToFill, kCAGravityResize}, + {UIViewContentModeScaleAspectFit, kCAGravityResizeAspect}, + {UIViewContentModeScaleAspectFill, kCAGravityResizeAspectFill}, + {UIViewContentModeCenter, kCAGravityCenter}, + {UIViewContentModeTop, kCAGravityBottom}, + {UIViewContentModeBottom, kCAGravityTop}, + {UIViewContentModeLeft, kCAGravityLeft}, + {UIViewContentModeRight, kCAGravityRight}, + {UIViewContentModeTopLeft, kCAGravityBottomLeft}, + {UIViewContentModeTopRight, kCAGravityBottomRight}, + {UIViewContentModeBottomLeft, kCAGravityTopLeft}, + {UIViewContentModeBottomRight, kCAGravityTopRight}, + }; + *count = AS_ARRAY_SIZE(sUIContentModeCAGravityLUT); + return sUIContentModeCAGravityLUT; +} + +static const _UIContentModeStringLUTEntry *UIContentModeDescriptionLUT(size_t *count) +{ + // Initialize this in a function (instead of at file level) to avoid + // startup initialization time. + static const _UIContentModeStringLUTEntry sUIContentModeDescriptionLUT[] = { + {UIViewContentModeScaleToFill, @"scaleToFill"}, + {UIViewContentModeScaleAspectFit, @"aspectFit"}, + {UIViewContentModeScaleAspectFill, @"aspectFill"}, + {UIViewContentModeRedraw, @"redraw"}, + {UIViewContentModeCenter, @"center"}, + {UIViewContentModeTop, @"top"}, + {UIViewContentModeBottom, @"bottom"}, + {UIViewContentModeLeft, @"left"}, + {UIViewContentModeRight, @"right"}, + {UIViewContentModeTopLeft, @"topLeft"}, + {UIViewContentModeTopRight, @"topRight"}, + {UIViewContentModeBottomLeft, @"bottomLeft"}, + {UIViewContentModeBottomRight, @"bottomRight"}, + }; + *count = AS_ARRAY_SIZE(sUIContentModeDescriptionLUT); + return sUIContentModeDescriptionLUT; +} + +NSString *ASDisplayNodeNSStringFromUIContentMode(UIViewContentMode contentMode) +{ + size_t lutSize; + const _UIContentModeStringLUTEntry *lut = UIContentModeDescriptionLUT(&lutSize); + for (size_t i = 0; i < lutSize; ++i) { + if (lut[i].contentMode == contentMode) { + return lut[i].string; + } + } + return [NSString stringWithFormat:@"%d", (int)contentMode]; +} + +UIViewContentMode ASDisplayNodeUIContentModeFromNSString(NSString *string) +{ + size_t lutSize; + const _UIContentModeStringLUTEntry *lut = UIContentModeDescriptionLUT(&lutSize); + for (size_t i = 0; i < lutSize; ++i) { + if (ASObjectIsEqual(lut[i].string, string)) { + return lut[i].contentMode; + } + } + return UIViewContentModeScaleToFill; +} + +NSString *const ASDisplayNodeCAContentsGravityFromUIContentMode(UIViewContentMode contentMode) +{ + size_t lutSize; + const _UIContentModeStringLUTEntry *lut = UIContentModeCAGravityLUT(&lutSize); + for (size_t i = 0; i < lutSize; ++i) { + if (lut[i].contentMode == contentMode) { + return lut[i].string; + } + } + ASDisplayNodeCAssert(contentMode == UIViewContentModeRedraw, @"Encountered an unknown contentMode %ld. Is this a new version of iOS?", (long)contentMode); + // Redraw is ok to return nil. + return nil; +} + +#define ContentModeCacheSize 10 +UIViewContentMode ASDisplayNodeUIContentModeFromCAContentsGravity(NSString *const contentsGravity) +{ + static int currentCacheIndex = 0; + static NSMutableArray *cachedStrings = [NSMutableArray arrayWithCapacity:ContentModeCacheSize]; + static UIViewContentMode cachedModes[ContentModeCacheSize] = {}; + + NSInteger foundCacheIndex = [cachedStrings indexOfObjectIdenticalTo:contentsGravity]; + if (foundCacheIndex != NSNotFound && foundCacheIndex < ContentModeCacheSize) { + return cachedModes[foundCacheIndex]; + } + + size_t lutSize; + const _UIContentModeStringLUTEntry *lut = UIContentModeCAGravityLUT(&lutSize); + for (size_t i = 0; i < lutSize; ++i) { + if (ASObjectIsEqual(lut[i].string, contentsGravity)) { + UIViewContentMode foundContentMode = lut[i].contentMode; + + if (currentCacheIndex < ContentModeCacheSize) { + // Cache the input value. This is almost always a different pointer than in our LUT and will frequently + // be the same value for an overwhelming majority of inputs. + [cachedStrings addObject:contentsGravity]; + cachedModes[currentCacheIndex] = foundContentMode; + currentCacheIndex++; + } + + return foundContentMode; + } + } + + ASDisplayNodeCAssert(contentsGravity, @"Encountered an unknown contentsGravity \"%@\". Is this a new version of iOS?", contentsGravity); + ASDisplayNodeCAssert(!contentsGravity, @"You passed nil to ASDisplayNodeUIContentModeFromCAContentsGravity. We're falling back to resize, but this is probably a bug."); + // If asserts disabled, fall back to this + return UIViewContentModeScaleToFill; +} + +BOOL ASDisplayNodeLayerHasAnimations(CALayer *layer) +{ + return (layer.animationKeys.count != 0); +} diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/_ASHierarchyChangeSet.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/_ASHierarchyChangeSet.h new file mode 100644 index 0000000..66f5966 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/_ASHierarchyChangeSet.h @@ -0,0 +1,214 @@ +// +// _ASHierarchyChangeSet.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +typedef NSUInteger ASDataControllerAnimationOptions; + +typedef NS_ENUM(NSInteger, _ASHierarchyChangeType) { + /** + * A reload change, as submitted by the user. When a change set is + * completed, these changes are decomposed into delete-insert pairs + * and combined with the original deletes and inserts of the change. + */ + _ASHierarchyChangeTypeReload, + + /** + * A change that was either an original delete, or the first + * part of a decomposed reload. + */ + _ASHierarchyChangeTypeDelete, + + /** + * A change that was submitted by the user as a delete. + */ + _ASHierarchyChangeTypeOriginalDelete, + + /** + * A change that was either an original insert, or the second + * part of a decomposed reload. + */ + _ASHierarchyChangeTypeInsert, + + /** + * A change that was submitted by the user as an insert. + */ + _ASHierarchyChangeTypeOriginalInsert +}; + +/** + * Returns YES if the given change type is either .Insert or .Delete, NO otherwise. + * Other change types – .Reload, .OriginalInsert, .OriginalDelete – are + * intermediary types used while building the change set. All changes will + * be reduced to either .Insert or .Delete when the change is marked completed. + */ +BOOL ASHierarchyChangeTypeIsFinal(_ASHierarchyChangeType changeType); + +NSString *NSStringFromASHierarchyChangeType(_ASHierarchyChangeType changeType); + +@interface _ASHierarchySectionChange : NSObject + +// FIXME: Generalize this to `changeMetadata` dict? +@property (nonatomic, readonly) ASDataControllerAnimationOptions animationOptions; + +@property (nonatomic, readonly) NSIndexSet *indexSet; + +@property (nonatomic, readonly) _ASHierarchyChangeType changeType; + +/** + * If this is a .OriginalInsert or .OriginalDelete change, this returns a copied change + * with type .Insert or .Delete. Calling this on changes of other types is an error. + */ +- (_ASHierarchySectionChange *)changeByFinalizingType; + +@end + +@interface _ASHierarchyItemChange : NSObject + +@property (nonatomic, readonly) ASDataControllerAnimationOptions animationOptions; + +/// Index paths are sorted descending for changeType .Delete, ascending otherwise +@property (nonatomic, readonly) NSArray *indexPaths; + +@property (nonatomic, readonly) _ASHierarchyChangeType changeType; + ++ (NSDictionary *)sectionToIndexSetMapFromChanges:(NSArray<_ASHierarchyItemChange *> *)changes; + +/** + * If this is a .OriginalInsert or .OriginalDelete change, this returns a copied change + * with type .Insert or .Delete. Calling this on changes of other types is an error. + */ +- (_ASHierarchyItemChange *)changeByFinalizingType; + +@end + +@interface _ASHierarchyChangeSet : NSObject + +/// @precondition The change set must be completed. +@property (nonatomic, readonly) NSIndexSet *deletedSections; + +/// @precondition The change set must be completed. +@property (nonatomic, readonly) NSIndexSet *insertedSections; + +@property (nonatomic, readonly) BOOL completed; + +/// Whether or not changes should be animated. +// TODO: if any update in this chagne set is non-animated, the whole update should be non-animated. +@property (nonatomic) BOOL animated; + +@property (nonatomic, readonly) BOOL includesReloadData; + +/// Indicates whether the change set is empty, that is it includes neither reload data nor per item or section changes. +@property (nonatomic, readonly) BOOL isEmpty; + +/// The count of new ASCellNodes that can undergo async layout calculation. May be zero if all UIKit passthrough cells. +@property (nonatomic, assign) NSUInteger countForAsyncLayout; + +/// The top-level activity for this update. +@property (nonatomic, OS_ACTIVITY_NULLABLE) os_activity_t rootActivity; + +/// The activity for submitting this update i.e. between -beginUpdates and -endUpdates. +@property (nonatomic, OS_ACTIVITY_NULLABLE) os_activity_t submitActivity; + +- (instancetype)initWithOldData:(std::vector)oldItemCounts NS_DESIGNATED_INITIALIZER; + +/** + * Append the given completion handler to the combined @c completionHandler. + * + * @discussion Since batch updates can be nested, we have to support multiple + * completion handlers per update. + * + * @precondition The change set must not be completed. + */ +- (void)addCompletionHandler:(nullable void(^)(BOOL finished))completion; + +/** + * Execute the combined completion handler. + * + * @warning The completion block is discarded after reading because it may have captured + * significant resources that we would like to reclaim as soon as possible. + */ +- (void)executeCompletionHandlerWithFinished:(BOOL)finished; + +/** + * Get the section index after the update for the given section before the update. + * + * @precondition The change set must be completed. + * @return The new section index, or NSNotFound if the given section was deleted. + */ +- (NSUInteger)newSectionForOldSection:(NSUInteger)oldSection; + +/** + * A table that maps old section indexes to new section indexes. + */ +@property (nonatomic, readonly) ASIntegerMap *sectionMapping; + +/** + * A table that maps new section indexes to old section indexes. + */ +@property (nonatomic, readonly) ASIntegerMap *reverseSectionMapping; + +/** + * A table that provides the item mapping for the old section. If the section was deleted + * or is out of bounds, returns the empty table. + */ +- (ASIntegerMap *)itemMappingInSection:(NSInteger)oldSection; + +/** + * A table that provides the reverse item mapping for the new section. If the section was inserted + * or is out of bounds, returns the empty table. + */ +- (ASIntegerMap *)reverseItemMappingInSection:(NSInteger)newSection; + +/** + * Get the old item index path for the given new index path. + * + * @precondition The change set must be completed. + * @return The old index path, or nil if the given item was inserted. + */ +- (nullable NSIndexPath *)oldIndexPathForNewIndexPath:(NSIndexPath *)indexPath; + +/** + * Get the new item index path for the given old index path. + * + * @precondition The change set must be completed. + * @return The new index path, or nil if the given item was deleted. + */ +- (nullable NSIndexPath *)newIndexPathForOldIndexPath:(NSIndexPath *)indexPath; + +/// Call this once the change set has been constructed to prevent future modifications to the changeset. Calling this more than once is a programmer error. +/// NOTE: Calling this method will cause the changeset to convert all reloads into delete/insert pairs. +- (void)markCompletedWithNewItemCounts:(std::vector)newItemCounts; + +- (nullable NSArray <_ASHierarchySectionChange *> *)sectionChangesOfType:(_ASHierarchyChangeType)changeType; + +- (nullable NSArray <_ASHierarchyItemChange *> *)itemChangesOfType:(_ASHierarchyChangeType)changeType; + +/// Returns all item indexes affected by changes of the given type in the given section. +- (NSIndexSet *)indexesForItemChangesOfType:(_ASHierarchyChangeType)changeType inSection:(NSUInteger)section; + +- (void)reloadData; +- (void)deleteSections:(NSIndexSet *)sections animationOptions:(ASDataControllerAnimationOptions)options; +- (void)insertSections:(NSIndexSet *)sections animationOptions:(ASDataControllerAnimationOptions)options; +- (void)reloadSections:(NSIndexSet *)sections animationOptions:(ASDataControllerAnimationOptions)options; +- (void)insertItems:(NSArray *)indexPaths animationOptions:(ASDataControllerAnimationOptions)options; +- (void)deleteItems:(NSArray *)indexPaths animationOptions:(ASDataControllerAnimationOptions)options; +- (void)reloadItems:(NSArray *)indexPaths animationOptions:(ASDataControllerAnimationOptions)options; +- (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection animationOptions:(ASDataControllerAnimationOptions)options; +- (void)moveItemAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath animationOptions:(ASDataControllerAnimationOptions)options; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/_ASHierarchyChangeSet.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/_ASHierarchyChangeSet.mm new file mode 100644 index 0000000..825c6b8 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/_ASHierarchyChangeSet.mm @@ -0,0 +1,1009 @@ +// +// _ASHierarchyChangeSet.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import +#import +#import +#import +#import + +// If assertions are enabled and they haven't forced us to suppress the exception, +// then throw, otherwise log. +#if ASDISPLAYNODE_ASSERTIONS_ENABLED + #define ASFailUpdateValidation(...)\ + _Pragma("clang diagnostic push")\ + _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"")\ + if ([ASDisplayNode suppressesInvalidCollectionUpdateExceptions]) {\ + NSLog(__VA_ARGS__);\ + } else {\ + NSLog(__VA_ARGS__);\ + [NSException raise:ASCollectionInvalidUpdateException format:__VA_ARGS__];\ + }\ + _Pragma("clang diagnostic pop") +#else + #define ASFailUpdateValidation(...) NSLog(__VA_ARGS__); +#endif + +BOOL ASHierarchyChangeTypeIsFinal(_ASHierarchyChangeType changeType) { + switch (changeType) { + case _ASHierarchyChangeTypeInsert: + case _ASHierarchyChangeTypeDelete: + return YES; + default: + return NO; + } +} + +NSString *NSStringFromASHierarchyChangeType(_ASHierarchyChangeType changeType) +{ + switch (changeType) { + case _ASHierarchyChangeTypeInsert: + return @"Insert"; + case _ASHierarchyChangeTypeOriginalInsert: + return @"OriginalInsert"; + case _ASHierarchyChangeTypeDelete: + return @"Delete"; + case _ASHierarchyChangeTypeOriginalDelete: + return @"OriginalDelete"; + case _ASHierarchyChangeTypeReload: + return @"Reload"; + default: + return @"(invalid)"; + } +} + +@interface _ASHierarchySectionChange () +- (instancetype)initWithChangeType:(_ASHierarchyChangeType)changeType indexSet:(NSIndexSet *)indexSet animationOptions:(ASDataControllerAnimationOptions)animationOptions; + +/** + On return `changes` is sorted according to the change type with changes coalesced by animationOptions + Assumes: `changes` all have the same changeType + */ ++ (void)sortAndCoalesceSectionChanges:(NSMutableArray<_ASHierarchySectionChange *> *)changes; + +/// Returns all the indexes from all the `indexSet`s of the given `_ASHierarchySectionChange` objects. ++ (NSMutableIndexSet *)allIndexesInSectionChanges:(NSArray *)changes; + ++ (NSString *)smallDescriptionForSectionChanges:(NSArray<_ASHierarchySectionChange *> *)changes; +@end + +@interface _ASHierarchyItemChange () +- (instancetype)initWithChangeType:(_ASHierarchyChangeType)changeType indexPaths:(NSArray *)indexPaths animationOptions:(ASDataControllerAnimationOptions)animationOptions presorted:(BOOL)presorted; + +/** + On return `changes` is sorted according to the change type with changes coalesced by animationOptions + Assumes: `changes` all have the same changeType + */ ++ (void)sortAndCoalesceItemChanges:(NSMutableArray<_ASHierarchyItemChange *> *)changes ignoringChangesInSections:(NSIndexSet *)sections; + ++ (NSString *)smallDescriptionForItemChanges:(NSArray<_ASHierarchyItemChange *> *)changes; + ++ (void)ensureItemChanges:(NSArray<_ASHierarchyItemChange *> *)changes ofSameType:(_ASHierarchyChangeType)changeType; +@end + +@interface _ASHierarchyChangeSet () + +// array index is old section index, map goes oldItem -> newItem +@property (nonatomic, readonly) NSMutableArray *itemMappings; + +// array index is new section index, map goes newItem -> oldItem +@property (nonatomic, readonly) NSMutableArray *reverseItemMappings; + +@property (nonatomic, readonly) NSMutableArray<_ASHierarchyItemChange *> *insertItemChanges; +@property (nonatomic, readonly) NSMutableArray<_ASHierarchyItemChange *> *originalInsertItemChanges; + +@property (nonatomic, readonly) NSMutableArray<_ASHierarchyItemChange *> *deleteItemChanges; +@property (nonatomic, readonly) NSMutableArray<_ASHierarchyItemChange *> *originalDeleteItemChanges; + +@property (nonatomic, readonly) NSMutableArray<_ASHierarchyItemChange *> *reloadItemChanges; + +@property (nonatomic, readonly) NSMutableArray<_ASHierarchySectionChange *> *insertSectionChanges; +@property (nonatomic, readonly) NSMutableArray<_ASHierarchySectionChange *> *originalInsertSectionChanges; + +@property (nonatomic, readonly) NSMutableArray<_ASHierarchySectionChange *> *deleteSectionChanges; +@property (nonatomic, readonly) NSMutableArray<_ASHierarchySectionChange *> *originalDeleteSectionChanges; + +@property (nonatomic, readonly) NSMutableArray<_ASHierarchySectionChange *> *reloadSectionChanges; + +@end + +@implementation _ASHierarchyChangeSet { + NSUInteger _countForAsyncLayout; + std::vector _oldItemCounts; + std::vector _newItemCounts; + void (^_completionHandler)(BOOL finished); +} +@synthesize sectionMapping = _sectionMapping; +@synthesize reverseSectionMapping = _reverseSectionMapping; +@synthesize itemMappings = _itemMappings; +@synthesize reverseItemMappings = _reverseItemMappings; +@synthesize countForAsyncLayout = _countForAsyncLayout; + +- (instancetype)init +{ + ASFailUpdateValidation(@"_ASHierarchyChangeSet: -init is not supported. Call -initWithOldData:"); + return [self initWithOldData:std::vector()]; +} + +- (instancetype)initWithOldData:(std::vector)oldItemCounts +{ + self = [super init]; + if (self) { + _oldItemCounts = oldItemCounts; + + _originalInsertItemChanges = [[NSMutableArray alloc] init]; + _insertItemChanges = [[NSMutableArray alloc] init]; + _originalDeleteItemChanges = [[NSMutableArray alloc] init]; + _deleteItemChanges = [[NSMutableArray alloc] init]; + _reloadItemChanges = [[NSMutableArray alloc] init]; + + _originalInsertSectionChanges = [[NSMutableArray alloc] init]; + _insertSectionChanges = [[NSMutableArray alloc] init]; + _originalDeleteSectionChanges = [[NSMutableArray alloc] init]; + _deleteSectionChanges = [[NSMutableArray alloc] init]; + _reloadSectionChanges = [[NSMutableArray alloc] init]; + } + return self; +} + +#pragma mark External API + +- (BOOL)isEmpty +{ + return (! _includesReloadData) && (! [self _includesPerItemOrSectionChanges]); +} + +- (void)addCompletionHandler:(void (^)(BOOL))completion +{ + [self _ensureNotCompleted]; + if (completion == nil) { + return; + } + + void (^oldCompletionHandler)(BOOL finished) = _completionHandler; + _completionHandler = ^(BOOL finished) { + if (oldCompletionHandler != nil) { + oldCompletionHandler(finished); + } + completion(finished); + }; +} + +- (void)executeCompletionHandlerWithFinished:(BOOL)finished +{ + if (_completionHandler != nil) { + _completionHandler(finished); + _completionHandler = nil; + } +} + +- (void)markCompletedWithNewItemCounts:(std::vector)newItemCounts +{ + NSAssert(!_completed, @"Attempt to mark already-completed changeset as completed."); + _completed = YES; + _newItemCounts = newItemCounts; + [self _sortAndCoalesceChangeArrays]; + [self _validateUpdate]; +} + +- (NSArray *)sectionChangesOfType:(_ASHierarchyChangeType)changeType +{ + [self _ensureCompleted]; + switch (changeType) { + case _ASHierarchyChangeTypeInsert: + return _insertSectionChanges; + case _ASHierarchyChangeTypeReload: + return _reloadSectionChanges; + case _ASHierarchyChangeTypeDelete: + return _deleteSectionChanges; + case _ASHierarchyChangeTypeOriginalDelete: + return _originalDeleteSectionChanges; + case _ASHierarchyChangeTypeOriginalInsert: + return _originalInsertSectionChanges; + default: + NSAssert(NO, @"Request for section changes with invalid type: %lu", (long)changeType); + return nil; + } +} + +- (NSArray *)itemChangesOfType:(_ASHierarchyChangeType)changeType +{ + [self _ensureCompleted]; + switch (changeType) { + case _ASHierarchyChangeTypeInsert: + return _insertItemChanges; + case _ASHierarchyChangeTypeReload: + return _reloadItemChanges; + case _ASHierarchyChangeTypeDelete: + return _deleteItemChanges; + case _ASHierarchyChangeTypeOriginalInsert: + return _originalInsertItemChanges; + case _ASHierarchyChangeTypeOriginalDelete: + return _originalDeleteItemChanges; + default: + NSAssert(NO, @"Request for item changes with invalid type: %lu", (long)changeType); + return nil; + } +} + +- (NSIndexSet *)indexesForItemChangesOfType:(_ASHierarchyChangeType)changeType inSection:(NSUInteger)section +{ + [self _ensureCompleted]; + const auto result = [[NSMutableIndexSet alloc] init]; + for (_ASHierarchyItemChange *change in [self itemChangesOfType:changeType]) { + [result addIndexes:[NSIndexSet as_indexSetFromIndexPaths:change.indexPaths inSection:section]]; + } + return result; +} + +- (NSUInteger)newSectionForOldSection:(NSUInteger)oldSection +{ + return [self.sectionMapping integerForKey:oldSection]; +} + +- (NSUInteger)oldSectionForNewSection:(NSUInteger)newSection +{ + return [self.reverseSectionMapping integerForKey:newSection]; +} + +- (ASIntegerMap *)sectionMapping +{ + ASDisplayNodeAssertNotNil(_deletedSections, @"Cannot call %s before `markCompleted` returns.", sel_getName(_cmd)); + ASDisplayNodeAssertNotNil(_insertedSections, @"Cannot call %s before `markCompleted` returns.", sel_getName(_cmd)); + [self _ensureCompleted]; + if (_sectionMapping == nil) { + _sectionMapping = [ASIntegerMap mapForUpdateWithOldCount:_oldItemCounts.size() deleted:_deletedSections inserted:_insertedSections]; + } + return _sectionMapping; +} + +- (ASIntegerMap *)reverseSectionMapping +{ + if (_reverseSectionMapping == nil) { + _reverseSectionMapping = [self.sectionMapping inverseMap]; + } + return _reverseSectionMapping; +} + +- (NSMutableArray *)itemMappings +{ + [self _ensureCompleted]; + + if (_itemMappings == nil) { + _itemMappings = [[NSMutableArray alloc] init]; + const auto insertMap = [_ASHierarchyItemChange sectionToIndexSetMapFromChanges:_originalInsertItemChanges]; + const auto deleteMap = [_ASHierarchyItemChange sectionToIndexSetMapFromChanges:_originalDeleteItemChanges]; + NSInteger oldSection = 0; + for (NSInteger oldCount : _oldItemCounts) { + NSInteger newSection = [self newSectionForOldSection:oldSection]; + ASIntegerMap *table; + if (newSection == NSNotFound) { + table = ASIntegerMap.emptyMap; + } else { + table = [ASIntegerMap mapForUpdateWithOldCount:oldCount deleted:deleteMap[@(oldSection)] inserted:insertMap[@(newSection)]]; + } + _itemMappings[oldSection] = table; + oldSection++; + } + } + return _itemMappings; +} + +- (NSMutableArray *)reverseItemMappings +{ + [self _ensureCompleted]; + + if (_reverseItemMappings == nil) { + _reverseItemMappings = [[NSMutableArray alloc] init]; + for (NSInteger newSection = 0; newSection < _newItemCounts.size(); newSection++) { + NSInteger oldSection = [self oldSectionForNewSection:newSection]; + ASIntegerMap *table; + if (oldSection == NSNotFound) { + table = ASIntegerMap.emptyMap; + } else { + table = [[self itemMappingInSection:oldSection] inverseMap]; + } + _reverseItemMappings[newSection] = table; + } + } + return _reverseItemMappings; +} + +- (ASIntegerMap *)itemMappingInSection:(NSInteger)oldSection +{ + if (self.includesReloadData || oldSection >= _oldItemCounts.size()) { + return ASIntegerMap.emptyMap; + } + return self.itemMappings[oldSection]; +} + +- (ASIntegerMap *)reverseItemMappingInSection:(NSInteger)newSection +{ + if (self.includesReloadData || newSection >= _newItemCounts.size()) { + return ASIntegerMap.emptyMap; + } + return self.reverseItemMappings[newSection]; +} + +- (NSIndexPath *)oldIndexPathForNewIndexPath:(NSIndexPath *)indexPath +{ + [self _ensureCompleted]; + NSInteger newSection = indexPath.section; + NSInteger oldSection = [self oldSectionForNewSection:newSection]; + if (oldSection == NSNotFound) { + return nil; + } + NSInteger oldItem = [[self reverseItemMappingInSection:newSection] integerForKey:indexPath.item]; + if (oldItem == NSNotFound) { + return nil; + } + return [NSIndexPath indexPathForItem:oldItem inSection:oldSection]; +} + +- (NSIndexPath *)newIndexPathForOldIndexPath:(NSIndexPath *)indexPath +{ + [self _ensureCompleted]; + NSInteger oldSection = indexPath.section; + NSInteger newSection = [self newSectionForOldSection:oldSection]; + if (newSection == NSNotFound) { + return nil; + } + NSInteger newItem = [[self itemMappingInSection:oldSection] integerForKey:indexPath.item]; + if (newItem == NSNotFound) { + return nil; + } + return [NSIndexPath indexPathForItem:newItem inSection:newSection]; +} + +- (void)reloadData +{ + [self _ensureNotCompleted]; + NSAssert(_includesReloadData == NO, @"Attempt to reload data multiple times %@", self); + _includesReloadData = YES; +} + +- (void)deleteItems:(NSArray *)indexPaths animationOptions:(ASDataControllerAnimationOptions)options +{ + [self _ensureNotCompleted]; + _ASHierarchyItemChange *change = [[_ASHierarchyItemChange alloc] initWithChangeType:_ASHierarchyChangeTypeOriginalDelete indexPaths:indexPaths animationOptions:options presorted:NO]; + [_originalDeleteItemChanges addObject:change]; +} + +- (void)deleteSections:(NSIndexSet *)sections animationOptions:(ASDataControllerAnimationOptions)options +{ + [self _ensureNotCompleted]; + _ASHierarchySectionChange *change = [[_ASHierarchySectionChange alloc] initWithChangeType:_ASHierarchyChangeTypeOriginalDelete indexSet:sections animationOptions:options]; + [_originalDeleteSectionChanges addObject:change]; +} + +- (void)insertItems:(NSArray *)indexPaths animationOptions:(ASDataControllerAnimationOptions)options +{ + [self _ensureNotCompleted]; + _ASHierarchyItemChange *change = [[_ASHierarchyItemChange alloc] initWithChangeType:_ASHierarchyChangeTypeOriginalInsert indexPaths:indexPaths animationOptions:options presorted:NO]; + [_originalInsertItemChanges addObject:change]; +} + +- (void)insertSections:(NSIndexSet *)sections animationOptions:(ASDataControllerAnimationOptions)options +{ + [self _ensureNotCompleted]; + _ASHierarchySectionChange *change = [[_ASHierarchySectionChange alloc] initWithChangeType:_ASHierarchyChangeTypeOriginalInsert indexSet:sections animationOptions:options]; + [_originalInsertSectionChanges addObject:change]; +} + +- (void)reloadItems:(NSArray *)indexPaths animationOptions:(ASDataControllerAnimationOptions)options +{ + [self _ensureNotCompleted]; + _ASHierarchyItemChange *change = [[_ASHierarchyItemChange alloc] initWithChangeType:_ASHierarchyChangeTypeReload indexPaths:indexPaths animationOptions:options presorted:NO]; + [_reloadItemChanges addObject:change]; +} + +- (void)reloadSections:(NSIndexSet *)sections animationOptions:(ASDataControllerAnimationOptions)options +{ + [self _ensureNotCompleted]; + _ASHierarchySectionChange *change = [[_ASHierarchySectionChange alloc] initWithChangeType:_ASHierarchyChangeTypeReload indexSet:sections animationOptions:options]; + [_reloadSectionChanges addObject:change]; +} + +- (void)moveItemAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath animationOptions:(ASDataControllerAnimationOptions)options +{ + /** + * TODO: Proper move implementation. + */ + [self deleteItems:@[ indexPath ] animationOptions:options]; + [self insertItems:@[ newIndexPath ] animationOptions:options]; +} + +- (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection animationOptions:(ASDataControllerAnimationOptions)options +{ + /** + * TODO: Proper move implementation. + */ + [self deleteSections:[NSIndexSet indexSetWithIndex:section] animationOptions:options]; + [self insertSections:[NSIndexSet indexSetWithIndex:newSection] animationOptions:options]; +} + +#pragma mark Private + +- (BOOL)_ensureNotCompleted +{ + NSAssert(!_completed, @"Attempt to modify completed changeset %@", self); + return !_completed; +} + +- (BOOL)_ensureCompleted +{ + NSAssert(_completed, @"Attempt to process incomplete changeset %@", self); + return _completed; +} + +- (void)_sortAndCoalesceChangeArrays +{ + if (_includesReloadData) { + return; + } + + @autoreleasepool { + + // Split reloaded sections into [delete(oldIndex), insert(newIndex)] + + // Give these their "pre-reloads" values. Once we add in the reloads we'll re-process them. + _deletedSections = [_ASHierarchySectionChange allIndexesInSectionChanges:_originalDeleteSectionChanges]; + _insertedSections = [_ASHierarchySectionChange allIndexesInSectionChanges:_originalInsertSectionChanges]; + for (_ASHierarchySectionChange *originalDeleteSectionChange in _originalDeleteSectionChanges) { + [_deleteSectionChanges addObject:[originalDeleteSectionChange changeByFinalizingType]]; + } + for (_ASHierarchySectionChange *originalInsertSectionChange in _originalInsertSectionChanges) { + [_insertSectionChanges addObject:[originalInsertSectionChange changeByFinalizingType]]; + } + + for (_ASHierarchySectionChange *change in _reloadSectionChanges) { + NSIndexSet *newSections = [change.indexSet as_indexesByMapping:^(NSUInteger idx) { + NSUInteger newSec = [self newSectionForOldSection:idx]; + ASDisplayNodeAssert(newSec != NSNotFound, @"Request to reload and delete same section %tu", idx); + return newSec; + }]; + + _ASHierarchySectionChange *deleteChange = [[_ASHierarchySectionChange alloc] initWithChangeType:_ASHierarchyChangeTypeDelete indexSet:change.indexSet animationOptions:change.animationOptions]; + [_deleteSectionChanges addObject:deleteChange]; + + _ASHierarchySectionChange *insertChange = [[_ASHierarchySectionChange alloc] initWithChangeType:_ASHierarchyChangeTypeInsert indexSet:newSections animationOptions:change.animationOptions]; + [_insertSectionChanges addObject:insertChange]; + } + + [_ASHierarchySectionChange sortAndCoalesceSectionChanges:_deleteSectionChanges]; + [_ASHierarchySectionChange sortAndCoalesceSectionChanges:_insertSectionChanges]; + _deletedSections = [_ASHierarchySectionChange allIndexesInSectionChanges:_deleteSectionChanges]; + _insertedSections = [_ASHierarchySectionChange allIndexesInSectionChanges:_insertSectionChanges]; + + // Split reloaded items into [delete(oldIndexPath), insert(newIndexPath)] + for (_ASHierarchyItemChange *originalDeleteItemChange in _originalDeleteItemChanges) { + [_deleteItemChanges addObject:[originalDeleteItemChange changeByFinalizingType]]; + } + for (_ASHierarchyItemChange *originalInsertItemChange in _originalInsertItemChanges) { + [_insertItemChanges addObject:[originalInsertItemChange changeByFinalizingType]]; + } + + [_ASHierarchyItemChange ensureItemChanges:_insertItemChanges ofSameType:_ASHierarchyChangeTypeInsert]; + [_ASHierarchyItemChange ensureItemChanges:_deleteItemChanges ofSameType:_ASHierarchyChangeTypeDelete]; + + for (_ASHierarchyItemChange *change in _reloadItemChanges) { + NSAssert(change.changeType == _ASHierarchyChangeTypeReload, @"It must be a reload change to be in here"); + + const auto newIndexPaths = ASArrayByFlatMapping(change.indexPaths, NSIndexPath *indexPath, [self newIndexPathForOldIndexPath:indexPath]); + + // All reload changes are translated into deletes and inserts + // We delete the items that needs reload together with other deleted items, at their original index + _ASHierarchyItemChange *deleteItemChangeFromReloadChange = [[_ASHierarchyItemChange alloc] initWithChangeType:_ASHierarchyChangeTypeDelete indexPaths:change.indexPaths animationOptions:change.animationOptions presorted:NO]; + [_deleteItemChanges addObject:deleteItemChangeFromReloadChange]; + // We insert the items that needs reload together with other inserted items, at their future index + _ASHierarchyItemChange *insertItemChangeFromReloadChange = [[_ASHierarchyItemChange alloc] initWithChangeType:_ASHierarchyChangeTypeInsert indexPaths:newIndexPaths animationOptions:change.animationOptions presorted:NO]; + [_insertItemChanges addObject:insertItemChangeFromReloadChange]; + } + + // Ignore item deletes in reloaded/deleted sections. + [_ASHierarchyItemChange sortAndCoalesceItemChanges:_deleteItemChanges ignoringChangesInSections:_deletedSections]; + + // Ignore item inserts in reloaded(new)/inserted sections. + [_ASHierarchyItemChange sortAndCoalesceItemChanges:_insertItemChanges ignoringChangesInSections:_insertedSections]; + } +} + +- (void)_validateUpdate +{ + // If reloadData exists, ignore other changes + if (_includesReloadData) { + if ([self _includesPerItemOrSectionChanges]) { + NSLog(@"Warning: A reload data shouldn't be used in conjuntion with other updates."); + } + return; + } + + NSIndexSet *allReloadedSections = [_ASHierarchySectionChange allIndexesInSectionChanges:_reloadSectionChanges]; + + NSInteger newSectionCount = _newItemCounts.size(); + NSInteger oldSectionCount = _oldItemCounts.size(); + + NSInteger insertedSectionCount = _insertedSections.count; + NSInteger deletedSectionCount = _deletedSections.count; + // Assert that the new section count is correct. + if (newSectionCount != oldSectionCount + insertedSectionCount - deletedSectionCount) { + ASFailUpdateValidation(@"Invalid number of sections. The number of sections after the update (%ld) must be equal to the number of sections before the update (%ld) plus or minus the number of sections inserted or deleted (%ld inserted, %ld deleted)", (long)newSectionCount, (long)oldSectionCount, (long)insertedSectionCount, (long)deletedSectionCount); + return; + } + + // Assert that no invalid deletes/reloads happened. + NSInteger invalidSectionDelete = NSNotFound; + if (oldSectionCount == 0) { + invalidSectionDelete = _deletedSections.firstIndex; + } else { + invalidSectionDelete = [_deletedSections indexGreaterThanIndex:oldSectionCount - 1]; + } + if (invalidSectionDelete != NSNotFound) { + ASFailUpdateValidation(@"Attempt to delete section %ld but there are only %ld sections before the update.", (long)invalidSectionDelete, (long)oldSectionCount); + return; + } + + for (_ASHierarchyItemChange *change in _deleteItemChanges) { + for (NSIndexPath *indexPath in change.indexPaths) { + // Assert that item delete happened in a valid section. + NSInteger section = indexPath.section; + NSInteger item = indexPath.item; + if (section >= oldSectionCount) { + ASFailUpdateValidation(@"Attempt to delete item %ld from section %ld, but there are only %ld sections before the update.", (long)item, (long)section, (long)oldSectionCount); + return; + } + + // Assert that item delete happened to a valid item. + NSInteger oldItemCount = _oldItemCounts[section]; + if (item >= oldItemCount) { + ASFailUpdateValidation(@"Attempt to delete item %ld from section %ld, which only contains %ld items before the update.", (long)item, (long)section, (long)oldItemCount); + return; + } + } + } + + for (_ASHierarchyItemChange *change in _insertItemChanges) { + for (NSIndexPath *indexPath in change.indexPaths) { + NSInteger section = indexPath.section; + NSInteger item = indexPath.item; + // Assert that item insert happened in a valid section. + if (section >= newSectionCount) { + ASFailUpdateValidation(@"Attempt to insert item %ld into section %ld, but there are only %ld sections after the update.", (long)item, (long)section, (long)newSectionCount); + return; + } + + // Assert that item delete happened to a valid item. + NSInteger newItemCount = _newItemCounts[section]; + if (item >= newItemCount) { + ASFailUpdateValidation(@"Attempt to insert item %ld into section %ld, which only contains %ld items after the update.", (long)item, (long)section, (long)newItemCount); + return; + } + } + } + + // Assert that no sections were inserted out of bounds. + NSInteger invalidSectionInsert = NSNotFound; + if (newSectionCount == 0) { + invalidSectionInsert = _insertedSections.firstIndex; + } else { + invalidSectionInsert = [_insertedSections indexGreaterThanIndex:newSectionCount - 1]; + } + if (invalidSectionInsert != NSNotFound) { + ASFailUpdateValidation(@"Attempt to insert section %ld but there are only %ld sections after the update.", (long)invalidSectionInsert, (long)newSectionCount); + return; + } + + for (NSUInteger oldSection = 0; oldSection < oldSectionCount; oldSection++) { + NSInteger oldItemCount = _oldItemCounts[oldSection]; + // If section was reloaded, ignore. + if ([allReloadedSections containsIndex:oldSection]) { + continue; + } + + // If section was deleted, ignore. + NSUInteger newSection = [self newSectionForOldSection:oldSection]; + if (newSection == NSNotFound) { + continue; + } + + NSIndexSet *originalInsertedItems = [self indexesForItemChangesOfType:_ASHierarchyChangeTypeOriginalInsert inSection:newSection]; + NSIndexSet *originalDeletedItems = [self indexesForItemChangesOfType:_ASHierarchyChangeTypeOriginalDelete inSection:oldSection]; + NSIndexSet *reloadedItems = [self indexesForItemChangesOfType:_ASHierarchyChangeTypeReload inSection:oldSection]; + + // Assert that no reloaded items were deleted. + NSInteger deletedReloadedItem = [originalDeletedItems as_intersectionWithIndexes:reloadedItems].firstIndex; + if (deletedReloadedItem != NSNotFound) { + ASFailUpdateValidation(@"Attempt to delete and reload the same item at index path %@", [NSIndexPath indexPathForItem:deletedReloadedItem inSection:oldSection]); + return; + } + + // Assert that the new item count is correct. + NSInteger newItemCount = _newItemCounts[newSection]; + NSInteger insertedItemCount = originalInsertedItems.count; + NSInteger deletedItemCount = originalDeletedItems.count; + if (newItemCount != oldItemCount + insertedItemCount - deletedItemCount) { + ASFailUpdateValidation(@"Invalid number of items in section %ld. The number of items after the update (%ld) must be equal to the number of items before the update (%ld) plus or minus the number of items inserted or deleted (%ld inserted, %ld deleted).", (long)oldSection, (long)newItemCount, (long)oldItemCount, (long)insertedItemCount, (long)deletedItemCount); + return; + } + } +} + +- (BOOL)_includesPerItemOrSectionChanges +{ + return 0 < (_originalDeleteSectionChanges.count + _originalDeleteItemChanges.count + +_originalInsertSectionChanges.count + _originalInsertItemChanges.count + + _reloadSectionChanges.count + _reloadItemChanges.count); +} + +#pragma mark - Debugging (Private) + +- (NSString *)description +{ + return ASObjectDescriptionMakeWithoutObject([self propertiesForDescription]); +} + +- (NSString *)debugDescription +{ + return ASObjectDescriptionMake(self, [self propertiesForDebugDescription]); +} + +- (NSMutableArray *)propertiesForDescription +{ + NSMutableArray *result = [NSMutableArray array]; + if (_includesReloadData) { + [result addObject:@{ @"reloadData" : @"YES" }]; + } + if (_reloadSectionChanges.count > 0) { + [result addObject:@{ @"reloadSections" : [_ASHierarchySectionChange smallDescriptionForSectionChanges:_reloadSectionChanges] }]; + } + if (_reloadItemChanges.count > 0) { + [result addObject:@{ @"reloadItems" : [_ASHierarchyItemChange smallDescriptionForItemChanges:_reloadItemChanges] }]; + } + if (_originalDeleteSectionChanges.count > 0) { + [result addObject:@{ @"deleteSections" : [_ASHierarchySectionChange smallDescriptionForSectionChanges:_originalDeleteSectionChanges] }]; + } + if (_originalDeleteItemChanges.count > 0) { + [result addObject:@{ @"deleteItems" : [_ASHierarchyItemChange smallDescriptionForItemChanges:_originalDeleteItemChanges] }]; + } + if (_originalInsertSectionChanges.count > 0) { + [result addObject:@{ @"insertSections" : [_ASHierarchySectionChange smallDescriptionForSectionChanges:_originalInsertSectionChanges] }]; + } + if (_originalInsertItemChanges.count > 0) { + [result addObject:@{ @"insertItems" : [_ASHierarchyItemChange smallDescriptionForItemChanges:_originalInsertItemChanges] }]; + } + return result; +} + +- (NSMutableArray *)propertiesForDebugDescription +{ + return [self propertiesForDescription]; +} + +@end + +@implementation _ASHierarchySectionChange + +- (instancetype)initWithChangeType:(_ASHierarchyChangeType)changeType indexSet:(NSIndexSet *)indexSet animationOptions:(ASDataControllerAnimationOptions)animationOptions +{ + self = [super init]; + if (self) { + ASDisplayNodeAssert(indexSet.count > 0, @"Request to create _ASHierarchySectionChange with no sections!"); + _changeType = changeType; + _indexSet = indexSet; + _animationOptions = animationOptions; + } + return self; +} + +- (_ASHierarchySectionChange *)changeByFinalizingType +{ + _ASHierarchyChangeType newType; + switch (_changeType) { + case _ASHierarchyChangeTypeOriginalInsert: + newType = _ASHierarchyChangeTypeInsert; + break; + case _ASHierarchyChangeTypeOriginalDelete: + newType = _ASHierarchyChangeTypeDelete; + break; + default: + ASFailUpdateValidation(@"Attempt to finalize section change of invalid type %@.", NSStringFromASHierarchyChangeType(_changeType)); + return self; + } + return [[_ASHierarchySectionChange alloc] initWithChangeType:newType indexSet:_indexSet animationOptions:_animationOptions]; +} + ++ (void)sortAndCoalesceSectionChanges:(NSMutableArray<_ASHierarchySectionChange *> *)changes +{ + _ASHierarchySectionChange *firstChange = changes.firstObject; + if (firstChange == nil) { + return; + } + _ASHierarchyChangeType type = [firstChange changeType]; + + ASDisplayNodeAssert(ASHierarchyChangeTypeIsFinal(type), @"Attempt to sort and coalesce section changes of intermediary type %@. Why?", NSStringFromASHierarchyChangeType(type)); + + // Lookup table [Int: AnimationOptions] + __block std::unordered_map animationOptions; + + // All changed indexes + NSMutableIndexSet *allIndexes = [NSMutableIndexSet new]; + + for (_ASHierarchySectionChange *change in changes) { + ASDataControllerAnimationOptions options = change.animationOptions; + NSIndexSet *indexes = change.indexSet; + [indexes enumerateRangesUsingBlock:^(NSRange range, BOOL * _Nonnull stop) { + for (NSUInteger i = range.location; i < NSMaxRange(range); i++) { + animationOptions[i] = options; + } + }]; + [allIndexes addIndexes:indexes]; + } + + // Create new changes by grouping sorted changes by animation option + NSMutableArray *result = [[NSMutableArray alloc] init]; + + __block ASDataControllerAnimationOptions currentOptions = 0; + const auto currentIndexes = [[NSMutableIndexSet alloc] init]; + + BOOL reverse = type == _ASHierarchyChangeTypeDelete || type == _ASHierarchyChangeTypeOriginalDelete; + NSEnumerationOptions options = reverse ? NSEnumerationReverse : kNilOptions; + + [allIndexes enumerateRangesWithOptions:options usingBlock:^(NSRange range, BOOL * _Nonnull stop) { + NSInteger increment = reverse ? -1 : 1; + NSUInteger start = reverse ? NSMaxRange(range) - 1 : range.location; + NSInteger limit = reverse ? range.location - 1 : NSMaxRange(range); + for (NSInteger i = start; i != limit; i += increment) { + ASDataControllerAnimationOptions options = animationOptions[i]; + + // End the previous group if needed. + if (options != currentOptions && currentIndexes.count > 0) { + _ASHierarchySectionChange *change = [[_ASHierarchySectionChange alloc] initWithChangeType:type indexSet:[currentIndexes copy] animationOptions:currentOptions]; + [result addObject:change]; + [currentIndexes removeAllIndexes]; + } + + // Start a new group if needed. + if (currentIndexes.count == 0) { + currentOptions = options; + } + + [currentIndexes addIndex:i]; + } + }]; + + // Finish up the last group. + if (currentIndexes.count > 0) { + _ASHierarchySectionChange *change = [[_ASHierarchySectionChange alloc] initWithChangeType:type indexSet:[currentIndexes copy] animationOptions:currentOptions]; + [result addObject:change]; + } + + [changes setArray:result]; +} + ++ (NSMutableIndexSet *)allIndexesInSectionChanges:(NSArray<_ASHierarchySectionChange *> *)changes +{ + const auto indexes = [[NSMutableIndexSet alloc] init]; + for (_ASHierarchySectionChange *change in changes) { + [indexes addIndexes:change.indexSet]; + } + return indexes; +} + +#pragma mark - Debugging (Private) + ++ (NSString *)smallDescriptionForSectionChanges:(NSArray<_ASHierarchySectionChange *> *)changes +{ + NSMutableIndexSet *unionIndexSet = [NSMutableIndexSet indexSet]; + for (_ASHierarchySectionChange *change in changes) { + [unionIndexSet addIndexes:change.indexSet]; + } + return [unionIndexSet as_smallDescription]; +} + +- (NSString *)description +{ + return ASObjectDescriptionMake(self, [self propertiesForDescription]); +} + +- (NSString *)debugDescription +{ + return ASObjectDescriptionMake(self, [self propertiesForDebugDescription]); +} + +- (NSString *)smallDescription +{ + return [self.indexSet as_smallDescription]; +} + +- (NSMutableArray *)propertiesForDescription +{ + NSMutableArray *result = [NSMutableArray array]; + [result addObject:@{ @"indexes" : [self.indexSet as_smallDescription] }]; + return result; +} + +- (NSMutableArray *)propertiesForDebugDescription +{ + NSMutableArray *result = [NSMutableArray array]; + [result addObject:@{ @"anim" : @(_animationOptions) }]; + [result addObject:@{ @"type" : NSStringFromASHierarchyChangeType(_changeType) }]; + [result addObject:@{ @"indexes" : self.indexSet }]; + return result; +} + +@end + +@implementation _ASHierarchyItemChange + +- (instancetype)initWithChangeType:(_ASHierarchyChangeType)changeType indexPaths:(NSArray *)indexPaths animationOptions:(ASDataControllerAnimationOptions)animationOptions presorted:(BOOL)presorted +{ + self = [super init]; + if (self) { + ASDisplayNodeAssert(indexPaths.count > 0, @"Request to create _ASHierarchyItemChange with no items!"); + _changeType = changeType; + if (presorted) { + _indexPaths = indexPaths; + } else { + SEL sorting = changeType == _ASHierarchyChangeTypeDelete ? @selector(asdk_inverseCompare:) : @selector(compare:); + _indexPaths = [indexPaths sortedArrayUsingSelector:sorting]; + } + _animationOptions = animationOptions; + } + return self; +} + +// Create a mapping out of changes indexPaths to a {@section : [indexSet]} fashion +// e.g. changes: (0 - 0), (0 - 1), (2 - 5) +// will become: {@0 : [0, 1], @2 : [5]} ++ (NSDictionary *)sectionToIndexSetMapFromChanges:(NSArray<_ASHierarchyItemChange *> *)changes +{ + NSMutableDictionary *sectionToIndexSetMap = [[NSMutableDictionary alloc] init]; + for (_ASHierarchyItemChange *change in changes) { + for (NSIndexPath *indexPath in change.indexPaths) { + NSNumber *sectionKey = @(indexPath.section); + NSMutableIndexSet *indexSet = sectionToIndexSetMap[sectionKey]; + if (indexSet) { + [indexSet addIndex:indexPath.item]; + } else { + indexSet = [NSMutableIndexSet indexSetWithIndex:indexPath.item]; + sectionToIndexSetMap[sectionKey] = indexSet; + } + } + } + return sectionToIndexSetMap; +} + ++ (void)ensureItemChanges:(NSArray<_ASHierarchyItemChange *> *)changes ofSameType:(_ASHierarchyChangeType)changeType +{ +#if ASDISPLAYNODE_ASSERTIONS_ENABLED + for (_ASHierarchyItemChange *change in changes) { + NSAssert(change.changeType == changeType, @"The map we created must all be of the same changeType as of now"); + } +#endif +} + +- (_ASHierarchyItemChange *)changeByFinalizingType +{ + _ASHierarchyChangeType newType; + switch (_changeType) { + case _ASHierarchyChangeTypeOriginalInsert: + newType = _ASHierarchyChangeTypeInsert; + break; + case _ASHierarchyChangeTypeOriginalDelete: + newType = _ASHierarchyChangeTypeDelete; + break; + default: + ASFailUpdateValidation(@"Attempt to finalize item change of invalid type %@.", NSStringFromASHierarchyChangeType(_changeType)); + return self; + } + return [[_ASHierarchyItemChange alloc] initWithChangeType:newType indexPaths:_indexPaths animationOptions:_animationOptions presorted:YES]; +} + ++ (void)sortAndCoalesceItemChanges:(NSMutableArray<_ASHierarchyItemChange *> *)changes ignoringChangesInSections:(NSIndexSet *)ignoredSections +{ + if (changes.count < 1) { + return; + } + + _ASHierarchyChangeType type = [changes.firstObject changeType]; + ASDisplayNodeAssert(ASHierarchyChangeTypeIsFinal(type), @"Attempt to sort and coalesce item changes of intermediary type %@. Why?", NSStringFromASHierarchyChangeType(type)); + + // Lookup table [NSIndexPath: AnimationOptions] + const auto animationOptions = [[NSMutableDictionary alloc] init]; + + // All changed index paths, sorted + const auto allIndexPaths = [[NSMutableArray alloc] init]; + + for (_ASHierarchyItemChange *change in changes) { + for (NSIndexPath *indexPath in change.indexPaths) { + if (![ignoredSections containsIndex:indexPath.section]) { + animationOptions[indexPath] = @(change.animationOptions); + [allIndexPaths addObject:indexPath]; + } + } + } + + SEL sorting = type == _ASHierarchyChangeTypeDelete ? @selector(asdk_inverseCompare:) : @selector(compare:); + [allIndexPaths sortUsingSelector:sorting]; + + // Create new changes by grouping sorted changes by animation option + const auto result = [[NSMutableArray<_ASHierarchyItemChange *> alloc] init]; + + ASDataControllerAnimationOptions currentOptions = 0; + const auto currentIndexPaths = [[NSMutableArray alloc] init]; + + for (NSIndexPath *indexPath in allIndexPaths) { + ASDataControllerAnimationOptions options = [animationOptions[indexPath] integerValue]; + + // End the previous group if needed. + if (options != currentOptions && currentIndexPaths.count > 0) { + _ASHierarchyItemChange *change = [[_ASHierarchyItemChange alloc] initWithChangeType:type indexPaths:[currentIndexPaths copy] animationOptions:currentOptions presorted:YES]; + [result addObject:change]; + [currentIndexPaths removeAllObjects]; + } + + // Start a new group if needed. + if (currentIndexPaths.count == 0) { + currentOptions = options; + } + + [currentIndexPaths addObject:indexPath]; + } + + // Finish up the last group. + if (currentIndexPaths.count > 0) { + _ASHierarchyItemChange *change = [[_ASHierarchyItemChange alloc] initWithChangeType:type indexPaths:[currentIndexPaths copy] animationOptions:currentOptions presorted:YES]; + [result addObject:change]; + } + + [changes setArray:result]; +} + +#pragma mark - Debugging (Private) + ++ (NSString *)smallDescriptionForItemChanges:(NSArray<_ASHierarchyItemChange *> *)changes +{ + NSDictionary *map = [self sectionToIndexSetMapFromChanges:changes]; + NSMutableString *str = [NSMutableString stringWithString:@"{ "]; + [map enumerateKeysAndObjectsUsingBlock:^(NSNumber * _Nonnull section, NSIndexSet * _Nonnull indexSet, BOOL * _Nonnull stop) { + [str appendFormat:@"@%lu : %@ ", (long)section.integerValue, [indexSet as_smallDescription]]; + }]; + [str appendString:@"}"]; + return str; +} + +- (NSString *)description +{ + return ASObjectDescriptionMake(self, [self propertiesForDescription]); +} + +- (NSString *)debugDescription +{ + return ASObjectDescriptionMake(self, [self propertiesForDebugDescription]); +} + +- (NSMutableArray *)propertiesForDescription +{ + NSMutableArray *result = [NSMutableArray array]; + [result addObject:@{ @"indexPaths" : self.indexPaths }]; + return result; +} + +- (NSMutableArray *)propertiesForDebugDescription +{ + NSMutableArray *result = [NSMutableArray array]; + [result addObject:@{ @"anim" : @(_animationOptions) }]; + [result addObject:@{ @"type" : NSStringFromASHierarchyChangeType(_changeType) }]; + [result addObject:@{ @"indexPaths" : self.indexPaths }]; + return result; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/_ASPendingState.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/_ASPendingState.h new file mode 100644 index 0000000..0a96e7a --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/_ASPendingState.h @@ -0,0 +1,41 @@ +// +// _ASPendingState.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import + +/** + + Private header for ASDisplayNode.mm + + _ASPendingState is a proxy for a UIView that has yet to be created. + In response to its setters, it sets an internal property and a flag that indicates that that property has been set. + + When you want to configure a view from this pending state information, just call -applyToView: + */ + +@interface _ASPendingState : NSObject + +// Supports all of the properties included in the ASDisplayNodeViewProperties protocol + +- (void)applyToView:(UIView *)view withSpecialPropertiesHandling:(BOOL)setFrameDirectly; +- (void)applyToLayer:(CALayer *)layer; + ++ (_ASPendingState *)pendingViewStateFromLayer:(CALayer *)layer; ++ (_ASPendingState *)pendingViewStateFromView:(UIView *)view; + +@property (nonatomic, readonly) BOOL hasSetNeedsLayout; +@property (nonatomic, readonly) BOOL hasSetNeedsDisplay; + +@property (nonatomic, readonly) BOOL hasChanges; + +- (void)clearChanges; + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/_ASPendingState.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/_ASPendingState.mm new file mode 100644 index 0000000..a9d760e --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/_ASPendingState.mm @@ -0,0 +1,1423 @@ +// +// _ASPendingState.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import +#import +#import + +#define __shouldSetNeedsDisplayForView(view) (flags.needsDisplay \ + || (flags.setOpaque && _flags.opaque != (view).opaque)\ + || (flags.setBackgroundColor && ![backgroundColor isEqual:(view).backgroundColor])\ + || (flags.setTintColor && ![tintColor isEqual:(view).tintColor])) + +#define __shouldSetNeedsDisplayForLayer(layer) (flags.needsDisplay \ + || (flags.setOpaque && _flags.opaque != (layer).opaque)\ + || (flags.setBackgroundColor && ![backgroundColor isEqual:[UIColor colorWithCGColor:(layer).backgroundColor]])) + +typedef struct { + // Properties + int needsDisplay:1; + int needsLayout:1; + int layoutIfNeeded:1; + + // Flags indicating that a given property should be applied to the view at creation + int setClipsToBounds:1; + int setOpaque:1; + int setNeedsDisplayOnBoundsChange:1; + int setAutoresizesSubviews:1; + int setAutoresizingMask:1; + int setFrame:1; + int setBounds:1; + int setBackgroundColor:1; + int setTintColor:1; + int setHidden:1; + int setAlpha:1; + int setCornerRadius:1; + int setContentMode:1; + int setNeedsDisplay:1; + int setAnchorPoint:1; + int setPosition:1; + int setZPosition:1; + int setTransform:1; + int setSublayerTransform:1; + int setContents:1; + int setContentsGravity:1; + int setContentsRect:1; + int setContentsCenter:1; + int setContentsScale:1; + int setRasterizationScale:1; + int setUserInteractionEnabled:1; + int setExclusiveTouch:1; + int setShadowColor:1; + int setShadowOpacity:1; + int setShadowOffset:1; + int setShadowRadius:1; + int setBorderWidth:1; + int setBorderColor:1; + int setAsyncTransactionContainer:1; + int setAllowsGroupOpacity:1; + int setAllowsEdgeAntialiasing:1; + int setEdgeAntialiasingMask:1; + int setIsAccessibilityElement:1; + int setAccessibilityLabel:1; + int setAccessibilityAttributedLabel:1; + int setAccessibilityHint:1; + int setAccessibilityAttributedHint:1; + int setAccessibilityValue:1; + int setAccessibilityAttributedValue:1; + int setAccessibilityTraits:1; + int setAccessibilityFrame:1; + int setAccessibilityLanguage:1; + int setAccessibilityElementsHidden:1; + int setAccessibilityViewIsModal:1; + int setShouldGroupAccessibilityChildren:1; + int setAccessibilityIdentifier:1; + int setAccessibilityNavigationStyle:1; + int setAccessibilityCustomActions:1; + int setAccessibilityHeaderElements:1; + int setAccessibilityActivationPoint:1; + int setAccessibilityPath:1; + int setSemanticContentAttribute:1; + int setLayoutMargins:1; + int setPreservesSuperviewLayoutMargins:1; + int setInsetsLayoutMarginsFromSafeArea:1; + int setActions:1; + int setMaskedCorners : 1; +} ASPendingStateFlags; + + +static constexpr ASPendingStateFlags kZeroFlags = {0}; + +@implementation _ASPendingState +{ + @package //Expose all ivars for ASDisplayNode to bypass getters for efficiency + + UIViewAutoresizing autoresizingMask; + CAEdgeAntialiasingMask edgeAntialiasingMask; + CGRect frame; // Frame is only to be used for synchronous views wrapped by nodes (see setFrame:) + CGRect bounds; + UIColor *backgroundColor; + UIColor *tintColor; + CGFloat alpha; + CGFloat cornerRadius; + UIViewContentMode contentMode; + CGPoint anchorPoint; + CGPoint position; + CGFloat zPosition; + CATransform3D transform; + CATransform3D sublayerTransform; + id contents; + NSString *contentsGravity; + CGRect contentsRect; + CGRect contentsCenter; + CGFloat contentsScale; + CGFloat rasterizationScale; + CGColorRef shadowColor; + CGFloat shadowOpacity; + CGSize shadowOffset; + CGFloat shadowRadius; + CGFloat borderWidth; + CGColorRef borderColor; + UIEdgeInsets layoutMargins; + NSString *accessibilityLabel; + NSAttributedString *accessibilityAttributedLabel; + NSString *accessibilityHint; + NSAttributedString *accessibilityAttributedHint; + NSString *accessibilityValue; + NSAttributedString *accessibilityAttributedValue; + UIAccessibilityTraits accessibilityTraits; + CGRect accessibilityFrame; + NSString *accessibilityLanguage; + NSString *accessibilityIdentifier; + UIAccessibilityNavigationStyle accessibilityNavigationStyle; + NSArray *accessibilityCustomActions; + NSArray *accessibilityHeaderElements; + CGPoint accessibilityActivationPoint; + UIBezierPath *accessibilityPath; + UISemanticContentAttribute semanticContentAttribute API_AVAILABLE(ios(9.0), tvos(9.0)); + NSDictionary> *actions; + + ASPendingStateFlags _stateToApplyFlags; + struct { + unsigned int asyncTransactionContainer:1; + unsigned int preservesSuperviewLayoutMargins:1; + unsigned int insetsLayoutMarginsFromSafeArea:1; + unsigned int isAccessibilityElement:1; + unsigned int accessibilityElementsHidden:1; + unsigned int accessibilityViewIsModal:1; + unsigned int shouldGroupAccessibilityChildren:1; + unsigned int clipsToBounds:1; + unsigned int opaque:1; + unsigned int hidden:1; + unsigned int needsDisplayOnBoundsChange:1; + unsigned int allowsGroupOpacity:1; + unsigned int allowsEdgeAntialiasing:1; + unsigned int autoresizesSubviews:1; + unsigned int userInteractionEnabled:1; + unsigned int exclusiveTouch:1; + } _flags; +} + +/** + * Apply the state's frame, bounds, and position to layer. This will not + * be called on synchronous view-backed nodes which require we directly + * call [view setFrame:]. + * + * FIXME: How should we reconcile order-of-operations between setting frame, bounds, position? + * Note we can't read bounds and position in the background, so we have to keep the frame + * value intact until application time (now). + */ +ASDISPLAYNODE_INLINE void ASPendingStateApplyMetricsToLayer(_ASPendingState *state, CALayer *layer) { + ASPendingStateFlags flags = state->_stateToApplyFlags; + if (flags.setFrame) { + CGRect _bounds = CGRectZero; + CGPoint _position = CGPointZero; + ASBoundsAndPositionForFrame(state->frame, layer.bounds.origin, layer.anchorPoint, &_bounds, &_position); + layer.bounds = _bounds; + layer.position = _position; + } else { + if (flags.setBounds) + layer.bounds = state->bounds; + if (flags.setPosition) + layer.position = state->position; + } +} + +@synthesize frame=frame; +@synthesize bounds=bounds; +@synthesize backgroundColor=backgroundColor; +@synthesize edgeAntialiasingMask=edgeAntialiasingMask; +@synthesize autoresizingMask=autoresizingMask; +@synthesize tintColor=tintColor; +@synthesize alpha=alpha; +@synthesize cornerRadius=cornerRadius; +@synthesize contentMode=contentMode; +@synthesize anchorPoint=anchorPoint; +@synthesize position=position; +@synthesize zPosition=zPosition; +@synthesize transform=transform; +@synthesize sublayerTransform=sublayerTransform; +@synthesize contents=contents; +@synthesize contentsGravity=contentsGravity; +@synthesize contentsRect=contentsRect; +@synthesize contentsCenter=contentsCenter; +@synthesize contentsScale=contentsScale; +@synthesize rasterizationScale=rasterizationScale; +@synthesize userInteractionEnabled=userInteractionEnabled; +@synthesize exclusiveTouch=exclusiveTouch; +@synthesize shadowColor=shadowColor; +@synthesize shadowOpacity=shadowOpacity; +@synthesize shadowOffset=shadowOffset; +@synthesize shadowRadius=shadowRadius; +@synthesize borderWidth=borderWidth; +@synthesize borderColor=borderColor; +@synthesize asyncdisplaykit_asyncTransactionContainer=asyncTransactionContainer; +@synthesize semanticContentAttribute=semanticContentAttribute; +@synthesize layoutMargins=layoutMargins; +@synthesize preservesSuperviewLayoutMargins=preservesSuperviewLayoutMargins; +@synthesize insetsLayoutMarginsFromSafeArea=insetsLayoutMarginsFromSafeArea; +@synthesize actions=actions; +@synthesize maskedCorners = maskedCorners; + +static CGColorRef blackColorRef = NULL; + +- (instancetype)init +{ + if (!(self = [super init])) + return nil; + + + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + // Default UIKit color is an RGB color + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + blackColorRef = CGColorCreate(colorSpace, (CGFloat[]){0,0,0,1} ); + CFRetain(blackColorRef); + CGColorSpaceRelease(colorSpace); + }); + + // Set defaults, these come from the defaults specified in CALayer and UIView + _flags.clipsToBounds = NO; + _flags.opaque = YES; + frame = CGRectZero; + bounds = CGRectZero; + backgroundColor = nil; + tintColor = nil; + _flags.hidden = NO; + _flags.needsDisplayOnBoundsChange = NO; + _flags.allowsGroupOpacity = ASDefaultAllowsGroupOpacity(); + _flags.allowsEdgeAntialiasing = ASDefaultAllowsEdgeAntialiasing(); + _flags.autoresizesSubviews = YES; + alpha = 1.0f; + cornerRadius = 0.0f; + contentMode = UIViewContentModeScaleToFill; + _stateToApplyFlags.needsDisplay = NO; + anchorPoint = CGPointMake(0.5, 0.5); + position = CGPointZero; + zPosition = 0.0; + transform = CATransform3DIdentity; + sublayerTransform = CATransform3DIdentity; + contents = nil; + contentsGravity = kCAGravityResize; + contentsRect = CGRectMake(0.0f, 0.0f, 1.0f, 1.0f); + contentsCenter = CGRectMake(0.0f, 0.0f, 1.0f, 1.0f); + contentsScale = 1.0f; + rasterizationScale = 1.0f; + _flags.userInteractionEnabled = YES; + shadowColor = blackColorRef; + shadowOpacity = 0.0; + shadowOffset = CGSizeMake(0, -3); + shadowRadius = 3; + borderWidth = 0; + borderColor = blackColorRef; + layoutMargins = UIEdgeInsetsMake(8, 8, 8, 8); + _flags.preservesSuperviewLayoutMargins = NO; + _flags.insetsLayoutMarginsFromSafeArea = YES; + _flags.isAccessibilityElement = NO; + accessibilityLabel = nil; + accessibilityAttributedLabel = nil; + accessibilityHint = nil; + accessibilityAttributedHint = nil; + accessibilityValue = nil; + accessibilityAttributedValue = nil; + accessibilityTraits = UIAccessibilityTraitNone; + accessibilityFrame = CGRectZero; + accessibilityLanguage = nil; + _flags.accessibilityElementsHidden = NO; + _flags.accessibilityViewIsModal = NO; + _flags.shouldGroupAccessibilityChildren = NO; + accessibilityIdentifier = nil; + accessibilityNavigationStyle = UIAccessibilityNavigationStyleAutomatic; + accessibilityCustomActions = nil; + accessibilityHeaderElements = nil; + accessibilityActivationPoint = CGPointZero; + accessibilityPath = nil; + edgeAntialiasingMask = (kCALayerLeftEdge | kCALayerRightEdge | kCALayerTopEdge | kCALayerBottomEdge); + semanticContentAttribute = UISemanticContentAttributeUnspecified; + + return self; +} + +- (void)setNeedsDisplay +{ + _stateToApplyFlags.needsDisplay = YES; +} + +- (void)setNeedsLayout +{ + _stateToApplyFlags.needsLayout = YES; +} + +- (void)layoutIfNeeded +{ + _stateToApplyFlags.layoutIfNeeded = YES; +} + +- (void)setClipsToBounds:(BOOL)flag +{ + _flags.clipsToBounds = flag; + _stateToApplyFlags.setClipsToBounds = YES; +} + +- (BOOL)clipsToBounds +{ + return _flags.clipsToBounds; +} + +- (void)setOpaque:(BOOL)flag +{ + _flags.opaque = flag; + _stateToApplyFlags.setOpaque = YES; +} + +- (BOOL)isOpaque +{ + return _flags.opaque; +} + +- (void)setNeedsDisplayOnBoundsChange:(BOOL)flag +{ + _flags.needsDisplayOnBoundsChange = flag; + _stateToApplyFlags.setNeedsDisplayOnBoundsChange = YES; +} + +- (BOOL)needsDisplayOnBoundsChange +{ + return _flags.needsDisplayOnBoundsChange; +} + +- (void)setAllowsGroupOpacity:(BOOL)flag +{ + _flags.allowsGroupOpacity = flag; + _stateToApplyFlags.setAllowsGroupOpacity = YES; +} + +- (BOOL)allowsGroupOpacity +{ + return _flags.allowsGroupOpacity; +} + +- (void)setAllowsEdgeAntialiasing:(BOOL)flag +{ + _flags.allowsEdgeAntialiasing = flag; + _stateToApplyFlags.setAllowsEdgeAntialiasing = YES; +} + +- (BOOL)allowsEdgeAntialiasing +{ + return _flags.allowsEdgeAntialiasing; +} + +- (void)setEdgeAntialiasingMask:(CAEdgeAntialiasingMask)mask +{ + edgeAntialiasingMask = mask; + _stateToApplyFlags.setEdgeAntialiasingMask = YES; +} + +- (void)setAutoresizesSubviews:(BOOL)flag +{ + _flags.autoresizesSubviews = flag; + _stateToApplyFlags.setAutoresizesSubviews = YES; +} + +- (BOOL)autoresizesSubviews +{ + return _flags.autoresizesSubviews; +} + +- (void)setAutoresizingMask:(UIViewAutoresizing)mask +{ + autoresizingMask = mask; + _stateToApplyFlags.setAutoresizingMask = YES; +} + +- (void)setFrame:(CGRect)newFrame +{ + frame = newFrame; + _stateToApplyFlags.setFrame = YES; +} + +- (void)setBounds:(CGRect)newBounds +{ + ASDisplayNodeAssert(!isnan(newBounds.size.width) && !isnan(newBounds.size.height), @"Invalid bounds %@ provided to %@", NSStringFromCGRect(newBounds), self); + if (isnan(newBounds.size.width)) + newBounds.size.width = 0.0; + if (isnan(newBounds.size.height)) + newBounds.size.height = 0.0; + bounds = newBounds; + _stateToApplyFlags.setBounds = YES; +} + +- (UIColor *)backgroundColor +{ + return backgroundColor; +} + +- (void)setBackgroundColor:(UIColor *)color +{ + if ([color isEqual:backgroundColor]) { + return; + } + backgroundColor = color; + _stateToApplyFlags.setBackgroundColor = YES; +} + +- (UIColor *)tintColor +{ + return tintColor; +} + +- (void)setTintColor:(UIColor *)newTintColor +{ + if ([newTintColor isEqual:tintColor]) { + return; + } + tintColor = newTintColor; + _stateToApplyFlags.setTintColor = YES; +} + +- (void)setHidden:(BOOL)flag +{ + _flags.hidden = flag; + _stateToApplyFlags.setHidden = YES; +} + +- (BOOL)isHidden +{ + return _flags.hidden; +} + +- (void)setAlpha:(CGFloat)newAlpha +{ + alpha = newAlpha; + _stateToApplyFlags.setAlpha = YES; +} + +- (void)setCornerRadius:(CGFloat)newCornerRadius +{ + cornerRadius = newCornerRadius; + _stateToApplyFlags.setCornerRadius = YES; +} + +- (void)setMaskedCorners:(CACornerMask)newMaskedCorners +{ + maskedCorners = newMaskedCorners; + _stateToApplyFlags.setMaskedCorners = YES; +} + +- (void)setContentMode:(UIViewContentMode)newContentMode +{ + contentMode = newContentMode; + _stateToApplyFlags.setContentMode = YES; +} + +- (void)setAnchorPoint:(CGPoint)newAnchorPoint +{ + anchorPoint = newAnchorPoint; + _stateToApplyFlags.setAnchorPoint = YES; +} + +- (void)setPosition:(CGPoint)newPosition +{ + ASDisplayNodeAssert(!isnan(newPosition.x) && !isnan(newPosition.y), @"Invalid position %@ provided to %@", NSStringFromCGPoint(newPosition), self); + if (isnan(newPosition.x)) + newPosition.x = 0.0; + if (isnan(newPosition.y)) + newPosition.y = 0.0; + position = newPosition; + _stateToApplyFlags.setPosition = YES; +} + +- (void)setZPosition:(CGFloat)newPosition +{ + zPosition = newPosition; + _stateToApplyFlags.setZPosition = YES; +} + +- (void)setTransform:(CATransform3D)newTransform +{ + transform = newTransform; + _stateToApplyFlags.setTransform = YES; +} + +- (void)setSublayerTransform:(CATransform3D)newSublayerTransform +{ + sublayerTransform = newSublayerTransform; + _stateToApplyFlags.setSublayerTransform = YES; +} + +- (void)setContents:(id)newContents +{ + if (contents == newContents) { + return; + } + + contents = newContents; + _stateToApplyFlags.setContents = YES; +} + +- (void)setContentsGravity:(NSString *)newContentsGravity +{ + contentsGravity = newContentsGravity; + _stateToApplyFlags.setContentsGravity = YES; +} + +- (void)setContentsRect:(CGRect)newContentsRect +{ + contentsRect = newContentsRect; + _stateToApplyFlags.setContentsRect = YES; +} + +- (void)setContentsCenter:(CGRect)newContentsCenter +{ + contentsCenter = newContentsCenter; + _stateToApplyFlags.setContentsCenter = YES; +} + +- (void)setContentsScale:(CGFloat)newContentsScale +{ + contentsScale = newContentsScale; + _stateToApplyFlags.setContentsScale = YES; +} + +- (void)setRasterizationScale:(CGFloat)newRasterizationScale +{ + rasterizationScale = newRasterizationScale; + _stateToApplyFlags.setRasterizationScale = YES; +} + +- (void)setUserInteractionEnabled:(BOOL)flag +{ + _flags.userInteractionEnabled = flag; + _stateToApplyFlags.setUserInteractionEnabled = YES; +} + +- (BOOL)isUserInteractionEnabled +{ + return _flags.userInteractionEnabled; +} + +- (void)setExclusiveTouch:(BOOL)flag +{ + _flags.exclusiveTouch = flag; + _stateToApplyFlags.setExclusiveTouch = YES; +} + +- (BOOL)isExclusiveTouch +{ + return _flags.exclusiveTouch; +} + +- (void)setShadowColor:(CGColorRef)color +{ + if (shadowColor == color) { + return; + } + + if (shadowColor != blackColorRef) { + CGColorRelease(shadowColor); + } + shadowColor = color; + CGColorRetain(shadowColor); + + _stateToApplyFlags.setShadowColor = YES; +} + +- (void)setShadowOpacity:(CGFloat)newOpacity +{ + shadowOpacity = newOpacity; + _stateToApplyFlags.setShadowOpacity = YES; +} + +- (void)setShadowOffset:(CGSize)newOffset +{ + shadowOffset = newOffset; + _stateToApplyFlags.setShadowOffset = YES; +} + +- (void)setShadowRadius:(CGFloat)newRadius +{ + shadowRadius = newRadius; + _stateToApplyFlags.setShadowRadius = YES; +} + +- (void)setBorderWidth:(CGFloat)newWidth +{ + borderWidth = newWidth; + _stateToApplyFlags.setBorderWidth = YES; +} + +- (void)setBorderColor:(CGColorRef)color +{ + if (borderColor == color) { + return; + } + + if (borderColor != blackColorRef) { + CGColorRelease(borderColor); + } + borderColor = color; + CGColorRetain(borderColor); + + _stateToApplyFlags.setBorderColor = YES; +} + +- (void)asyncdisplaykit_setAsyncTransactionContainer:(BOOL)flag +{ + _flags.asyncTransactionContainer = flag; + _stateToApplyFlags.setAsyncTransactionContainer = YES; +} + +- (BOOL)asyncdisplaykit_isAsyncTransactionContainer +{ + return _flags.asyncTransactionContainer; +} + +- (void)setLayoutMargins:(UIEdgeInsets)margins +{ + layoutMargins = margins; + _stateToApplyFlags.setLayoutMargins = YES; +} + +- (void)setPreservesSuperviewLayoutMargins:(BOOL)flag +{ + _flags.preservesSuperviewLayoutMargins = flag; + _stateToApplyFlags.setPreservesSuperviewLayoutMargins = YES; +} + +- (BOOL)preservesSuperviewLayoutMargins +{ + return _flags.preservesSuperviewLayoutMargins; +} + +- (void)setInsetsLayoutMarginsFromSafeArea:(BOOL)flag +{ + _flags.insetsLayoutMarginsFromSafeArea = flag; + _stateToApplyFlags.setInsetsLayoutMarginsFromSafeArea = YES; +} + +- (BOOL)insetsLayoutMarginsFromSafeArea +{ + return _flags.insetsLayoutMarginsFromSafeArea; +} + +- (void)setSemanticContentAttribute:(UISemanticContentAttribute)attribute API_AVAILABLE(ios(9.0), tvos(9.0)) { + semanticContentAttribute = attribute; + _stateToApplyFlags.setSemanticContentAttribute = YES; +} + +- (void)setActions:(NSDictionary> *)actionsArg +{ + actions = [actionsArg copy]; + _stateToApplyFlags.setActions = YES; +} + +- (BOOL)isAccessibilityElement +{ + return _flags.isAccessibilityElement; +} + +- (void)setIsAccessibilityElement:(BOOL)newIsAccessibilityElement +{ + _flags.isAccessibilityElement = newIsAccessibilityElement; + _stateToApplyFlags.setIsAccessibilityElement = YES; +} + +- (NSString *)accessibilityLabel +{ + if (_stateToApplyFlags.setAccessibilityAttributedLabel) { + return accessibilityAttributedLabel.string; + } + return accessibilityLabel; +} + +- (void)setAccessibilityLabel:(NSString *)newAccessibilityLabel +{ + ASCompareAssignCopy(accessibilityLabel, newAccessibilityLabel); + _stateToApplyFlags.setAccessibilityLabel = YES; + _stateToApplyFlags.setAccessibilityAttributedLabel = NO; +} + +- (NSAttributedString *)accessibilityAttributedLabel +{ + if (_stateToApplyFlags.setAccessibilityLabel) { + return [[NSAttributedString alloc] initWithString:accessibilityLabel]; + } + return accessibilityAttributedLabel; +} + +- (void)setAccessibilityAttributedLabel:(NSAttributedString *)newAccessibilityAttributedLabel +{ + ASCompareAssignCopy(accessibilityAttributedLabel, newAccessibilityAttributedLabel); + _stateToApplyFlags.setAccessibilityAttributedLabel = YES; + _stateToApplyFlags.setAccessibilityLabel = NO; +} + +- (NSString *)accessibilityHint +{ + if (_stateToApplyFlags.setAccessibilityAttributedHint) { + return accessibilityAttributedHint.string; + } + return accessibilityHint; +} + +- (void)setAccessibilityHint:(NSString *)newAccessibilityHint +{ + ASCompareAssignCopy(accessibilityHint, newAccessibilityHint); + _stateToApplyFlags.setAccessibilityHint = YES; + _stateToApplyFlags.setAccessibilityAttributedHint = NO; +} + +- (NSAttributedString *)accessibilityAttributedHint +{ + if (_stateToApplyFlags.setAccessibilityHint) { + return [[NSAttributedString alloc] initWithString:accessibilityHint]; + } + return accessibilityAttributedHint; +} + +- (void)setAccessibilityAttributedHint:(NSAttributedString *)newAccessibilityAttributedHint +{ + ASCompareAssignCopy(accessibilityAttributedHint, newAccessibilityAttributedHint); + _stateToApplyFlags.setAccessibilityAttributedHint = YES; + _stateToApplyFlags.setAccessibilityHint = NO; +} + +- (NSString *)accessibilityValue +{ + if (_stateToApplyFlags.setAccessibilityAttributedValue) { + return accessibilityAttributedValue.string; + } + return accessibilityValue; +} + +- (void)setAccessibilityValue:(NSString *)newAccessibilityValue +{ + ASCompareAssignCopy(accessibilityValue, newAccessibilityValue); + _stateToApplyFlags.setAccessibilityValue = YES; + _stateToApplyFlags.setAccessibilityAttributedValue = NO; +} + +- (NSAttributedString *)accessibilityAttributedValue +{ + if (_stateToApplyFlags.setAccessibilityValue) { + return [[NSAttributedString alloc] initWithString:accessibilityValue]; + } + return accessibilityAttributedValue; +} + +- (void)setAccessibilityAttributedValue:(NSAttributedString *)newAccessibilityAttributedValue +{ + ASCompareAssignCopy(accessibilityAttributedValue, newAccessibilityAttributedValue); + _stateToApplyFlags.setAccessibilityAttributedValue = YES; + _stateToApplyFlags.setAccessibilityValue = NO; +} + +- (UIAccessibilityTraits)accessibilityTraits +{ + return accessibilityTraits; +} + +- (void)setAccessibilityTraits:(UIAccessibilityTraits)newAccessibilityTraits +{ + accessibilityTraits = newAccessibilityTraits; + _stateToApplyFlags.setAccessibilityTraits = YES; +} + +- (CGRect)accessibilityFrame +{ + return accessibilityFrame; +} + +- (void)setAccessibilityFrame:(CGRect)newAccessibilityFrame +{ + accessibilityFrame = newAccessibilityFrame; + _stateToApplyFlags.setAccessibilityFrame = YES; +} + +- (NSString *)accessibilityLanguage +{ + return accessibilityLanguage; +} + +- (void)setAccessibilityLanguage:(NSString *)newAccessibilityLanguage +{ + _stateToApplyFlags.setAccessibilityLanguage = YES; + accessibilityLanguage = newAccessibilityLanguage; +} + +- (BOOL)accessibilityElementsHidden +{ + return _flags.accessibilityElementsHidden; +} + +- (void)setAccessibilityElementsHidden:(BOOL)newAccessibilityElementsHidden +{ + _flags.accessibilityElementsHidden = newAccessibilityElementsHidden; + _stateToApplyFlags.setAccessibilityElementsHidden = YES; +} + +- (BOOL)accessibilityViewIsModal +{ + return _flags.accessibilityViewIsModal; +} + +- (void)setAccessibilityViewIsModal:(BOOL)newAccessibilityViewIsModal +{ + _flags.accessibilityViewIsModal = newAccessibilityViewIsModal; + _stateToApplyFlags.setAccessibilityViewIsModal = YES; +} + +- (BOOL)shouldGroupAccessibilityChildren +{ + return _flags.shouldGroupAccessibilityChildren; +} + +- (void)setShouldGroupAccessibilityChildren:(BOOL)newShouldGroupAccessibilityChildren +{ + _flags.shouldGroupAccessibilityChildren = newShouldGroupAccessibilityChildren; + _stateToApplyFlags.setShouldGroupAccessibilityChildren = YES; +} + +- (NSString *)accessibilityIdentifier +{ + return accessibilityIdentifier; +} + +- (void)setAccessibilityIdentifier:(NSString *)newAccessibilityIdentifier +{ + _stateToApplyFlags.setAccessibilityIdentifier = YES; + if (accessibilityIdentifier != newAccessibilityIdentifier) { + accessibilityIdentifier = [newAccessibilityIdentifier copy]; + } +} + +- (UIAccessibilityNavigationStyle)accessibilityNavigationStyle +{ + return accessibilityNavigationStyle; +} + +- (void)setAccessibilityNavigationStyle:(UIAccessibilityNavigationStyle)newAccessibilityNavigationStyle +{ + _stateToApplyFlags.setAccessibilityNavigationStyle = YES; + accessibilityNavigationStyle = newAccessibilityNavigationStyle; +} + +- (NSArray *)accessibilityCustomActions +{ + return accessibilityCustomActions; +} + +- (void)setAccessibilityCustomActions:(NSArray *)newAccessibilityCustomActions +{ + _stateToApplyFlags.setAccessibilityCustomActions = YES; + if (accessibilityCustomActions != newAccessibilityCustomActions) { + accessibilityCustomActions = [newAccessibilityCustomActions copy]; + } +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-implementations" +- (NSArray *)accessibilityHeaderElements +{ + return accessibilityHeaderElements; +} + +- (void)setAccessibilityHeaderElements:(NSArray *)newAccessibilityHeaderElements +{ + _stateToApplyFlags.setAccessibilityHeaderElements = YES; + if (accessibilityHeaderElements != newAccessibilityHeaderElements) { + accessibilityHeaderElements = [newAccessibilityHeaderElements copy]; + } +} +#pragma clang diagnostic pop + +- (CGPoint)accessibilityActivationPoint +{ + if (_stateToApplyFlags.setAccessibilityActivationPoint) { + return accessibilityActivationPoint; + } + + // Default == Mid-point of the accessibilityFrame + return CGPointMake(CGRectGetMidX(accessibilityFrame), CGRectGetMidY(accessibilityFrame)); +} + +- (void)setAccessibilityActivationPoint:(CGPoint)newAccessibilityActivationPoint +{ + _stateToApplyFlags.setAccessibilityActivationPoint = YES; + accessibilityActivationPoint = newAccessibilityActivationPoint; +} + +- (UIBezierPath *)accessibilityPath +{ + return accessibilityPath; +} + +- (void)setAccessibilityPath:(UIBezierPath *)newAccessibilityPath +{ + _stateToApplyFlags.setAccessibilityPath = YES; + if (accessibilityPath != newAccessibilityPath) { + accessibilityPath = newAccessibilityPath; + } +} + +- (void)applyToLayer:(CALayer *)layer +{ + ASPendingStateFlags flags = _stateToApplyFlags; + + if (__shouldSetNeedsDisplayForLayer(layer)) { + [layer setNeedsDisplay]; + } + + if (flags.setAnchorPoint) + layer.anchorPoint = anchorPoint; + + if (flags.setZPosition) + layer.zPosition = zPosition; + + if (flags.setTransform) + layer.transform = transform; + + if (flags.setSublayerTransform) + layer.sublayerTransform = sublayerTransform; + + if (flags.setContents) + layer.contents = contents; + + if (flags.setContentsGravity) + layer.contentsGravity = contentsGravity; + + if (flags.setContentsRect) + layer.contentsRect = contentsRect; + + if (flags.setContentsCenter) + layer.contentsCenter = contentsCenter; + + if (flags.setContentsScale) + layer.contentsScale = contentsScale; + + if (flags.setRasterizationScale) + layer.rasterizationScale = rasterizationScale; + + if (flags.setClipsToBounds) + layer.masksToBounds = _flags.clipsToBounds; + + if (flags.setBackgroundColor) + layer.backgroundColor = backgroundColor.CGColor; + + if (flags.setOpaque) + layer.opaque = _flags.opaque; + + if (flags.setHidden) + layer.hidden = _flags.hidden; + + if (flags.setAlpha) + layer.opacity = alpha; + + if (flags.setCornerRadius) + layer.cornerRadius = cornerRadius; + + if (AS_AVAILABLE_IOS_TVOS(11, 11)) { + if (flags.setMaskedCorners) { + layer.maskedCorners = maskedCorners; + } + } + + if (flags.setContentMode) + layer.contentsGravity = ASDisplayNodeCAContentsGravityFromUIContentMode(contentMode); + + if (flags.setShadowColor) + layer.shadowColor = shadowColor; + + if (flags.setShadowOpacity) + layer.shadowOpacity = shadowOpacity; + + if (flags.setShadowOffset) + layer.shadowOffset = shadowOffset; + + if (flags.setShadowRadius) + layer.shadowRadius = shadowRadius; + + if (flags.setBorderWidth) + layer.borderWidth = borderWidth; + + if (flags.setBorderColor) + layer.borderColor = borderColor; + + if (flags.setNeedsDisplayOnBoundsChange) + layer.needsDisplayOnBoundsChange = _flags.needsDisplayOnBoundsChange; + + if (flags.setAllowsGroupOpacity) + layer.allowsGroupOpacity = _flags.allowsGroupOpacity; + + if (flags.setAllowsEdgeAntialiasing) + layer.allowsEdgeAntialiasing = _flags.allowsEdgeAntialiasing; + + if (flags.setEdgeAntialiasingMask) + layer.edgeAntialiasingMask = edgeAntialiasingMask; + + if (flags.setAsyncTransactionContainer) + layer.asyncdisplaykit_asyncTransactionContainer = _flags.asyncTransactionContainer; + + if (flags.setOpaque) + ASDisplayNodeAssert(layer.opaque == _flags.opaque, @"Didn't set opaque as desired"); + + if (flags.setActions) + layer.actions = actions; + + ASPendingStateApplyMetricsToLayer(self, layer); + + if (flags.needsLayout) + [layer setNeedsLayout]; + + if (flags.layoutIfNeeded) + [layer layoutIfNeeded]; +} + +- (void)applyToView:(UIView *)view withSpecialPropertiesHandling:(BOOL)specialPropertiesHandling +{ + /* + Use our convenience setters blah here instead of layer.blah + We were accidentally setting some properties on layer here, but view in UIViewBridgeOptimizations. + + That could easily cause bugs where it mattered whether you set something up on a bg thread on in -didLoad + because a different setter would be called. + */ + + unowned CALayer *layer = view.layer; + + ASPendingStateFlags flags = _stateToApplyFlags; + if (__shouldSetNeedsDisplayForView(view)) { + [view setNeedsDisplay]; + } + + if (flags.setAnchorPoint) + layer.anchorPoint = anchorPoint; + + if (flags.setPosition) + layer.position = position; + + if (flags.setZPosition) + layer.zPosition = zPosition; + + if (flags.setBounds) + view.bounds = bounds; + + if (flags.setTransform) + layer.transform = transform; + + if (flags.setSublayerTransform) + layer.sublayerTransform = sublayerTransform; + + if (flags.setContents) + layer.contents = contents; + + if (flags.setContentsGravity) + layer.contentsGravity = contentsGravity; + + if (flags.setContentsRect) + layer.contentsRect = contentsRect; + + if (flags.setContentsCenter) + layer.contentsCenter = contentsCenter; + + if (flags.setContentsScale) + layer.contentsScale = contentsScale; + + if (flags.setRasterizationScale) + layer.rasterizationScale = rasterizationScale; + + if (flags.setActions) + layer.actions = actions; + + if (flags.setClipsToBounds) + view.clipsToBounds = _flags.clipsToBounds; + + if (flags.setBackgroundColor) { + view.backgroundColor = backgroundColor; + layer.backgroundColor = backgroundColor.CGColor; + } + + if (flags.setTintColor) + view.tintColor = tintColor; + + if (flags.setOpaque) { + view.opaque = _flags.opaque; + layer.opaque = _flags.opaque; + } + + if (flags.setHidden) + view.hidden = _flags.hidden; + + if (flags.setAlpha) + view.alpha = alpha; + + if (flags.setCornerRadius) + layer.cornerRadius = cornerRadius; + + if (flags.setContentMode) + view.contentMode = contentMode; + + if (flags.setUserInteractionEnabled) + view.userInteractionEnabled = _flags.userInteractionEnabled; + + #if TARGET_OS_IOS + if (flags.setExclusiveTouch) + view.exclusiveTouch = _flags.exclusiveTouch; + #endif + + if (flags.setShadowColor) + layer.shadowColor = shadowColor; + + if (flags.setShadowOpacity) + layer.shadowOpacity = shadowOpacity; + + if (flags.setShadowOffset) + layer.shadowOffset = shadowOffset; + + if (flags.setShadowRadius) + layer.shadowRadius = shadowRadius; + + if (flags.setBorderWidth) + layer.borderWidth = borderWidth; + + if (flags.setBorderColor) + layer.borderColor = borderColor; + + if (flags.setAutoresizingMask) + view.autoresizingMask = autoresizingMask; + + if (flags.setAutoresizesSubviews) + view.autoresizesSubviews = _flags.autoresizesSubviews; + + if (flags.setNeedsDisplayOnBoundsChange) + layer.needsDisplayOnBoundsChange = _flags.needsDisplayOnBoundsChange; + + if (flags.setAllowsGroupOpacity) + layer.allowsGroupOpacity = _flags.allowsGroupOpacity; + + if (flags.setAllowsEdgeAntialiasing) + layer.allowsEdgeAntialiasing = _flags.allowsEdgeAntialiasing; + + if (flags.setEdgeAntialiasingMask) + layer.edgeAntialiasingMask = edgeAntialiasingMask; + + if (flags.setAsyncTransactionContainer) + view.asyncdisplaykit_asyncTransactionContainer = _flags.asyncTransactionContainer; + + if (flags.setOpaque) + ASDisplayNodeAssert(layer.opaque == _flags.opaque, @"Didn't set opaque as desired"); + + if (flags.setLayoutMargins) + view.layoutMargins = layoutMargins; + + if (flags.setPreservesSuperviewLayoutMargins) + view.preservesSuperviewLayoutMargins = _flags.preservesSuperviewLayoutMargins; + + if (AS_AVAILABLE_IOS_TVOS(11.0, 11.0)) { + if (flags.setInsetsLayoutMarginsFromSafeArea) { + view.insetsLayoutMarginsFromSafeArea = _flags.insetsLayoutMarginsFromSafeArea; + } + } + + if (flags.setSemanticContentAttribute) { + view.semanticContentAttribute = semanticContentAttribute; + } + + if (flags.setIsAccessibilityElement) + view.isAccessibilityElement = _flags.isAccessibilityElement; + + if (flags.setAccessibilityLabel) + view.accessibilityLabel = accessibilityLabel; + + if (flags.setAccessibilityHint) + view.accessibilityHint = accessibilityHint; + + if (flags.setAccessibilityValue) + view.accessibilityValue = accessibilityValue; + + if (AS_AVAILABLE_IOS_TVOS(11, 11)) { + if (flags.setAccessibilityAttributedLabel) { + view.accessibilityAttributedLabel = accessibilityAttributedLabel; + } + if (flags.setAccessibilityAttributedHint) { + view.accessibilityAttributedHint = accessibilityAttributedHint; + } + if (flags.setAccessibilityAttributedValue) { + view.accessibilityAttributedValue = accessibilityAttributedValue; + } + } + + if (flags.setAccessibilityTraits) + view.accessibilityTraits = accessibilityTraits; + + if (flags.setAccessibilityFrame) + view.accessibilityFrame = accessibilityFrame; + + if (flags.setAccessibilityLanguage) + view.accessibilityLanguage = accessibilityLanguage; + + if (flags.setAccessibilityElementsHidden) + view.accessibilityElementsHidden = _flags.accessibilityElementsHidden; + + if (flags.setAccessibilityViewIsModal) + view.accessibilityViewIsModal = _flags.accessibilityViewIsModal; + + if (flags.setShouldGroupAccessibilityChildren) + view.shouldGroupAccessibilityChildren = _flags.shouldGroupAccessibilityChildren; + + if (flags.setAccessibilityIdentifier) + view.accessibilityIdentifier = accessibilityIdentifier; + + if (flags.setAccessibilityNavigationStyle) + view.accessibilityNavigationStyle = accessibilityNavigationStyle; + + if (flags.setAccessibilityCustomActions) { + view.accessibilityCustomActions = accessibilityCustomActions; + } + +#if TARGET_OS_TV + if (flags.setAccessibilityHeaderElements) + view.accessibilityHeaderElements = accessibilityHeaderElements; +#endif + + if (flags.setAccessibilityActivationPoint) + view.accessibilityActivationPoint = accessibilityActivationPoint; + + if (flags.setAccessibilityPath) + view.accessibilityPath = accessibilityPath; + + if (flags.setFrame && specialPropertiesHandling) { + // Frame is only defined when transform is identity because we explicitly diverge from CALayer behavior and define frame without transform +//#if DEBUG +// // Checking if the transform is identity is expensive, so disable when unnecessary. We have assertions on in Release, so DEBUG is the only way I know of. +// ASDisplayNodeAssert(CATransform3DIsIdentity(layer.transform), @"-[ASDisplayNode setFrame:] - self.transform must be identity in order to set the frame property. (From Apple's UIView documentation: If the transform property is not the identity transform, the value of this property is undefined and therefore should be ignored.)"); +//#endif + view.frame = frame; + } else { + ASPendingStateApplyMetricsToLayer(self, layer); + } + + if (flags.needsLayout) + [view setNeedsLayout]; + + if (flags.layoutIfNeeded) + [view layoutIfNeeded]; +} + +// FIXME: Make this more efficient by tracking which properties are set rather than reading everything. ++ (_ASPendingState *)pendingViewStateFromLayer:(CALayer *)layer +{ + if (!layer) { + return nil; + } + _ASPendingState *pendingState = [[_ASPendingState alloc] init]; + pendingState.anchorPoint = layer.anchorPoint; + pendingState.position = layer.position; + pendingState.zPosition = layer.zPosition; + pendingState.bounds = layer.bounds; + pendingState.transform = layer.transform; + pendingState.sublayerTransform = layer.sublayerTransform; + pendingState.contents = layer.contents; + pendingState.contentsGravity = layer.contentsGravity; + pendingState.contentsRect = layer.contentsRect; + pendingState.contentsCenter = layer.contentsCenter; + pendingState.contentsScale = layer.contentsScale; + pendingState.rasterizationScale = layer.rasterizationScale; + pendingState.clipsToBounds = layer.masksToBounds; + pendingState.backgroundColor = [UIColor colorWithCGColor:layer.backgroundColor]; + pendingState.opaque = layer.opaque; + pendingState.hidden = layer.hidden; + pendingState.alpha = layer.opacity; + pendingState.cornerRadius = layer.cornerRadius; + pendingState.contentMode = ASDisplayNodeUIContentModeFromCAContentsGravity(layer.contentsGravity); + pendingState.shadowColor = layer.shadowColor; + pendingState.shadowOpacity = layer.shadowOpacity; + pendingState.shadowOffset = layer.shadowOffset; + pendingState.shadowRadius = layer.shadowRadius; + pendingState.borderWidth = layer.borderWidth; + pendingState.borderColor = layer.borderColor; + pendingState.needsDisplayOnBoundsChange = layer.needsDisplayOnBoundsChange; + pendingState.allowsGroupOpacity = layer.allowsGroupOpacity; + pendingState.allowsEdgeAntialiasing = layer.allowsEdgeAntialiasing; + pendingState.edgeAntialiasingMask = layer.edgeAntialiasingMask; + return pendingState; +} + +// FIXME: Make this more efficient by tracking which properties are set rather than reading everything. ++ (_ASPendingState *)pendingViewStateFromView:(UIView *)view +{ + if (!view) { + return nil; + } + _ASPendingState *pendingState = [[_ASPendingState alloc] init]; + + CALayer *layer = view.layer; + pendingState.anchorPoint = layer.anchorPoint; + pendingState.position = layer.position; + pendingState.zPosition = layer.zPosition; + pendingState.bounds = view.bounds; + pendingState.transform = layer.transform; + pendingState.sublayerTransform = layer.sublayerTransform; + pendingState.contents = layer.contents; + pendingState.contentsGravity = layer.contentsGravity; + pendingState.contentsRect = layer.contentsRect; + pendingState.contentsCenter = layer.contentsCenter; + pendingState.contentsScale = layer.contentsScale; + pendingState.rasterizationScale = layer.rasterizationScale; + pendingState.clipsToBounds = view.clipsToBounds; + pendingState.backgroundColor = view.backgroundColor; + pendingState.tintColor = view.tintColor; + pendingState.opaque = layer.opaque; + pendingState.hidden = view.hidden; + pendingState.alpha = view.alpha; + pendingState.cornerRadius = layer.cornerRadius; + pendingState.contentMode = view.contentMode; + pendingState.userInteractionEnabled = view.userInteractionEnabled; +#if TARGET_OS_IOS + pendingState.exclusiveTouch = view.exclusiveTouch; +#endif + pendingState.shadowColor = layer.shadowColor; + pendingState.shadowOpacity = layer.shadowOpacity; + pendingState.shadowOffset = layer.shadowOffset; + pendingState.shadowRadius = layer.shadowRadius; + pendingState.borderWidth = layer.borderWidth; + pendingState.borderColor = layer.borderColor; + pendingState.autoresizingMask = view.autoresizingMask; + pendingState.autoresizesSubviews = view.autoresizesSubviews; + pendingState.needsDisplayOnBoundsChange = layer.needsDisplayOnBoundsChange; + pendingState.allowsGroupOpacity = layer.allowsGroupOpacity; + pendingState.allowsEdgeAntialiasing = layer.allowsEdgeAntialiasing; + pendingState.edgeAntialiasingMask = layer.edgeAntialiasingMask; + pendingState.semanticContentAttribute = view.semanticContentAttribute; + pendingState.layoutMargins = view.layoutMargins; + pendingState.preservesSuperviewLayoutMargins = view.preservesSuperviewLayoutMargins; + if (AS_AVAILABLE_IOS_TVOS(11, 11)) { + pendingState.insetsLayoutMarginsFromSafeArea = view.insetsLayoutMarginsFromSafeArea; + } + pendingState.isAccessibilityElement = view.isAccessibilityElement; + pendingState.accessibilityLabel = view.accessibilityLabel; + pendingState.accessibilityHint = view.accessibilityHint; + pendingState.accessibilityValue = view.accessibilityValue; + if (AS_AVAILABLE_IOS_TVOS(11, 11)) { + pendingState.accessibilityAttributedLabel = view.accessibilityAttributedLabel; + pendingState.accessibilityAttributedHint = view.accessibilityAttributedHint; + pendingState.accessibilityAttributedValue = view.accessibilityAttributedValue; + } + pendingState.accessibilityTraits = view.accessibilityTraits; + pendingState.accessibilityFrame = view.accessibilityFrame; + pendingState.accessibilityLanguage = view.accessibilityLanguage; + pendingState.accessibilityElementsHidden = view.accessibilityElementsHidden; + pendingState.accessibilityViewIsModal = view.accessibilityViewIsModal; + pendingState.shouldGroupAccessibilityChildren = view.shouldGroupAccessibilityChildren; + pendingState.accessibilityIdentifier = view.accessibilityIdentifier; + pendingState.accessibilityNavigationStyle = view.accessibilityNavigationStyle; + pendingState.accessibilityCustomActions = view.accessibilityCustomActions; +#if TARGET_OS_TV + pendingState.accessibilityHeaderElements = view.accessibilityHeaderElements; +#endif + pendingState.accessibilityActivationPoint = view.accessibilityActivationPoint; + pendingState.accessibilityPath = view.accessibilityPath; + return pendingState; +} + +- (void)clearChanges +{ + _stateToApplyFlags = kZeroFlags; +} + +- (BOOL)hasSetNeedsLayout +{ + return _stateToApplyFlags.needsLayout; +} + +- (BOOL)hasSetNeedsDisplay +{ + return _stateToApplyFlags.needsDisplay; +} + +- (BOOL)hasChanges +{ + return memcmp(&_stateToApplyFlags, &kZeroFlags, sizeof(ASPendingStateFlags)); +} + +- (void)dealloc +{ + if (shadowColor != blackColorRef) { + CGColorRelease(shadowColor); + } + + if (borderColor != blackColorRef) { + CGColorRelease(borderColor); + } +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/_ASScopeTimer.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/_ASScopeTimer.h new file mode 100644 index 0000000..523599d --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/Private/_ASScopeTimer.h @@ -0,0 +1,56 @@ +// +// _ASScopeTimer.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#pragma once + +/** + Must compile as c++ for this to work. + + Usage: + // Can be an ivar or local variable + NSTimeInterval placeToStoreTiming; + + { + // some scope + AS::ScopeTimer t(placeToStoreTiming); + DoPotentiallySlowWork(); + MorePotentiallySlowWork(); + } + + */ + +namespace AS { + struct ScopeTimer { + NSTimeInterval begin; + NSTimeInterval &outT; + ScopeTimer(NSTimeInterval &outRef) : outT(outRef) { + begin = CACurrentMediaTime(); + } + ~ScopeTimer() { + outT = CACurrentMediaTime() - begin; + } + }; + + // variant where repeated calls are summed + struct SumScopeTimer { + NSTimeInterval begin; + NSTimeInterval &outT; + BOOL enable; + SumScopeTimer(NSTimeInterval &outRef, BOOL enable = YES) : outT(outRef), enable(enable) { + if (enable) { + begin = CACurrentMediaTime(); + } + } + ~SumScopeTimer() { + if (enable) { + outT += CACurrentMediaTime() - begin; + } + } + }; +} diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASLayoutManager.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASLayoutManager.h new file mode 100644 index 0000000..1396bf2 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASLayoutManager.h @@ -0,0 +1,16 @@ +// +// ASLayoutManager.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +AS_SUBCLASSING_RESTRICTED +@interface ASLayoutManager : NSLayoutManager + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASLayoutManager.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASLayoutManager.mm new file mode 100644 index 0000000..fbb3b49 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASLayoutManager.mm @@ -0,0 +1,42 @@ +// +// ASLayoutManager.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +@implementation ASLayoutManager + +- (void)showCGGlyphs:(const CGGlyph *)glyphs + positions:(const CGPoint *)positions + count:(NSUInteger)glyphCount + font:(UIFont *)font + matrix:(CGAffineTransform)textMatrix + attributes:(NSDictionary *)attributes + inContext:(CGContextRef)graphicsContext +{ + + // NSLayoutManager has a hard coded internal color for hyperlinks which ignores + // NSForegroundColorAttributeName. To get around this, we force the fill color + // in the current context to match NSForegroundColorAttributeName. + UIColor *foregroundColor = attributes[NSForegroundColorAttributeName]; + + if (foregroundColor) + { + CGContextSetFillColorWithColor(graphicsContext, foregroundColor.CGColor); + } + + [super showCGGlyphs:glyphs + positions:positions + count:glyphCount + font:font + matrix:textMatrix + attributes:attributes + inContext:graphicsContext]; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitAttributes.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitAttributes.h new file mode 100644 index 0000000..cf09ad6 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitAttributes.h @@ -0,0 +1,134 @@ +// +// ASTextKitAttributes.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#pragma once + +#import + +#import + +#if AS_ENABLE_TEXTNODE + +#import + +ASDK_EXTERN NSString *const ASTextKitTruncationAttributeName; +/** + Use ASTextKitEntityAttribute as the value of this attribute to embed a link or other interactable content inside the + text. + */ +ASDK_EXTERN NSString *const ASTextKitEntityAttributeName; + +/** + All NSObject values in this struct should be copied when passed into the TextComponent. + */ +struct ASTextKitAttributes { + /** + The string to be drawn. ASTextKit will not augment this string with default colors, etc. so this must be complete. + */ + NSAttributedString *attributedString; + /** + The string to use as the truncation string, usually just "...". If you have a range of text you would like to + restrict highlighting to (for instance if you have "... Continue Reading", use the ASTextKitTruncationAttributeName + to mark the specific range of the string that should be highlightable. + */ + NSAttributedString *truncationAttributedString; + /** + This is the character set that ASTextKit should attempt to avoid leaving as a trailing character before your + truncation token. By default this set includes "\s\t\n\r.,!?:;" so you don't end up with ugly looking truncation + text like "Hey, this is some fancy Truncation!\n\n...". Instead it would be truncated as "Hey, this is some fancy + truncation...". This is not always possible. + + Set this to the empty charset if you want to just use the "dumb" truncation behavior. A nil value will be + substituted with the default described above. + */ + NSCharacterSet *avoidTailTruncationSet; + /** + The line-break mode to apply to the text. Since this also impacts how TextKit will attempt to truncate the text + in your string, we only support NSLineBreakByWordWrapping and NSLineBreakByCharWrapping. + */ + NSLineBreakMode lineBreakMode; + /** + The maximum number of lines to draw in the drawable region. Leave blank or set to 0 to define no maximum. + This is required to apply scale factors to shrink text to fit within a number of lines + */ + NSUInteger maximumNumberOfLines; + /** + An array of UIBezierPath objects representing the exclusion paths inside the receiver's bounding rectangle. Default value: nil. + */ + NSArray *exclusionPaths; + /** + The shadow offset for any shadows applied to the text. The coordinate space for this is the same as UIKit, so a + positive width means towards the right, and a positive height means towards the bottom. + */ + CGSize shadowOffset; + /** + The color to use in drawing the text's shadow. + */ + UIColor *shadowColor; + /** + The opacity of the shadow from 0 to 1. + */ + CGFloat shadowOpacity; + /** + The radius that should be applied to the shadow blur. Larger values mean a larger, more blurred shadow. + */ + CGFloat shadowRadius; + /** + An array of scale factors in descending order to apply to the text to try to make it fit into a constrained size. + */ + NSArray *pointSizeScaleFactors; + + /** + The tint color to use in drawing the text foreground color. Only applied if the attributedString does not define foreground color + */ + UIColor *tintColor; + /** + We provide an explicit copy function so we can use aggregate initializer syntax while providing copy semantics for + the NSObjects inside. + */ + const ASTextKitAttributes copy() const + { + return { + [attributedString copy], + [truncationAttributedString copy], + [avoidTailTruncationSet copy], + lineBreakMode, + maximumNumberOfLines, + [exclusionPaths copy], + shadowOffset, + [shadowColor copy], + shadowOpacity, + shadowRadius, + pointSizeScaleFactors, + [tintColor copy] + }; + }; + + bool operator==(const ASTextKitAttributes &other) const + { + // These comparisons are in a specific order to reduce the overall cost of this function. + return lineBreakMode == other.lineBreakMode + && maximumNumberOfLines == other.maximumNumberOfLines + && shadowOpacity == other.shadowOpacity + && shadowRadius == other.shadowRadius + && (pointSizeScaleFactors == other.pointSizeScaleFactors + || [pointSizeScaleFactors isEqualToArray:other.pointSizeScaleFactors]) + && CGSizeEqualToSize(shadowOffset, other.shadowOffset) + && ASObjectIsEqual(exclusionPaths, other.exclusionPaths) + && ASObjectIsEqual(avoidTailTruncationSet, other.avoidTailTruncationSet) + && ASObjectIsEqual(shadowColor, other.shadowColor) + && ASObjectIsEqual(attributedString, other.attributedString) + && ASObjectIsEqual(truncationAttributedString, other.truncationAttributedString) + && ASObjectIsEqual(tintColor, other.tintColor); + } + + size_t hash() const; +}; + +#endif diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitAttributes.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitAttributes.mm new file mode 100644 index 0000000..400ef43 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitAttributes.mm @@ -0,0 +1,50 @@ +// +// ASTextKitAttributes.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#if AS_ENABLE_TEXTNODE + +#import + +NSString *const ASTextKitTruncationAttributeName = @"ck_truncation"; +NSString *const ASTextKitEntityAttributeName = @"ck_entity"; + +size_t ASTextKitAttributes::hash() const +{ +#pragma clang diagnostic push +#pragma clang diagnostic warning "-Wpadded" + struct { + NSUInteger attrStringHash; + NSUInteger truncationStringHash; + NSUInteger avoidTrunactionSetHash; + NSLineBreakMode lineBreakMode; + NSUInteger maximumNumberOfLines; + NSUInteger exclusionPathsHash; + CGSize shadowOffset; + NSUInteger shadowColorHash; + CGFloat shadowOpacity; + CGFloat shadowRadius; +#pragma clang diagnostic pop + } data = { + [attributedString hash], + [truncationAttributedString hash], + [avoidTailTruncationSet hash], + lineBreakMode, + maximumNumberOfLines, + [exclusionPaths hash], + shadowOffset, + [shadowColor hash], + shadowOpacity, + shadowRadius, + }; + return ASHashBytes(&data, sizeof(data)); +} + +#endif diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitComponents.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitComponents.h new file mode 100644 index 0000000..df89db2 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitComponents.h @@ -0,0 +1,64 @@ +// +// ASTextKitComponents.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface ASTextKitComponentsTextView : UITextView +- (instancetype)initWithFrame:(CGRect)frame textContainer:(nullable NSTextContainer *)textContainer NS_DESIGNATED_INITIALIZER; +- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder __unavailable; +- (instancetype)init __unavailable; +@end + +AS_SUBCLASSING_RESTRICTED +@interface ASTextKitComponents : NSObject + +/** + @abstract Creates the stack of TextKit components. + @param attributedSeedString The attributed string to seed the returned text storage with, or nil to receive an blank text storage. + @param textContainerSize The size of the text-container. Typically, size specifies the constraining width of the layout, and CGFLOAT_MAX for height. Pass CGSizeZero if these components will be hooked up to a UITextView, which will manage the text container's size itself. + @return An `ASTextKitComponents` containing the created components. The text view component will be nil. + @discussion The returned components will be hooked up together, so they are ready for use as a system upon return. + */ ++ (instancetype)componentsWithAttributedSeedString:(nullable NSAttributedString *)attributedSeedString + textContainerSize:(CGSize)textContainerSize NS_RETURNS_RETAINED; + +/** + @abstract Creates the stack of TextKit components. + @param textStorage The NSTextStorage to use. + @param textContainerSize The size of the text-container. Typically, size specifies the constraining width of the layout, and CGFLOAT_MAX for height. Pass CGSizeZero if these components will be hooked up to a UITextView, which will manage the text container's size itself. + @param layoutManager The NSLayoutManager to use. + @return An `ASTextKitComponents` containing the created components. The text view component will be nil. + @discussion The returned components will be hooked up together, so they are ready for use as a system upon return. + */ ++ (instancetype)componentsWithTextStorage:(NSTextStorage *)textStorage + textContainerSize:(CGSize)textContainerSize + layoutManager:(NSLayoutManager *)layoutManager NS_RETURNS_RETAINED; + +/** + @abstract Returns the bounding size for the text view's text. + @param constrainedWidth The constraining width to be used during text-sizing. Usually, this value should be the receiver's calculated size. + @result A CGSize representing the bounding size for the receiver's text. + */ +- (CGSize)sizeForConstrainedWidth:(CGFloat)constrainedWidth; + +- (CGSize)sizeForConstrainedWidth:(CGFloat)constrainedWidth + forMaxNumberOfLines:(NSInteger)numberOfLines; + +@property (nonatomic, readonly) NSTextStorage *textStorage; +@property (nonatomic, readonly) NSTextContainer *textContainer; +@property (nonatomic, readonly) NSLayoutManager *layoutManager; +@property (nonatomic, nullable) ASTextKitComponentsTextView *textView; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitComponents.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitComponents.mm new file mode 100644 index 0000000..95ae1db --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitComponents.mm @@ -0,0 +1,193 @@ +// +// ASTextKitComponents.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +#import + +@interface ASTextKitComponentsTextView () { + // Prevent UITextView from updating contentOffset while deallocating: https://github.com/TextureGroup/Texture/issues/860 + BOOL _deallocating; +} +@property CGRect threadSafeBounds; +@end + +@implementation ASTextKitComponentsTextView + +- (instancetype)initWithFrame:(CGRect)frame textContainer:(NSTextContainer *)textContainer +{ + self = [super initWithFrame:frame textContainer:textContainer]; + if (self) { + _threadSafeBounds = self.bounds; + _deallocating = NO; + } + return self; +} + +- (void)dealloc +{ + _deallocating = YES; +} + +- (void)setFrame:(CGRect)frame +{ + ASDisplayNodeAssertMainThread(); + [super setFrame:frame]; + self.threadSafeBounds = self.bounds; +} + +- (void)setBounds:(CGRect)bounds +{ + ASDisplayNodeAssertMainThread(); + [super setBounds:bounds]; + self.threadSafeBounds = bounds; +} + +- (void)setContentOffset:(CGPoint)contentOffset +{ + if (_deallocating) { + return; + } + + [super setContentOffset:contentOffset]; +} + + +@end + +@interface ASTextKitComponents () + +// read-write redeclarations +@property (nonatomic) NSTextStorage *textStorage; +@property (nonatomic) NSTextContainer *textContainer; +@property (nonatomic) NSLayoutManager *layoutManager; + +@end + +@implementation ASTextKitComponents + +#pragma mark - Class + ++ (instancetype)componentsWithAttributedSeedString:(NSAttributedString *)attributedSeedString + textContainerSize:(CGSize)textContainerSize NS_RETURNS_RETAINED +{ + NSTextStorage *textStorage = attributedSeedString ? [[NSTextStorage alloc] initWithAttributedString:attributedSeedString] : [[NSTextStorage alloc] init]; + + return [self componentsWithTextStorage:textStorage + textContainerSize:textContainerSize + layoutManager:[[NSLayoutManager alloc] init]]; +} + ++ (instancetype)componentsWithTextStorage:(NSTextStorage *)textStorage + textContainerSize:(CGSize)textContainerSize + layoutManager:(NSLayoutManager *)layoutManager NS_RETURNS_RETAINED +{ + ASTextKitComponents *components = [[self alloc] init]; + + components.textStorage = textStorage; + + components.layoutManager = layoutManager; + [components.textStorage addLayoutManager:components.layoutManager]; + + components.textContainer = [[NSTextContainer alloc] initWithSize:textContainerSize]; + components.textContainer.lineFragmentPadding = 0.0; // We want the text laid out up to the very edges of the text-view. + [components.layoutManager addTextContainer:components.textContainer]; + + return components; +} + ++ (BOOL)needsMainThreadDeallocation +{ + return YES; +} + +#pragma mark - Lifecycle + +- (void)dealloc +{ + // Nil out all delegates to prevent crash + if (_textView) { + ASDisplayNodeAssertMainThread(); + _textView.delegate = nil; + } + _layoutManager.delegate = nil; +} + +#pragma mark - Sizing + +- (CGSize)sizeForConstrainedWidth:(CGFloat)constrainedWidth +{ + ASTextKitComponents *components = self; + + // If our text-view's width is already the constrained width, we can use our existing TextKit stack for this sizing calculation. + // Otherwise, we create a temporary stack to size for `constrainedWidth`. + if (CGRectGetWidth(components.textView.threadSafeBounds) != constrainedWidth) { + components = [ASTextKitComponents componentsWithAttributedSeedString:components.textStorage textContainerSize:CGSizeMake(constrainedWidth, CGFLOAT_MAX)]; + } + + // Force glyph generation and layout, which may not have happened yet (and isn't triggered by -usedRectForTextContainer:). + [components.layoutManager ensureLayoutForTextContainer:components.textContainer]; + CGSize textSize = [components.layoutManager usedRectForTextContainer:components.textContainer].size; + + return textSize; +} + +- (CGSize)sizeForConstrainedWidth:(CGFloat)constrainedWidth + forMaxNumberOfLines:(NSInteger)maxNumberOfLines +{ + if (maxNumberOfLines == 0) { + return [self sizeForConstrainedWidth:constrainedWidth]; + } + + ASTextKitComponents *components = self; + + // Always use temporary stack in case of threading issues + components = [ASTextKitComponents componentsWithAttributedSeedString:components.textStorage textContainerSize:CGSizeMake(constrainedWidth, CGFLOAT_MAX)]; + + // Force glyph generation and layout, which may not have happened yet (and isn't triggered by - usedRectForTextContainer:). + [components.layoutManager ensureLayoutForTextContainer:components.textContainer]; + + CGFloat width = [components.layoutManager usedRectForTextContainer:components.textContainer].size.width; + + // Calculate height based on line fragments + // Based on calculating number of lines from: http://asciiwwdc.com/2013/sessions/220 + NSRange glyphRange, lineRange = NSMakeRange(0, 0); + CGRect rect = CGRectZero; + CGFloat height = 0; + CGFloat lastOriginY = -1.0; + NSUInteger numberOfLines = 0; + + glyphRange = [components.layoutManager glyphRangeForTextContainer:components.textContainer]; + + while (lineRange.location < NSMaxRange(glyphRange)) { + rect = [components.layoutManager lineFragmentRectForGlyphAtIndex:lineRange.location + effectiveRange:&lineRange]; + + if (CGRectGetMinY(rect) > lastOriginY) { + ++numberOfLines; + if (numberOfLines == maxNumberOfLines) { + height = rect.origin.y + rect.size.height; + break; + } + } + + lastOriginY = CGRectGetMinY(rect); + lineRange.location = NSMaxRange(lineRange); + } + + CGFloat fragmentHeight = rect.origin.y + rect.size.height; + CGFloat finalHeight = std::ceil(std::fmax(height, fragmentHeight)); + + CGSize size = CGSizeMake(width, finalHeight); + + return size; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitContext.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitContext.h new file mode 100644 index 0000000..df9d0a0 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitContext.h @@ -0,0 +1,54 @@ +// +// ASTextKitContext.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import + +#if AS_ENABLE_TEXTNODE + +#import + +/** + A threadsafe container for the TextKit components that ASTextKit uses to lay out and truncate its text. + + This container is the sole owner and manager of the TextKit classes. This is an important model because of major + thread safety issues inside vanilla TextKit. It provides a central locking location for accessing TextKit methods. + */ +AS_SUBCLASSING_RESTRICTED +@interface ASTextKitContext : NSObject + +/** + Initializes a context and its associated TextKit components. + + Initialization of TextKit components is a globally locking operation so be careful of bottlenecks with this class. + */ +- (instancetype)initWithAttributedString:(NSAttributedString *)attributedString + tintColor:(UIColor *)tintColor + lineBreakMode:(NSLineBreakMode)lineBreakMode + maximumNumberOfLines:(NSUInteger)maximumNumberOfLines + exclusionPaths:(NSArray *)exclusionPaths + constrainedSize:(CGSize)constrainedSize; + +/** + All operations on TextKit values MUST occur within this locked context. Simultaneous access (even non-mutative) to + TextKit components may cause crashes. + + The block provided MUST not call out to client code from within its scope or it is possible for this to cause deadlocks + in your application. Use with EXTREME care. + + Callers MUST NOT keep a ref to these internal objects and use them later. This WILL cause crashes in your application. + */ +- (void)performBlockWithLockedTextKitComponents:(AS_NOESCAPE void (^)(NSLayoutManager *layoutManager, + NSTextStorage *textStorage, + NSTextContainer *textContainer))block; + +@end + +#endif diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitContext.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitContext.mm new file mode 100644 index 0000000..8c24644 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitContext.mm @@ -0,0 +1,98 @@ +// +// ASTextKitContext.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#if AS_ENABLE_TEXTNODE + +#import +#import + +@implementation ASTextKitContext +{ + // All TextKit operations (even non-mutative ones) must be executed serially. + std::shared_ptr __instanceLock__; + + NSLayoutManager *_layoutManager; + NSTextStorage *_textStorage; + NSTextContainer *_textContainer; +} + +- (instancetype)initWithAttributedString:(NSAttributedString *)attributedString + tintColor:(UIColor *)tintColor + lineBreakMode:(NSLineBreakMode)lineBreakMode + maximumNumberOfLines:(NSUInteger)maximumNumberOfLines + exclusionPaths:(NSArray *)exclusionPaths + constrainedSize:(CGSize)constrainedSize + +{ + if (self = [super init]) { + static AS::Mutex *mutex = NULL; + static dispatch_once_t onceToken; + // Concurrently initialising TextKit components crashes (rdar://18448377) so we use a global lock. + dispatch_once(&onceToken, ^{ + mutex = new AS::Mutex(); + }); + if (mutex != NULL) { + mutex->lock(); + } + + __instanceLock__ = std::make_shared(); + + // Create the TextKit component stack with our default configuration. + _textStorage = [[NSTextStorage alloc] init]; + _layoutManager = [[ASLayoutManager alloc] init]; + _layoutManager.usesFontLeading = NO; + [_textStorage addLayoutManager:_layoutManager]; + + // Instead of calling [NSTextStorage initWithAttributedString:], setting attributedString just after calling addlayoutManager can fix CJK language layout issues. + // See https://github.com/facebook/AsyncDisplayKit/issues/2894 + if (attributedString && attributedString.length > 0) { + [_textStorage setAttributedString:attributedString]; + + // Apply tint color if specified and if foreground color is undefined for attributedString + NSRange limit = NSMakeRange(0, attributedString.length); + // Look for previous attributes that define foreground color + UIColor *attributeValue = (UIColor *)[attributedString attribute:NSForegroundColorAttributeName atIndex:limit.location effectiveRange:NULL]; + if (attributeValue == nil) { + // None are found, apply tint color if available. Fallback to "black" text color + if (tintColor) { + [_textStorage addAttributes:@{ NSForegroundColorAttributeName : tintColor } range:limit]; + } + } + } + + _textContainer = [[NSTextContainer alloc] initWithSize:constrainedSize]; + // We want the text laid out up to the very edges of the container. + _textContainer.lineFragmentPadding = 0; + _textContainer.lineBreakMode = lineBreakMode; + _textContainer.maximumNumberOfLines = maximumNumberOfLines; + _textContainer.exclusionPaths = exclusionPaths; + [_layoutManager addTextContainer:_textContainer]; + + if (mutex != NULL) { + mutex->unlock(); + } + } + return self; +} + +- (void)performBlockWithLockedTextKitComponents:(NS_NOESCAPE void (^)(NSLayoutManager *, + NSTextStorage *, + NSTextContainer *))block +{ + AS::MutexLocker l(*__instanceLock__); + if (block) { + block(_layoutManager, _textStorage, _textContainer); + } +} + +@end + +#endif diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitCoreTextAdditions.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitCoreTextAdditions.h new file mode 100644 index 0000000..0da484d --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitCoreTextAdditions.h @@ -0,0 +1,89 @@ +// +// ASTextKitCoreTextAdditions.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import + +#if AS_ENABLE_TEXTNODE + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + @abstract Returns whether a given attribute is an unsupported Core Text attribute. + @param attributeName The name of the attribute + @discussion The following Core Text attributes are not supported on NSAttributedString, and thus will not be preserved during the conversion: + - kCTForegroundColorFromContextAttributeName + - kCTSuperscriptAttributeName + - kCTGlyphInfoAttributeName + - kCTCharacterShapeAttributeName + - kCTLanguageAttributeName + - kCTRunDelegateAttributeName + - kCTBaselineClassAttributeName + - kCTBaselineInfoAttributeName + - kCTBaselineReferenceInfoAttributeName + - kCTWritingDirectionAttributeName + - kCTUnderlineColorAttributeName + @result Whether attributeName is an unsupported Core Text attribute. + */ +ASDK_EXTERN BOOL ASAttributeWithNameIsUnsupportedCoreTextAttribute(NSString *attributeName); + + +/** + @abstract Returns an attributes dictionary for use by NSAttributedString, given a dictionary of Core Text attributes. + @param coreTextAttributes An NSDictionary whose keys are CFAttributedStringRef attributes. + @discussion The following Core Text attributes are not supported on NSAttributedString, and thus will not be preserved during the conversion: + - kCTForegroundColorFromContextAttributeName + - kCTSuperscriptAttributeName + - kCTGlyphInfoAttributeName + - kCTCharacterShapeAttributeName + - kCTLanguageAttributeName + - kCTRunDelegateAttributeName + - kCTBaselineClassAttributeName + - kCTBaselineInfoAttributeName + - kCTBaselineReferenceInfoAttributeName + - kCTWritingDirectionAttributeName + - kCTUnderlineColorAttributeName + @result An NSDictionary of attributes for use by NSAttributedString. + */ +ASDK_EXTERN NSDictionary *NSAttributedStringAttributesForCoreTextAttributes(NSDictionary *coreTextAttributes); + +/** + @abstract Returns an NSAttributedString whose Core Text attributes have been converted, where possible, to NSAttributedString attributes. + @param dirtyAttributedString An NSAttributedString that may contain Core Text attributes. + @result An NSAttributedString that's preserved as many CFAttributedString attributes as possible. + */ +ASDK_EXTERN NSAttributedString *ASCleanseAttributedStringOfCoreTextAttributes(NSAttributedString *dirtyAttributedString); + +#pragma mark - +#pragma mark - +@interface NSParagraphStyle (ASTextKitCoreTextAdditions) + +/** + @abstract Returns an NSParagraphStyle initialized with the paragraph specifiers from the given CTParagraphStyleRef. + @param coreTextParagraphStyle A Core Text paragraph style. + @discussion It is important to note that not all CTParagraphStyle specifiers are supported by NSParagraphStyle, and consequently, this is a lossy conversion. Notably, the following specifiers will not preserved: + - kCTParagraphStyleSpecifierTabStops + - kCTParagraphStyleSpecifierDefaultTabInterval + - kCTParagraphStyleSpecifierMaximumLineSpacing + - kCTParagraphStyleSpecifierMinimumLineSpacing + - kCTParagraphStyleSpecifierLineSpacingAdjustment + - kCTParagraphStyleSpecifierLineBoundsOptions + @result An NSParagraphStyle initialized with as many of the paragraph specifiers from `coreTextParagraphStyle` as possible. + + */ ++ (NSParagraphStyle *)paragraphStyleWithCTParagraphStyle:(CTParagraphStyleRef)coreTextParagraphStyle NS_RETURNS_RETAINED; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitCoreTextAdditions.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitCoreTextAdditions.mm new file mode 100644 index 0000000..a6c72d5 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitCoreTextAdditions.mm @@ -0,0 +1,330 @@ +// +// ASTextKitCoreTextAdditions.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#if AS_ENABLE_TEXTNODE + +#import +#import + +#pragma mark - Public +BOOL ASAttributeWithNameIsUnsupportedCoreTextAttribute(NSString *attributeName) +{ + static NSSet *coreTextAttributes; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + coreTextAttributes = [NSSet setWithObjects:(__bridge id)kCTForegroundColorAttributeName, + kCTForegroundColorFromContextAttributeName, + kCTForegroundColorAttributeName, + kCTStrokeColorAttributeName, + kCTUnderlineStyleAttributeName, + kCTVerticalFormsAttributeName, + kCTRunDelegateAttributeName, + kCTBaselineClassAttributeName, + kCTBaselineInfoAttributeName, + kCTBaselineReferenceInfoAttributeName, + kCTUnderlineColorAttributeName, + kCTParagraphStyleAttributeName, + nil]; + }); + return [coreTextAttributes containsObject:attributeName]; +} + +NSDictionary *NSAttributedStringAttributesForCoreTextAttributes(NSDictionary *coreTextAttributes) +{ + NSMutableDictionary *cleanAttributes = [[NSMutableDictionary alloc] initWithCapacity:coreTextAttributes.count]; + + [coreTextAttributes enumerateKeysAndObjectsUsingBlock:^(NSString *coreTextKey, id coreTextValue, BOOL *stop) { + // The following attributes are not supported on NSAttributedString. Should they become available, we should add them. + /* + kCTForegroundColorFromContextAttributeName + kCTSuperscriptAttributeName + kCTGlyphInfoAttributeName + kCTCharacterShapeAttributeName + kCTLanguageAttributeName + kCTRunDelegateAttributeName + kCTBaselineClassAttributeName + kCTBaselineInfoAttributeName + kCTBaselineReferenceInfoAttributeName + kCTWritingDirectionAttributeName + kCTUnderlineColorAttributeName + */ + + // Conversely, the following attributes are not supported on CFAttributedString. Should they become available, we should add them. + /* + NSStrikethroughStyleAttributeName + NSShadowAttributeName + NSBackgroundColorAttributeName + */ + + // kCTFontAttributeName -> NSFontAttributeName + if ([coreTextKey isEqualToString:(NSString *)kCTFontAttributeName]) { + // Its reference type, CTFontRef, is toll-free bridged with UIFont in iOS and NSFont in OS X + CTFontRef coreTextFont = (__bridge CTFontRef)coreTextValue; + cleanAttributes[NSFontAttributeName] = (__bridge UIFont *)coreTextFont; + } + // kCTKernAttributeName -> NSKernAttributeName + else if ([coreTextKey isEqualToString:(NSString *)kCTKernAttributeName]) { + cleanAttributes[NSKernAttributeName] = (NSNumber *)coreTextValue; + } + // kCTLigatureAttributeName -> NSLigatureAttributeName + else if ([coreTextKey isEqualToString:(NSString *)kCTLigatureAttributeName]) { + cleanAttributes[NSLigatureAttributeName] = (NSNumber *)coreTextValue; + } + // kCTForegroundColorAttributeName -> NSForegroundColorAttributeName + else if ([coreTextKey isEqualToString:(NSString *)kCTForegroundColorAttributeName]) { + cleanAttributes[NSForegroundColorAttributeName] = [UIColor colorWithCGColor:(CGColorRef)coreTextValue]; + } + // kCTParagraphStyleAttributeName -> NSParagraphStyleAttributeName + else if ([coreTextKey isEqualToString:(NSString *)kCTParagraphStyleAttributeName]) { + if ([coreTextValue isKindOfClass:[NSParagraphStyle class]]) { + cleanAttributes[NSParagraphStyleAttributeName] = (NSParagraphStyle *)coreTextValue; + } + else { + cleanAttributes[NSParagraphStyleAttributeName] = [NSParagraphStyle paragraphStyleWithCTParagraphStyle:(CTParagraphStyleRef)coreTextValue]; + } + } + // kCTStrokeWidthAttributeName -> NSStrokeWidthAttributeName + else if ([coreTextKey isEqualToString:(NSString *)kCTStrokeWidthAttributeName]) { + cleanAttributes[NSStrokeWidthAttributeName] = (NSNumber *)coreTextValue; + } + // kCTStrokeColorAttributeName -> NSStrokeColorAttributeName + else if ([coreTextKey isEqualToString:(NSString *)kCTStrokeColorAttributeName]) { + cleanAttributes[NSStrokeColorAttributeName] = [UIColor colorWithCGColor:(CGColorRef)coreTextValue]; + } + // kCTUnderlineStyleAttributeName -> NSUnderlineStyleAttributeName + else if ([coreTextKey isEqualToString:(NSString *)kCTUnderlineStyleAttributeName]) { + cleanAttributes[NSUnderlineStyleAttributeName] = (NSNumber *)coreTextValue; + } + // kCTVerticalFormsAttributeName -> NSVerticalGlyphFormAttributeName + else if ([coreTextKey isEqualToString:(NSString *)kCTVerticalFormsAttributeName]) { + BOOL flag = (BOOL)CFBooleanGetValue((CFBooleanRef)coreTextValue); + cleanAttributes[NSVerticalGlyphFormAttributeName] = @((int)flag); // NSVerticalGlyphFormAttributeName is documented to be an NSNumber with an integer that's either 0 or 1. + } + // Don't filter out any internal text attributes + else if (!ASAttributeWithNameIsUnsupportedCoreTextAttribute(coreTextKey)){ + cleanAttributes[coreTextKey] = coreTextValue; + } + }]; + + return cleanAttributes; +} + +NSAttributedString *ASCleanseAttributedStringOfCoreTextAttributes(NSAttributedString *dirtyAttributedString) +{ + if (!dirtyAttributedString) + return nil; + + // First see if there are any core text attributes on the string + __block BOOL containsCoreTextAttributes = NO; + [dirtyAttributedString enumerateAttributesInRange:NSMakeRange(0, dirtyAttributedString.length) + options:0 + usingBlock:^(NSDictionary *dirtyAttributes, NSRange range, BOOL *stop) { + [dirtyAttributes enumerateKeysAndObjectsUsingBlock:^(NSString *coreTextKey, id coreTextValue, BOOL *innerStop) { + if (ASAttributeWithNameIsUnsupportedCoreTextAttribute(coreTextKey)) { + containsCoreTextAttributes = YES; + *innerStop = YES; + } + }]; + *stop = containsCoreTextAttributes; + }]; + if (containsCoreTextAttributes) { + + NSString *plainString = dirtyAttributedString.string; + NSMutableAttributedString *cleanAttributedString = [[NSMutableAttributedString alloc] initWithString:plainString]; + + // Iterate over all of the attributes, cleaning them as appropriate and applying them as we go. + [dirtyAttributedString enumerateAttributesInRange:NSMakeRange(0, plainString.length) + options:0 + usingBlock:^(NSDictionary *dirtyAttributes, NSRange range, BOOL *stop) { + [cleanAttributedString addAttributes:NSAttributedStringAttributesForCoreTextAttributes(dirtyAttributes) range:range]; + }]; + + return cleanAttributedString; + } else { + return [dirtyAttributedString copy]; + } +} + +#pragma mark - +#pragma mark - +@implementation NSParagraphStyle (ASTextKitCoreTextAdditions) + ++ (NSParagraphStyle *)paragraphStyleWithCTParagraphStyle:(CTParagraphStyleRef)coreTextParagraphStyle NS_RETURNS_RETAINED +{ + NSMutableParagraphStyle *newParagraphStyle = [[NSMutableParagraphStyle alloc] init]; + + if (!coreTextParagraphStyle) { + return newParagraphStyle; + } + + // The following paragraph style specifiers are not supported on NSParagraphStyle. Should they become available, we should add them. + /* + kCTParagraphStyleSpecifierTabStops + kCTParagraphStyleSpecifierDefaultTabInterval + kCTParagraphStyleSpecifierMaximumLineSpacing + kCTParagraphStyleSpecifierMinimumLineSpacing + kCTParagraphStyleSpecifierLineSpacingAdjustment + kCTParagraphStyleSpecifierLineBoundsOptions + */ + + // Conversely, the following paragraph styles are not supported on CTParagraphStyle. Should they become available, we should add them. + /* + hyphenationFactor + */ + + // kCTParagraphStyleSpecifierAlignment -> alignment + CTTextAlignment coreTextAlignment; + if (CTParagraphStyleGetValueForSpecifier(coreTextParagraphStyle, + kCTParagraphStyleSpecifierAlignment, + sizeof(coreTextAlignment), + &coreTextAlignment)) { + newParagraphStyle.alignment = NSTextAlignmentFromCTTextAlignment(coreTextAlignment); + } + + // kCTParagraphStyleSpecifierFirstLineHeadIndent -> firstLineHeadIndent + CGFloat firstLineHeadIndent; + if (CTParagraphStyleGetValueForSpecifier(coreTextParagraphStyle, + kCTParagraphStyleSpecifierFirstLineHeadIndent, + sizeof(firstLineHeadIndent), + &firstLineHeadIndent)) { + newParagraphStyle.firstLineHeadIndent = firstLineHeadIndent; + } + + // kCTParagraphStyleSpecifierHeadIndent -> headIndent + CGFloat headIndent; + if (CTParagraphStyleGetValueForSpecifier(coreTextParagraphStyle, + kCTParagraphStyleSpecifierHeadIndent, + sizeof(headIndent), + &headIndent)) { + newParagraphStyle.headIndent = headIndent; + } + + // kCTParagraphStyleSpecifierTailIndent -> tailIndent + CGFloat tailIndent; + if (CTParagraphStyleGetValueForSpecifier(coreTextParagraphStyle, + kCTParagraphStyleSpecifierTailIndent, + sizeof(tailIndent), + &tailIndent)) { + newParagraphStyle.tailIndent = tailIndent; + } + + // kCTParagraphStyleSpecifierLineBreakMode -> lineBreakMode + CTLineBreakMode coreTextLineBreakMode; + if (CTParagraphStyleGetValueForSpecifier(coreTextParagraphStyle, + kCTParagraphStyleSpecifierLineBreakMode, + sizeof(coreTextLineBreakMode), + &coreTextLineBreakMode)) { + newParagraphStyle.lineBreakMode = (NSLineBreakMode)coreTextLineBreakMode; // They're the same enum. + } + + // kCTParagraphStyleSpecifierLineHeightMultiple -> lineHeightMultiple + CGFloat lineHeightMultiple; + if (CTParagraphStyleGetValueForSpecifier(coreTextParagraphStyle, + kCTParagraphStyleSpecifierLineHeightMultiple, + sizeof(lineHeightMultiple), + &lineHeightMultiple)) { + newParagraphStyle.lineHeightMultiple = lineHeightMultiple; + } + + // kCTParagraphStyleSpecifierMaximumLineHeight -> maximumLineHeight + CGFloat maximumLineHeight; + if (CTParagraphStyleGetValueForSpecifier(coreTextParagraphStyle, + kCTParagraphStyleSpecifierMaximumLineHeight, + sizeof(maximumLineHeight), + &maximumLineHeight)) { + newParagraphStyle.maximumLineHeight = maximumLineHeight; + } + + // kCTParagraphStyleSpecifierMinimumLineHeight -> minimumLineHeight + CGFloat minimumLineHeight; + if (CTParagraphStyleGetValueForSpecifier(coreTextParagraphStyle, + kCTParagraphStyleSpecifierMinimumLineHeight, + sizeof(minimumLineHeight), + &minimumLineHeight)) { + newParagraphStyle.minimumLineHeight = minimumLineHeight; + } + + CGFloat lineSpacing = 0; +#if TARGET_OS_IOS +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + // kCTParagraphStyleSpecifierLineSpacing -> lineSpacing + // Note that kCTParagraphStyleSpecifierLineSpacing is deprecated and will die soon. We should not be using it. + if (CTParagraphStyleGetValueForSpecifier(coreTextParagraphStyle, + kCTParagraphStyleSpecifierLineSpacing, + sizeof(lineSpacing), + &lineSpacing)) { + newParagraphStyle.lineSpacing = lineSpacing; + } +#pragma clang diagnostic pop +#endif + + // Attempt to weakly map the following onto -[NSParagraphStyle lineSpacing]: + // - kCTParagraphStyleSpecifierMinimumLineSpacing + // - kCTParagraphStyleSpecifierMaximumLineSpacing + // - kCTParagraphStyleSpecifierLineSpacingAdjustment + if (fabs(lineSpacing) <= FLT_EPSILON && + CTParagraphStyleGetValueForSpecifier(coreTextParagraphStyle, + kCTParagraphStyleSpecifierMinimumLineSpacing, + sizeof(lineSpacing), + &lineSpacing)) { + newParagraphStyle.lineSpacing = lineSpacing; + } + + if (fabs(lineSpacing) <= FLT_EPSILON && + CTParagraphStyleGetValueForSpecifier(coreTextParagraphStyle, + kCTParagraphStyleSpecifierMaximumLineSpacing, + sizeof(lineSpacing), + &lineSpacing)) { + newParagraphStyle.lineSpacing = lineSpacing; + } + + if (fabs(lineSpacing) <= FLT_EPSILON && + CTParagraphStyleGetValueForSpecifier(coreTextParagraphStyle, + kCTParagraphStyleSpecifierLineSpacingAdjustment, + sizeof(lineSpacing), + &lineSpacing)) { + newParagraphStyle.lineSpacing = lineSpacing; + } + + // kCTParagraphStyleSpecifierParagraphSpacing -> paragraphSpacing + CGFloat paragraphSpacing; + if (CTParagraphStyleGetValueForSpecifier(coreTextParagraphStyle, + kCTParagraphStyleSpecifierParagraphSpacing, + sizeof(paragraphSpacing), + ¶graphSpacing)) { + newParagraphStyle.paragraphSpacing = paragraphSpacing; + } + + // kCTParagraphStyleSpecifierParagraphSpacingBefore -> paragraphSpacingBefore + CGFloat paragraphSpacingBefore; + if (CTParagraphStyleGetValueForSpecifier(coreTextParagraphStyle, + kCTParagraphStyleSpecifierParagraphSpacingBefore, + sizeof(paragraphSpacingBefore), + ¶graphSpacingBefore)) { + newParagraphStyle.paragraphSpacingBefore = paragraphSpacingBefore; + } + + // kCTParagraphStyleSpecifierBaseWritingDirection -> baseWritingDirection + CTWritingDirection coreTextBaseWritingDirection; + if (CTParagraphStyleGetValueForSpecifier(coreTextParagraphStyle, + kCTParagraphStyleSpecifierBaseWritingDirection, + sizeof(coreTextBaseWritingDirection), + &coreTextBaseWritingDirection)) { + newParagraphStyle.baseWritingDirection = (NSWritingDirection)coreTextBaseWritingDirection; // They're the same enum. + } + + return newParagraphStyle; +} + +@end + +#endif diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitEntityAttribute.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitEntityAttribute.h new file mode 100644 index 0000000..3655138 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitEntityAttribute.h @@ -0,0 +1,36 @@ +// +// ASTextKitEntityAttribute.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import + +#if AS_ENABLE_TEXTNODE + +#import + +/** + The object that should be embedded with ASTextKitEntityAttributeName. Please note that the entity you provide MUST + implement a proper hash and isEqual function or your application performance will grind to a halt due to + NSMutableAttributedString's usage of a global hash table of all attributes. This means the entity should NOT be a + Foundation Collection (NSArray, NSDictionary, NSSet, etc.) since their hash function is a simple count of the values + in the collection, which causes pathological performance problems deep inside NSAttributedString's implementation. + + rdar://19352367 + */ +AS_SUBCLASSING_RESTRICTED +@interface ASTextKitEntityAttribute : NSObject + +@property (nonatomic, readonly) id entity; + +- (instancetype)initWithEntity:(id)entity; + +@end + +#endif diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitEntityAttribute.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitEntityAttribute.mm new file mode 100644 index 0000000..fb87e9b --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitEntityAttribute.mm @@ -0,0 +1,43 @@ +// +// ASTextKitEntityAttribute.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#if AS_ENABLE_TEXTNODE + +@implementation ASTextKitEntityAttribute + +- (instancetype)initWithEntity:(id)entity +{ + if (self = [super init]) { + _entity = entity; + } + return self; +} + +- (NSUInteger)hash +{ + return [_entity hash]; +} + +- (BOOL)isEqual:(id)object +{ + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + ASTextKitEntityAttribute *other = (ASTextKitEntityAttribute *)object; + return _entity == other.entity || [_entity isEqual:other.entity]; +} + +@end + +#endif diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitFontSizeAdjuster.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitFontSizeAdjuster.h new file mode 100644 index 0000000..1b7b10f --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitFontSizeAdjuster.h @@ -0,0 +1,58 @@ +// +// ASTextKitFontSizeAdjuster.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import + +#if AS_ENABLE_TEXTNODE + +#import + +NS_ASSUME_NONNULL_BEGIN + +@class ASTextKitContext; + +AS_SUBCLASSING_RESTRICTED +@interface ASTextKitFontSizeAdjuster : NSObject + +@property (nonatomic) CGSize constrainedSize; + +/** + * Creates a class that will return a scale factor the will make a string fit inside the constrained size. + * + * "Fitting" means that both the longest word in the string will fit without breaking in the constrained + * size's width AND that the entire string will try to fit within attribute's maximumLineCount. The amount + * that the string will scale is based upon the attribute's pointSizeScaleFactors. If the string cannot fit + * in the given width/number of lines, the smallest scale factor will be returned. + * + * @param context The text kit context + * @param constrainedSize The constrained size to render into + * @param textComponentAttributes The renderer's text attributes + */ +- (instancetype)initWithContext:(ASTextKitContext *)context + constrainedSize:(CGSize)constrainedSize + textKitAttributes:(const ASTextKitAttributes &)textComponentAttributes; + +/** + * Returns the best fit scale factor for the text + */ +- (CGFloat)scaleFactor; + +/** + * Takes all of the attributed string attributes dealing with size (font size, line spacing, kerning, etc) and + * scales them by the scaleFactor. I wouldn't be surprised if I missed some in here. + */ ++ (void)adjustFontSizeForAttributeString:(NSMutableAttributedString *)attrString withScaleFactor:(CGFloat)scaleFactor; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitFontSizeAdjuster.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitFontSizeAdjuster.mm new file mode 100644 index 0000000..30591a8 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitFontSizeAdjuster.mm @@ -0,0 +1,240 @@ +// +// ASTextKitFontSizeAdjuster.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + + +#import + +#if AS_ENABLE_TEXTNODE + +#import + +#import +#import +#import + +//#define LOG(...) NSLog(__VA_ARGS__) +#define LOG(...) + +@interface ASTextKitFontSizeAdjuster() +@property (nonatomic, readonly) NSLayoutManager *sizingLayoutManager; +@property (nonatomic, readonly) NSTextContainer *sizingTextContainer; +@end + +@implementation ASTextKitFontSizeAdjuster +{ + __weak ASTextKitContext *_context; + ASTextKitAttributes _attributes; + BOOL _measured; + CGFloat _scaleFactor; + AS::Mutex __instanceLock__; +} + +@synthesize sizingLayoutManager = _sizingLayoutManager; +@synthesize sizingTextContainer = _sizingTextContainer; + +- (instancetype)initWithContext:(ASTextKitContext *)context + constrainedSize:(CGSize)constrainedSize + textKitAttributes:(const ASTextKitAttributes &)textComponentAttributes; +{ + if (self = [super init]) { + _context = context; + _constrainedSize = constrainedSize; + _attributes = textComponentAttributes; + } + return self; +} + ++ (void)adjustFontSizeForAttributeString:(NSMutableAttributedString *)attrString withScaleFactor:(CGFloat)scaleFactor +{ + if (scaleFactor == 1.0) return; + + [attrString beginEditing]; + + // scale all the attributes that will change the bounding box + [attrString enumerateAttributesInRange:NSMakeRange(0, attrString.length) options:0 usingBlock:^(NSDictionary * _Nonnull attrs, NSRange range, BOOL * _Nonnull stop) { + if (attrs[NSFontAttributeName] != nil) { + UIFont *font = attrs[NSFontAttributeName]; + font = [font fontWithSize:std::round(font.pointSize * scaleFactor)]; + [attrString removeAttribute:NSFontAttributeName range:range]; + [attrString addAttribute:NSFontAttributeName value:font range:range]; + } + + if (attrs[NSKernAttributeName] != nil) { + NSNumber *kerning = attrs[NSKernAttributeName]; + [attrString removeAttribute:NSKernAttributeName range:range]; + [attrString addAttribute:NSKernAttributeName value:@([kerning floatValue] * scaleFactor) range:range]; + } + + if (attrs[NSParagraphStyleAttributeName] != nil) { + NSMutableParagraphStyle *paragraphStyle = [attrs[NSParagraphStyleAttributeName] mutableCopy]; + paragraphStyle.lineSpacing = (paragraphStyle.lineSpacing * scaleFactor); + paragraphStyle.paragraphSpacing = (paragraphStyle.paragraphSpacing * scaleFactor); + paragraphStyle.firstLineHeadIndent = (paragraphStyle.firstLineHeadIndent * scaleFactor); + paragraphStyle.headIndent = (paragraphStyle.headIndent * scaleFactor); + paragraphStyle.tailIndent = (paragraphStyle.tailIndent * scaleFactor); + paragraphStyle.minimumLineHeight = (paragraphStyle.minimumLineHeight * scaleFactor); + paragraphStyle.maximumLineHeight = (paragraphStyle.maximumLineHeight * scaleFactor); + + [attrString removeAttribute:NSParagraphStyleAttributeName range:range]; + [attrString addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:range]; + } + + }]; + + [attrString endEditing]; +} + +- (NSUInteger)lineCountForString:(NSAttributedString *)attributedString +{ + NSUInteger lineCount = 0; + + NSLayoutManager *sizingLayoutManager = [self sizingLayoutManager]; + NSTextContainer *sizingTextContainer = [self sizingTextContainer]; + + NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:attributedString]; + [textStorage addLayoutManager:sizingLayoutManager]; + + [sizingLayoutManager ensureLayoutForTextContainer:sizingTextContainer]; + for (NSRange lineRange = { 0, 0 }; NSMaxRange(lineRange) < [sizingLayoutManager numberOfGlyphs] && lineCount <= _attributes.maximumNumberOfLines; lineCount++) { + [sizingLayoutManager lineFragmentRectForGlyphAtIndex:NSMaxRange(lineRange) effectiveRange:&lineRange]; + } + + [textStorage removeLayoutManager:sizingLayoutManager]; + return lineCount; +} + +- (CGSize)boundingBoxForString:(NSAttributedString *)attributedString +{ + NSLayoutManager *sizingLayoutManager = [self sizingLayoutManager]; + NSTextContainer *sizingTextContainer = [self sizingTextContainer]; + + NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:attributedString]; + [textStorage addLayoutManager:sizingLayoutManager]; + + [sizingLayoutManager ensureLayoutForTextContainer:sizingTextContainer]; + CGRect textRect = [sizingLayoutManager boundingRectForGlyphRange:NSMakeRange(0, [textStorage length]) + inTextContainer:sizingTextContainer]; + [textStorage removeLayoutManager:sizingLayoutManager]; + return textRect.size; +} + +- (NSLayoutManager *)sizingLayoutManager +{ + AS::MutexLocker l(__instanceLock__); + if (_sizingLayoutManager == nil) { + _sizingLayoutManager = [[ASLayoutManager alloc] init]; + _sizingLayoutManager.usesFontLeading = NO; + + if (_sizingTextContainer == nil) { + // make this text container unbounded in height so that the layout manager will compute the total + // number of lines and not stop counting when height runs out. + _sizingTextContainer = [[NSTextContainer alloc] initWithSize:CGSizeMake(_constrainedSize.width, CGFLOAT_MAX)]; + _sizingTextContainer.lineFragmentPadding = 0; + + // use 0 regardless of what is in the attributes so that we get an accurate line count + _sizingTextContainer.maximumNumberOfLines = 0; + + _sizingTextContainer.lineBreakMode = _attributes.lineBreakMode; + _sizingTextContainer.exclusionPaths = _attributes.exclusionPaths; + } + [_sizingLayoutManager addTextContainer:_sizingTextContainer]; + } + + return _sizingLayoutManager; +} + +- (CGFloat)scaleFactor +{ + if (_measured) { + return _scaleFactor; + } + + if ([_attributes.pointSizeScaleFactors count] == 0 || isinf(_constrainedSize.width)) { + _measured = YES; + _scaleFactor = 1.0; + return _scaleFactor; + } + + __block CGFloat adjustedScale = 1.0; + + // We add the scale factor of 1 to our scaleFactors array so that in the first iteration of the loop below, we are + // actually determining if we need to scale at all. If something doesn't fit, we will continue to iterate our scale factors. + NSArray *scaleFactors = [@[@(1)] arrayByAddingObjectsFromArray:_attributes.pointSizeScaleFactors]; + + [_context performBlockWithLockedTextKitComponents:^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) { + + // Check for two different situations (and correct for both) + // 1. The longest word in the string fits without being wrapped + // 2. The entire text fits in the given constrained size. + + NSString *str = textStorage.string; + NSArray *words = [str componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; + NSString *longestWordNeedingResize = @""; + for (NSString *word in words) { + if ([word length] > [longestWordNeedingResize length]) { + longestWordNeedingResize = word; + } + } + + // check to see if we may need to shrink for any of these things + BOOL longestWordFits = [longestWordNeedingResize length] ? NO : YES; + BOOL maxLinesFits = self->_attributes.maximumNumberOfLines > 0 ? NO : YES; + BOOL heightFits = isinf(self->_constrainedSize.height) ? YES : NO; + + CGSize longestWordSize = CGSizeZero; + if (longestWordFits == NO) { + NSRange longestWordRange = [str rangeOfString:longestWordNeedingResize]; + NSAttributedString *attrString = [textStorage attributedSubstringFromRange:longestWordRange]; + longestWordSize = [attrString boundingRectWithSize:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin context:nil].size; + } + + // we may need to shrink for some reason, so let's iterate through our scale factors to see if we actually need to shrink + // Note: the first scale factor in the array is 1.0 so will make sure that things don't fit without shrinking + for (NSNumber *adjustedScaleObj in scaleFactors) { + if (longestWordFits && maxLinesFits && heightFits) { + break; + } + + adjustedScale = [adjustedScaleObj floatValue]; + + if (longestWordFits == NO) { + // we need to check the longest word to make sure it fits + longestWordFits = std::ceil((longestWordSize.width * adjustedScale) <= self->_constrainedSize.width); + } + + // if the longest word fits, go ahead and check max line and height. If it didn't fit continue to the next scale factor + if (longestWordFits == YES) { + + // scale our string by the current scale factor + NSMutableAttributedString *scaledString = [[NSMutableAttributedString alloc] initWithAttributedString:textStorage]; + [[self class] adjustFontSizeForAttributeString:scaledString withScaleFactor:adjustedScale]; + + // check to see if this scaled string fit in the max lines + if (maxLinesFits == NO) { + maxLinesFits = ([self lineCountForString:scaledString] <= self->_attributes.maximumNumberOfLines); + } + + // if max lines still doesn't fit, continue without checking that we fit in the constrained height + if (maxLinesFits == YES && heightFits == NO) { + // max lines fit so make sure that we fit in the constrained height. + CGSize stringSize = [self boundingBoxForString:scaledString]; + heightFits = (stringSize.height <= self->_constrainedSize.height); + } + } + } + + }]; + _measured = YES; + _scaleFactor = adjustedScale; + return _scaleFactor; +} + +@end + +#endif diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitRenderer+Positioning.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitRenderer+Positioning.h new file mode 100644 index 0000000..c887282 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitRenderer+Positioning.h @@ -0,0 +1,106 @@ +// +// ASTextKitRenderer+Positioning.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#if AS_ENABLE_TEXTNODE + +typedef void (^as_text_component_index_block_t)(NSUInteger characterIndex, + CGRect glyphBoundingRect, + BOOL *stop); + +/** + Measure options are used to specify which type of line height measurement to use. + + ASTextNodeRendererMeasureOptionLineHeight is faster and will give the height from the baseline to the next line. + + ASTextNodeRendererMeasureOptionCapHeight is a more nuanced measure of the glyphs in the given range that attempts to + produce a visually balanced rectangle above and below the glyphs to produce nice looking text highlights. + + ASTextNodeRendererMeasureOptionBlock uses the cap height option to generate each glyph index, but combines all but the + first and last line rect into a single block. Looks nice for multiline selection. + */ +typedef NS_ENUM(NSUInteger, ASTextKitRendererMeasureOption) { + ASTextKitRendererMeasureOptionLineHeight, + ASTextKitRendererMeasureOptionCapHeight, + ASTextKitRendererMeasureOptionBlock +}; + +@interface ASTextKitRenderer (Positioning) + +/** + Returns the bounding rect for the given character range. + + @param textRange The character range for which the bounding rect will be computed. Should be within the range of the + attributedString of this renderer. + + @discussion In the external, shadowed coordinate space. + */ +- (CGRect)frameForTextRange:(NSRange)textRange; + +/** + Returns an array of rects representing the lines in the given character range + + @param textRange The character range for which the rects will be computed. Should be within the range of the + attributedString of this renderer. + @param measureOption The measure option to use for construction of the rects. See ASTextKitRendererMeasureOption + docs for usage. + + @discussion This method is useful for providing highlighting text. Returned rects are in the coordinate space of the + renderer. + + Triggers initialization of textkit components, truncation, and sizing. + */ +- (NSArray *)rectsForTextRange:(NSRange)textRange + measureOption:(ASTextKitRendererMeasureOption)measureOption; + +/** + Enumerate the text character indexes at a position within the coordinate space of the renderer. + + @param position The point in the shadowed coordinate space at which text indexes will be enumerated. + @param block The block that will be executed for each index identified that may correspond to the given position. The + block is given the character index that corresponds to the glyph at each index in question, as well as the bounding + rect for that glyph. + + @discussion Glyph location based on a touch point is not an exact science because user touches are not well-represented + by a simple point, especially in the context of link-heavy text. So we have this method to make it a bit easier. This + method checks a grid of candidate positions around the touch point you give it, and computes the bounding rect of the + glyph corresponding to the character index given. + + The bounding rect of the glyph can be used to identify the best glyph index that corresponds to your touch. For + instance, comparing centroidal distance from the glyph bounding rect to the touch center is useful for identifying + which link a user actually intended to select. + + Triggers initialization of textkit components, truncation, and sizing. + */ +- (void)enumerateTextIndexesAtPosition:(CGPoint)position + usingBlock:(as_text_component_index_block_t)block; + +/** + Returns the single text index whose glyph's centroid is closest to the given position. + + @param position The point in the shadowed coordinate space that should be checked. + + @discussion This will use the grid enumeration function above, `enumerateTextIndexesAtPosition...`, in order to find + the closest glyph, so it is possible that a glyph could be missed, but ultimately unlikely. + */ +- (NSUInteger)nearestTextIndexAtPosition:(CGPoint)position; + +/** + Returns the trailing rect unused by the renderer in the last rendered line. + + @discussion In the external shadowed coordinate space. + + Triggers initialization of textkit components, truncation, and sizing. + */ +- (CGRect)trailingRect; + +@end + +#endif diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitRenderer+Positioning.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitRenderer+Positioning.mm new file mode 100644 index 0000000..9dc770e --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitRenderer+Positioning.mm @@ -0,0 +1,386 @@ +// +// ASTextKitRenderer+Positioning.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#if AS_ENABLE_TEXTNODE + +#import +#import + +#import + +#import +#import + +static const CGFloat ASTextKitRendererGlyphTouchHitSlop = 5.0; +static const CGFloat ASTextKitRendererTextCapHeightPadding = 1.3; + +@implementation ASTextKitRenderer (Tracking) + +- (NSArray *)rectsForTextRange:(NSRange)textRange measureOption:(ASTextKitRendererMeasureOption)measureOption +{ + __block NSArray *textRects = nil; + [self.context performBlockWithLockedTextKitComponents:^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) { + textRects = [self unlockedRectsForTextRange:textRange measureOptions:measureOption layoutManager:layoutManager textStorage:textStorage textContainer:textContainer]; + }]; + return textRects; +} + +/** + Helper function that should be called within performBlockWithLockedTextKitComponents: in an already locked state to + prevent a deadlock + */ +- (NSArray *)unlockedRectsForTextRange:(NSRange)textRange measureOptions:(ASTextKitRendererMeasureOption)measureOption layoutManager:(NSLayoutManager *)layoutManager textStorage:(NSTextStorage *)textStorage textContainer:(NSTextContainer *)textContainer +{ + NSRange clampedRange = NSIntersectionRange(textRange, NSMakeRange(0, [textStorage length])); + if (clampedRange.location == NSNotFound || clampedRange.length == 0) { + return @[]; + } + + // Used for block measure option + __block CGRect firstRect = CGRectNull; + __block CGRect lastRect = CGRectNull; + __block CGRect blockRect = CGRectNull; + NSMutableArray *mutableTextRects = [NSMutableArray array]; + + NSString *string = textStorage.string; + + NSRange totalGlyphRange = [layoutManager glyphRangeForCharacterRange:clampedRange actualCharacterRange:NULL]; + + [layoutManager enumerateLineFragmentsForGlyphRange:totalGlyphRange usingBlock:^(CGRect rect, + CGRect usedRect, + NSTextContainer *innerTextContainer, + NSRange glyphRange, + BOOL *stop) { + + CGRect lineRect = CGRectNull; + // If we're empty, don't bother looping through glyphs, use the default. + if (CGRectIsEmpty(usedRect)) { + lineRect = usedRect; + } else { + // TextKit's bounding rect computations are just a touch off, so we actually + // compose the rects by hand from the center of the given TextKit bounds and + // imposing the font attributes returned by the glyph's font. + NSRange lineGlyphRange = NSIntersectionRange(totalGlyphRange, glyphRange); + for (NSUInteger i = lineGlyphRange.location; i < NSMaxRange(lineGlyphRange) && i < string.length; i++) { + // We grab the properly sized rect for the glyph + CGRect properGlyphRect = [self _internalRectForGlyphAtIndex:i + measureOption:measureOption + layoutManager:layoutManager + textContainer:textContainer + textStorage:textStorage]; + + // Don't count empty glyphs towards our line rect. + if (!CGRectIsEmpty(properGlyphRect)) { + lineRect = CGRectIsNull(lineRect) ? properGlyphRect + : CGRectUnion(lineRect, properGlyphRect); + } + } + } + + if (!CGRectIsNull(lineRect)) { + if (measureOption == ASTextKitRendererMeasureOptionBlock) { + // For the block measurement option we store the first & last rect as + // special cases, then merge everything else into a single block rect + if (CGRectIsNull(firstRect)) { + // We don't have a firstRect, so we must be on the first line. + firstRect = lineRect; + } else if(CGRectIsNull(lastRect)) { + // We don't have a lastRect, but we do have a firstRect, so we must + // be on the second line. No need to merge in the blockRect just yet + lastRect = lineRect; + } else if(CGRectIsNull(blockRect)) { + // We have both a first and last rect, so we must be on the third line + // we don't have any blockRect to merge it into, so we just set it + // directly. + blockRect = lastRect; + lastRect = lineRect; + } else { + // Everything is already set, so we just merge this line into the + // block. + blockRect = CGRectUnion(blockRect, lastRect); + lastRect = lineRect; + } + } else { + // If the block option isn't being used then each line is being treated + // individually. + [mutableTextRects addObject:[NSValue valueWithCGRect:[self.shadower offsetRectWithInternalRect:lineRect]]]; + } + } + }]; + + if (measureOption == ASTextKitRendererMeasureOptionBlock) { + // Block measure option is handled differently with just 3 vars for the entire range. + if (!CGRectIsNull(firstRect)) { + if (!CGRectIsNull(blockRect)) { + CGFloat rightEdge = MAX(CGRectGetMaxX(blockRect), CGRectGetMaxX(lastRect)); + if (rightEdge > CGRectGetMaxX(firstRect)) { + // Force the right side of the first rect to properly align with the + // right side of the rightmost of the block and last rect + firstRect.size.width += rightEdge - CGRectGetMaxX(firstRect); + } + + // Force the left side of the block rect to properly align with the + // left side of the leftmost of the first and last rect + blockRect.origin.x = MIN(CGRectGetMinX(firstRect), CGRectGetMinX(lastRect)); + // Force the right side of the block rect to properly align with the + // right side of the rightmost of the first and last rect + blockRect.size.width += MAX(CGRectGetMaxX(firstRect), CGRectGetMaxX(lastRect)) - CGRectGetMaxX(blockRect); + } + if (!CGRectIsNull(lastRect)) { + // Force the left edge of the last rect to properly align with the + // left side of the leftmost of the first and block rect, if necessary. + CGFloat leftEdge = MIN(CGRectGetMinX(blockRect), CGRectGetMinX(firstRect)); + CGFloat lastRectNudgeAmount = MAX(CGRectGetMinX(lastRect) - leftEdge, 0); + lastRect.origin.x = MIN(leftEdge, CGRectGetMinX(lastRect)); + lastRect.size.width += lastRectNudgeAmount; + } + + [mutableTextRects addObject:[NSValue valueWithCGRect:[self.shadower offsetRectWithInternalRect:firstRect]]]; + } + if (!CGRectIsNull(blockRect)) { + [mutableTextRects addObject:[NSValue valueWithCGRect:[self.shadower offsetRectWithInternalRect:blockRect]]]; + } + if (!CGRectIsNull(lastRect)) { + [mutableTextRects addObject:[NSValue valueWithCGRect:[self.shadower offsetRectWithInternalRect:lastRect]]]; + } + } + + return [mutableTextRects copy]; +} + +- (NSUInteger)nearestTextIndexAtPosition:(CGPoint)position +{ + // Check in a 9-point region around the actual touch point so we make sure + // we get the best attribute for the touch. + __block CGFloat minimumGlyphDistance = CGFLOAT_MAX; + __block NSUInteger minimumGlyphCharacterIndex = NSNotFound; + + [self enumerateTextIndexesAtPosition:position usingBlock:^(NSUInteger characterIndex, CGRect glyphBoundingRect, BOOL *stop) { + CGPoint glyphLocation = CGPointMake(CGRectGetMidX(glyphBoundingRect), CGRectGetMidY(glyphBoundingRect)); + CGFloat currentDistance = std::sqrt(std::pow(position.x - glyphLocation.x, 2.f) + std::pow(position.y - glyphLocation.y, 2.f)); + if (currentDistance < minimumGlyphDistance) { + minimumGlyphDistance = currentDistance; + minimumGlyphCharacterIndex = characterIndex; + } + }]; + return minimumGlyphCharacterIndex; +} + +/** + Measured from the internal coordinate space of the context, not accounting for shadow offsets. Actually uses CoreText + as an approximation to work around problems in TextKit's glyph sizing. + */ +- (CGRect)_internalRectForGlyphAtIndex:(NSUInteger)glyphIndex + measureOption:(ASTextKitRendererMeasureOption)measureOption + layoutManager:(NSLayoutManager *)layoutManager + textContainer:(NSTextContainer *)textContainer + textStorage:(NSTextStorage *)textStorage +{ + NSUInteger charIndex = [layoutManager characterIndexForGlyphAtIndex:glyphIndex]; + CGGlyph glyph = [layoutManager glyphAtIndex:glyphIndex]; + CTFontRef font = (__bridge_retained CTFontRef)[textStorage attribute:NSFontAttributeName + atIndex:charIndex + effectiveRange:NULL]; + if (font == nil) { + font = (__bridge_retained CTFontRef)[UIFont systemFontOfSize:12.0]; + } + + // Glyph Advance + // +-------------------------+ + // | | + // | | + // +------------------------+--|-------------------------|--+-----------+-----+ What TextKit returns sometimes + // | | | XXXXXXXXXXX + | | | (approx. correct height, but + // | ---------|--+---------+ XXX XXXX +|-----------|-----| sometimes inaccurate bounding + // | | | XXX XXXXX| | | widths) + // | | | XX XX | | | + // | | | XX | | | + // | | | XXX | | | + // | | | XX | | | + // | | | XXXXXXXXXXX | | | + // | Cap Height->| | XX | | | + // | | | XX | Ascent-->| | + // | | | XX | | | + // | | | XX | | | + // | | | X | | | + // | | | X | | | + // | | | X | | | + // | | | XX | | | + // | | | X | | | + // | ---------|-------+ X +-------------------------------------| + // | | XX | | + // | | X | | + // | | XX Descent------>| | + // | | XXXXXX | | + // | | XXX | | + // +------------------------+-------------------------------------------------+ + // | + // +--+Actual bounding box + + CGRect glyphRect = [layoutManager boundingRectForGlyphRange:NSMakeRange(glyphIndex, 1) + inTextContainer:textContainer]; + + // If it is a NSTextAttachment, we don't have the matched glyph and use width of glyphRect instead of advance. + CGFloat advance = (glyph == kCGFontIndexInvalid) ? glyphRect.size.width : CTFontGetAdvancesForGlyphs(font, kCTFontOrientationHorizontal, &glyph, NULL, 1); + + // We treat the center of the glyph's bounding box as the center of our new rect + CGPoint glyphCenter = CGPointMake(CGRectGetMidX(glyphRect), CGRectGetMidY(glyphRect)); + + CGRect properGlyphRect; + if (measureOption == ASTextKitRendererMeasureOptionCapHeight + || measureOption == ASTextKitRendererMeasureOptionBlock) { + CGFloat ascent = CTFontGetAscent(font); + CGFloat descent = CTFontGetDescent(font); + CGFloat capHeight = CTFontGetCapHeight(font); + CGFloat leading = CTFontGetLeading(font); + CGFloat glyphHeight = ascent + descent; + + // For visual balance, we add the cap height padding above the cap, and + // below the baseline, we scale by the descent so it grows with the size of + // the text. + CGFloat topPadding = ASTextKitRendererTextCapHeightPadding * descent; + CGFloat bottomPadding = topPadding; + + properGlyphRect = CGRectMake(glyphCenter.x - advance * 0.5, + glyphCenter.y - glyphHeight * 0.5 + (ascent - capHeight) - topPadding + leading, + advance, + capHeight + topPadding + bottomPadding); + } else { + // We are just measuring the line heights here, so we can use the + // heights used by TextKit, which tend to be pretty good. + properGlyphRect = CGRectMake(glyphCenter.x - advance * 0.5, + glyphRect.origin.y, + advance, + glyphRect.size.height); + } + + CFRelease(font); + + return properGlyphRect; +} + +- (void)enumerateTextIndexesAtPosition:(CGPoint)externalPosition usingBlock:(as_text_component_index_block_t)block +{ + // This method is a little complex because it has to call out to client code from inside an enumeration that needs + // to achieve a lock on the textkit components. It cannot call out to client code from within that lock so we just + // perform the textkit-locked ops inside the locked context. + ASTextKitContext *lockingContext = self.context; + CGPoint internalPosition = [self.shadower offsetPointWithExternalPoint:externalPosition]; + __block BOOL invalidPosition = NO; + [lockingContext performBlockWithLockedTextKitComponents:^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) { + invalidPosition = internalPosition.x > textContainer.size.width + || internalPosition.y > textContainer.size.height + || block == NULL; + }]; + if (invalidPosition) { + // Short circuit if the position is outside the size of this renderer, or if the block is null. + return; + } + + // We break it up into a 44pt box for the touch, and find the closest link attribute-containing glyph to the center of + // the touch. + CGFloat squareSide = 44.f; + // Should be odd if you want to test the center of the touch. + NSInteger pointsOnASide = 3; + + // The distance between any 2 of the adjacent points + CGFloat pointSeparation = squareSide / pointsOnASide; + // These are for tracking which point we're on. We start with -pointsOnASide/2 and go to pointsOnASide/2. So if + // pointsOnASide=3, we go from -1 to 1. + NSInteger endIndex = pointsOnASide / 2; + NSInteger startIndex = -endIndex; + + BOOL stop = NO; + for (NSInteger i = startIndex; i <= endIndex && !stop; i++) { + for (NSInteger j = startIndex; j <= endIndex && !stop; j++) { + CGPoint currentPoint = CGPointMake(internalPosition.x + i * pointSeparation, + internalPosition.y + j * pointSeparation); + + __block NSUInteger characterIndex = NSNotFound; + __block BOOL isValidGlyph = NO; + __block CGRect glyphRect = CGRectNull; + + [lockingContext performBlockWithLockedTextKitComponents:^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) { + // We ask the layout manager for the proper glyph at the touch point + NSUInteger glyphIndex = [layoutManager glyphIndexForPoint:currentPoint + inTextContainer:textContainer]; + + // If it's an invalid glyph, quit. + + [layoutManager glyphAtIndex:glyphIndex isValidIndex:&isValidGlyph]; + if (!isValidGlyph) { + return; + } + + characterIndex = [layoutManager characterIndexForGlyphAtIndex:glyphIndex]; + + glyphRect = [self _internalRectForGlyphAtIndex:glyphIndex + measureOption:ASTextKitRendererMeasureOptionLineHeight + layoutManager:layoutManager + textContainer:textContainer + textStorage:textStorage]; + }]; + + // Sometimes TextKit plays jokes on us and returns glyphs that really aren't close to the point in question. + // Silly TextKit... + if (!isValidGlyph || !CGRectContainsPoint(CGRectInset(glyphRect, -ASTextKitRendererGlyphTouchHitSlop, -ASTextKitRendererGlyphTouchHitSlop), currentPoint)) { + continue; + } + + block(characterIndex, [self.shadower offsetRectWithInternalRect:glyphRect], &stop); + } + } +} + +- (CGRect)trailingRect +{ + __block CGRect trailingRect = CGRectNull; + [self.context performBlockWithLockedTextKitComponents:^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) { + CGSize calculatedSize = textContainer.size; + // If have an empty string, then our whole bounds constitute trailing space. + if ([textStorage length] == 0) { + trailingRect = CGRectMake(0, 0, calculatedSize.width, calculatedSize.height); + return; + } + + // Take everything after our final character as trailing space. + NSRange textRange = NSMakeRange([textStorage length] - 1, 1); + NSArray *finalRects = [self unlockedRectsForTextRange:textRange measureOptions:ASTextKitRendererMeasureOptionLineHeight layoutManager:layoutManager textStorage:textStorage textContainer:textContainer]; + CGRect finalGlyphRect = [[finalRects lastObject] CGRectValue]; + CGPoint origin = CGPointMake(CGRectGetMaxX(finalGlyphRect), CGRectGetMinY(finalGlyphRect)); + CGSize size = CGSizeMake(calculatedSize.width - origin.x, calculatedSize.height - origin.y); + trailingRect = (CGRect){origin, size}; + }]; + return trailingRect; +} + +- (CGRect)frameForTextRange:(NSRange)textRange +{ + __block CGRect textRect = CGRectNull; + [self.context performBlockWithLockedTextKitComponents:^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) { + // Bail on invalid range. + if (NSMaxRange(textRange) > [textStorage length]) { + ASDisplayNodeCFailAssert(@"Invalid range"); + return; + } + + // Force glyph generation and layout. + [layoutManager ensureLayoutForTextContainer:textContainer]; + + NSRange glyphRange = [layoutManager glyphRangeForCharacterRange:textRange actualCharacterRange:NULL]; + textRect = [layoutManager boundingRectForGlyphRange:glyphRange inTextContainer:textContainer]; + }]; + return textRect; +} + +@end + +#endif diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitRenderer+TextChecking.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitRenderer+TextChecking.h new file mode 100644 index 0000000..d4ba74f --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitRenderer+TextChecking.h @@ -0,0 +1,32 @@ +// +// ASTextKitRenderer+TextChecking.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#if AS_ENABLE_TEXTNODE + +/** + Application extensions to NSTextCheckingType. We're allowed to do this (see NSTextCheckingAllCustomTypes). + */ +static uint64_t const ASTextKitTextCheckingTypeEntity = 1ULL << 33; +static uint64_t const ASTextKitTextCheckingTypeTruncation = 1ULL << 34; + +@class ASTextKitEntityAttribute; + +@interface ASTextKitTextCheckingResult : NSTextCheckingResult +@property (nonatomic, readonly) ASTextKitEntityAttribute *entityAttribute; +@end + +@interface ASTextKitRenderer (TextChecking) + +- (NSTextCheckingResult *)textCheckingResultAtPoint:(CGPoint)point; + +@end + +#endif diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitRenderer+TextChecking.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitRenderer+TextChecking.mm new file mode 100644 index 0000000..e556393 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitRenderer+TextChecking.mm @@ -0,0 +1,104 @@ +// +// ASTextKitRenderer+TextChecking.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#if AS_ENABLE_TEXTNODE + +#import +#import +#import + +@implementation ASTextKitTextCheckingResult + +{ + // Be explicit about the fact that we are overriding the super class' implementation of -range and -resultType + // and substituting our own custom values. (We could use @synthesize to make these ivars, but our linter correctly + // complains; it's weird to use @synthesize for properties that are redeclared on top of an original declaration in + // the superclass. We only do it here because NSTextCheckingResult doesn't expose an initializer, which is silly.) + NSRange _rangeOverride; + NSTextCheckingType _resultTypeOverride; +} + +- (instancetype)initWithType:(NSTextCheckingType)type + entityAttribute:(ASTextKitEntityAttribute *)entityAttribute + range:(NSRange)range +{ + if ((self = [super init])) { + _resultTypeOverride = type; + _rangeOverride = range; + _entityAttribute = entityAttribute; + } + return self; +} + +- (NSTextCheckingType)resultType +{ + return _resultTypeOverride; +} + +- (NSRange)range +{ + return _rangeOverride; +} + +@end + +@implementation ASTextKitRenderer (TextChecking) + +- (NSTextCheckingResult *)textCheckingResultAtPoint:(CGPoint)point +{ + __block NSTextCheckingResult *result = nil; + NSAttributedString *attributedString = self.attributes.attributedString; + NSAttributedString *truncationAttributedString = self.attributes.truncationAttributedString; + + // get the index of the last character, so we can handle text in the truncation token + __block NSRange truncationTokenRange = { NSNotFound, 0 }; + + [truncationAttributedString enumerateAttribute:ASTextKitTruncationAttributeName inRange:NSMakeRange(0, truncationAttributedString.length) + options:0 + usingBlock:^(id value, NSRange range, BOOL *stop) { + if (value != nil && range.length > 0) { + truncationTokenRange = range; + } + }]; + + if (truncationTokenRange.location == NSNotFound) { + // The truncation string didn't specify a substring which should be highlighted, so we just highlight it all + truncationTokenRange = { 0, truncationAttributedString.length }; + } + + NSRange visibleRange = self.truncater.firstVisibleRange; + truncationTokenRange.location += NSMaxRange(visibleRange); + + __block CGFloat minDistance = CGFLOAT_MAX; + [self enumerateTextIndexesAtPosition:point usingBlock:^(NSUInteger index, CGRect glyphBoundingRect, BOOL *stop){ + if (index >= truncationTokenRange.location) { + result = [[ASTextKitTextCheckingResult alloc] initWithType:ASTextKitTextCheckingTypeTruncation + entityAttribute:nil + range:truncationTokenRange]; + } else { + NSRange range; + NSDictionary *attributes = [attributedString attributesAtIndex:index effectiveRange:&range]; + ASTextKitEntityAttribute *entityAttribute = attributes[ASTextKitEntityAttributeName]; + CGFloat distance = hypot(CGRectGetMidX(glyphBoundingRect) - point.x, CGRectGetMidY(glyphBoundingRect) - point.y); + if (entityAttribute && distance < minDistance) { + result = [[ASTextKitTextCheckingResult alloc] initWithType:ASTextKitTextCheckingTypeEntity + entityAttribute:entityAttribute + range:range]; + minDistance = distance; + } + } + }]; + return result; +} + +@end + +#endif diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitRenderer.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitRenderer.h new file mode 100644 index 0000000..0f0cc00 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitRenderer.h @@ -0,0 +1,108 @@ +// +// ASTextKitRenderer.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import + +#if AS_ENABLE_TEXTNODE + +#import + +#import + +@class ASTextKitContext; +@class ASTextKitShadower; +@class ASTextKitFontSizeAdjuster; +@protocol ASTextKitTruncating; + +/** + ASTextKitRenderer is a modular object that is responsible for laying out and drawing text. + + A renderer will hold onto the TextKit layouts for the given attributes after initialization. This may constitute a + large amount of memory for large enough applications, so care must be taken when keeping many of these around in-memory + at once. + + This object is designed to be modular and simple. All complex maintenance of state should occur in sub-objects or be + derived via pure functions or categories. No touch-related handling belongs in this class. + + ALL sizing and layout information from this class is in the external coordinate space of the TextKit components. This + is an important distinction because all internal sizing and layout operations are carried out within the shadowed + coordinate space. Padding will be added for you in order to ensure clipping does not occur, and additional information + on this transform is available via the shadower should you need it. + */ +@interface ASTextKitRenderer : NSObject + +/** + Designated Initializer + @discussion Sizing will occur as a result of initialization, so be careful when/where you use this. + */ +- (instancetype)initWithTextKitAttributes:(const ASTextKitAttributes &)textComponentAttributes + constrainedSize:(const CGSize)constrainedSize; + +@property (nonatomic, readonly) ASTextKitContext *context; + +@property (nonatomic, readonly) id truncater; + +@property (nonatomic, readonly) ASTextKitFontSizeAdjuster *fontSizeAdjuster; + +@property (nonatomic, readonly) ASTextKitShadower *shadower; + +@property (nonatomic, readonly) ASTextKitAttributes attributes; + +@property (nonatomic, readonly) CGSize constrainedSize; + +@property (nonatomic, readonly) CGFloat currentScaleFactor; + +#pragma mark - Drawing +/** + Draw the renderer's text content into the bounds provided. + + @param bounds The rect in which to draw the contents of the renderer. + */ +- (void)drawInContext:(CGContextRef)context bounds:(CGRect)bounds; + +#pragma mark - Layout + +/** + Returns the computed size of the renderer given the constrained size and other parameters in the initializer. + */ +- (CGSize)size; + +#pragma mark - Text Ranges + +/** + The character range from the original attributedString that is displayed by the renderer given the parameters in the + initializer. + */ +@property (nonatomic, readonly) std::vector visibleRanges; + +/** + The number of lines shown in the string. + */ +- (NSUInteger)lineCount; + +/** + Whether or not the text is truncated. + */ +- (BOOL)isTruncated; + +@end + +@interface ASTextKitRenderer (ASTextKitRendererConvenience) + +/** + Returns the first visible range or an NSRange with location of NSNotFound and size of 0 if no first visible + range exists + */ +@property (nonatomic, readonly) NSRange firstVisibleRange; + +@end + +#endif diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitRenderer.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitRenderer.mm new file mode 100644 index 0000000..34895cc --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitRenderer.mm @@ -0,0 +1,294 @@ +// +// ASTextKitRenderer.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#if AS_ENABLE_TEXTNODE + +#import + +#import +#import +#import +#import + +//#define LOG(...) NSLog(__VA_ARGS__) +#define LOG(...) + +static NSCharacterSet *_defaultAvoidTruncationCharacterSet() +{ + static NSCharacterSet *truncationCharacterSet; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + NSMutableCharacterSet *mutableCharacterSet = [[NSMutableCharacterSet alloc] init]; + [mutableCharacterSet formUnionWithCharacterSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; + [mutableCharacterSet addCharactersInString:@".,!?:;"]; + truncationCharacterSet = mutableCharacterSet; + }); + return truncationCharacterSet; +} + +@implementation ASTextKitRenderer { + CGSize _calculatedSize; +} +@synthesize attributes = _attributes, context = _context, shadower = _shadower, truncater = _truncater, fontSizeAdjuster = _fontSizeAdjuster; + +#pragma mark - Initialization + +- (instancetype)initWithTextKitAttributes:(const ASTextKitAttributes &)attributes + constrainedSize:(const CGSize)constrainedSize +{ + if (self = [super init]) { + _constrainedSize = constrainedSize; + _attributes = attributes; + _currentScaleFactor = 1; + + // As the renderer should be thread safe, create all subcomponents in the initialization method + _shadower = [ASTextKitShadower shadowerWithShadowOffset:attributes.shadowOffset + shadowColor:attributes.shadowColor + shadowOpacity:attributes.shadowOpacity + shadowRadius:attributes.shadowRadius]; + + // We must inset the constrained size by the size of the shadower. + CGSize shadowConstrainedSize = [[self shadower] insetSizeWithConstrainedSize:_constrainedSize]; + + _context = [[ASTextKitContext alloc] initWithAttributedString:attributes.attributedString + tintColor:attributes.tintColor + lineBreakMode:attributes.lineBreakMode + maximumNumberOfLines:attributes.maximumNumberOfLines + exclusionPaths:attributes.exclusionPaths + constrainedSize:shadowConstrainedSize]; + + NSCharacterSet *avoidTailTruncationSet = attributes.avoidTailTruncationSet ?: _defaultAvoidTruncationCharacterSet(); + _truncater = [[ASTextKitTailTruncater alloc] initWithContext:[self context] + truncationAttributedString:attributes.truncationAttributedString + avoidTailTruncationSet:avoidTailTruncationSet]; + + ASTextKitAttributes attributes = _attributes; + // We must inset the constrained size by the size of the shadower. + _fontSizeAdjuster = [[ASTextKitFontSizeAdjuster alloc] initWithContext:[self context] + constrainedSize:shadowConstrainedSize + textKitAttributes:attributes]; + + // Calcualate size immediately + [self _calculateSize]; + } + return self; +} + +- (NSStringDrawingContext *)stringDrawingContext +{ + // String drawing contexts are not safe to use from more than one thread. + // i.e. if they are created on one thread, it is unsafe to use them on another. + // Therefore we always need to create a new one. + // + // http://web.archive.org/web/20140703122636/https://developer.apple.com/library/ios/documentation/uikit/reference/NSAttributedString_UIKit_Additions/Reference/Reference.html + NSStringDrawingContext *stringDrawingContext = [[NSStringDrawingContext alloc] init]; + + if (isinf(_constrainedSize.width) == NO && _attributes.maximumNumberOfLines > 0) { + ASDisplayNodeAssert(_attributes.maximumNumberOfLines != 1, @"Max line count 1 is not supported in fast-path."); + [stringDrawingContext setValue:@(_attributes.maximumNumberOfLines) forKey:@"maximumNumberOfLines"]; + } + return stringDrawingContext; +} + +#pragma mark - Sizing + +- (CGSize)size +{ + return _calculatedSize; +} + +- (void)_calculateSize +{ + // if we have no scale factors or an unconstrained width, there is no reason to try to adjust the font size + if (isinf(_constrainedSize.width) == NO && [_attributes.pointSizeScaleFactors count] > 0) { + _currentScaleFactor = [[self fontSizeAdjuster] scaleFactor]; + } + + const CGRect constrainedRect = {CGPointZero, _constrainedSize}; + + // If we do not scale, do exclusion, or do custom truncation, we should just use NSAttributedString drawing for a fast-path. + if (self.canUseFastPath) { + CGRect rect = [_attributes.attributedString boundingRectWithSize:_constrainedSize options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingTruncatesLastVisibleLine context:self.stringDrawingContext]; + // Intersect with constrained rect, in case text kit goes out-of-bounds. + rect = CGRectIntersection(rect, constrainedRect); + _calculatedSize = [self.shadower outsetSizeWithInsetSize:rect.size]; + return; + } + + BOOL isScaled = [self isScaled]; + __block NSTextStorage *scaledTextStorage = nil; + if (isScaled) { + // apply the string scale before truncating or else we may truncate the string after we've done the work to shrink it. + [[self context] performBlockWithLockedTextKitComponents:^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) { + NSMutableAttributedString *scaledString = [[NSMutableAttributedString alloc] initWithAttributedString:textStorage]; + [ASTextKitFontSizeAdjuster adjustFontSizeForAttributeString:scaledString withScaleFactor:self->_currentScaleFactor]; + scaledTextStorage = [[NSTextStorage alloc] initWithAttributedString:scaledString]; + + [textStorage removeLayoutManager:layoutManager]; + [scaledTextStorage addLayoutManager:layoutManager]; + }]; + } + + [[self truncater] truncate]; + + __block CGRect boundingRect; + + // Force glyph generation and layout, which may not have happened yet (and isn't triggered by + // -usedRectForTextContainer:). + [[self context] performBlockWithLockedTextKitComponents:^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) { + [layoutManager ensureLayoutForTextContainer:textContainer]; + boundingRect = [layoutManager usedRectForTextContainer:textContainer]; + if (isScaled) { + // put the non-scaled version back + [scaledTextStorage removeLayoutManager:layoutManager]; + [textStorage addLayoutManager:layoutManager]; + } + }]; + + // TextKit often returns incorrect glyph bounding rects in the horizontal direction, so we clip to our bounding rect + // to make sure our width calculations aren't being offset by glyphs going beyond the constrained rect. + boundingRect = CGRectIntersection(boundingRect, constrainedRect); + _calculatedSize = [_shadower outsetSizeWithInsetSize:boundingRect.size]; +} + +- (BOOL)isScaled +{ + return (_currentScaleFactor > 0 && _currentScaleFactor < 1.0); +} + +- (BOOL)usesCustomTruncation +{ + // NOTE: This code does not correctly handle if they set `…` with different attributes. + return _attributes.avoidTailTruncationSet != nil || [_attributes.truncationAttributedString.string isEqualToString:@"\u2026"] == NO; +} + +- (BOOL)usesExclusionPaths +{ + return _attributes.exclusionPaths.count > 0; +} + +- (BOOL)canUseFastPath +{ + return NO; +// Fast path is temporarily disabled, because it's crashing in production. +// NOTE: Remember to re-enable testFastPathTruncation when we re-enable this. +// return self.isScaled == NO +// && self.usesCustomTruncation == NO +// && self.usesExclusionPaths == NO +// // NSAttributedString drawing methods ignore usesLineFragmentOrigin if max line count 1, +// // rendering them useless: +// && (_attributes.maximumNumberOfLines != 1 || isinf(_constrainedSize.width)); +} + +#pragma mark - Drawing + +- (void)drawInContext:(CGContextRef)context bounds:(CGRect)bounds; +{ + // We add an assertion so we can track the rare conditions where a graphics context is not present + ASDisplayNodeAssertNotNil(context, @"This is no good without a context."); + + bounds = CGRectIntersection(bounds, { .size = _constrainedSize }); + CGRect shadowInsetBounds = [[self shadower] insetRectWithConstrainedRect:bounds]; + + CGContextSaveGState(context); + [[self shadower] setShadowInContext:context]; + UIGraphicsPushContext(context); + + LOG(@"%@, shadowInsetBounds = %@",self, NSStringFromCGRect(shadowInsetBounds)); + + // If we use default options, we can use NSAttributedString for a + // fast path. + if (self.canUseFastPath) { + CGRect drawingBounds = shadowInsetBounds; + [_attributes.attributedString drawWithRect:drawingBounds options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingTruncatesLastVisibleLine context:self.stringDrawingContext]; + } else { + BOOL isScaled = [self isScaled]; + [[self context] performBlockWithLockedTextKitComponents:^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) { + + NSTextStorage *scaledTextStorage = nil; + + if (isScaled) { + // if we are going to scale the text, swap out the non-scaled text for the scaled version. + NSMutableAttributedString *scaledString = [[NSMutableAttributedString alloc] initWithAttributedString:textStorage]; + [ASTextKitFontSizeAdjuster adjustFontSizeForAttributeString:scaledString withScaleFactor:self->_currentScaleFactor]; + scaledTextStorage = [[NSTextStorage alloc] initWithAttributedString:scaledString]; + + [textStorage removeLayoutManager:layoutManager]; + [scaledTextStorage addLayoutManager:layoutManager]; + } + + LOG(@"usedRect: %@", NSStringFromCGRect([layoutManager usedRectForTextContainer:textContainer])); + + NSRange glyphRange = [layoutManager glyphRangeForBoundingRect:(CGRect){ .size = textContainer.size } inTextContainer:textContainer]; + LOG(@"boundingRect: %@", NSStringFromCGRect([layoutManager boundingRectForGlyphRange:glyphRange inTextContainer:textContainer])); + + [layoutManager drawBackgroundForGlyphRange:glyphRange atPoint:shadowInsetBounds.origin]; + [layoutManager drawGlyphsForGlyphRange:glyphRange atPoint:shadowInsetBounds.origin]; + + if (isScaled) { + // put the non-scaled version back + [scaledTextStorage removeLayoutManager:layoutManager]; + [textStorage addLayoutManager:layoutManager]; + } + }]; + } + + UIGraphicsPopContext(); + CGContextRestoreGState(context); +} + +#pragma mark - String Ranges + +- (NSUInteger)lineCount +{ + __block NSUInteger lineCount = 0; + [[self context] performBlockWithLockedTextKitComponents:^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) { + for (NSRange lineRange = { 0, 0 }; NSMaxRange(lineRange) < [layoutManager numberOfGlyphs]; lineCount++) { + [layoutManager lineFragmentRectForGlyphAtIndex:NSMaxRange(lineRange) effectiveRange:&lineRange]; + } + }]; + return lineCount; +} + +- (BOOL)isTruncated +{ + if (self.canUseFastPath) { + CGRect boundedRect = [_attributes.attributedString boundingRectWithSize:CGSizeMake(_constrainedSize.width, CGFLOAT_MAX) + options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingTruncatesLastVisibleLine + context:nil]; + return boundedRect.size.height > _constrainedSize.height; + } else { + return self.firstVisibleRange.length < _attributes.attributedString.length; + } +} + +- (std::vector)visibleRanges +{ + return _truncater.visibleRanges; +} + +@end + +@implementation ASTextKitRenderer (ASTextKitRendererConvenience) + +- (NSRange)firstVisibleRange +{ + std::vector visibleRanges = self.visibleRanges; + if (visibleRanges.size() > 0) { + return visibleRanges[0]; + } + + return NSMakeRange(0, 0); +} + +@end + +#endif diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitShadower.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitShadower.h new file mode 100644 index 0000000..2f9c604 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitShadower.h @@ -0,0 +1,78 @@ +// +// ASTextKitShadower.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import + +#if AS_ENABLE_TEXTNODE + +#import + +/** + * @abstract an immutable class for calculating shadow padding drawing a shadowed background for text + */ +AS_SUBCLASSING_RESTRICTED +@interface ASTextKitShadower : NSObject + ++ (ASTextKitShadower *)shadowerWithShadowOffset:(CGSize)shadowOffset + shadowColor:(UIColor *)shadowColor + shadowOpacity:(CGFloat)shadowOpacity + shadowRadius:(CGFloat)shadowRadius; + +/** + * @abstract The offset from the top-left corner at which the shadow starts. + * @discussion A positive width will move the shadow to the right. + * A positive height will move the shadow downwards. + */ +@property (nonatomic, readonly) CGSize shadowOffset; + +//! CGColor in which the shadow is drawn +@property (nonatomic, copy, readonly) UIColor *shadowColor; + +//! Alpha of the shadow +@property (nonatomic, readonly) CGFloat shadowOpacity; + +//! Radius, in pixels +@property (nonatomic, readonly) CGFloat shadowRadius; + +/** + * @abstract The edge insets which represent shadow padding + * @discussion Each edge inset is less than or equal to zero. + * + * Example: + * CGRect boundsWithoutShadowPadding; // Large enough to fit text, not large enough to fit the shadow as well + * UIEdgeInsets shadowPadding = [shadower shadowPadding]; + * CGRect boundsWithShadowPadding = UIEdgeInsetsRect(boundsWithoutShadowPadding, shadowPadding); + */ +- (UIEdgeInsets)shadowPadding; + +- (CGSize)insetSizeWithConstrainedSize:(CGSize)constrainedSize; + +- (CGRect)insetRectWithConstrainedRect:(CGRect)constrainedRect; + +- (CGSize)outsetSizeWithInsetSize:(CGSize)insetSize; + +- (CGRect)outsetRectWithInsetRect:(CGRect)insetRect; + +- (CGRect)offsetRectWithInternalRect:(CGRect)internalRect; + +- (CGPoint)offsetPointWithInternalPoint:(CGPoint)internalPoint; + +- (CGPoint)offsetPointWithExternalPoint:(CGPoint)externalPoint; + +/** + * @abstract draws the shadow for text in the provided CGContext + * @discussion Call within the text node's +drawRect method + */ +- (void)setShadowInContext:(CGContextRef)context; + +@end + +#endif diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitShadower.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitShadower.mm new file mode 100644 index 0000000..a2f37f7 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitShadower.mm @@ -0,0 +1,177 @@ +// +// ASTextKitShadower.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#if AS_ENABLE_TEXTNODE + +#import + +static inline CGSize _insetSize(CGSize size, UIEdgeInsets insets) +{ + size.width -= (insets.left + insets.right); + size.height -= (insets.top + insets.bottom); + return size; +} + +static inline UIEdgeInsets _invertInsets(UIEdgeInsets insets) +{ + return { + .top = -insets.top, + .left = -insets.left, + .bottom = -insets.bottom, + .right = -insets.right + }; +} + +@implementation ASTextKitShadower { + UIEdgeInsets _calculatedShadowPadding; +} + ++ (ASTextKitShadower *)shadowerWithShadowOffset:(CGSize)shadowOffset + shadowColor:(UIColor *)shadowColor + shadowOpacity:(CGFloat)shadowOpacity + shadowRadius:(CGFloat)shadowRadius +{ + /** + * For all cases where no shadow is drawn, we share this singleton shadower to save resources. + */ + static dispatch_once_t onceToken; + static ASTextKitShadower *sharedNonShadower; + dispatch_once(&onceToken, ^{ + sharedNonShadower = [[ASTextKitShadower alloc] initWithShadowOffset:CGSizeZero shadowColor:nil shadowOpacity:0 shadowRadius:0]; + }); + + BOOL hasShadow = shadowOpacity > 0 && (shadowRadius > 0 || CGSizeEqualToSize(shadowOffset, CGSizeZero) == NO) && CGColorGetAlpha(shadowColor.CGColor) > 0; + if (hasShadow == NO) { + return sharedNonShadower; + } else { + return [[ASTextKitShadower alloc] initWithShadowOffset:shadowOffset shadowColor:shadowColor shadowOpacity:shadowOpacity shadowRadius:shadowRadius]; + } +} + +- (instancetype)initWithShadowOffset:(CGSize)shadowOffset + shadowColor:(UIColor *)shadowColor + shadowOpacity:(CGFloat)shadowOpacity + shadowRadius:(CGFloat)shadowRadius +{ + if (self = [super init]) { + _shadowOffset = shadowOffset; + _shadowColor = shadowColor; + _shadowOpacity = shadowOpacity; + _shadowRadius = shadowRadius; + _calculatedShadowPadding = UIEdgeInsetsMake(-INFINITY, -INFINITY, INFINITY, INFINITY); + } + return self; +} + +/* + * This method is duplicated here because it gets called frequently, and we were + * wasting valuable time constructing a state object to ask it. + */ +- (BOOL)_shouldDrawShadow +{ + return _shadowOpacity != 0.0 && (_shadowRadius != 0 || !CGSizeEqualToSize(_shadowOffset, CGSizeZero)) && CGColorGetAlpha(_shadowColor.CGColor) > 0; +} + +- (void)setShadowInContext:(CGContextRef)context +{ + if ([self _shouldDrawShadow]) { + CGColorRef textShadowColor = CGColorRetain(_shadowColor.CGColor); + CGSize textShadowOffset = _shadowOffset; + CGFloat textShadowOpacity = _shadowOpacity; + CGFloat textShadowRadius = _shadowRadius; + + if (textShadowOpacity != 1.0) { + CGFloat inherentAlpha = CGColorGetAlpha(textShadowColor); + + CGColorRef oldTextShadowColor = textShadowColor; + textShadowColor = CGColorCreateCopyWithAlpha(textShadowColor, inherentAlpha * textShadowOpacity); + CGColorRelease(oldTextShadowColor); + } + + CGContextSetShadowWithColor(context, textShadowOffset, textShadowRadius, textShadowColor); + + CGColorRelease(textShadowColor); + } +} + + +- (UIEdgeInsets)shadowPadding +{ + if (_calculatedShadowPadding.top == -INFINITY) { + if (![self _shouldDrawShadow]) { + return UIEdgeInsetsZero; + } + + UIEdgeInsets shadowPadding = UIEdgeInsetsZero; + + // min values are expected to be negative for most typical shadowOffset and + // blurRadius settings: + shadowPadding.top = std::fmin(0.0f, _shadowOffset.height - _shadowRadius); + shadowPadding.left = std::fmin(0.0f, _shadowOffset.width - _shadowRadius); + + shadowPadding.bottom = std::fmin(0.0f, -_shadowOffset.height - _shadowRadius); + shadowPadding.right = std::fmin(0.0f, -_shadowOffset.width - _shadowRadius); + + _calculatedShadowPadding = shadowPadding; + } + + return _calculatedShadowPadding; +} + +- (CGSize)insetSizeWithConstrainedSize:(CGSize)constrainedSize +{ + return _insetSize(constrainedSize, _invertInsets([self shadowPadding])); +} + +- (CGRect)insetRectWithConstrainedRect:(CGRect)constrainedRect +{ + return UIEdgeInsetsInsetRect(constrainedRect, _invertInsets([self shadowPadding])); +} + +- (CGSize)outsetSizeWithInsetSize:(CGSize)insetSize +{ + return _insetSize(insetSize, [self shadowPadding]); +} + +- (CGRect)outsetRectWithInsetRect:(CGRect)insetRect +{ + return UIEdgeInsetsInsetRect(insetRect, [self shadowPadding]); +} + +- (CGRect)offsetRectWithInternalRect:(CGRect)internalRect +{ + return (CGRect){ + .origin = [self offsetPointWithInternalPoint:internalRect.origin], + .size = internalRect.size + }; +} + +- (CGPoint)offsetPointWithInternalPoint:(CGPoint)internalPoint +{ + UIEdgeInsets shadowPadding = [self shadowPadding]; + return (CGPoint){ + internalPoint.x + shadowPadding.left, + internalPoint.y + shadowPadding.top + }; +} + +- (CGPoint)offsetPointWithExternalPoint:(CGPoint)externalPoint +{ + UIEdgeInsets shadowPadding = [self shadowPadding]; + return (CGPoint){ + externalPoint.x - shadowPadding.left, + externalPoint.y - shadowPadding.top + }; +} + +@end + +#endif diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitTailTruncater.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitTailTruncater.h new file mode 100644 index 0000000..70734d9 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitTailTruncater.h @@ -0,0 +1,21 @@ +// +// ASTextKitTailTruncater.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import + +#if AS_ENABLE_TEXTNODE + +AS_SUBCLASSING_RESTRICTED +@interface ASTextKitTailTruncater : NSObject + +@end + +#endif diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitTailTruncater.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitTailTruncater.mm new file mode 100644 index 0000000..0fd8a1c --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitTailTruncater.mm @@ -0,0 +1,197 @@ +// +// ASTextKitTailTruncater.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#if AS_ENABLE_TEXTNODE + +#import + +@implementation ASTextKitTailTruncater +{ + __weak ASTextKitContext *_context; + NSAttributedString *_truncationAttributedString; + NSCharacterSet *_avoidTailTruncationSet; +} +@synthesize visibleRanges = _visibleRanges; + +- (instancetype)initWithContext:(ASTextKitContext *)context + truncationAttributedString:(NSAttributedString *)truncationAttributedString + avoidTailTruncationSet:(NSCharacterSet *)avoidTailTruncationSet +{ + if (self = [super init]) { + _context = context; + _truncationAttributedString = truncationAttributedString; + _avoidTailTruncationSet = avoidTailTruncationSet; + } + return self; +} + +/** + Calculates the intersection of the truncation message within the end of the last line. + */ +- (NSUInteger)_calculateCharacterIndexBeforeTruncationMessage:(NSLayoutManager *)layoutManager + textStorage:(NSTextStorage *)textStorage + textContainer:(NSTextContainer *)textContainer +{ + CGRect constrainedRect = (CGRect){ .size = textContainer.size }; + + NSRange visibleGlyphRange = [layoutManager glyphRangeForBoundingRect:constrainedRect + inTextContainer:textContainer]; + NSInteger lastVisibleGlyphIndex = (NSMaxRange(visibleGlyphRange) - 1); + + if (lastVisibleGlyphIndex < 0) { + return NSNotFound; + } + + CGRect lastLineRect = [layoutManager lineFragmentRectForGlyphAtIndex:lastVisibleGlyphIndex + effectiveRange:NULL]; + CGRect lastLineUsedRect = [layoutManager lineFragmentUsedRectForGlyphAtIndex:lastVisibleGlyphIndex + effectiveRange:NULL]; + NSParagraphStyle *paragraphStyle = [textStorage attributesAtIndex:[layoutManager characterIndexForGlyphAtIndex:lastVisibleGlyphIndex] + effectiveRange:NULL][NSParagraphStyleAttributeName]; + + // We assume LTR so long as the writing direction is not + BOOL rtlWritingDirection = paragraphStyle ? paragraphStyle.baseWritingDirection == NSWritingDirectionRightToLeft : NO; + // We only want to treat the truncation rect as left-aligned in the case that we are right-aligned and our writing + // direction is RTL. + BOOL leftAligned = CGRectGetMinX(lastLineRect) == CGRectGetMinX(lastLineUsedRect) || !rtlWritingDirection; + + // Calculate the bounding rectangle for the truncation message + ASTextKitContext *truncationContext = [[ASTextKitContext alloc] initWithAttributedString:_truncationAttributedString + tintColor:nil + lineBreakMode:NSLineBreakByWordWrapping + maximumNumberOfLines:1 + exclusionPaths:nil + constrainedSize:constrainedRect.size]; + __block CGRect truncationUsedRect; + + [truncationContext performBlockWithLockedTextKitComponents:^(NSLayoutManager *truncationLayoutManager, NSTextStorage *truncationTextStorage, NSTextContainer *truncationTextContainer) { + // Size the truncation message + [truncationLayoutManager ensureLayoutForTextContainer:truncationTextContainer]; + NSRange truncationGlyphRange = [truncationLayoutManager glyphRangeForTextContainer:truncationTextContainer]; + truncationUsedRect = [truncationLayoutManager boundingRectForGlyphRange:truncationGlyphRange + inTextContainer:truncationTextContainer]; + }]; + CGFloat truncationOriginX = (leftAligned ? + CGRectGetMaxX(constrainedRect) - truncationUsedRect.size.width : + CGRectGetMinX(constrainedRect)); + CGRect translatedTruncationRect = CGRectMake(truncationOriginX, + CGRectGetMinY(lastLineRect), + truncationUsedRect.size.width, + truncationUsedRect.size.height); + + // Determine which glyph is the first to be clipped / overlaps the truncation message. + CGFloat truncationMessageX = (leftAligned ? + CGRectGetMinX(translatedTruncationRect) : + CGRectGetMaxX(translatedTruncationRect)); + CGPoint beginningOfTruncationMessage = CGPointMake(truncationMessageX, + CGRectGetMidY(translatedTruncationRect)); + NSUInteger firstClippedGlyphIndex = [layoutManager glyphIndexForPoint:beginningOfTruncationMessage + inTextContainer:textContainer + fractionOfDistanceThroughGlyph:NULL]; + // If it didn't intersect with any text then it should just return the last visible character index, since the + // truncation rect can fully fit on the line without clipping any other text. + if (firstClippedGlyphIndex == NSNotFound) { + return [layoutManager characterIndexForGlyphAtIndex:lastVisibleGlyphIndex]; + } + NSUInteger firstCharacterIndexToReplace = [layoutManager characterIndexForGlyphAtIndex:firstClippedGlyphIndex]; + + // Break on word boundaries + return [self _findTruncationInsertionPointAtOrBeforeCharacterIndex:firstCharacterIndexToReplace + layoutManager:layoutManager + textStorage:textStorage]; +} + +/** + Finds the first whitespace at or before the character index do we don't truncate in the middle of words + If there are multiple whitespaces together (say a space and a newline), this will backtrack to the first one + */ +- (NSUInteger)_findTruncationInsertionPointAtOrBeforeCharacterIndex:(NSUInteger)firstCharacterIndexToReplace + layoutManager:(NSLayoutManager *)layoutManager + textStorage:(NSTextStorage *)textStorage +{ + // Don't attempt to truncate beyond the end of the string + if (firstCharacterIndexToReplace >= textStorage.length) { + return 0; + } + + // Find the glyph range of the line fragment containing the first character to replace. + NSRange lineGlyphRange; + [layoutManager lineFragmentRectForGlyphAtIndex:[layoutManager glyphIndexForCharacterAtIndex:firstCharacterIndexToReplace] + effectiveRange:&lineGlyphRange]; + + // Look for the first whitespace from the end of the line, starting from the truncation point + NSUInteger startingSearchIndex = [layoutManager characterIndexForGlyphAtIndex:lineGlyphRange.location]; + NSUInteger endingSearchIndex = firstCharacterIndexToReplace; + NSRange rangeToSearch = NSMakeRange(startingSearchIndex, (endingSearchIndex - startingSearchIndex)); + + NSRange rangeOfLastVisibleAvoidedChars = { .location = NSNotFound }; + if (_avoidTailTruncationSet) { + rangeOfLastVisibleAvoidedChars = [textStorage.string rangeOfCharacterFromSet:_avoidTailTruncationSet + options:NSBackwardsSearch + range:rangeToSearch]; + } + + // Couldn't find a good place to truncate. Might be because there is no whitespace in the text, or we're dealing + // with a foreign language encoding. Settle for truncating at the original place, which may be mid-word. + if (rangeOfLastVisibleAvoidedChars.location == NSNotFound) { + return firstCharacterIndexToReplace; + } else { + return rangeOfLastVisibleAvoidedChars.location; + } +} + +- (void)truncate +{ + [_context performBlockWithLockedTextKitComponents:^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) { + NSUInteger originalStringLength = textStorage.length; + + [layoutManager ensureLayoutForTextContainer:textContainer]; + + NSRange visibleGlyphRange = [layoutManager glyphRangeForBoundingRect:{ .size = textContainer.size } + inTextContainer:textContainer]; + NSRange visibleCharacterRange = [layoutManager characterRangeForGlyphRange:visibleGlyphRange + actualGlyphRange:NULL]; + + // Check if text is truncated, and if so apply our truncation string + if (visibleCharacterRange.length < originalStringLength && self->_truncationAttributedString.length > 0) { + NSInteger firstCharacterIndexToReplace = [self _calculateCharacterIndexBeforeTruncationMessage:layoutManager + textStorage:textStorage + textContainer:textContainer]; + if (firstCharacterIndexToReplace == 0 || firstCharacterIndexToReplace == NSNotFound) { + return; + } + + // Update/truncate the visible range of text + visibleCharacterRange = NSMakeRange(0, firstCharacterIndexToReplace); + NSRange truncationReplacementRange = NSMakeRange(firstCharacterIndexToReplace, + textStorage.length - firstCharacterIndexToReplace); + // Replace the end of the visible message with the truncation string + [textStorage replaceCharactersInRange:truncationReplacementRange + withAttributedString:self->_truncationAttributedString]; + } + + self->_visibleRanges = { visibleCharacterRange }; + }]; +} + +- (NSRange)firstVisibleRange +{ + std::vector visibleRanges = _visibleRanges; + if (visibleRanges.size() > 0) { + return visibleRanges[0]; + } + + return NSMakeRange(NSNotFound, 0); +} + +@end + +#endif diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitTruncating.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitTruncating.h new file mode 100644 index 0000000..35392fd --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextKitTruncating.h @@ -0,0 +1,61 @@ +// +// ASTextKitTruncating.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import + +#if AS_ENABLE_TEXTNODE + +#import + +#import + +NS_ASSUME_NONNULL_BEGIN + +@class ASTextKitContext; + +@protocol ASTextKitTruncating + +/** + The character range from the original attributedString that is displayed by the renderer given the parameters in the + initializer. + */ +@property (nonatomic, readonly) std::vector visibleRanges; + +/** + Returns the first visible range or an NSRange with location of NSNotFound and size of 0 if no first visible + range exists + */ +@property (nonatomic, readonly) NSRange firstVisibleRange; + +/** + A truncater object is initialized with the full state of the text. It is a Single Responsibility Object that is + mutative. It configures the state of the TextKit components (layout manager, text container, text storage) to achieve + the intended truncation, then it stores the resulting state for later fetching. + + The truncater may mutate the state of the text storage such that only the drawn string is actually present in the + text storage itself. + + The truncater should not store a strong reference to the context to prevent retain cycles. + */ +- (instancetype)initWithContext:(ASTextKitContext *)context + truncationAttributedString:(NSAttributedString * _Nullable)truncationAttributedString + avoidTailTruncationSet:(NSCharacterSet * _Nullable)avoidTailTruncationSet; + +/** + Actually do the truncation. + */ +- (void)truncate; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextNodeTypes.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextNodeTypes.h new file mode 100644 index 0000000..694d9ca --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextNodeTypes.h @@ -0,0 +1,13 @@ +// +// ASTextNodeTypes.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#pragma once + +// Use this attribute name to add "word kerning" +static NSString *const ASTextNodeWordKerningAttributeName = @"ASAttributedStringWordKerning"; diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextNodeWordKerner.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextNodeWordKerner.h new file mode 100644 index 0000000..795c4ea --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextNodeWordKerner.h @@ -0,0 +1,37 @@ +// +// ASTextNodeWordKerner.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + @abstract This class acts as the NSLayoutManagerDelegate for ASTextNode. + @discussion Its current job is word kerning, i.e. adjusting the width of spaces to match the set + wordKernedSpaceWidth. If word kerning is not needed, set the layoutManager's delegate to nil. + */ +AS_SUBCLASSING_RESTRICTED +@interface ASTextNodeWordKerner : NSObject + +/** + The following @optional NSLayoutManagerDelegate methods are implemented: + +- (NSUInteger)layoutManager:(NSLayoutManager *)layoutManager shouldGenerateGlyphs:(const CGGlyph *)glyphs properties:(const NSGlyphProperty *)props characterIndexes:(const NSUInteger *)charIndexes font:(UIFont *)aFont forGlyphRange:(NSRange)glyphRange NS_AVAILABLE_IOS(7_0); + +- (NSControlCharacterAction)layoutManager:(NSLayoutManager *)layoutManager shouldUseAction:(NSControlCharacterAction)action forControlCharacterAtIndex:(NSUInteger)charIndex NS_AVAILABLE_IOS(7_0); + +- (CGRect)layoutManager:(NSLayoutManager *)layoutManager boundingBoxForControlGlyphAtIndex:(NSUInteger)glyphIndex forTextContainer:(NSTextContainer *)textContainer proposedLineFragment:(CGRect)proposedRect glyphPosition:(CGPoint)glyphPosition characterIndex:(NSUInteger)charIndex NS_AVAILABLE_IOS(7_0); + */ + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextNodeWordKerner.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextNodeWordKerner.mm new file mode 100644 index 0000000..67e6405 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/TextKit/ASTextNodeWordKerner.mm @@ -0,0 +1,130 @@ +// +// ASTextNodeWordKerner.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import + +#import + +@implementation ASTextNodeWordKerner + +#pragma mark - NSLayoutManager Delegate +- (NSUInteger)layoutManager:(NSLayoutManager *)layoutManager shouldGenerateGlyphs:(const CGGlyph *)glyphs properties:(const NSGlyphProperty *)properties characterIndexes:(const NSUInteger *)characterIndexes font:(UIFont *)aFont forGlyphRange:(NSRange)glyphRange +{ + NSUInteger glyphCount = glyphRange.length; + NSGlyphProperty *newGlyphProperties = NULL; + + BOOL usesWordKerning = NO; + + // If our typing attributes specify word kerning, specify the spaces as whitespace control characters so we can customize their width. + // Are any of the characters spaces? + NSString *textStorageString = layoutManager.textStorage.string; + for (NSUInteger arrayIndex = 0; arrayIndex < glyphCount; arrayIndex++) { + NSUInteger characterIndex = characterIndexes[arrayIndex]; + if ([textStorageString characterAtIndex:characterIndex] != ' ') + continue; + + // If we've set the whitespace control character for this space already, we have nothing to do. + if (properties[arrayIndex] == NSGlyphPropertyControlCharacter) { + usesWordKerning = YES; + continue; + } + + // Create new glyph properties, if necessary. + if (!newGlyphProperties) { + newGlyphProperties = (NSGlyphProperty *)malloc(sizeof(NSGlyphProperty) * glyphCount); + memcpy(newGlyphProperties, properties, (sizeof(NSGlyphProperty) * glyphCount)); + } + + // It's a space. Make it a whitespace control character. + newGlyphProperties[arrayIndex] = NSGlyphPropertyControlCharacter; + } + + // If we don't have any custom glyph properties, return 0 to indicate to the layout manager that it should use the standard glyphs+properties. + if (!newGlyphProperties) { + if (usesWordKerning) { + // If the text does use word kerning we have to make sure we return the correct glyphCount, or the layout manager will just use the default properties and ignore our kerning. + [layoutManager setGlyphs:glyphs properties:properties characterIndexes:characterIndexes font:aFont forGlyphRange:glyphRange]; + return glyphCount; + } else { + return 0; + } + } + + // Otherwise, use our custom glyph properties. + [layoutManager setGlyphs:glyphs properties:newGlyphProperties characterIndexes:characterIndexes font:aFont forGlyphRange:glyphRange]; + free(newGlyphProperties); + + return glyphCount; +} + +- (NSControlCharacterAction)layoutManager:(NSLayoutManager *)layoutManager shouldUseAction:(NSControlCharacterAction)defaultAction forControlCharacterAtIndex:(NSUInteger)characterIndex +{ + // If it's a space character and we have custom word kerning, use the whitespace action control character. + if ([layoutManager.textStorage.string characterAtIndex:characterIndex] == ' ') + return NSControlCharacterActionWhitespace; + + return defaultAction; +} + +- (CGRect)layoutManager:(NSLayoutManager *)layoutManager boundingBoxForControlGlyphAtIndex:(NSUInteger)glyphIndex forTextContainer:(NSTextContainer *)textContainer proposedLineFragment:(CGRect)proposedRect glyphPosition:(CGPoint)glyphPosition characterIndex:(NSUInteger)characterIndex +{ + CGFloat wordKernedSpaceWidth = [self _wordKernedSpaceWidthForCharacterAtIndex:characterIndex atGlyphPosition:glyphPosition forTextContainer:textContainer layoutManager:layoutManager]; + return CGRectMake(glyphPosition.x, glyphPosition.y, wordKernedSpaceWidth, CGRectGetHeight(proposedRect)); +} + +- (CGFloat)_wordKernedSpaceWidthForCharacterAtIndex:(NSUInteger)characterIndex atGlyphPosition:(CGPoint)glyphPosition forTextContainer:(NSTextContainer *)textContainer layoutManager:(NSLayoutManager *)layoutManager +{ + // We use a map table for pointer equality and non-copying keys. + static NSMapTable *spaceSizes; + // NSMapTable is a defined thread unsafe class, so we need to synchronize + // access in a light manner. So we use dispatch_sync on this queue for all + // access to the map table. + static dispatch_queue_t mapQueue; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + spaceSizes = [[NSMapTable alloc] initWithKeyOptions:NSMapTableStrongMemory valueOptions:NSMapTableStrongMemory capacity:1]; + mapQueue = dispatch_queue_create("org.AsyncDisplayKit.wordKerningQueue", DISPATCH_QUEUE_SERIAL); + }); + CGFloat ordinarySpaceWidth; + UIFont *font = [layoutManager.textStorage attribute:NSFontAttributeName atIndex:characterIndex effectiveRange:NULL]; + CGFloat wordKerning = [[layoutManager.textStorage attribute:ASTextNodeWordKerningAttributeName atIndex:characterIndex effectiveRange:NULL] floatValue]; + __block NSNumber *ordinarySpaceSizeValue; + dispatch_sync(mapQueue, ^{ + ordinarySpaceSizeValue = [spaceSizes objectForKey:font]; + }); + if (ordinarySpaceSizeValue == nil) { + ordinarySpaceWidth = [@" " sizeWithAttributes:@{ NSFontAttributeName : font }].width; + dispatch_async(mapQueue, ^{ + [spaceSizes setObject:@(ordinarySpaceWidth) forKey:font]; + }); + } else { + ordinarySpaceWidth = [ordinarySpaceSizeValue floatValue]; + } + + CGFloat totalKernedWidth = (ordinarySpaceWidth + wordKerning); + + // TextKit normally handles whitespace by increasing the advance of the previous glyph, rather than displaying an + // actual glyph for the whitespace itself. However, in order to implement word kerning, we explicitly require a + // discrete glyph whose bounding box we can specify. The problem is that TextKit does not know this glyph is + // invisible. From TextKit's perspective, this whitespace glyph is a glyph that MUST be displayed. Thus when it + // comes to determining linebreaks, the width of this trailing whitespace glyph is considered. This causes + // our text to wrap sooner than it otherwise would, as room is allocated at the end of each line for a glyph that + // isn't actually visible. To implement our desired behavior, we check to see if the current whitespace glyph + // would break to the next line. If it breaks to the next line, then this constitutes trailing whitespace, and + // we specify enough room to fill up the remainder of the line, but nothing more. + if (glyphPosition.x + totalKernedWidth > textContainer.size.width) { + return (textContainer.size.width - glyphPosition.x); + } + + return totalKernedWidth; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/UIImage+ASConvenience.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/UIImage+ASConvenience.h new file mode 100644 index 0000000..e662a6a --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/UIImage+ASConvenience.h @@ -0,0 +1,162 @@ +// +// UIImage+ASConvenience.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * Dramatically faster version of +[UIImage imageNamed:]. Although it is believed that imageNamed: + * has a cache and is fast, it actually performs expensive asset catalog lookups and is often a + * performance bottleneck (verified on iOS 7 through iOS 10). + * + * Use [UIImage as_imageNamed:] anywhere in your app, even if you aren't using other parts of ASDK. + * Although not the best choice for extremely large assets that are only used once, it is the ideal + * choice for any assets used in tab bars, nav bars, buttons, table or collection cells, etc. + */ + +@interface UIImage (ASDKFastImageNamed) + +/** + * A version of imageNamed that caches results because loading an image is expensive. + * Calling with the same name value will usually return the same object. A UIImage, + * after creation, is immutable and thread-safe so it's fine to share these objects across multiple threads. + * + * @param imageName The name of the image to load + * @return The loaded image or nil + */ ++ (nullable UIImage *)as_imageNamed:(NSString *)imageName NS_RETURNS_RETAINED; + +/** + * A version of imageNamed that caches results because loading an image is expensive. + * Calling with the same name value will usually return the same object. A UIImage, + * after creation, is immutable and thread-safe so it's fine to share these objects across multiple threads. + * + * @param imageName The name of the image to load + * @param traitCollection The traits associated with the intended environment for the image. + * @return The loaded image or nil + */ ++ (nullable UIImage *)as_imageNamed:(NSString *)imageName compatibleWithTraitCollection:(nullable UITraitCollection *)traitCollection NS_RETURNS_RETAINED; + +@end + +/** + * High-performance flat-colored, rounded-corner resizable images + * + * For "Baked-in Opaque" corners, set cornerColor equal to the color behind the rounded image object, + * i.e. the background color. + * For "Baked-in Alpha" corners, set cornerColor = [UIColor clearColor] + * + * See http://asyncdisplaykit.org/docs/corner-rounding.html for an explanation. + */ + +@interface UIImage (ASDKResizableRoundedRects) + +/** + * This generates a flat-color, rounded-corner resizeable image + * + * @param cornerRadius The radius of the rounded-corner + * @param cornerColor The fill color of the corners (For Alpha corners use clearColor) + * @param fillColor The fill color of the rounded-corner image + */ ++ (UIImage *)as_resizableRoundedImageWithCornerRadius:(CGFloat)cornerRadius + cornerColor:(nullable UIColor *)cornerColor + fillColor:(UIColor *)fillColor NS_RETURNS_RETAINED AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED_MSG("Use as_resizableRoundedImageWithCornerRadius:cornerColor:fillColor:traitCollection: instead"); + +/** + * This generates a flat-color, rounded-corner resizeable image + * + * @param cornerRadius The radius of the rounded-corner + * @param cornerColor The fill color of the corners (For Alpha corners use clearColor) + * @param fillColor The fill color of the rounded-corner image + * @param traitCollection The trait collection. + */ ++ (UIImage *)as_resizableRoundedImageWithCornerRadius:(CGFloat)cornerRadius + cornerColor:(nullable UIColor *)cornerColor + fillColor:(UIColor *)fillColor + traitCollection:(ASPrimitiveTraitCollection) traitCollection NS_RETURNS_RETAINED AS_WARN_UNUSED_RESULT; + +/** + * This generates a flat-color, rounded-corner resizeable image with a border + * + * @param cornerRadius The radius of the rounded-corner + * @param cornerColor The fill color of the corners (For Alpha corners use clearColor) + * @param fillColor The fill color of the rounded-corner image + * @param borderColor The border color. Set to nil for no border. + * @param borderWidth The border width. Dummy value if borderColor = nil. + */ ++ (UIImage *)as_resizableRoundedImageWithCornerRadius:(CGFloat)cornerRadius + cornerColor:(UIColor *)cornerColor + fillColor:(UIColor *)fillColor + borderColor:(nullable UIColor *)borderColor + borderWidth:(CGFloat)borderWidth NS_RETURNS_RETAINED AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED_MSG("Use as_resizableRoundedImageWithCornerRadius:cornerColor:fillColor:borderColor:borderWidth:traitCollection: instead"); + +/** + * This generates a flat-color, rounded-corner resizeable image with a border + * + * @param cornerRadius The radius of the rounded-corner + * @param cornerColor The fill color of the corners (For Alpha corners use clearColor) + * @param fillColor The fill color of the rounded-corner image + * @param borderColor The border color. Set to nil for no border. + * @param borderWidth The border width. Dummy value if borderColor = nil. + * @param traitCollection The trait collection. + */ ++ (UIImage *)as_resizableRoundedImageWithCornerRadius:(CGFloat)cornerRadius + cornerColor:(UIColor *)cornerColor + fillColor:(UIColor *)fillColor + borderColor:(nullable UIColor *)borderColor + borderWidth:(CGFloat)borderWidth traitCollection:(ASPrimitiveTraitCollection) traitCollection NS_RETURNS_RETAINED AS_WARN_UNUSED_RESULT; + +/** + * This generates a flat-color, rounded-corner resizeable image with a border + * + * @param cornerRadius The radius of the rounded-corner + * @param cornerColor The fill color of the corners (For Alpha corners use clearColor) + * @param fillColor The fill color of the rounded-corner image + * @param borderColor The border color. Set to nil for no border. + * @param borderWidth The border width. Dummy value if borderColor = nil. + * @param roundedCorners Select individual or multiple corners to round. Set to UIRectCornerAllCorners to round all 4 corners. + * @param scale The number of pixels per point. Provide 0.0 to use the screen scale. + */ ++ (UIImage *)as_resizableRoundedImageWithCornerRadius:(CGFloat)cornerRadius + cornerColor:(nullable UIColor *)cornerColor + fillColor:(UIColor *)fillColor + borderColor:(nullable UIColor *)borderColor + borderWidth:(CGFloat)borderWidth + roundedCorners:(UIRectCorner)roundedCorners + scale:(CGFloat)scale NS_RETURNS_RETAINED AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED_MSG("Use as_resizableRoundedImageWithCornerRadius:cornerColor:fillColor:borderColor:borderWidth:roundedCorners:traitCollection: instead"); +; + +/** + * This generates a flat-color, rounded-corner resizeable image with a border + * + * @param cornerRadius The radius of the rounded-corner + * @param cornerColor The fill color of the corners (For Alpha corners use clearColor) + * @param fillColor The fill color of the rounded-corner image + * @param borderColor The border color. Set to nil for no border. + * @param borderWidth The border width. Dummy value if borderColor = nil. + * @param roundedCorners Select individual or multiple corners to round. Set to UIRectCornerAllCorners to round all 4 corners. + * @param scale The number of pixels per point. Provide 0.0 to use the screen scale. + * @param traitCollection The trait collection. + */ ++ (UIImage *)as_resizableRoundedImageWithCornerRadius:(CGFloat)cornerRadius + cornerColor:(nullable UIColor *)cornerColor + fillColor:(UIColor *)fillColor + borderColor:(nullable UIColor *)borderColor + borderWidth:(CGFloat)borderWidth + roundedCorners:(UIRectCorner)roundedCorners + scale:(CGFloat)scale + traitCollection:(ASPrimitiveTraitCollection) traitCollection +NS_RETURNS_RETAINED AS_WARN_UNUSED_RESULT; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/UIImage+ASConvenience.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/UIImage+ASConvenience.mm new file mode 100644 index 0000000..cc5896b --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/UIImage+ASConvenience.mm @@ -0,0 +1,225 @@ +// +// UIImage+ASConvenience.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import + +#pragma mark - ASDKFastImageNamed + +@implementation UIImage (ASDKFastImageNamed) + +UIImage *cachedImageNamed(NSString *imageName, UITraitCollection *traitCollection) NS_RETURNS_RETAINED +{ + static NSCache *imageCache = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + // Because NSCache responds to memory warnings, we do not need an explicit limit. + // all of these objects contain compressed image data and are relatively small + // compared to the backing stores of text and image views. + imageCache = [[NSCache alloc] init]; + }); + + UIImage *image = nil; + if ([imageName length] > 0) { + NSString *imageKey = imageName; + if (traitCollection) { + char imageKeyBuffer[256]; + if (@available(iOS 12.0, tvOS 10.0, *)) { + snprintf(imageKeyBuffer, sizeof(imageKeyBuffer), "%s|%ld|%ld|%ld", imageName.UTF8String, (long)traitCollection.horizontalSizeClass, (long)traitCollection.verticalSizeClass, (long)traitCollection.userInterfaceStyle); + } else { + // Fallback on earlier versions + snprintf(imageKeyBuffer, sizeof(imageKeyBuffer), "%s|%ld|%ld", imageName.UTF8String, (long)traitCollection.horizontalSizeClass, (long)traitCollection.verticalSizeClass); + } + imageKey = [NSString stringWithUTF8String:imageKeyBuffer]; + } + + image = [imageCache objectForKey:imageKey]; + if (!image) { + image = [UIImage imageNamed:imageName inBundle:nil compatibleWithTraitCollection:traitCollection]; + if (image) { + [imageCache setObject:image forKey:imageKey]; + } + } + } + return image; +} + ++ (UIImage *)as_imageNamed:(NSString *)imageName NS_RETURNS_RETAINED +{ + return cachedImageNamed(imageName, nil); +} + ++ (UIImage *)as_imageNamed:(NSString *)imageName compatibleWithTraitCollection:(UITraitCollection *)traitCollection NS_RETURNS_RETAINED +{ + return cachedImageNamed(imageName, traitCollection); +} + +@end + +#pragma mark - ASDKResizableRoundedRects + +@implementation UIImage (ASDKResizableRoundedRects) + ++ (UIImage *)as_resizableRoundedImageWithCornerRadius:(CGFloat)cornerRadius + cornerColor:(UIColor *)cornerColor + fillColor:(UIColor *)fillColor NS_RETURNS_RETAINED +{ + return [self as_resizableRoundedImageWithCornerRadius:cornerRadius + cornerColor:cornerColor + fillColor:fillColor + borderColor:nil + borderWidth:1.0 + roundedCorners:UIRectCornerAllCorners + scale:0.0]; +} + ++ (UIImage *)as_resizableRoundedImageWithCornerRadius:(CGFloat)cornerRadius + cornerColor:(UIColor *)cornerColor + fillColor:(UIColor *)fillColor + traitCollection:(ASPrimitiveTraitCollection) traitCollection NS_RETURNS_RETAINED +{ + return [self as_resizableRoundedImageWithCornerRadius:cornerRadius + cornerColor:cornerColor + fillColor:fillColor + borderColor:nil + borderWidth:1.0 + roundedCorners:UIRectCornerAllCorners + scale:0.0 + traitCollection:traitCollection]; +} + ++ (UIImage *)as_resizableRoundedImageWithCornerRadius:(CGFloat)cornerRadius + cornerColor:(UIColor *)cornerColor + fillColor:(UIColor *)fillColor + borderColor:(UIColor *)borderColor + borderWidth:(CGFloat)borderWidth + traitCollection:(ASPrimitiveTraitCollection) traitCollection NS_RETURNS_RETAINED { + return [self as_resizableRoundedImageWithCornerRadius:cornerRadius + cornerColor:cornerColor + fillColor:fillColor + borderColor:borderColor + borderWidth:borderWidth + roundedCorners:UIRectCornerAllCorners + scale:0.0 + traitCollection:traitCollection]; +} + + ++ (UIImage *)as_resizableRoundedImageWithCornerRadius:(CGFloat)cornerRadius + cornerColor:(UIColor *)cornerColor + fillColor:(UIColor *)fillColor + borderColor:(UIColor *)borderColor + borderWidth:(CGFloat)borderWidth NS_RETURNS_RETAINED +{ + return [self as_resizableRoundedImageWithCornerRadius:cornerRadius + cornerColor:cornerColor + fillColor:fillColor + borderColor:borderColor + borderWidth:borderWidth + roundedCorners:UIRectCornerAllCorners + scale:0.0]; +} + ++ (UIImage *)as_resizableRoundedImageWithCornerRadius:(CGFloat)cornerRadius + cornerColor:(UIColor *)cornerColor + fillColor:(UIColor *)fillColor + borderColor:(UIColor *)borderColor + borderWidth:(CGFloat)borderWidth + roundedCorners:(UIRectCorner)roundedCorners + scale:(CGFloat)scale NS_RETURNS_RETAINED { + + return [self as_resizableRoundedImageWithCornerRadius:cornerRadius + cornerColor:cornerColor + fillColor:fillColor + borderColor:borderColor + borderWidth:borderWidth + roundedCorners:roundedCorners + scale:scale + traitCollection:ASPrimitiveTraitCollectionMakeDefault()]; +} + + ++ (UIImage *)as_resizableRoundedImageWithCornerRadius:(CGFloat)cornerRadius + cornerColor:(UIColor *)cornerColor + fillColor:(UIColor *)fillColor + borderColor:(UIColor *)borderColor + borderWidth:(CGFloat)borderWidth + roundedCorners:(UIRectCorner)roundedCorners + scale:(CGFloat)scale + traitCollection:(ASPrimitiveTraitCollection) traitCollection NS_RETURNS_RETAINED +{ + static NSCache *__pathCache = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + __pathCache = [[NSCache alloc] init]; + // UIBezierPath objects are fairly small and these are equally sized. 20 should be plenty for many different parameters. + __pathCache.countLimit = 20; + }); + + // Treat clear background color as no background color + if ([cornerColor isEqual:[UIColor clearColor]]) { + cornerColor = nil; + } + + CGFloat dimension = (cornerRadius * 2) + 1; + CGRect bounds = CGRectMake(0, 0, dimension, dimension); + + typedef struct { + UIRectCorner corners; + CGFloat radius; + } PathKey; + PathKey key = { roundedCorners, cornerRadius }; + NSValue *pathKeyObject = [[NSValue alloc] initWithBytes:&key objCType:@encode(PathKey)]; + + CGSize cornerRadii = CGSizeMake(cornerRadius, cornerRadius); + UIBezierPath *path = [__pathCache objectForKey:pathKeyObject]; + if (path == nil) { + path = [UIBezierPath bezierPathWithRoundedRect:bounds byRoundingCorners:roundedCorners cornerRadii:cornerRadii]; + [__pathCache setObject:path forKey:pathKeyObject]; + } + + // We should probably check if the background color has any alpha component but that + // might be expensive due to needing to check mulitple color spaces. + UIImage *result = ASGraphicsCreateImage(traitCollection, bounds.size, cornerColor != nil, scale, nil, nil, ^{ + BOOL contextIsClean = YES; + if (cornerColor) { + contextIsClean = NO; + [cornerColor setFill]; + // Copy "blend" mode is extra fast because it disregards any value currently in the buffer and overrides directly. + UIRectFillUsingBlendMode(bounds, kCGBlendModeCopy); + } + + BOOL canUseCopy = contextIsClean || (CGColorGetAlpha(fillColor.CGColor) == 1); + [fillColor setFill]; + [path fillWithBlendMode:(canUseCopy ? kCGBlendModeCopy : kCGBlendModeNormal) alpha:1]; + + if (borderColor) { + [borderColor setStroke]; + + // Inset border fully inside filled path (not halfway on each side of path) + CGRect strokeRect = CGRectInset(bounds, borderWidth / 2.0, borderWidth / 2.0); + + // It is rarer to have a stroke path, and our cache key only handles rounded rects for the exact-stretchable + // size calculated by cornerRadius, so we won't bother caching this path. Profiling validates this decision. + UIBezierPath *strokePath = [UIBezierPath bezierPathWithRoundedRect:strokeRect + byRoundingCorners:roundedCorners + cornerRadii:cornerRadii]; + [strokePath setLineWidth:borderWidth]; + BOOL canUseCopy = (CGColorGetAlpha(borderColor.CGColor) == 1); + [strokePath strokeWithBlendMode:(canUseCopy ? kCGBlendModeCopy : kCGBlendModeNormal) alpha:1]; + } + }); + + UIEdgeInsets capInsets = UIEdgeInsetsMake(cornerRadius, cornerRadius, cornerRadius, cornerRadius); + result = [result resizableImageWithCapInsets:capInsets resizingMode:UIImageResizingModeStretch]; + + return result; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/UIResponder+AsyncDisplayKit.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/UIResponder+AsyncDisplayKit.h new file mode 100644 index 0000000..17afab3 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/UIResponder+AsyncDisplayKit.h @@ -0,0 +1,25 @@ +// +// UIResponder+AsyncDisplayKit.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface UIResponder (AsyncDisplayKit) + +/** + * The nearest view controller above this responder, if one exists. + * + * This property must be accessed on the main thread. + */ +@property (nonatomic, nullable, readonly) __kindof UIViewController *asdk_associatedViewController; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/UIResponder+AsyncDisplayKit.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/UIResponder+AsyncDisplayKit.mm new file mode 100644 index 0000000..957e64b --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/UIResponder+AsyncDisplayKit.mm @@ -0,0 +1,31 @@ +// +// UIResponder+AsyncDisplayKit.m +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import "UIResponder+AsyncDisplayKit.h" + +#import +#import + +@implementation UIResponder (AsyncDisplayKit) + +- (__kindof UIViewController *)asdk_associatedViewController +{ + ASDisplayNodeAssertMainThread(); + + for (UIResponder *responder in [self asdk_responderChainEnumerator]) { + UIViewController *vc = ASDynamicCast(responder, UIViewController); + if (vc) { + return vc; + } + } + return nil; +} + +@end + diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/_ASTransitionContext.h b/Starbucks_Clone_TexBrother/Pods/Texture/Source/_ASTransitionContext.h new file mode 100644 index 0000000..44c4906 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/_ASTransitionContext.h @@ -0,0 +1,49 @@ +// +// _ASTransitionContext.h +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import + +#import + +@class ASLayout; +@class _ASTransitionContext; + +@protocol _ASTransitionContextLayoutDelegate + +- (NSArray *)currentSubnodesWithTransitionContext:(_ASTransitionContext *)context; + +- (NSArray *)insertedSubnodesWithTransitionContext:(_ASTransitionContext *)context; +- (NSArray *)removedSubnodesWithTransitionContext:(_ASTransitionContext *)context; + +- (ASLayout *)transitionContext:(_ASTransitionContext *)context layoutForKey:(NSString *)key; +- (ASSizeRange)transitionContext:(_ASTransitionContext *)context constrainedSizeForKey:(NSString *)key; + +@end + +@protocol _ASTransitionContextCompletionDelegate + +- (void)transitionContext:(_ASTransitionContext *)context didComplete:(BOOL)didComplete; + +@end + +@interface _ASTransitionContext : NSObject + +@property (nonatomic, readonly, getter=isAnimated) BOOL animated; + +- (instancetype)initWithAnimation:(BOOL)animated + layoutDelegate:(id<_ASTransitionContextLayoutDelegate>)layoutDelegate + completionDelegate:(id<_ASTransitionContextCompletionDelegate>)completionDelegate; + +@end + +@interface _ASAnimatedTransitionContext : NSObject +@property (nonatomic, readonly) ASDisplayNode *node; +@property (nonatomic, readonly) CGFloat alpha; ++ (instancetype)contextForNode:(ASDisplayNode *)node alpha:(CGFloat)alphaValue NS_RETURNS_RETAINED; +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/_ASTransitionContext.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/_ASTransitionContext.mm new file mode 100644 index 0000000..40a3573 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/_ASTransitionContext.mm @@ -0,0 +1,104 @@ +// +// _ASTransitionContext.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#import +#import + + +NSString * const ASTransitionContextFromLayoutKey = @"org.asyncdisplaykit.ASTransitionContextFromLayoutKey"; +NSString * const ASTransitionContextToLayoutKey = @"org.asyncdisplaykit.ASTransitionContextToLayoutKey"; + +@interface _ASTransitionContext () + +@property (weak, nonatomic) id<_ASTransitionContextLayoutDelegate> layoutDelegate; +@property (weak, nonatomic) id<_ASTransitionContextCompletionDelegate> completionDelegate; + +@end + +@implementation _ASTransitionContext + +- (instancetype)initWithAnimation:(BOOL)animated + layoutDelegate:(id<_ASTransitionContextLayoutDelegate>)layoutDelegate + completionDelegate:(id<_ASTransitionContextCompletionDelegate>)completionDelegate +{ + self = [super init]; + if (self) { + _animated = animated; + _layoutDelegate = layoutDelegate; + _completionDelegate = completionDelegate; + } + return self; +} + +#pragma mark - ASContextTransitioning Protocol Implementation + +- (ASLayout *)layoutForKey:(NSString *)key +{ + return [_layoutDelegate transitionContext:self layoutForKey:key]; +} + +- (ASSizeRange)constrainedSizeForKey:(NSString *)key +{ + return [_layoutDelegate transitionContext:self constrainedSizeForKey:key]; +} + +- (CGRect)initialFrameForNode:(ASDisplayNode *)node +{ + return [[self layoutForKey:ASTransitionContextFromLayoutKey] frameForElement:node]; +} + +- (CGRect)finalFrameForNode:(ASDisplayNode *)node +{ + return [[self layoutForKey:ASTransitionContextToLayoutKey] frameForElement:node]; +} + +- (NSArray *)subnodesForKey:(NSString *)key +{ + NSMutableArray *subnodes = [[NSMutableArray alloc] init]; + for (ASLayout *sublayout in [self layoutForKey:key].sublayouts) { + [subnodes addObject:(ASDisplayNode *)sublayout.layoutElement]; + } + return subnodes; +} + +- (NSArray *)insertedSubnodes +{ + return [_layoutDelegate insertedSubnodesWithTransitionContext:self]; +} + +- (NSArray *)removedSubnodes +{ + return [_layoutDelegate removedSubnodesWithTransitionContext:self]; +} + +- (void)completeTransition:(BOOL)didComplete +{ + [_completionDelegate transitionContext:self didComplete:didComplete]; +} + +@end + + +@interface _ASAnimatedTransitionContext () +@property (nonatomic) ASDisplayNode *node; +@property (nonatomic) CGFloat alpha; +@end + +@implementation _ASAnimatedTransitionContext + ++ (instancetype)contextForNode:(ASDisplayNode *)node alpha:(CGFloat)alpha NS_RETURNS_RETAINED +{ + _ASAnimatedTransitionContext *context = [[_ASAnimatedTransitionContext alloc] init]; + context.node = node; + context.alpha = alpha; + return context; +} + +@end diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/tvOS/ASControlNode+tvOS.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/tvOS/ASControlNode+tvOS.mm new file mode 100644 index 0000000..a823d96 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/tvOS/ASControlNode+tvOS.mm @@ -0,0 +1,92 @@ +// +// ASControlNode+tvOS.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#if TARGET_OS_TV +#import +#import + +@implementation ASControlNode (tvOS) + +#pragma mark - tvOS +- (void)_pressDown +{ + [UIView animateWithDuration:0.1 delay:0 options:UIViewAnimationOptionCurveLinear animations:^{ + [self setPressedState]; + } completion:^(BOOL finished) { + if (finished) { + [UIView animateWithDuration:0.1 delay:0 options:UIViewAnimationOptionCurveLinear animations:^{ + [self setFocusedState]; + } completion:nil]; + } + }]; +} + +- (BOOL)canBecomeFocused +{ + return YES; +} + +- (BOOL)shouldUpdateFocusInContext:(nonnull UIFocusUpdateContext *)context +{ + return YES; +} + +- (void)didUpdateFocusInContext:(UIFocusUpdateContext *)context withAnimationCoordinator:(UIFocusAnimationCoordinator *)coordinator +{ + //FIXME: This is never valid inside an ASCellNode + if (context.nextFocusedView && context.nextFocusedView == self.view) { + //Focused + [coordinator addCoordinatedAnimations:^{ + [self setFocusedState]; + } completion:nil]; + } else{ + //Not focused + [coordinator addCoordinatedAnimations:^{ + [self setDefaultFocusAppearance]; + } completion:nil]; + } +} + +- (void)setFocusedState +{ + CALayer *layer = self.layer; + layer.shadowOffset = CGSizeMake(2, 10); + [self applyDefaultShadowProperties: layer]; + self.view.transform = CGAffineTransformScale(CGAffineTransformIdentity, 1.1, 1.1); +} + +- (void)setPressedState +{ + CALayer *layer = self.layer; + layer.shadowOffset = CGSizeMake(2, 2); + [self applyDefaultShadowProperties: layer]; + self.view.transform = CGAffineTransformScale(CGAffineTransformIdentity, 1, 1); +} + +- (void)applyDefaultShadowProperties:(CALayer *)layer +{ + layer.shadowColor = [UIColor blackColor].CGColor; + layer.shadowRadius = 12.0; + layer.shadowOpacity = 0.45; + layer.shadowPath = [UIBezierPath bezierPathWithRect:self.layer.bounds].CGPath; +} + +- (void)setDefaultFocusAppearance +{ + CALayer *layer = self.layer; + layer.shadowOffset = CGSizeZero; + layer.shadowColor = [UIColor blackColor].CGColor; + layer.shadowRadius = 0; + layer.shadowOpacity = 0; + layer.shadowPath = [UIBezierPath bezierPathWithRect:self.layer.bounds].CGPath; + self.view.transform = CGAffineTransformScale(CGAffineTransformIdentity, 1, 1); +} +@end +#endif diff --git a/Starbucks_Clone_TexBrother/Pods/Texture/Source/tvOS/ASImageNode+tvOS.mm b/Starbucks_Clone_TexBrother/Pods/Texture/Source/tvOS/ASImageNode+tvOS.mm new file mode 100644 index 0000000..fa760f1 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Pods/Texture/Source/tvOS/ASImageNode+tvOS.mm @@ -0,0 +1,191 @@ +// +// ASImageNode+tvOS.mm +// Texture +// +// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. +// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved. +// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 +// + +#import +#if TARGET_OS_TV +#import +#import + +// TODO: Remove this – we don't need to link GLKit just to convert degrees to radians. +#import +#import + +#import + +@implementation ASImageNode (tvOS) + +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event +{ + [super touchesBegan:touches withEvent:event]; + self.isDefaultFocusAppearance = NO; + UIView *view = [self getView]; + CALayer *layer = view.layer; + + CGSize targetShadowOffset = CGSizeMake(0.0, self.bounds.size.height/8); + [layer removeAllAnimations]; + [CATransaction begin]; + [CATransaction setCompletionBlock:^{ + layer.shadowOffset = targetShadowOffset; + }]; + + CABasicAnimation *shadowOffsetAnimation = [CABasicAnimation animationWithKeyPath:@"shadowOffset"]; + shadowOffsetAnimation.toValue = [NSValue valueWithCGSize:targetShadowOffset]; + shadowOffsetAnimation.duration = 0.4; + shadowOffsetAnimation.removedOnCompletion = NO; + shadowOffsetAnimation.fillMode = kCAFillModeForwards; + shadowOffsetAnimation.timingFunction = [CAMediaTimingFunction functionWithName:@"easeOut"]; + [layer addAnimation:shadowOffsetAnimation forKey:@"shadowOffset"]; + [CATransaction commit]; + + CABasicAnimation *shadowOpacityAnimation = [CABasicAnimation animationWithKeyPath:@"shadowOpacity"]; + shadowOpacityAnimation.toValue = [NSNumber numberWithFloat:0.45]; + shadowOpacityAnimation.duration = 0.4; + shadowOpacityAnimation.removedOnCompletion = false; + shadowOpacityAnimation.fillMode = kCAFillModeForwards; + shadowOpacityAnimation.timingFunction = [CAMediaTimingFunction functionWithName:@"easeOut"]; + [layer addAnimation:shadowOpacityAnimation forKey:@"shadowOpacityAnimation"]; + + view.transform = CGAffineTransformScale(CGAffineTransformIdentity, 1.25, 1.25); + + [CATransaction commit]; +} + +- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event +{ + [super touchesMoved:touches withEvent:event]; + + // TODO: Clean up, and improve visuals. + + if (!self.isDefaultFocusAppearance) { + // This view may correspond to either self.view + // or our superview if we are in a ASCellNode + UIView *view = [self getView]; + + UITouch *touch = [touches anyObject]; + // Get the specific point that was touched + + // This is quite messy in it's current state so is not ready for production. + // The reason it is here is for others to contribute and to make it clear what is occuring. + + // We get the touch location in self.view because + // we are operating in that coordinate system. + // BUT we apply our transforms to *view since we want to apply + // the transforms to the root view (L: 107) + CGPoint point = [touch locationInView:self.view]; + CGFloat pitch = 0; + CGFloat yaw = 0; + BOOL topHalf = NO; + if (point.y > CGRectGetHeight(self.view.frame)) { + pitch = 15; + } else if (point.y < -CGRectGetHeight(self.view.frame)) { + pitch = -15; + } else { + pitch = (point.y/CGRectGetHeight(self.view.frame))*15; + } + if (pitch < 0) { + topHalf = YES; + } + + if (point.x > CGRectGetWidth(self.view.frame)) { + yaw = 10; + } else if (point.x < -CGRectGetWidth(self.view.frame)) { + yaw = -10; + } else { + yaw = (point.x/CGRectGetWidth(self.view.frame))*10; + } + if (!topHalf) { + if (yaw > 0) { + yaw = -yaw; + } else { + yaw = fabs(yaw); + } + } + + CATransform3D pitchTransform = CATransform3DMakeRotation(GLKMathDegreesToRadians(pitch),1.0,0.0,0.0); + CATransform3D yawTransform = CATransform3DMakeRotation(GLKMathDegreesToRadians(yaw),0.0,1.0,0.0); + CATransform3D transform = CATransform3DConcat(pitchTransform, yawTransform); + CATransform3D scaleAndTransform = CATransform3DConcat(transform, CATransform3DMakeAffineTransform(CGAffineTransformScale(CGAffineTransformIdentity, 1.25, 1.25))); + + [UIView animateWithDuration:0.5 animations:^{ + view.layer.transform = scaleAndTransform; + }]; + } else { + [self setDefaultFocusAppearance]; + } +} + + +- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event +{ + [super touchesEnded:touches withEvent:event]; + [self finishTouches]; +} + +- (void)finishTouches +{ + if (!self.isDefaultFocusAppearance) { + UIView *view = [self getView]; + CALayer *layer = view.layer; + + CGSize targetShadowOffset = CGSizeMake(0.0, self.bounds.size.height/8); + CATransform3D targetScaleTransform = CATransform3DMakeScale(1.2, 1.2, 1.2); + [CATransaction begin]; + [CATransaction setCompletionBlock:^{ + layer.shadowOffset = targetShadowOffset; + }]; + [CATransaction commit]; + + [UIView animateWithDuration:0.4 delay:0.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{ + view.layer.transform = targetScaleTransform; + } completion:^(BOOL finished) { + if (finished) { + [layer removeAnimationForKey:@"shadowOffset"]; + [layer removeAnimationForKey:@"shadowOpacity"]; + } + }]; + } else { + [self setDefaultFocusAppearance]; + } +} + +- (void)setFocusedState +{ + UIView *view = [self getView]; + CALayer *layer = view.layer; + layer.shadowOffset = CGSizeMake(2, 10); + layer.shadowColor = [UIColor blackColor].CGColor; + layer.shadowRadius = 12.0; + layer.shadowOpacity = 0.45; + layer.shadowPath = [UIBezierPath bezierPathWithRect:self.layer.bounds].CGPath; + view.transform = CGAffineTransformScale(CGAffineTransformIdentity, 1.25, 1.25); +} + +- (void)setDefaultFocusAppearance +{ + UIView *view = [self getView]; + CALayer *layer = view.layer; + view.transform = CGAffineTransformIdentity; + layer.shadowOpacity = 0; + layer.shadowOffset = CGSizeZero; + layer.shadowRadius = 0; + layer.shadowPath = nil; + [layer removeAnimationForKey:@"shadowOffset"]; + [layer removeAnimationForKey:@"shadowOpacity"]; + self.isDefaultFocusAppearance = YES; +} + +- (UIView *)getView +{ + // TODO: This needs to be re-visited to handle all possibilities. + // If we are inside a ASCellNode, then we need to apply our focus effects to the ASCellNode view/layer rather than the ASImageNode view/layer. + return ASDisplayNodeUltimateParentOfNode(self).view; +} + +@end +#endif diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother.xcodeproj/project.pbxproj b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother.xcodeproj/project.pbxproj new file mode 100644 index 0000000..2caad8c --- /dev/null +++ b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother.xcodeproj/project.pbxproj @@ -0,0 +1,550 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 52; + objects = { + +/* Begin PBXBuildFile section */ + 912A93FCF008DEC8C2D9A8D9 /* Pods_Starbucks_Clone_TexBrother.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D008B44055C8D19550BEAF50 /* Pods_Starbucks_Clone_TexBrother.framework */; }; + CCDD92B826FA28C7008B3D8A /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCDD92B726FA28C7008B3D8A /* AppDelegate.swift */; }; + CCDD92BA26FA28C7008B3D8A /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCDD92B926FA28C7008B3D8A /* SceneDelegate.swift */; }; + CCDD92BC26FA28C7008B3D8A /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCDD92BB26FA28C7008B3D8A /* ViewController.swift */; }; + CCDD92BF26FA28C7008B3D8A /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CCDD92BD26FA28C7008B3D8A /* Main.storyboard */; }; + CCDD92C126FA28CA008B3D8A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CCDD92C026FA28CA008B3D8A /* Assets.xcassets */; }; + CCDD92C426FA28CA008B3D8A /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CCDD92C226FA28CA008B3D8A /* LaunchScreen.storyboard */; }; + CCDD92CE26FA2AA1008B3D8A /* StarbucksTabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCDD92CD26FA2AA1008B3D8A /* StarbucksTabBarController.swift */; }; + CCDD92D126FA2BAB008B3D8A /* Then in Frameworks */ = {isa = PBXBuildFile; productRef = CCDD92D026FA2BAB008B3D8A /* Then */; }; + CCDD92D426FA2BEA008B3D8A /* SwiftyColor in Frameworks */ = {isa = PBXBuildFile; productRef = CCDD92D326FA2BEA008B3D8A /* SwiftyColor */; }; + CCDD92DA26FA2DA3008B3D8A /* PayNodeController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCDD92D926FA2DA3008B3D8A /* PayNodeController.swift */; }; + CCDD92DD26FA33AA008B3D8A /* BaseScrollNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCDD92DC26FA33AA008B3D8A /* BaseScrollNode.swift */; }; + CCDD92E126FA37F0008B3D8A /* CustomNavigationBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCDD92E026FA37F0008B3D8A /* CustomNavigationBar.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + CCDD92B426FA28C7008B3D8A /* Starbucks_Clone_TexBrother.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Starbucks_Clone_TexBrother.app; sourceTree = BUILT_PRODUCTS_DIR; }; + CCDD92B726FA28C7008B3D8A /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + CCDD92B926FA28C7008B3D8A /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; + CCDD92BB26FA28C7008B3D8A /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; + CCDD92BE26FA28C7008B3D8A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + CCDD92C026FA28CA008B3D8A /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + CCDD92C326FA28CA008B3D8A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + CCDD92C526FA28CA008B3D8A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + CCDD92CD26FA2AA1008B3D8A /* StarbucksTabBarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StarbucksTabBarController.swift; sourceTree = ""; }; + CCDD92D926FA2DA3008B3D8A /* PayNodeController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PayNodeController.swift; sourceTree = ""; }; + CCDD92DC26FA33AA008B3D8A /* BaseScrollNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseScrollNode.swift; sourceTree = ""; }; + CCDD92E026FA37F0008B3D8A /* CustomNavigationBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomNavigationBar.swift; sourceTree = ""; }; + D008B44055C8D19550BEAF50 /* Pods_Starbucks_Clone_TexBrother.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Starbucks_Clone_TexBrother.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + DF822A713505798452F11100 /* Pods-Starbucks_Clone_TexBrother.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Starbucks_Clone_TexBrother.debug.xcconfig"; path = "Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother.debug.xcconfig"; sourceTree = ""; }; + E6B8769D1B4DCD3E98B54AB6 /* Pods-Starbucks_Clone_TexBrother.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Starbucks_Clone_TexBrother.release.xcconfig"; path = "Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother.release.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + CCDD92B126FA28C7008B3D8A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 912A93FCF008DEC8C2D9A8D9 /* Pods_Starbucks_Clone_TexBrother.framework in Frameworks */, + CCDD92D426FA2BEA008B3D8A /* SwiftyColor in Frameworks */, + CCDD92D126FA2BAB008B3D8A /* Then in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 261C259A99A737AA39AF67B5 /* Pods */ = { + isa = PBXGroup; + children = ( + DF822A713505798452F11100 /* Pods-Starbucks_Clone_TexBrother.debug.xcconfig */, + E6B8769D1B4DCD3E98B54AB6 /* Pods-Starbucks_Clone_TexBrother.release.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; + C3893B57B8FBCD0B1AF91316 /* Frameworks */ = { + isa = PBXGroup; + children = ( + D008B44055C8D19550BEAF50 /* Pods_Starbucks_Clone_TexBrother.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + CCDD92AB26FA28C7008B3D8A = { + isa = PBXGroup; + children = ( + CCDD92B626FA28C7008B3D8A /* Starbucks_Clone_TexBrother */, + CCDD92B526FA28C7008B3D8A /* Products */, + 261C259A99A737AA39AF67B5 /* Pods */, + C3893B57B8FBCD0B1AF91316 /* Frameworks */, + ); + sourceTree = ""; + }; + CCDD92B526FA28C7008B3D8A /* Products */ = { + isa = PBXGroup; + children = ( + CCDD92B426FA28C7008B3D8A /* Starbucks_Clone_TexBrother.app */, + ); + name = Products; + sourceTree = ""; + }; + CCDD92B626FA28C7008B3D8A /* Starbucks_Clone_TexBrother */ = { + isa = PBXGroup; + children = ( + CCDD92DE26FA37BF008B3D8A /* Global */, + CCDD92CC26FA2A7B008B3D8A /* Supports */, + CCDD92CB26FA2A51008B3D8A /* Screens */, + CCDD92BB26FA28C7008B3D8A /* ViewController.swift */, + CCDD92BD26FA28C7008B3D8A /* Main.storyboard */, + CCDD92C026FA28CA008B3D8A /* Assets.xcassets */, + CCDD92C226FA28CA008B3D8A /* LaunchScreen.storyboard */, + ); + path = Starbucks_Clone_TexBrother; + sourceTree = ""; + }; + CCDD92CB26FA2A51008B3D8A /* Screens */ = { + isa = PBXGroup; + children = ( + CCDD92DB26FA3393008B3D8A /* Base */, + CCDD92D526FA2D5B008B3D8A /* Pay */, + CCDD92CD26FA2AA1008B3D8A /* StarbucksTabBarController.swift */, + ); + path = Screens; + sourceTree = ""; + }; + CCDD92CC26FA2A7B008B3D8A /* Supports */ = { + isa = PBXGroup; + children = ( + CCDD92C526FA28CA008B3D8A /* Info.plist */, + CCDD92B726FA28C7008B3D8A /* AppDelegate.swift */, + CCDD92B926FA28C7008B3D8A /* SceneDelegate.swift */, + ); + path = Supports; + sourceTree = ""; + }; + CCDD92D526FA2D5B008B3D8A /* Pay */ = { + isa = PBXGroup; + children = ( + CCDD92D826FA2D83008B3D8A /* Nodes */, + CCDD92D726FA2D6B008B3D8A /* NodeControllers */, + CCDD92D626FA2D63008B3D8A /* Cells */, + ); + path = Pay; + sourceTree = ""; + }; + CCDD92D626FA2D63008B3D8A /* Cells */ = { + isa = PBXGroup; + children = ( + ); + path = Cells; + sourceTree = ""; + }; + CCDD92D726FA2D6B008B3D8A /* NodeControllers */ = { + isa = PBXGroup; + children = ( + CCDD92D926FA2DA3008B3D8A /* PayNodeController.swift */, + ); + path = NodeControllers; + sourceTree = ""; + }; + CCDD92D826FA2D83008B3D8A /* Nodes */ = { + isa = PBXGroup; + children = ( + ); + path = Nodes; + sourceTree = ""; + }; + CCDD92DB26FA3393008B3D8A /* Base */ = { + isa = PBXGroup; + children = ( + CCDD92DC26FA33AA008B3D8A /* BaseScrollNode.swift */, + ); + path = Base; + sourceTree = ""; + }; + CCDD92DE26FA37BF008B3D8A /* Global */ = { + isa = PBXGroup; + children = ( + CCDD92DF26FA37C7008B3D8A /* Customs */, + ); + path = Global; + sourceTree = ""; + }; + CCDD92DF26FA37C7008B3D8A /* Customs */ = { + isa = PBXGroup; + children = ( + CCDD92E026FA37F0008B3D8A /* CustomNavigationBar.swift */, + ); + path = Customs; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + CCDD92B326FA28C7008B3D8A /* Starbucks_Clone_TexBrother */ = { + isa = PBXNativeTarget; + buildConfigurationList = CCDD92C826FA28CA008B3D8A /* Build configuration list for PBXNativeTarget "Starbucks_Clone_TexBrother" */; + buildPhases = ( + 932A9697733A188365731500 /* [CP] Check Pods Manifest.lock */, + CCDD92B026FA28C7008B3D8A /* Sources */, + CCDD92B126FA28C7008B3D8A /* Frameworks */, + CCDD92B226FA28C7008B3D8A /* Resources */, + 9F6A62987C311935EF7B541C /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Starbucks_Clone_TexBrother; + packageProductDependencies = ( + CCDD92D026FA2BAB008B3D8A /* Then */, + CCDD92D326FA2BEA008B3D8A /* SwiftyColor */, + ); + productName = Starbucks_Clone_TexBrother; + productReference = CCDD92B426FA28C7008B3D8A /* Starbucks_Clone_TexBrother.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + CCDD92AC26FA28C7008B3D8A /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1250; + LastUpgradeCheck = 1250; + TargetAttributes = { + CCDD92B326FA28C7008B3D8A = { + CreatedOnToolsVersion = 12.5; + }; + }; + }; + buildConfigurationList = CCDD92AF26FA28C7008B3D8A /* Build configuration list for PBXProject "Starbucks_Clone_TexBrother" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = CCDD92AB26FA28C7008B3D8A; + packageReferences = ( + CCDD92CF26FA2BAB008B3D8A /* XCRemoteSwiftPackageReference "Then" */, + CCDD92D226FA2BEA008B3D8A /* XCRemoteSwiftPackageReference "SwiftyColor" */, + ); + productRefGroup = CCDD92B526FA28C7008B3D8A /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + CCDD92B326FA28C7008B3D8A /* Starbucks_Clone_TexBrother */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + CCDD92B226FA28C7008B3D8A /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + CCDD92C426FA28CA008B3D8A /* LaunchScreen.storyboard in Resources */, + CCDD92C126FA28CA008B3D8A /* Assets.xcassets in Resources */, + CCDD92BF26FA28C7008B3D8A /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 932A9697733A188365731500 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Starbucks_Clone_TexBrother-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 9F6A62987C311935EF7B541C /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + CCDD92B026FA28C7008B3D8A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + CCDD92BC26FA28C7008B3D8A /* ViewController.swift in Sources */, + CCDD92DA26FA2DA3008B3D8A /* PayNodeController.swift in Sources */, + CCDD92B826FA28C7008B3D8A /* AppDelegate.swift in Sources */, + CCDD92BA26FA28C7008B3D8A /* SceneDelegate.swift in Sources */, + CCDD92CE26FA2AA1008B3D8A /* StarbucksTabBarController.swift in Sources */, + CCDD92DD26FA33AA008B3D8A /* BaseScrollNode.swift in Sources */, + CCDD92E126FA37F0008B3D8A /* CustomNavigationBar.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + CCDD92BD26FA28C7008B3D8A /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + CCDD92BE26FA28C7008B3D8A /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + CCDD92C226FA28CA008B3D8A /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + CCDD92C326FA28CA008B3D8A /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + CCDD92C626FA28CA008B3D8A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.5; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + CCDD92C726FA28CA008B3D8A /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.5; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + CCDD92C926FA28CA008B3D8A /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = DF822A713505798452F11100 /* Pods-Starbucks_Clone_TexBrother.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = QS42RPKQCD; + INFOPLIST_FILE = Starbucks_Clone_TexBrother/Supports/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "ryan.Starbucks-Clone-TexBrother"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + CCDD92CA26FA28CA008B3D8A /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = E6B8769D1B4DCD3E98B54AB6 /* Pods-Starbucks_Clone_TexBrother.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = QS42RPKQCD; + INFOPLIST_FILE = Starbucks_Clone_TexBrother/Supports/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "ryan.Starbucks-Clone-TexBrother"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + CCDD92AF26FA28C7008B3D8A /* Build configuration list for PBXProject "Starbucks_Clone_TexBrother" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + CCDD92C626FA28CA008B3D8A /* Debug */, + CCDD92C726FA28CA008B3D8A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + CCDD92C826FA28CA008B3D8A /* Build configuration list for PBXNativeTarget "Starbucks_Clone_TexBrother" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + CCDD92C926FA28CA008B3D8A /* Debug */, + CCDD92CA26FA28CA008B3D8A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + CCDD92CF26FA2BAB008B3D8A /* XCRemoteSwiftPackageReference "Then" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/devxoul/Then"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 2.7.0; + }; + }; + CCDD92D226FA2BEA008B3D8A /* XCRemoteSwiftPackageReference "SwiftyColor" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/devxoul/SwiftyColor"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 1.2.1; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + CCDD92D026FA2BAB008B3D8A /* Then */ = { + isa = XCSwiftPackageProductDependency; + package = CCDD92CF26FA2BAB008B3D8A /* XCRemoteSwiftPackageReference "Then" */; + productName = Then; + }; + CCDD92D326FA2BEA008B3D8A /* SwiftyColor */ = { + isa = XCSwiftPackageProductDependency; + package = CCDD92D226FA2BEA008B3D8A /* XCRemoteSwiftPackageReference "SwiftyColor" */; + productName = SwiftyColor; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = CCDD92AC26FA28C7008B3D8A /* Project object */; +} diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother.xcworkspace/contents.xcworkspacedata b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..d2f19fc --- /dev/null +++ b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 0000000..3b6dffc --- /dev/null +++ b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,25 @@ +{ + "object": { + "pins": [ + { + "package": "SwiftyColor", + "repositoryURL": "https://github.com/devxoul/SwiftyColor", + "state": { + "branch": null, + "revision": "4b169b77446ef1d9878b162a0a38258f7563ed5b", + "version": "1.2.1" + } + }, + { + "package": "Then", + "repositoryURL": "https://github.com/devxoul/Then", + "state": { + "branch": null, + "revision": "e421a7b3440a271834337694e6050133a3958bc7", + "version": "2.7.0" + } + } + ] + }, + "version": 1 +} diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/AccentColor.colorset/Contents.json b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/AppIcon.appiconset/Contents.json b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..9221b9b --- /dev/null +++ b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/Contents.json b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Base.lproj/LaunchScreen.storyboard b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..865e932 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Base.lproj/Main.storyboard b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Base.lproj/Main.storyboard new file mode 100644 index 0000000..25a7638 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Base.lproj/Main.storyboard @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Global/Customs/CustomNavigationBar.swift b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Global/Customs/CustomNavigationBar.swift new file mode 100644 index 0000000..bddf655 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Global/Customs/CustomNavigationBar.swift @@ -0,0 +1,51 @@ +// +// CustomNavigationBar.swift +// Starbucks_Clone_TexBrother +// +// Created by 노한솔 on 2021/09/22. +// + +import UIKit + +import AsyncDisplayKit + +// MARK: - CustomNavigationBar + +class CustomNavigationBar: ASDisplayNode { + + // MARK: - Components + + let backButton: ASImageNode = { + let node = ASImageNode() + node.backgroundColor = .black + return node + }() + + let titleLabel: ASTextNode = { + let node = ASTextNode() + return node + }() + + let rightButton: ASImageNode = { + let node = ASImageNode() + node.backgroundColor = .black + return node + }() + + // MARK: - LifeCycles + + override init() { + super.init() + } +} + +// MARK: - Extensions + +extension CustomNavigationBar { + + // MARK: - Helpers + + func setupBar(isBackButton: Bool, title: String, rightButton: String) { + + } +} diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Screens/Base/BaseScrollNode.swift b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Screens/Base/BaseScrollNode.swift new file mode 100644 index 0000000..6037653 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Screens/Base/BaseScrollNode.swift @@ -0,0 +1,31 @@ +// +// BaseScrollNode.swift +// Starbucks_Clone_TexBrother +// +// Created by 노한솔 on 2021/09/22. +// + +import UIKit + +import AsyncDisplayKit + +// MARK: - BaseScrollNode + +class BaseScrollNode: ASScrollNode { + + + // MARK: - LifeCycles + + override init() { + super.init() + self.automaticallyManagesSubnodes = true + } + + // MARK: - Layout + + override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec { + <#code#> + } + + +} diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Screens/Pay/NodeControllers/PayNodeController.swift b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Screens/Pay/NodeControllers/PayNodeController.swift new file mode 100644 index 0000000..fcb020a --- /dev/null +++ b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Screens/Pay/NodeControllers/PayNodeController.swift @@ -0,0 +1,55 @@ +// +// PayNodeController.swift +// Starbucks_Clone_TexBrother +// +// Created by 노한솔 on 2021/09/22. +// + +import UIKit + +import AsyncDisplayKit + +// MARK: - PayNodeController + +final class PayNodeController: ASDKViewController { + + // MARK: - Lazy Components + + private lazy var cardCollectionNode = { () -> ASPagerNode in + let layout = UICollectionViewFlowLayout() + layout.scrollDirection = .vertical + + let node = ASPagerNode(frame: .zero, collectionViewLayout: layout) + node.isPagingEnabled = true + return node + }() + + // MARK: - Components + + private let listIcon: ASImageNode = { + let node = ASImageNode() + node.backgroundColor = .black + return node + }() + + + // MARK: - Background Thread + + override init() { + super.init(node: ASScrollNode()) + node.automaticallyManagesSubnodes = true + node.automaticallyRelayoutOnSafeAreaChanges = true + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: - Main Thread + + override func viewDidLoad() { + super.viewDidLoad() + + // Do any additional setup after loading the view. + } +} diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Screens/StarbucksTabBarController.swift b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Screens/StarbucksTabBarController.swift new file mode 100644 index 0000000..4d23c50 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Screens/StarbucksTabBarController.swift @@ -0,0 +1,68 @@ +// +// StarbucksTabBarController.swift +// Starbucks_Clone_TexBrother +// +// Created by 노한솔 on 2021/09/21. +// + +import UIKit + +import AsyncDisplayKit + +// MARK: - StarbucksTabBarController + +final class StarbucksTabBarController: ASTabBarController { + + // MARK: - Variables + + var defaultIndex = 0 { + didSet { + self.selectedIndex = defaultIndex + } + } + + // MARK: - LifeCycles + + override func viewDidLoad() { + super.viewDidLoad() + self.selectedIndex = defaultIndex + config() + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + setupTabBar() + } +} + +// MARK: - Extensions + +extension StarbucksTabBarController { + + // MARK: - Helpers + + private func setupTabBar() { + let homeNC = ViewController() + let homeNavi = ASNavigationController(rootViewController: homeNC) + + let payNC = PayNodeController() + let payNavi = ASNavigationController(rootViewController: payNC) + + let orderNC = ViewController() + let orderNavi = ASNavigationController(rootViewController: orderNC) + + let giftNC = ViewController() + let giftNavi = ASNavigationController(rootViewController: giftNC) + + let otherNC = ViewController() + let otherNavi = ASNavigationController(rootViewController: otherNC) + + let naviControllers = [homeNavi, payNavi, orderNavi, giftNavi, otherNavi] + self.setViewControllers(naviControllers, animated: true) + } + + private func config() { + tabBar.isTranslucent = false + tabBar.tintColor = .systemGreen + } +} diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Supports/AppDelegate.swift b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Supports/AppDelegate.swift new file mode 100644 index 0000000..61cd6c8 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Supports/AppDelegate.swift @@ -0,0 +1,36 @@ +// +// AppDelegate.swift +// Starbucks_Clone_TexBrother +// +// Created by 노한솔 on 2021/09/21. +// + +import UIKit + +@main +class AppDelegate: UIResponder, UIApplicationDelegate { + + + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + // Override point for customization after application launch. + return true + } + + // MARK: UISceneSession Lifecycle + + func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { + // Called when a new scene session is being created. + // Use this method to select a configuration to create the new scene with. + return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) + } + + func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { + // Called when the user discards a scene session. + // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. + // Use this method to release any resources that were specific to the discarded scenes, as they will not return. + } + + +} + diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Supports/Info.plist b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Supports/Info.plist new file mode 100644 index 0000000..5b531f7 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Supports/Info.plist @@ -0,0 +1,66 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneConfigurationName + Default Configuration + UISceneDelegateClassName + $(PRODUCT_MODULE_NAME).SceneDelegate + UISceneStoryboardFile + Main + + + + + UIApplicationSupportsIndirectInputEvents + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Supports/SceneDelegate.swift b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Supports/SceneDelegate.swift new file mode 100644 index 0000000..df38d95 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Supports/SceneDelegate.swift @@ -0,0 +1,68 @@ +// +// SceneDelegate.swift +// Starbucks_Clone_TexBrother +// +// Created by 노한솔 on 2021/09/21. +// + +import UIKit + +import AsyncDisplayKit + +class SceneDelegate: UIResponder, UIWindowSceneDelegate { + + var window: UIWindow? + + + func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { + // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. + // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. + // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). + if let windowScene = scene as? UIWindowScene { + + let window = UIWindow(windowScene: windowScene) + + let rootVC = StarbucksTabBarController() + rootVC.view.backgroundColor = .clear + + let rootNavi = ASNavigationController(rootViewController: rootVC) + rootNavi.view.backgroundColor = .white + rootNavi.navigationBar.isHidden = true + + window.rootViewController = rootNavi + self.window = window + window.makeKeyAndVisible() + } + } + + func sceneDidDisconnect(_ scene: UIScene) { + // Called as the scene is being released by the system. + // This occurs shortly after the scene enters the background, or when its session is discarded. + // Release any resources associated with this scene that can be re-created the next time the scene connects. + // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead). + } + + func sceneDidBecomeActive(_ scene: UIScene) { + // Called when the scene has moved from an inactive state to an active state. + // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. + } + + func sceneWillResignActive(_ scene: UIScene) { + // Called when the scene will move from an active state to an inactive state. + // This may occur due to temporary interruptions (ex. an incoming phone call). + } + + func sceneWillEnterForeground(_ scene: UIScene) { + // Called as the scene transitions from the background to the foreground. + // Use this method to undo the changes made on entering the background. + } + + func sceneDidEnterBackground(_ scene: UIScene) { + // Called as the scene transitions from the foreground to the background. + // Use this method to save data, release shared resources, and store enough scene-specific state information + // to restore the scene back to its current state. + } + + +} + diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/ViewController.swift b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/ViewController.swift new file mode 100644 index 0000000..ee148f9 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/ViewController.swift @@ -0,0 +1,19 @@ +// +// ViewController.swift +// Starbucks_Clone_TexBrother +// +// Created by 노한솔 on 2021/09/21. +// + +import UIKit + +import AsyncDisplayKit + +class ViewController: ASDKViewController { + + override func viewDidLoad() { + super.viewDidLoad() + // Do any additional setup after loading the view. + } +} + From b43562ca749998c44ff862644dcd27ee297553c6 Mon Sep 17 00:00:00 2001 From: hansolnoh95 Date: Wed, 22 Sep 2021 17:18:41 +0900 Subject: [PATCH 2/4] =?UTF-8?q?:construction:=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EC=84=B8=ED=8C=85=20=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../project.pbxproj | 4 ++ .../Global/Customs/CustomNavigationBar.swift | 38 +++++++++--- .../NodeControllers/PayNodeController.swift | 29 ++++++++- .../Screens/Pay/Nodes/PayMenuNode.swift | 61 +++++++++++++++++++ 4 files changed, 122 insertions(+), 10 deletions(-) create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Screens/Pay/Nodes/PayMenuNode.swift diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother.xcodeproj/project.pbxproj b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother.xcodeproj/project.pbxproj index 2caad8c..358af44 100644 --- a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother.xcodeproj/project.pbxproj +++ b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother.xcodeproj/project.pbxproj @@ -20,6 +20,7 @@ CCDD92DA26FA2DA3008B3D8A /* PayNodeController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCDD92D926FA2DA3008B3D8A /* PayNodeController.swift */; }; CCDD92DD26FA33AA008B3D8A /* BaseScrollNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCDD92DC26FA33AA008B3D8A /* BaseScrollNode.swift */; }; CCDD92E126FA37F0008B3D8A /* CustomNavigationBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCDD92E026FA37F0008B3D8A /* CustomNavigationBar.swift */; }; + CCDD92E326FB043E008B3D8A /* PayMenuNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCDD92E226FB043E008B3D8A /* PayMenuNode.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -35,6 +36,7 @@ CCDD92D926FA2DA3008B3D8A /* PayNodeController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PayNodeController.swift; sourceTree = ""; }; CCDD92DC26FA33AA008B3D8A /* BaseScrollNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseScrollNode.swift; sourceTree = ""; }; CCDD92E026FA37F0008B3D8A /* CustomNavigationBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomNavigationBar.swift; sourceTree = ""; }; + CCDD92E226FB043E008B3D8A /* PayMenuNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PayMenuNode.swift; sourceTree = ""; }; D008B44055C8D19550BEAF50 /* Pods_Starbucks_Clone_TexBrother.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Starbucks_Clone_TexBrother.framework; sourceTree = BUILT_PRODUCTS_DIR; }; DF822A713505798452F11100 /* Pods-Starbucks_Clone_TexBrother.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Starbucks_Clone_TexBrother.debug.xcconfig"; path = "Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother.debug.xcconfig"; sourceTree = ""; }; E6B8769D1B4DCD3E98B54AB6 /* Pods-Starbucks_Clone_TexBrother.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Starbucks_Clone_TexBrother.release.xcconfig"; path = "Target Support Files/Pods-Starbucks_Clone_TexBrother/Pods-Starbucks_Clone_TexBrother.release.xcconfig"; sourceTree = ""; }; @@ -151,6 +153,7 @@ CCDD92D826FA2D83008B3D8A /* Nodes */ = { isa = PBXGroup; children = ( + CCDD92E226FB043E008B3D8A /* PayMenuNode.swift */, ); path = Nodes; sourceTree = ""; @@ -302,6 +305,7 @@ buildActionMask = 2147483647; files = ( CCDD92BC26FA28C7008B3D8A /* ViewController.swift in Sources */, + CCDD92E326FB043E008B3D8A /* PayMenuNode.swift in Sources */, CCDD92DA26FA2DA3008B3D8A /* PayNodeController.swift in Sources */, CCDD92B826FA28C7008B3D8A /* AppDelegate.swift in Sources */, CCDD92BA26FA28C7008B3D8A /* SceneDelegate.swift in Sources */, diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Global/Customs/CustomNavigationBar.swift b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Global/Customs/CustomNavigationBar.swift index bddf655..7c8cecd 100644 --- a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Global/Customs/CustomNavigationBar.swift +++ b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Global/Customs/CustomNavigationBar.swift @@ -15,20 +15,24 @@ class CustomNavigationBar: ASDisplayNode { // MARK: - Components - let backButton: ASImageNode = { - let node = ASImageNode() + let backButton: ASButtonNode = { + let node = ASButtonNode() node.backgroundColor = .black + node.style.preferredSize = CGSize(width: 15, height: 15) return node }() let titleLabel: ASTextNode = { let node = ASTextNode() + node.isHidden = true return node }() - let rightButton: ASImageNode = { - let node = ASImageNode() + let rightButton: ASButtonNode = { + let node = ASButtonNode() node.backgroundColor = .black + node.isHidden = true + node.style.preferredSize = CGSize(width: 15, height: 15) return node }() @@ -43,9 +47,29 @@ class CustomNavigationBar: ASDisplayNode { extension CustomNavigationBar { - // MARK: - Helpers + // MARK: - Layout Helpers - func setupBar(isBackButton: Bool, title: String, rightButton: String) { - + override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec { + return ASStackLayoutSpec( + direction: .horizontal, + spacing: 0, + justifyContent: .spaceBetween, + alignItems: .center, + children: [backButton, titleLabel, rightButton] + ) + } + + // MARK: - General Helpers + + func setupBar(isBackButton: Bool, title: String? = nil, right: String? = nil) { + if !isBackButton { + backButton.isHidden = true + } + if let text = title { + titleLabel.attributedText = NSAttributedString(string: text) + } + if let rightText = right { + rightButton.setBackgroundImage(UIImage(named: rightText), for: .normal) + } } } diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Screens/Pay/NodeControllers/PayNodeController.swift b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Screens/Pay/NodeControllers/PayNodeController.swift index fcb020a..99d329f 100644 --- a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Screens/Pay/NodeControllers/PayNodeController.swift +++ b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Screens/Pay/NodeControllers/PayNodeController.swift @@ -8,6 +8,7 @@ import UIKit import AsyncDisplayKit +import Then // MARK: - PayNodeController @@ -26,12 +27,34 @@ final class PayNodeController: ASDKViewController { // MARK: - Components - private let listIcon: ASImageNode = { - let node = ASImageNode() - node.backgroundColor = .black + private let navigationView: CustomNavigationBar = { + let node = CustomNavigationBar() + node.setupBar(isBackButton: false, right: "") + node.style.height = ASDimension(unit: .points, value: 30) return node }() + private let titleLabel = ASTextNode().then { + $0.attributedText = NSAttributedString( + string: "Pay", + attributes: [ + .font: UIFont.boldSystemFont(ofSize: 24), + .foregroundColor: UIColor.black + ] + ) + } + + private let menuView = PayMenuNode().then { + $0.backgroundColor = .clear + $0.style.height = ASDimension(unit: .points, value: 40) + } + + private let bannerButton: ASButtonNode = { + let node = ASButtonNode() + node.backgroundColor = .black + node.style.height = ASDimension(unit: .points, value: 60) + }() + // MARK: - Background Thread diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Screens/Pay/Nodes/PayMenuNode.swift b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Screens/Pay/Nodes/PayMenuNode.swift new file mode 100644 index 0000000..9206462 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Screens/Pay/Nodes/PayMenuNode.swift @@ -0,0 +1,61 @@ +// +// PayMenuNode.swift +// Starbucks_Clone_TexBrother +// +// Created by 노한솔 on 2021/09/22. +// + +import Foundation + +import AsyncDisplayKit + +// MARK: - PayMenuNode + +final class PayMenuNode: ASDisplayNode { + + // MARK: - Components + + private let couponButton: ASButtonNode = { + let node = ASButtonNode() + node.setTitle("Coupon", with: .boldSystemFont(ofSize: 15), with: .black, for: .normal) + node.backgroundColor = .clear + return node + }() + + private let giftButton: ASButtonNode = { + let node = ASButtonNode() + node.setTitle("e-Gift Item", with: .boldSystemFont(ofSize: 15), with: .black, for: .normal) + node.backgroundColor = .clear + return node + }() + + private let separatorView: ASDisplayNode = { + let node = ASDisplayNode() + node.backgroundColor = .gray + node.style.preferredSize = CGSize(width: 1, height: 5) + return node + }() + + // MARK: - LifeCycles + + override init() { + super.init() + } +} + +// MARK: - Extensions + +extension PayMenuNode { + + // MARK: - Layout Helpers + + override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec { + return ASStackLayoutSpec( + direction: .horizontal, + spacing: 0, + justifyContent: .spaceAround, + alignItems: .center, + children: [couponButton, separatorView, giftButton] + ) + } +} From 8e3b5f676fd8ea4ec071279b92268fdb2b488371 Mon Sep 17 00:00:00 2001 From: hansolnoh95 Date: Wed, 29 Sep 2021 11:40:14 +0900 Subject: [PATCH 3/4] =?UTF-8?q?:construction:=20Pay=20=ED=83=AD=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20=EC=A4=91,,,?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../project.pbxproj | 32 +++ .../autoChargeIcon.imageset/52.png | Bin 0 -> 413 bytes .../autoChargeIcon.imageset/52@2x.png | Bin 0 -> 687 bytes .../autoChargeIcon.imageset/52@3x.png | Bin 0 -> 985 bytes .../autoChargeIcon.imageset/Contents.json | 23 ++ .../Assets.xcassets/banner.imageset/4.png | Bin 0 -> 6352 bytes .../Assets.xcassets/banner.imageset/4@2x.png | Bin 0 -> 19276 bytes .../Assets.xcassets/banner.imageset/4@3x.png | Bin 0 -> 35994 bytes .../banner.imageset/Contents.json | 23 ++ .../Assets.xcassets/barcode.imageset/3.png | Bin 0 -> 2461 bytes .../Assets.xcassets/barcode.imageset/3@2x.png | Bin 0 -> 6583 bytes .../Assets.xcassets/barcode.imageset/3@3x.png | Bin 0 -> 12046 bytes .../barcode.imageset/Contents.json | 23 ++ .../favoriteIcon.imageset/55.png | Bin 0 -> 338 bytes .../favoriteIcon.imageset/55@2x.png | Bin 0 -> 550 bytes .../favoriteIcon.imageset/55@3x.png | Bin 0 -> 765 bytes .../favoriteIcon.imageset/Contents.json | 23 ++ .../generalChargeIcon.imageset/53.png | Bin 0 -> 603 bytes .../generalChargeIcon.imageset/53@2x.png | Bin 0 -> 812 bytes .../generalChargeIcon.imageset/53@3x.png | Bin 0 -> 1138 bytes .../generalChargeIcon.imageset/Contents.json | 23 ++ .../limitedCard.imageset/5.png | Bin 0 -> 35387 bytes .../limitedCard.imageset/5@2x.png | Bin 0 -> 112637 bytes .../limitedCard.imageset/5@3x.png | Bin 0 -> 209682 bytes .../limitedCard.imageset/Contents.json | 23 ++ .../Assets.xcassets/listIcon.imageset/58.png | Bin 0 -> 140 bytes .../listIcon.imageset/58@2x.png | Bin 0 -> 183 bytes .../listIcon.imageset/58@3x.png | Bin 0 -> 240 bytes .../listIcon.imageset/Contents.json | 23 ++ .../thankYouCard.imageset/2.png | Bin 0 -> 19574 bytes .../thankYouCard.imageset/2@2x.png | Bin 0 -> 61281 bytes .../thankYouCard.imageset/2@3x.png | Bin 0 -> 112900 bytes .../thankYouCard.imageset/Contents.json | 23 ++ .../Global/UIExtensions/CALayer+.swift | 23 ++ .../Global/UIExtensions/UIColor+.swift | 26 ++ .../Screens/Base/BaseScrollNode.swift | 3 +- .../Pay/Cells/PayCardCollectionViewCell.swift | 243 ++++++++++++++++++ .../Screens/Pay/Models/CardModel.swift | 14 + .../NodeControllers/PayNodeController.swift | 85 +++++- 39 files changed, 603 insertions(+), 7 deletions(-) create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/autoChargeIcon.imageset/52.png create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/autoChargeIcon.imageset/52@2x.png create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/autoChargeIcon.imageset/52@3x.png create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/autoChargeIcon.imageset/Contents.json create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/banner.imageset/4.png create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/banner.imageset/4@2x.png create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/banner.imageset/4@3x.png create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/banner.imageset/Contents.json create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/barcode.imageset/3.png create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/barcode.imageset/3@2x.png create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/barcode.imageset/3@3x.png create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/barcode.imageset/Contents.json create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/favoriteIcon.imageset/55.png create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/favoriteIcon.imageset/55@2x.png create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/favoriteIcon.imageset/55@3x.png create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/favoriteIcon.imageset/Contents.json create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/generalChargeIcon.imageset/53.png create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/generalChargeIcon.imageset/53@2x.png create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/generalChargeIcon.imageset/53@3x.png create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/generalChargeIcon.imageset/Contents.json create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/limitedCard.imageset/5.png create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/limitedCard.imageset/5@2x.png create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/limitedCard.imageset/5@3x.png create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/limitedCard.imageset/Contents.json create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/listIcon.imageset/58.png create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/listIcon.imageset/58@2x.png create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/listIcon.imageset/58@3x.png create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/listIcon.imageset/Contents.json create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/thankYouCard.imageset/2.png create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/thankYouCard.imageset/2@2x.png create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/thankYouCard.imageset/2@3x.png create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/thankYouCard.imageset/Contents.json create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Global/UIExtensions/CALayer+.swift create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Global/UIExtensions/UIColor+.swift create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Screens/Pay/Cells/PayCardCollectionViewCell.swift create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Screens/Pay/Models/CardModel.swift diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother.xcodeproj/project.pbxproj b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother.xcodeproj/project.pbxproj index 358af44..d48b451 100644 --- a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother.xcodeproj/project.pbxproj +++ b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother.xcodeproj/project.pbxproj @@ -8,6 +8,10 @@ /* Begin PBXBuildFile section */ 912A93FCF008DEC8C2D9A8D9 /* Pods_Starbucks_Clone_TexBrother.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D008B44055C8D19550BEAF50 /* Pods_Starbucks_Clone_TexBrother.framework */; }; + CC3806FD2703F76F006AA4AF /* CardModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC3806FC2703F76F006AA4AF /* CardModel.swift */; }; + CC3806FF2703F7C7006AA4AF /* PayCardCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC3806FE2703F7C7006AA4AF /* PayCardCollectionViewCell.swift */; }; + CC3807022703FAD1006AA4AF /* UIColor+.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC3807012703FAD0006AA4AF /* UIColor+.swift */; }; + CC3807042704018C006AA4AF /* CALayer+.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC3807032704018C006AA4AF /* CALayer+.swift */; }; CCDD92B826FA28C7008B3D8A /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCDD92B726FA28C7008B3D8A /* AppDelegate.swift */; }; CCDD92BA26FA28C7008B3D8A /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCDD92B926FA28C7008B3D8A /* SceneDelegate.swift */; }; CCDD92BC26FA28C7008B3D8A /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCDD92BB26FA28C7008B3D8A /* ViewController.swift */; }; @@ -24,6 +28,10 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + CC3806FC2703F76F006AA4AF /* CardModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardModel.swift; sourceTree = ""; }; + CC3806FE2703F7C7006AA4AF /* PayCardCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PayCardCollectionViewCell.swift; sourceTree = ""; }; + CC3807012703FAD0006AA4AF /* UIColor+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+.swift"; sourceTree = ""; }; + CC3807032704018C006AA4AF /* CALayer+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CALayer+.swift"; sourceTree = ""; }; CCDD92B426FA28C7008B3D8A /* Starbucks_Clone_TexBrother.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Starbucks_Clone_TexBrother.app; sourceTree = BUILT_PRODUCTS_DIR; }; CCDD92B726FA28C7008B3D8A /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; CCDD92B926FA28C7008B3D8A /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; @@ -73,6 +81,23 @@ name = Frameworks; sourceTree = ""; }; + CC3806FB2703F756006AA4AF /* Models */ = { + isa = PBXGroup; + children = ( + CC3806FC2703F76F006AA4AF /* CardModel.swift */, + ); + path = Models; + sourceTree = ""; + }; + CC3807002703FAC2006AA4AF /* UIExtensions */ = { + isa = PBXGroup; + children = ( + CC3807012703FAD0006AA4AF /* UIColor+.swift */, + CC3807032704018C006AA4AF /* CALayer+.swift */, + ); + path = UIExtensions; + sourceTree = ""; + }; CCDD92AB26FA28C7008B3D8A = { isa = PBXGroup; children = ( @@ -128,6 +153,7 @@ CCDD92D526FA2D5B008B3D8A /* Pay */ = { isa = PBXGroup; children = ( + CC3806FB2703F756006AA4AF /* Models */, CCDD92D826FA2D83008B3D8A /* Nodes */, CCDD92D726FA2D6B008B3D8A /* NodeControllers */, CCDD92D626FA2D63008B3D8A /* Cells */, @@ -138,6 +164,7 @@ CCDD92D626FA2D63008B3D8A /* Cells */ = { isa = PBXGroup; children = ( + CC3806FE2703F7C7006AA4AF /* PayCardCollectionViewCell.swift */, ); path = Cells; sourceTree = ""; @@ -169,6 +196,7 @@ CCDD92DE26FA37BF008B3D8A /* Global */ = { isa = PBXGroup; children = ( + CC3807002703FAC2006AA4AF /* UIExtensions */, CCDD92DF26FA37C7008B3D8A /* Customs */, ); path = Global; @@ -305,10 +333,14 @@ buildActionMask = 2147483647; files = ( CCDD92BC26FA28C7008B3D8A /* ViewController.swift in Sources */, + CC3807042704018C006AA4AF /* CALayer+.swift in Sources */, CCDD92E326FB043E008B3D8A /* PayMenuNode.swift in Sources */, CCDD92DA26FA2DA3008B3D8A /* PayNodeController.swift in Sources */, CCDD92B826FA28C7008B3D8A /* AppDelegate.swift in Sources */, + CC3807022703FAD1006AA4AF /* UIColor+.swift in Sources */, CCDD92BA26FA28C7008B3D8A /* SceneDelegate.swift in Sources */, + CC3806FF2703F7C7006AA4AF /* PayCardCollectionViewCell.swift in Sources */, + CC3806FD2703F76F006AA4AF /* CardModel.swift in Sources */, CCDD92CE26FA2AA1008B3D8A /* StarbucksTabBarController.swift in Sources */, CCDD92DD26FA33AA008B3D8A /* BaseScrollNode.swift in Sources */, CCDD92E126FA37F0008B3D8A /* CustomNavigationBar.swift in Sources */, diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/autoChargeIcon.imageset/52.png b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/autoChargeIcon.imageset/52.png new file mode 100644 index 0000000000000000000000000000000000000000..bf5dae348bc07df52b37c4d72854c3d67d1bbec6 GIT binary patch literal 413 zcmeAS@N?(olHy`uVBq!ia0vp^vOp}(!3-o1T-y>2q&xzALR{SoeB3=fJ%9)Zzzioh zHweSk-5tUJl2A3Co-Xb{Q4bFMNirjQWf#ZnicT>af`(Iw3G0XGJ>W9ah*mkWJ zd?4(*aD&@UmiH%;zDG5w9c>f$DF2yj@{99P*?FN;)vKR*lCSPS*tYh;LSS{J=}ql%x_<)!XSiSkCXv+{H}yS`hU&?>T>vdXMSaH)vwG%K0eu@P9r?CJ4 literal 0 HcmV?d00001 diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/autoChargeIcon.imageset/52@2x.png b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/autoChargeIcon.imageset/52@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..b6e5c09e5284395398f117ae215c520659ae08d6 GIT binary patch literal 687 zcmV;g0#N;lP)gbN=iveN=HdaNlHqC6hLAC z000nlQchCW8)(&6GpW%natW#|KOC`$(c3q!R)q)CDhaY7>Zr7TH#(&&rm<#a?6dq zL*q9w$(wB=_=HT8V@sd(dnb4ywKaoC)m`XG^#hQo?Gly9b)@crbFUtyx(i!IZTYDB ztXnyz0c%WTx_1^Cdm8hL^;Q3@yWacC0gS}Mha+&~cpLJS1+FTPdT5O$P%2-~xC<0G zcwJ@4z`qfi3dv4bP7i9L4yYi}0rx=KHK++;ClLbIYG8XIc=93G6UsCvFj+~`G$_(+ z*&l1Gm{N6&%-_P%Ffnk2BfC{Dwj}Xa@W!=LGr5{`!j7k7IX*POrwZix+Q(Kr)M%H4 zvhtx75BVk!SC)bsHHA6*dx-@iJvPi=3J#Ab9aMp&qsQ@ZHOxJ-h!@DcR3u*7gs2C> z`gv&}_TsLjn$bk`G8Zlt3r&r>*2}qX)+W@LGOS^^(U*NfD#k!nwJ6@&kag6Tw`>`8 za+n`8Rltx7Xkka_f%Ewxd#G)X_+{|u0eYPfsbg6E59KE@!{d~~Lw{dS+Gx|Q$ zSoS2}{!Kz|X4sjxz&(n@hZ&}H`Fqk50i+CB`=4+Hi?MHB0?qax3y|reWa3p`{sAsW V8+5hDKi~iW002ovPDHLkV1i&SIAH() literal 0 HcmV?d00001 diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/autoChargeIcon.imageset/52@3x.png b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/autoChargeIcon.imageset/52@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..879069fcb674b12279614a9bdd37eaa10b58127c GIT binary patch literal 985 zcmV;~119{5P)w5D+UyVvEcID!U-y5t zM<|V=#U6k5(RN3SWRh5x??HEU9EY`59*0t5`F)uE)b=sXo&JQm9=1LtnT2FZ1_*ub zLcqqU5Gp>L#NETrPhy^=kU+`=F7+m9Wg6S?xCa~D{FgC-9=(nls@2C=er45d?h4!6 z%;$Fk7MGo{V1w>D33dXk>G_nym%VOIzyjI_y7iwe(LJ!RI3IFB{mrcy1-)RuBI@&t z(q%d%5QEqZfdB? z7Gj)Xks#GGhz5W_Cn)TmPF*U4+|%VK@H>{ z_du4gJX!_t@t92!o<4)Rrw9Pd*7UiQ+q5fM;g$P2HmHB%QWU%;;lw^Xza@h!BbOD8l8qocg1wLdVke_S=d73sVkPVoL{_iI##JZAQ zU3Q(BJSigBEbK^mBkOqyKoUDJ*@r4I-Z%vC%=8M%(Ehv^j;lrNt=FO18fgt1^+yO? zh9*Oq|EyCWkX-xHr#nSQ;n$6nn3l^XU1cbcrs|Jxx5sHI3v6!2j8&L$UUh5G{e8^X z%sEIku#dOMSgWNt%OtM0@cQm;&JDDwY=L04S!-R)n~H&G%SzN4yf7Q?xM;1*A$TDr zE0%n9hkFIqV7C{195mH8?VgAfpjLEgm*u^(EVS*Euz^-yS=+6PKDFlU=SEkxnEYq--EA+sFiO=G-})M}>BzHqJNY88OZT#c3P z3Fj~|ZsksM8q+%u;l=f<>o(2u^Pozdo4@1Rw)6ko70-=-R$4yGVCa9(00000NkvXX Hu0mjf#dOy? literal 0 HcmV?d00001 diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/autoChargeIcon.imageset/Contents.json b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/autoChargeIcon.imageset/Contents.json new file mode 100644 index 0000000..e6ca6df --- /dev/null +++ b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/autoChargeIcon.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "scale" : "1x", + "idiom" : "universal", + "filename" : "52.png" + }, + { + "filename" : "52@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "52@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/banner.imageset/4.png b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/banner.imageset/4.png new file mode 100644 index 0000000000000000000000000000000000000000..f038669c6f46d927e78ecd7073ed7409f16a2a3d GIT binary patch literal 6352 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 literal 0 HcmV?d00001 diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/banner.imageset/4@3x.png b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/banner.imageset/4@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..1a01ff6ddb0d0bd3bdabc95859508e7c24d72dfc GIT binary patch literal 35994 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 bHd*^Wah=`wSNL4y00000NkvXXu0mjfcB`^r literal 0 HcmV?d00001 diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/barcode.imageset/3@2x.png b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/barcode.imageset/3@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..a0f41987f0ea21fdab73b6961bcf64c58841062d GIT binary patch literal 6583 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 Q8r!LK9_VY8YCH@5A3MFCU;qFB literal 0 HcmV?d00001 diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/barcode.imageset/Contents.json b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/barcode.imageset/Contents.json new file mode 100644 index 0000000..ce44f99 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/barcode.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "3.png", + "scale" : "1x", + "idiom" : "universal" + }, + { + "filename" : "3@2x.png", + "scale" : "2x", + "idiom" : "universal" + }, + { + "idiom" : "universal", + "filename" : "3@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/favoriteIcon.imageset/55.png b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/favoriteIcon.imageset/55.png new file mode 100644 index 0000000000000000000000000000000000000000..1ca3b4e6939cd9a9e1f18c4bc19089208c4884ba GIT binary patch literal 338 zcmeAS@N?(olHy`uVBq!ia0vp^q9Dw{3?%2B3|#`GTmyVUT-^(N9zTBk2nHbRCr_RL z1)ko!03?qsOFX(X0mwMIJmL8Agrmz7k1hjqmnDJ(mw`k)r56eV4HPR0@(X5IoiM+3 zf2IBf{yZmxN!JuUl$<%xKOr*eVF^&hGEW!B5Q)pBfw#q46gXVYYUEGd`tSepbxhmd z-RxbS$j8IX5U{|w`|L4A9>dk%$_x6k_U$zFH=p-OYl-ad3r2JIA7L=g{Pt~ecSvr? zq?}aYB?5P@dY|?VC`m98t(t%8o$b_YX7Aae-wthLpO?PU^M#1U=DXq-GvD5e5LYmr zmUy*vqPWg-`=ilc73!RCAC$lQKHXfRta``c!)O29pI#?eUw5$n^x^yWR-a){6n9e8 Q2D+HR)78&qol`;+0C^jsSO5S3 literal 0 HcmV?d00001 diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/favoriteIcon.imageset/55@2x.png b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/favoriteIcon.imageset/55@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..0dea13bc154602ff5be85fc235f5f2e7dc82ccd9 GIT binary patch literal 550 zcmeAS@N?(olHy`uVBq!ia0vp^S|H593?x6vT4n+%?*N|=SN8&+=g*%%e*E~+qel=D z#JG9r$&)7!A3g-Kk1S6-wk!e227;r@6M!N|mn8rh5Oic&!Z8p#5hxz%SYZV;R=gz0 zFPK4L!U3lX3xo_FG%U7$|Dk@vgvTcn3jFI@=Xf$OFcy2dIEGl9o*Q~|TC)O=t7|07 z9V^xU|Ccg<3vq6GYhjbQBW35!7LERGvDcZu#HMfkQSjseqpbZ2?gZoI*_XX$Udhyq zo?Q2QSz8WclWv}rG(!|y$jO6_hA$`FQ47jF5OO#{{MP2&dmv zyd+t3LFd^Oso`rgl3X<&6|D>jO5D9xWkKVCu7a-9-nWxuBL%%aoVZf-rOV2`*!y(V z;gDa|`!bx(lJ4bJEZ+IPr{~X`_01kHK4k7;{kwl*WxB|Iwmr+4?_Fp6vv%u7{lyK_ h19!x@-4DB?pZ8xeFneQG3NVHkJYD@<);T3K0RX6l3&{Wg literal 0 HcmV?d00001 diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/favoriteIcon.imageset/55@3x.png b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/favoriteIcon.imageset/55@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..e02017c8ebca14d96ac217038a9ef479326743a5 GIT binary patch literal 765 zcmVP)t-sM{rEy z;^N}s;>D+7TS)J#js?P`vBURj>d)yd(9 zt*{yDYf#3`Nq;1sP9eMK zm(+iUb+^+$%0IXFALbv*|Hbic;juya59&|K{UP*E19IS-<-T v`S0@y_3QKbVg9>6e}egUDxZIUn!o=6xqnaqW8{L=00000NkvXXu0mjfH8+t& literal 0 HcmV?d00001 diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/favoriteIcon.imageset/Contents.json b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/favoriteIcon.imageset/Contents.json new file mode 100644 index 0000000..2514321 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/favoriteIcon.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "scale" : "1x", + "idiom" : "universal", + "filename" : "55.png" + }, + { + "filename" : "55@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "55@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/generalChargeIcon.imageset/53.png b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/generalChargeIcon.imageset/53.png new file mode 100644 index 0000000000000000000000000000000000000000..21b1a3394aa2e386374a1c7aae66d22d09286086 GIT binary patch literal 603 zcmV-h0;K(kP)X0{{R39Q7V=0002AP)t-sM{rC= zM@LmwR!&b(adL80R#t?Bgj7{kla!QdYind>Wqf^oSz1~~M@Np1j#^q;M@L7Ro11-p zeq&=}m6erPSy`^HuTfG`TU=aQTU%jaVS9XhM@L6cQBiGeZIF?XqokxpMn*$JL;wH( zy}iANiHYau=c=lz?(gq`f`a$=_eo1j#m2_;^z>d|U$(ZkpP!%J-`~l~%2HHR^5{{H*_`#bBpL;mq$ z@z(ru%wJUFPw(&Q?;#WL#p+`K00A>eL_t(2&wY~Vc7iYzgu@TgptQFacl8nOH0nsW zL{Iim0c+O>iKFsFVzb%;u?bYM*o-gPbSBnS3i#gu6)&I}T$~cIE^46mc5tQb`r>6| z!Zxjs$GUXWJlzYDcc9q#l=|`U^`(r%b6#ipDUSoE;%&?kAOKbq!1hzkUib pc{>#6>h({ZklEaMo@?Yk{Q#0hF>^`rZzcc$002ovPDHLkV1nEuCrkhU literal 0 HcmV?d00001 diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/generalChargeIcon.imageset/53@2x.png b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/generalChargeIcon.imageset/53@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..d1a6009d588260c488ee1a8f97cecef8ad3aa573 GIT binary patch literal 812 zcmV+{1JnG8P)O0001BP)t-sM{rC? zNl8gcN=ZpcKtMo9N=iscNk~aaMoCFXNl8dbN=ZpcNlHrp|No4QjZ;)qc6N57q@>*4 z-O$;;L46NFU+7U(YemwjBZ@D01z~XlE zr}c9PlgZ7;Bd{W8tO63oUd7{^6bz#^PVG;n1}G02aJjo7a>>lp{&6L8Mvcy+%p~Fi0yDBe2o$~ZGdq<2du|Ad0s$t zaI$T!-x-*WbMDQAH;H+wA*^2k3zVdTK<4m_w3pUztMPIN33|f_PwLnJbe-#_-Har8R(Tf;s{0AZJn6RM{r zuMM>GT@B{4oaXKb?;^l)(ECFTz%s1H2icaOt;r z+3uzngR!j-@sFZ3N1g(Ecu7;@YK&$N;ai{n zWffD(y#bQmuh;Bkv?#}hhG>-05bQM7&gZNl8gcO8@`=qokx%RaNx#^pKH}=;-Lt(b2E3 zuU}wbfr5f|c6P?c$K2iBySuw;Yinz4Y$^`2egFUf3UpFVQvi7o@I_#ozSkwuKDed; z00YKJL_t(o!?jr37NZ~xtkxD(K)`GL{|{YmDuHP2?%6!lR_%}slLUA?aI1=f5ZW}9 zkatm)kH4_;MV<|`cTxWcQjrEV`uYK+4j_HUl$zUCDy7-S-y_vskhgn()UpL8mw`K= zTcK5sk}kpUCYS@d5!uUmf`T}gI!E?56dO>l+6$AGByu_XrRDY`+DjAGA$moSMd{kF z#w<>G8If*p*kYI_SM1(GY1`?X=jtpXjB$N7@3qm(2OWCEZd#rzoR}V+qq#xBKPWx5 zLC5u?@%s{W$ISJJ(>XNkY}zTt?@0#m6MVsQmI;`pnT;xPo*%?^i-_HMCRqTT=^_Cw z=Fz-4V7^EG^eH<=c|)wy=w}y+a)O%ZQVkNQ-#CyyQYC`|ol=sY3Bi01M7nCa39)IW z7_6vm z`PxDH9j7SIir-dD#$Ek3txOc|e;@6oDyGhXMXLgmW{an9%dAL|aQ&n#(6frj84z;z zJXZnaGiP0lM{U?NJ1&s+x`PK-khVHoochplI#;gl>DV=V=_AauXT7%uF7xiXC;k9Y z#1c^{`B8RAK3E_12M!l=3#je$K@ZY>yMvl3dCDLM47jt3lpK9H*0tv_wk{UO%A`xl zQ3p-p0>+L=qop59>Utg?4r8@iMhj7(=2*4hS5>~EGb;l z&gy_^uVFyrt0}5gnKs=oFn-gp%DkXDmTVKujCWxqnlAV`6DZ7tH!h?NhYb~HSWvP* zZGS3TCHw7a-%X&r|HMsji2A69Ma?%SoWTXt1{CeV`;li`n+#^fcMy;2O-~m9IgE^w zA=^gyqlIAd3Wi;5(>AoK%!(RPDDIpWRLTA(f$5N4$k{eD-fc0!`E;^H!}oq`ycz#U z&|=8lGnA7yWK7a3Vb6xN zFF*9>L$9*c@YuKMb6n@&sQjH~;_u07*qoM6N<$ Eg0rtACjbBd literal 0 HcmV?d00001 diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/generalChargeIcon.imageset/Contents.json b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/generalChargeIcon.imageset/Contents.json new file mode 100644 index 0000000..988985f --- /dev/null +++ b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/generalChargeIcon.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "scale" : "1x", + "filename" : "53.png", + "idiom" : "universal" + }, + { + "filename" : "53@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "53@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/limitedCard.imageset/5.png b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/limitedCard.imageset/5.png new file mode 100644 index 0000000000000000000000000000000000000000..766e921498d05943acc7d6bcea0cfb4a59caa867 GIT binary patch literal 35387 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 t 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 literal 0 HcmV?d00001 diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/limitedCard.imageset/5@2x.png b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/limitedCard.imageset/5@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..6f7fed0a8e19dcc3e62a1f97ad0048d982f4465b GIT binary patch literal 112637 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 literal 0 HcmV?d00001 diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/limitedCard.imageset/5@3x.png b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/limitedCard.imageset/5@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..1e536667f32fcc430abb7bc640f0e9af47a9f751 GIT binary patch literal 209682 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 literal 0 HcmV?d00001 diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/limitedCard.imageset/Contents.json b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/limitedCard.imageset/Contents.json new file mode 100644 index 0000000..74e2327 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/limitedCard.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "5.png", + "scale" : "1x" + }, + { + "scale" : "2x", + "idiom" : "universal", + "filename" : "5@2x.png" + }, + { + "scale" : "3x", + "idiom" : "universal", + "filename" : "5@3x.png" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/listIcon.imageset/58.png b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/listIcon.imageset/58.png new file mode 100644 index 0000000000000000000000000000000000000000..f91ce0234abd89e258c49340edce09a58fb2bb82 GIT binary patch literal 140 zcmeAS@N?(olHy`uVBq!ia0vp^qChOn!VDyP6QXy31Oj|QT-^(N&YwSj<;s-{7cQJR zbLRj5|BoL(*5A%A093$I666=mu;unHi#=r^zLKYlV+hCfzopr05$k8Qvd(} literal 0 HcmV?d00001 diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/listIcon.imageset/58@2x.png b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/listIcon.imageset/58@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..6da49d859dacaac8f54c420550fa3e44db950365 GIT binary patch literal 183 zcmeAS@N?(olHy`uVBq!ia0vp^T0pGE!VDxG^hJb$lyrbkh^u>n&zUo4PQw6@4Fv!H z|9|)H-G!Xyn?Pl3B|(0{3?Cd0H0<{&S^pd;;_B(*7@~1LIUzulC+Pr#PG)8%pTwdp z$)~xV5{uH_J{3JFu_)>3)2E$0Jee8{?HXmm8*aDVP31Y&+? literal 0 HcmV?d00001 diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/listIcon.imageset/58@3x.png b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/listIcon.imageset/58@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..0844f8552b65a1788a7fc0f2fb9c9292fd6e4ab1 GIT binary patch literal 240 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 literal 0 HcmV?d00001 diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/thankYouCard.imageset/2@2x.png b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/thankYouCard.imageset/2@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..8053958144055940a5c415fe01197edaddaac618 GIT binary patch literal 61281 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%0AqU~s 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 literal 0 HcmV?d00001 diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/thankYouCard.imageset/Contents.json b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/thankYouCard.imageset/Contents.json new file mode 100644 index 0000000..ba1bb49 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/thankYouCard.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "2.png", + "scale" : "1x", + "idiom" : "universal" + }, + { + "scale" : "2x", + "idiom" : "universal", + "filename" : "2@2x.png" + }, + { + "scale" : "3x", + "idiom" : "universal", + "filename" : "2@3x.png" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Global/UIExtensions/CALayer+.swift b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Global/UIExtensions/CALayer+.swift new file mode 100644 index 0000000..3527291 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Global/UIExtensions/CALayer+.swift @@ -0,0 +1,23 @@ +// +// CALayer+.swift +// Starbucks_Clone_TexBrother +// +// Created by 노한솔 on 2021/09/29. +// + +import UIKit + +extension CALayer { + func applyCardShadow( + color: UIColor = .black, + alpha: Float = 0.16, + x: CGFloat = 0, + y: CGFloat = 3, + blur: CGFloat = 6 + ) { + shadowColor = color.cgColor + shadowOpacity = alpha + shadowOffset = CGSize(width: x, height: y) + shadowRadius = blur / 1.0 + } +} diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Global/UIExtensions/UIColor+.swift b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Global/UIExtensions/UIColor+.swift new file mode 100644 index 0000000..f953b31 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Global/UIExtensions/UIColor+.swift @@ -0,0 +1,26 @@ +// +// UIColor+.swift +// Starbucks_Clone_TexBrother +// +// Created by 노한솔 on 2021/09/29. +// + +import UIKit + +// MARK: - UIColor Extensions + +extension UIColor { + + @nonobjc class var seaweedGreen: UIColor { + return UIColor(red: 46.0 / 255.0, green: 153.0 / 255.0, blue: 95.0 / 255.0, alpha: 1.0) + } + + @nonobjc class var brownGrey: UIColor { + return UIColor(white: 126.0 / 255.0, alpha: 1.0) + } + + @nonobjc class var primary: UIColor { + return UIColor(red: 1.0 / 255.0, green: 176.0 / 255.0, blue: 122.0 / 255.0, alpha: 1.0) + } + +} diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Screens/Base/BaseScrollNode.swift b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Screens/Base/BaseScrollNode.swift index 6037653..619cbdc 100644 --- a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Screens/Base/BaseScrollNode.swift +++ b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Screens/Base/BaseScrollNode.swift @@ -24,7 +24,8 @@ class BaseScrollNode: ASScrollNode { // MARK: - Layout override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec { - <#code#> + // TODO + return ASStackLayoutSpec() } diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Screens/Pay/Cells/PayCardCollectionViewCell.swift b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Screens/Pay/Cells/PayCardCollectionViewCell.swift new file mode 100644 index 0000000..8739af6 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Screens/Pay/Cells/PayCardCollectionViewCell.swift @@ -0,0 +1,243 @@ +// +// PayCardCollectionViewCell.swift +// Starbucks_Clone_TexBrother +// +// Created by 노한솔 on 2021/09/29. +// + +import UIKit + +import AsyncDisplayKit + +// MARK: - PayCardCollectionViewCell + +final class PayCardCollectionViewCell: ASCellNode { + + // MARK: - Components + + private let cardImageNode: ASImageNode = { + let node = ASImageNode() + node.style.preferredSize = CGSize(width: 296, height: 186) + return node + }() + + private let titleLabelNode: ASTextNode = { + let node = ASTextNode() + return node + }() + + private let favoriteImageView: ASImageNode = { + let node = ASImageNode() + node.image = UIImage(named: "favoriteIcon") + node.style.preferredSize = CGSize(width: 21, height: 21) + return node + }() + + private let priceLabelNode: ASTextNode = { + let node = ASTextNode() + return node + }() + + private let barcodeImageNode: ASImageNode = { + let node = ASImageNode() + node.image = UIImage(named: "barcode") + return node + }() + + private let cardNumberTextNode: ASTextNode = { + let node = ASTextNode() + node.attributedText = NSAttributedString( + string: "****-****-**36-6582", + attributes: + [.font: UIFont.systemFont(ofSize: 15, weight: .regular), + .foregroundColor: UIColor.black + ] + ) + return node + }() + + private let validationTitleLabelNode: ASTextNode = { + let node = ASTextNode() + node.attributedText = NSAttributedString( + string: "바코드 유효시간", + attributes: + [.font: UIFont.systemFont(ofSize: 12, weight: .semibold), + .foregroundColor: UIColor.black + ] + ) + return node + }() + + private let validationLabelNode: ASTextNode = { + let node = ASTextNode() + node.attributedText = NSAttributedString( + string: "09:51", + attributes: + [.font: UIFont.systemFont(ofSize: 12, weight: .semibold), + .foregroundColor: UIColor.primary + ] + ) + return node + }() + + private let autoChargeContainerNode: ASDisplayNode = { + let node = ASDisplayNode() + node.backgroundColor = .clear + return node + }() + + private let autoChargeImageNode: ASImageNode = { + let node = ASImageNode() + node.image = UIImage(named: "autoChargeIcon") + node.style.preferredSize = CGSize(width: 27.5, height: 21.3) + return node + }() + + private let autoChargeTextNode: ASTextNode = { + let node = ASTextNode() + node.attributedText = NSAttributedString( + string: "자동 충전", + attributes: + [.font: UIFont.systemFont(ofSize: 12, weight: .semibold), + .foregroundColor: UIColor.black + ] + ) + return node + }() + + private let generalChargeContainerView: ASDisplayNode = { + let node = ASDisplayNode() + node.backgroundColor = .clear + return node + }() + + private let generalChargeImageNode: ASImageNode = { + let node = ASImageNode() + node.image = UIImage(named: "generalChargeIcon") + node.style.preferredSize = CGSize(width: 27.5, height: 21.3) + return node + }() + + private let generalChargeTextNode: ASTextNode = { + let node = ASTextNode() + node.attributedText = NSAttributedString( + string: "일반 충전", + attributes: + [.font: UIFont.systemFont(ofSize: 12, weight: .semibold), + .foregroundColor: UIColor.black + ] + ) + return node + }() + + // MARK: - LifeCycles + + override init() { + super.init() + self.automaticallyManagesSubnodes = true + self.automaticallyRelayoutOnSafeAreaChanges = true + self.layer.applyCardShadow() + } +} + +// MARK: - Extensions + +extension PayCardCollectionViewCell { + + // MARK: - Layout Helpers + + override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec { + cardImageNode.style.spacingAfter = 26 + titleLabelNode.style.spacingAfter = 2 + priceLabelNode.style.spacingAfter = 13 + barcodeImageNode.style.spacingAfter = 6 + cardNumberTextNode.style.spacingAfter = 8 + validationLayoutSpec().style.spacingAfter = 33 + + let layout = ASStackLayoutSpec( + direction: .vertical, + spacing: 0, + justifyContent: .center, + alignItems: .center, + children: [ + cardImageNode, + titleLabelNode, + priceLabelNode, + barcodeImageNode, + cardNumberTextNode, + validationLayoutSpec(), + chargeLayoutSpec() + ] + ) + return layout + } + + private func autoChargeLayoutSpec() -> ASLayoutSpec { + autoChargeContainerNode.addSubnode(autoChargeImageNode) + autoChargeContainerNode.addSubnode(autoChargeTextNode) + let layout = ASStackLayoutSpec( + direction: .vertical, + spacing: 11.6, + justifyContent: .center, + alignItems: .center, + children: [autoChargeImageNode, autoChargeTextNode] + ) + return layout + } + + private func generalChargeLayoutSpec() -> ASLayoutSpec { + generalChargeContainerView.addSubnode(generalChargeImageNode) + generalChargeContainerView.addSubnode(generalChargeTextNode) + let layout = ASStackLayoutSpec( + direction: .vertical, + spacing: 11.6, + justifyContent: .center, + alignItems: .center, + children: [generalChargeImageNode, generalChargeTextNode] + ) + return layout + } + + private func chargeLayoutSpec() -> ASLayoutSpec { + let layout = ASStackLayoutSpec( + direction: .horizontal, + spacing: 83, + justifyContent: .spaceAround, + alignItems: .center, + children: [autoChargeLayoutSpec(), generalChargeLayoutSpec()] + ) + return layout + } + + private func validationLayoutSpec() -> ASLayoutSpec { + let layout = ASStackLayoutSpec( + direction: .horizontal, + spacing: 3, + justifyContent: .center, + alignItems: .center, + children: [validationTitleLabelNode, validationLabelNode] + ) + return layout + } + + // MARK: - General Helpers + + func dataBind(cardImage: String, title: String, price: String, isFavorite: Bool) { + cardImageNode.image = UIImage(named: cardImage) + titleLabelNode.attributedText = NSAttributedString( + string: title, + attributes: [ + .font: UIFont.systemFont(ofSize: 13, weight: .semibold), + .foregroundColor: UIColor.black + ] + ) + + priceLabelNode.attributedText = NSAttributedString( + string: price, + attributes: [ + .font: UIFont.systemFont(ofSize: 23, weight: .regular), + .foregroundColor: UIColor.black + ] + ) + } +} diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Screens/Pay/Models/CardModel.swift b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Screens/Pay/Models/CardModel.swift new file mode 100644 index 0000000..83fd632 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Screens/Pay/Models/CardModel.swift @@ -0,0 +1,14 @@ +// +// CardModel.swift +// Starbucks_Clone_TexBrother +// +// Created by 노한솔 on 2021/09/29. +// + +import Foundation + +struct CardModel { + let cardImageName: String + let title: String + let price: String +} diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Screens/Pay/NodeControllers/PayNodeController.swift b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Screens/Pay/NodeControllers/PayNodeController.swift index 99d329f..17a98c3 100644 --- a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Screens/Pay/NodeControllers/PayNodeController.swift +++ b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Screens/Pay/NodeControllers/PayNodeController.swift @@ -13,14 +13,14 @@ import Then // MARK: - PayNodeController final class PayNodeController: ASDKViewController { - + // MARK: - Lazy Components - private lazy var cardCollectionNode = { () -> ASPagerNode in + private lazy var cardCollectionNode = { () -> ASCollectionNode in let layout = UICollectionViewFlowLayout() - layout.scrollDirection = .vertical + layout.scrollDirection = .horizontal - let node = ASPagerNode(frame: .zero, collectionViewLayout: layout) + let node = ASCollectionNode(frame: .zero, collectionViewLayout: layout) node.isPagingEnabled = true return node }() @@ -29,7 +29,7 @@ final class PayNodeController: ASDKViewController { private let navigationView: CustomNavigationBar = { let node = CustomNavigationBar() - node.setupBar(isBackButton: false, right: "") + node.setupBar(isBackButton: false, right: "listIcon") node.style.height = ASDimension(unit: .points, value: 30) return node }() @@ -52,9 +52,17 @@ final class PayNodeController: ASDKViewController { private let bannerButton: ASButtonNode = { let node = ASButtonNode() node.backgroundColor = .black + node.setBackgroundImage(UIImage(named: "banner"), for: .normal) node.style.height = ASDimension(unit: .points, value: 60) + return node }() + // MARK: - Variables + + var cardList: [CardModel] = [ + CardModel(cardImageName: "thankYouCard", title: "Thank You 카드", price: "2,300원"), + CardModel(cardImageName: "limitedCard", title: "2021 한정 카드", price: "32,000원") + ] // MARK: - Background Thread @@ -62,6 +70,11 @@ final class PayNodeController: ASDKViewController { super.init(node: ASScrollNode()) node.automaticallyManagesSubnodes = true node.automaticallyRelayoutOnSafeAreaChanges = true + cardCollectionNode.delegate = self + cardCollectionNode.dataSource = self + node.layoutSpecBlock = { [weak self] (scrollNode, constraintedSize) -> ASLayoutSpec in + return self?.layoutSpecThatFits(constraintedSize) ?? ASLayoutSpec() + } } required init?(coder: NSCoder) { @@ -72,7 +85,67 @@ final class PayNodeController: ASDKViewController { override func viewDidLoad() { super.viewDidLoad() + } +} + +// MARK: - Extension + +extension PayNodeController { + + // MARK: - Layout Helpers + + private func layoutSpecThatFits(_ constraintedSize: ASSizeRange) -> ASLayoutSpec { + return ASInsetLayoutSpec( + insets: UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 20), + child: componentsLayoutSpec() + ) + } + + private func componentsLayoutSpec() -> ASLayoutSpec { + navigationView.style.spacingAfter = 22 + titleLabel.style.spacingAfter = 24 + cardCollectionNode.style.spacingAfter = 28 + bannerButton.style.spacingAfter = 20 + + let layout = ASStackLayoutSpec( + direction: .vertical, + spacing: 0, + justifyContent: .spaceBetween, + alignItems: .center, + children: [navigationView, titleLabel, cardCollectionNode, menuView, bannerButton]) + return layout + } +} + +// MARK: - UICollectionViewDelegateFlowLayout + +extension PayNodeController: ASCollectionDelegateFlowLayout { + func collectionNode(_ collectionNode: ASCollectionNode, constrainedSizeForItemAt indexPath: IndexPath) -> ASSizeRange { + let width = UIScreen.main.bounds.width - 40 + return ASSizeRange(min: .zero, max: CGSize(width: width, height: 475)) + } + + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets { + return UIEdgeInsets(top: 0, left: 15, bottom: 0, right: 15) + } +} - // Do any additional setup after loading the view. +extension PayNodeController: ASCollectionDataSource { + func collectionNode(_ collectionNode: ASCollectionNode, numberOfItemsInSection section: Int) -> Int { + return cardList.count + } + + func collectionNode(_ collectionNode: ASCollectionNode, nodeBlockForItemAt indexPath: IndexPath) -> ASCellNodeBlock { + return { + guard self.cardList.count > indexPath.row else { return ASCellNode() } + let payCell = PayCardCollectionViewCell() + payCell.dataBind( + cardImage: self.cardList[indexPath.item].cardImageName, + title: self.cardList[indexPath.item].title, + price: self.cardList[indexPath.item].price, + isFavorite: false + ) + return PayCardCollectionViewCell() + } } } From 8b175d3b600241a92fa5227ee73444c3a0474370 Mon Sep 17 00:00:00 2001 From: hansolnoh95 Date: Wed, 29 Sep 2021 12:09:18 +0900 Subject: [PATCH 4/4] =?UTF-8?q?:art:=20tabbar=20=EC=97=90=EC=85=8B=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Assets.xcassets/giftIcon.imageset/45.png | Bin 0 -> 308 bytes .../giftIcon.imageset/45@2x.png | Bin 0 -> 502 bytes .../giftIcon.imageset/45@3x.png | Bin 0 -> 683 bytes .../giftIcon.imageset/Contents.json | 23 +++++++++++++++++ .../giftSelectedIcon.imageset/37.png | Bin 0 -> 315 bytes .../giftSelectedIcon.imageset/37@2x.png | Bin 0 -> 535 bytes .../giftSelectedIcon.imageset/37@3x.png | Bin 0 -> 735 bytes .../giftSelectedIcon.imageset/Contents.json | 23 +++++++++++++++++ .../Assets.xcassets/homeIcon.imageset/48.png | Bin 0 -> 337 bytes .../homeIcon.imageset/48@2x.png | Bin 0 -> 545 bytes .../homeIcon.imageset/48@3x.png | Bin 0 -> 730 bytes .../homeIcon.imageset/Contents.json | 23 +++++++++++++++++ .../homeSelectedIcon.imageset/34.png | Bin 0 -> 345 bytes .../homeSelectedIcon.imageset/34@2x.png | Bin 0 -> 571 bytes .../homeSelectedIcon.imageset/34@3x.png | Bin 0 -> 752 bytes .../homeSelectedIcon.imageset/Contents.json | 23 +++++++++++++++++ .../Assets.xcassets/orderIcon.imageset/44.png | Bin 0 -> 365 bytes .../orderIcon.imageset/44@2x.png | Bin 0 -> 616 bytes .../orderIcon.imageset/44@3x.png | Bin 0 -> 932 bytes .../orderIcon.imageset/Contents.json | 23 +++++++++++++++++ .../orderSelectedIcon.imageset/36.png | Bin 0 -> 381 bytes .../orderSelectedIcon.imageset/36@2x.png | Bin 0 -> 622 bytes .../orderSelectedIcon.imageset/36@3x.png | Bin 0 -> 1003 bytes .../orderSelectedIcon.imageset/Contents.json | 23 +++++++++++++++++ .../Assets.xcassets/otherIcon.imageset/46.png | Bin 0 -> 288 bytes .../otherIcon.imageset/46@2x.png | Bin 0 -> 449 bytes .../otherIcon.imageset/46@3x.png | Bin 0 -> 707 bytes .../otherIcon.imageset/Contents.json | 23 +++++++++++++++++ .../otherSelectedIcon.imageset/38.png | Bin 0 -> 294 bytes .../otherSelectedIcon.imageset/38@2x.png | Bin 0 -> 482 bytes .../otherSelectedIcon.imageset/38@3x.png | Bin 0 -> 719 bytes .../otherSelectedIcon.imageset/Contents.json | 23 +++++++++++++++++ .../Assets.xcassets/payIcon.imageset/47.png | Bin 0 -> 300 bytes .../payIcon.imageset/47@2x.png | Bin 0 -> 434 bytes .../payIcon.imageset/47@3x.png | Bin 0 -> 641 bytes .../payIcon.imageset/Contents.json | 23 +++++++++++++++++ .../paySelectedIcon.imageset/35.png | Bin 0 -> 367 bytes .../paySelectedIcon.imageset/35@2x.png | Bin 0 -> 514 bytes .../paySelectedIcon.imageset/35@3x.png | Bin 0 -> 710 bytes .../paySelectedIcon.imageset/Contents.json | 23 +++++++++++++++++ .../Pay/Cells/PayCardCollectionViewCell.swift | 1 - .../NodeControllers/PayNodeController.swift | 3 ++- .../Screens/StarbucksTabBarController.swift | 24 +++++++++++++++++- 43 files changed, 255 insertions(+), 3 deletions(-) create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/giftIcon.imageset/45.png create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/giftIcon.imageset/45@2x.png create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/giftIcon.imageset/45@3x.png create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/giftIcon.imageset/Contents.json create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/giftSelectedIcon.imageset/37.png create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/giftSelectedIcon.imageset/37@2x.png create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/giftSelectedIcon.imageset/37@3x.png create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/giftSelectedIcon.imageset/Contents.json create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/homeIcon.imageset/48.png create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/homeIcon.imageset/48@2x.png create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/homeIcon.imageset/48@3x.png create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/homeIcon.imageset/Contents.json create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/homeSelectedIcon.imageset/34.png create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/homeSelectedIcon.imageset/34@2x.png create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/homeSelectedIcon.imageset/34@3x.png create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/homeSelectedIcon.imageset/Contents.json create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/orderIcon.imageset/44.png create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/orderIcon.imageset/44@2x.png create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/orderIcon.imageset/44@3x.png create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/orderIcon.imageset/Contents.json create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/orderSelectedIcon.imageset/36.png create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/orderSelectedIcon.imageset/36@2x.png create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/orderSelectedIcon.imageset/36@3x.png create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/orderSelectedIcon.imageset/Contents.json create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/otherIcon.imageset/46.png create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/otherIcon.imageset/46@2x.png create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/otherIcon.imageset/46@3x.png create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/otherIcon.imageset/Contents.json create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/otherSelectedIcon.imageset/38.png create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/otherSelectedIcon.imageset/38@2x.png create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/otherSelectedIcon.imageset/38@3x.png create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/otherSelectedIcon.imageset/Contents.json create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/payIcon.imageset/47.png create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/payIcon.imageset/47@2x.png create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/payIcon.imageset/47@3x.png create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/payIcon.imageset/Contents.json create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/paySelectedIcon.imageset/35.png create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/paySelectedIcon.imageset/35@2x.png create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/paySelectedIcon.imageset/35@3x.png create mode 100644 Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/paySelectedIcon.imageset/Contents.json diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/giftIcon.imageset/45.png b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/giftIcon.imageset/45.png new file mode 100644 index 0000000000000000000000000000000000000000..38f072b7edf312526730761d036517adda47e569 GIT binary patch literal 308 zcmeAS@N?(olHy`uVBq!ia0vp^azL!c!VDx8&CLRVluCe4h^u>n&s;E=GiMHvhLH2; z&j+%BB!tUcBPs<{&s`Ga7tC;=!RGq>WPuL~0a7X2zktG9JY5_^G|nd{9AN92B00%K zFlL$Vy-w+Ji32}3Gw;|YGR5M+&zr0~(vmy2E&gbJ;HM~;gPk|Sob%U|b~s;H%8=+d zbzopr0JTGQS^xk5 literal 0 HcmV?d00001 diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/giftIcon.imageset/45@2x.png b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/giftIcon.imageset/45@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..d79e7c7e193431e8d6c2ea14385baed78d48b7bc GIT binary patch literal 502 zcmVSXot&JUpP!$dot>SXot>SXoSdAToSdDVo!4Ac$^ZZW3UpFVQvkq#K`Pf~@TQPN z88!+400DYQL_t(Y$L-ZIOT$1E0N`hOiLp~6RxnfP;Ls@$6wTBkTIpn|(@o+cxCtWK zNf*UV?LSBxn#A`jy$gv=o0otP1kpF#kT1F1z4y2Ss48l<9GwoW2Z%#pV+@;=>L_B; z6GKRS-^Rx&`h4-u@glDbBY;!HE2xd;dEP=pz9YpA^SpubkLZg*MVnwqS@}Ne*~y8N z;d9DvMC{uN5@y^dxQE-0gMo867JDc1Vqp>wbirQCI9{R3U>u2fN?AoR($v*yZvF%- zEi&wC^3siSy$4e>6U(x>@ls}BQ7ilBzsCJ?-lTZhOd7$9OfR0vzTSuH>bv|G#W~-} zKRn{ND~jU*@Wqh6rkav9c~7<0?kXO2;5e>=GLr}QYEHFffhbmI$vI)LMrd}XI54u@ z50bv|YVx=gmG!dh`YDr>-+HCo78^26qlm@iiu_+O$1@jj{iBriu$0;8vW(#d2aU|q sth?2svNzHBlP~V#{aQ&+9HKQo0Un0YGe`I~c>n+a07*qoM6N<$f;e&R+W-In literal 0 HcmV?d00001 diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/giftIcon.imageset/45@3x.png b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/giftIcon.imageset/45@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..b499408c74f7764014d8f7c8b6789bc202378322 GIT binary patch literal 683 zcmV;c0#yBpP)SXoSdAWpP!wbot&JUot>SXot-d1s}ukL010$bPE!Ctfxy2QW2V?xj&l0| z00J{fL_t(o!|j+oPs1<}hV51E_F;YcH-O>fou++0!#iLbmv-QnrgKHu4QCghK3$Mbr8q>o;`od(|n(+h(G{v`$9f>2%Z z5RPLXr3ZqZQZd3A2A3Fc;a~fIsFT0SX*}|(PO=oM(CkdB4P9-M&DrcOsiDrqJ+`zoL!RZpQ-6)+H@LLyf=W> z_Ot@<)(Jb8wuoYc7TqleTl}L0bIVGt>m6x(OC-VRWm4mM?vrNKk@GH!W+$PYvSqX5 zFU|o6p)X<$IZD$N2NYbB>{Oxdn$(4vd$6vLS{mW24A<-w)*TRGpZdN5U6%b{L^t=L zXZeN&({)K-Ua3_>f1|71jJ7$=tH`U&wu@p-Ur!64WTR!sWu!|osIt+9cm>HwN6mXi zx+NZkGn55uNPhD}urn=3$! z&0zNW28NAQAOzuVs&bb&)&tbSQxfDC%y6K=;krh$!1NCR8|LWD@CAx*_jGX#(Kw%+ zaDc66isYma!I)*rcAf10QU`t-^2WGxdC5HZGl!|InJ4D>#UJbs{&=w}%ui*WbKcDL z!;%HwjE9=ER8>u9e0<~SWw3y4+aDe#mIWUjt8TCeDlp7WFF7|yutCG#M&qYwQxmVi zb9I9NhMouW((e|lICOk%VbpQaz8c9aF+)}&L2wb%kz7S#Wj?33uNjgQ(-?RBQF~_0 zb)v?V%}u243=_wHjVb4!zMRSMC_nQ+i73OQ-sX<4r|aW^USRNa^>bP0l+XkK;r?(q literal 0 HcmV?d00001 diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/giftSelectedIcon.imageset/37@2x.png b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/giftSelectedIcon.imageset/37@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..23a4ad03b6665a94741b467039b2f080d5aa9105 GIT binary patch literal 535 zcmV+y0_gpTP)uC{0J3^QpTwvD000bhQchCPHmqD#TWMFl(bBRDxYHKwMdz7OSHnuN4@4G2LHeZvj;<^I0Q3n6*M z(H3tt8`n#K10uG;-@vp4ELvUG_*nl2Jex40!&eIXX%)dU$Spvh6npR3*b;w-i|(g+ z0r-sIvPJ+EVG?oS`&>#6Zlt&jFA?jX$i9_v$Rc$S?!uC$!C2dAv%MqdqCgmrXiAEv z9K@SJa*PTEBpxCb01YeTD$eUa!G$^+YCh-E3C?ul$7H>rHZz|1JMTaZH!f4|gf78AKnjiaXp(>FFd3jLZB#Ri7!G)U(NU zkcEQ73%5E`?BlN;W5EgQgjA{dvTZ_BO7ht#yf=d)TZ76nJ4mwbMvM-A{gKHHwjo)T z=Js~k&yCGDhl9EGxm@)!x#SyJEcOnk*_uA{D`?gdOH=Mwr@NoCo8OAL15Yz4z0r`X Z@dYK5EY0o72FU;b002ovPDHLkV1l-E?b84N literal 0 HcmV?d00001 diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/giftSelectedIcon.imageset/37@3x.png b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/giftSelectedIcon.imageset/37@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..d2de70f24ee4b5eff406d903e58efef52518648e GIT binary patch literal 735 zcmV<50wDc~P)uC{0JC}kvUvcpdH}V00J3^Q8%sg}000bhQchCZSM%%d}%NBis1L)dd`^Pa~l-k4TO5Z9*1`5k#t|uqo%a@K*1&l z7Th<$7X;hFjwyNrc+HsAhmQ-~9s$b)y(SmqyPpVK7ar*4 zTEVfNHUKX{&*6lmS%R)%bSC$7D>>UJ@~3M>2vD-6!L{p}bPGL4QkA1zgcr zF(h;F0K<0?y<3oj>^>5j4Cr!LOVEb}m1f(qwyggNqeY7)LO47Ah`gF1p8CG;fS9C@H1$2(}wB>seil@`}D4 z>s7;elU|;U{wiqEMJ|?JyC!}5)%2X2iObOqb@xz?HdRuUqb>D!DMwF-_Y8fi8Ro-F zp$z>amu~q87Vch5(>}IWiSax|i)k`<^<4prBO*>WecjDc>)OL?c3QJz;wSXN0*Nke R8)yIk002ovPDHLkV1m7iO)&rf literal 0 HcmV?d00001 diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/giftSelectedIcon.imageset/Contents.json b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/giftSelectedIcon.imageset/Contents.json new file mode 100644 index 0000000..e28d598 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/giftSelectedIcon.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "scale" : "1x", + "idiom" : "universal", + "filename" : "37.png" + }, + { + "idiom" : "universal", + "scale" : "2x", + "filename" : "37@2x.png" + }, + { + "idiom" : "universal", + "filename" : "37@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/homeIcon.imageset/48.png b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/homeIcon.imageset/48.png new file mode 100644 index 0000000000000000000000000000000000000000..42ff619246a09c8c3177c95e2fbe57a19c6e24cb GIT binary patch literal 337 zcmeAS@N?(olHy`uVBq!ia0vp^azL!g!VDyrFLwD4q*MZYLR{SoeCE%eKX>ljdGqE0 z8E_I=6u#)`;uxZFJ~`n4Tf{Oy=ZCxB z-<#8*6T7#1d*o!cNnhUGoxOIuk~j0gu#`*{yAY{!?Ouo1GxXxI`4sGw=j?A}RJT7N)w+MOqGap-iH)4S=NpfutIR&%bS%B2?W3r~35Fz|PK`gk zJeMSD+%(%l_tD9Wkb@Plch5ED-nPq&2F*#vi5c)I$ztaD0e0svbahgARo literal 0 HcmV?d00001 diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/homeIcon.imageset/48@2x.png b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/homeIcon.imageset/48@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..795c2592166fca2b4d643b2168a712e43cdbb71f GIT binary patch literal 545 zcmeAS@N?(olHy`uVBq!ia0vp^)^nY6klgyIaOEFeX?+lr{y%6=Y3N`d)!l=t>!FKnX0*`JklsBKFBg? z@2peJtmeU%H$o4G*l~7gd(J6h{>{6m{I}9W$pfo&pS2hCUE1OLcHRv6m2-uSqYuBa z*KhAGI(M71ujWB^;k4t`gh3i(CmYwZwKDRmfzgWSs)z|qKulaE1*OXNEV{0#QuN2<*ts}kl-TcoJ7VhLP z5be8NRQ^Wlcw70d*DW=km#;pJSQz|3&f)!~^M1>XVKMUq>3!>$ VPHl*LavB(c44$rjF6*2UngBc$`gi~U literal 0 HcmV?d00001 diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/homeIcon.imageset/48@3x.png b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/homeIcon.imageset/48@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..b7dc3c21b98186ef5a4f24e6e14e20c2896e08fc GIT binary patch literal 730 zcmV<00ww*4P)SXou8kdo}Qkbo}Qkbo}QkbnILc_0000BbW%=J0K|Y6o@-h_ z>31(>B6KeR00LS`L_t(o!|m5GPuoBg2XH$MHe|{PR3yBhlr4)}7Njg77?83c!NvlD zp;DHJg&_+qph|dQV9Sz`F>Wj5>V3Jscew!8X5BO1daMl{vj6LKKmLAw?HDWt<`gf%({ob_C z(hZZ|G%R4}bio!P*b}OJn4I`DJr+k!RjT5sj&Uxmw3^=A@PF&=*#(crj5l2gZWrj! zAtcB2;udF@knTH|F|4aSe)=)5gIMjhUD%@$`Gv+ME(X2sbQhl014s@++HK>eJ{3fT zTp+uUY=v^UkkMY8j2u5s=<*T)OvBZ_-bibZ(3( zlo+PEcIC2s2S25*kq%J%GNnhWi;V8zA5J25U4t8WQ+=)8@>u@2uIuov)oOJq%R;-a z6XHM6&4PYXewX6{yYT<# z`6{UYXxfj}nA%YFl?-1*i^-^ z6-ZVxtZx8u*H?f+>Vu35cH0@1BjO%09kJP_mB=a(uMiZ~;Mlydb78tzvqC#l exbw9oYzzg7dp!cun%#h&WAJqKb6Mw<&;$S%D0{;I literal 0 HcmV?d00001 diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/homeSelectedIcon.imageset/34@2x.png b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/homeSelectedIcon.imageset/34@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..e853cf82b0a50ab24be5706885aa9d4d69f8d317 GIT binary patch literal 571 zcmV-B0>u4^P)fqdH}L{0J3@jvw8rse*m+30I_-ivUvcpdH}L{0J3@jv3UTsdH}L|hZSSV0000F zbW%=J06>7h+fWZjk)rXiWFarKSc?e&00FB>L_t(Y$L-as&hDH=wto6AEvLwy1k}180*zvOR%^$uz zNJiBBSV3EOIEq|Wxa>^!u*PLqvYQnykD@z{?40&+gEKOPF7lEQuQ?+-);;aZ)d3fL z+1yn%UtTu{AMs+_HnHEp%ZCpf?gGSBHeXra$d4=3F0=WL+95U@YZ(0J;jqBxi0Wa- z0iSSl`xO3qB@nFuX!xnXjQ&OQOLKwYIX zVY-HzX6M0X|7LPP&t_MgEZEuTlb7w7ca&Zz+Ok-{x}62Ozx6DJ!VJBdwFKZ3}00L}DL_t(o!|j*9Z__{&$DL5e;4HBaWxzv~p#$>Jty1NIsRPQ8 zjGzom2vCMhR6-rHf;v!y5Go$rP9Sy2$`)g_!qWGT;=QXA`RDl#2?>PX;K;tueeXx! zd-u*T{$jHZx1TD*rVk{KROuPW;)NRAfw0dS(lnQ)3fx0#Uo@mie4!x?oEhg1*?kph z+LrK=f3B3IfrBP`mZhTxIwuXda@s@LYUwTL&?6nXQo5EN>Cj#1(4G!mBV9v}bm%SU z&~?(d){zFDX-HRzn}CM+v5fwqBV7_7$?5#eEkHy3dlQ|Tx!Xh|n~D5Jda1rh$`#p} z^p9T4*!`n7%5DPeX*g9&{|3Fi8>rJS3hz={IK@^1u`@~T-&f-+Ryj?x#`g#sXB=RQ|w0-C`aj4n$^3`Pt(TF)b uH^o;r|KK@!i1Q6^j_Ok$Rlhg9JPb}V8f%tW7S0BGn!(f6&t;ucLK6V6M4Pk# literal 0 HcmV?d00001 diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/orderIcon.imageset/44@2x.png b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/orderIcon.imageset/44@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..d685b50b5c8483cf7db7610db7a31f83df69f307 GIT binary patch literal 616 zcmV-u0+;=XP)YbpPrtcpP!$eo}QhZot>SXo}QkcpP!zdo}Qkbr!p?<0000BbW%=J0DwQl7^LY| zZ!nMvrL-FW00HPpL_t(Y$JNz4Ps1<}0AM$Sl)%!?Swd6>=u!b&mkNo8V5wS|vVepT z!b`=%&;=F*VyPJT2?&X$AtgoipToX5adL4gAy~Mfwf*Jc#CI_v|2&>O6ggdFIJUF= zUf?jJCM$-kcuB+EdADZlRPlm_$BGSa17Xi|vr{(^*YdCBhkTo7yZosk{)8{&{|GUK zbr=xC2gVED7J~!v477}=(0VE3Zn}ICW|0t`bQ&RMM~(StGcL77-#$vJ!$>_f-wILUcEM~~!yYS}7Fa$P0-j!!Y?tFVQ+ z@`v2TT%A{exCVd61I*^!3Y1kOBkB-cx+F0zess;c3in@b8)gHK?AISND~k(gIk$Bb z*q>eBtJjFR$}7`scgxrkWj~yd)kc3r*0xSKA8z#sSzYf7J`x4BG9^x%xyw+eHof2p z!TYdO6Hx^Lan8I>b4qptVtt80=MuKU=2$Y%AipN=J!~a?#MBk+^t?62;^J1^Wxzgc zl9FL+{6{fA+gnd~K|Gjq4&C+u7-~pzm8Y#uYw^@tg5i1z6aVwU5997$c>NmiVt%{Z zOu0f?REycB>?(k-_jby}xDlzj(}CSothebkWd9ooBkA!Eg8g3r0000VZo}Qkbo}QkcpP!zdo}Qkbo}QkcpP!zdo{y>TY5b#q&s=%lu6h1dQwRD^m=d|dQY!?y}ngQub0oa^D{4baIr;yU{m-u zxXeT=%a?zM86QtFAWca_Lce28VzyDg6}RV`ggkMQ~Sy^TR?fQezs8~RZn!OmQK#T5&~x3Rl;ip>2dnAY^+P;qbdf4Q6tPJp%Njm5Mr;zY5uf2lWQ&eNHlm~d6PqIzh~z$`JG<*T zq$h=RuRz2CAzMuQkk&@>1*EIH>j2Vaodyqi6%7urNtbkI$3l7zYG-F1f!fM-DouxM zUX5}b11hv#3hx_bfI#!LFKuhrQJ=rTDF`*x5=_rwC}(JjVq46Fuyll9j9!nOU$Wf!_u z!*?3-795CwF`^mq2-Dr5#?Bs?>Cg1e3zlL{EYsGT%Urvn`Z?P>UQx?eS&4)XNEpz+ z<2ajfB(vv|-op3o0C}NL%Q|B_=m7nq8!Ua-u^c~H451JBy_Niy?B>uTN{M_=GG|k_ zc%S-0+x+B1pD(Qs2N_-FMWWi`BZe17UFDl3)ed>eu2N^48qcL(Nxc{AQXD}W+FQyB zPP6(6%b@0_9*WVa(r4m+`*FX!@gCD!k9=L6P6mli)g-MeY;2J|4w2LjuP&=Ko@KO+ z>CmCyEZBv%+341o9I{&4^-WF3Hhgo+qgrS>&kh?ND{Pr8@820uQ@X~Fq|Z>I*`^$_ zo`}n@R!Sr1goOO}fSN0gX#05iZl674o$z0B9Jl`$8S@8Z&I>z6m21QR0000A%WGHk8@ksGT( z}HTiZC_6|P%nQ;kY6yv0f$))`vpE+ODHf<*bwmBiMtIb z_14qHF+}5ha)N?n!R$7t1GyaY|JsYx_%nR|n)v6x@PQi9YK`kIjf}30Q%)b~Z?0!J zs9(13|9|;s4B!6S^BJTl{OI{#Z*1^E;j?r@%76ZleSh2~=KuU}t`N!peDk0GJW3OO z{MRqM{r`W{5q3t4`NF;DTju`f6ra!Adp>~wS$*?od9J1dER&K{rH^^=-D6lJk~l4N z?i$Bu#jyznX=bUrFP=y%?7b->A;HJf*ORx0Th7SZ!g!bCPp&uYB9gv5l8uIkL=Pk| zJ;@J9DY?Y<@#d;6rzAvY^vdj0xRH<9Bbx&Z40Ww@POhqUSOoMq NgQu&X%Q~loCIDq9my`ei literal 0 HcmV?d00001 diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/orderSelectedIcon.imageset/36@2x.png b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/orderSelectedIcon.imageset/36@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..06cfe2e1408f293942ad92e8ecd2c898851bf173 GIT binary patch literal 622 zcmV-!0+IcRP)uF{0I_)hvU&iudH}I{0J3^v4hRPT000bhQchCNL00HVrL_t(Y$JN!nPr^VH0Pq8fATYq*p;Y343`7_ihz@FWAZ{id z2pFdhgvEtH6PfIwgM-9@IQb`N;$Y>|aDS9{uh*~ZwTVFo-vD?0wbyI!y&gjUJf2l9 ze44;~)V=&3;E6{i91O+fO#!!~ZjEs(nm=5Qt2-QaRK+{>ILCSaYxyDH`8gB*)DXYI zL-s%BSc7%wp|}prFL;9 zJ=2K+#`EBrPE>sN7BPF1k`cmHXvLftLmE{X4 z>)Sd4tgreH?rY>ZMJiL$+F_ieWj~mrVyQbuTeSH?2e$j*S8El0WS}_U2B!e1xdhbEY?sKdul;vG@+hsa< z+UjCp9X3hEFg5byvfNSCvd!>d@{Kik4;YGsaf+mE#>higuTqBbY)1P0QJ+}d!%6?u zL>u_+{?y@^ftPuUGa9}%;p<&@7{xDXYVI{*H>K+>sU_C`254gyxm}HH=>Px#07*qo IM6N<$f`(up5&!@I literal 0 HcmV?d00001 diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/orderSelectedIcon.imageset/36@3x.png b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/orderSelectedIcon.imageset/36@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..0153f42d54e13c6e035bca7955dd2943a4b64b22 GIT binary patch literal 1003 zcmVuF`0I_)hvUvcrc>u9|0J3@jvUvcqdH}O|0J3@jvU--9Bya!#01b3fPE!DY zz(47kClO!KuTO9Qe-dT@00U!5L_t(o!|m5iYZE~j2k=c|cJm4eRS-l7i-?GYV)P;u zs#trFfoMyy6d?yM9s=pXQz=BM7co#TdJ4pwmq5NiAzr){l4dt;V&}U!Z=38+=9%nz z66ru_UVhum&OGyGW7RpxL4#&|HN(;AKIvR>8cf~;Tb^pUKQ zPM^u+r^EPngdxHBLe{oz1D!dw?R$#(0d90nJ@+?54hU~2YF$!mpiYiF3sC7fF^J?8y>nm!#qSmwJ^X2e!XZoVLKyT9$z6vfg-s0lpue{>DA_Ed7 z!#{P!%bZ>(rJCda+yQh<>Lm#+ED*^B zNY^6ig9s4|=q{x55h50dWKb(lG}TDDZs5 z&@QMGk+d1&xF*MDunl3|34LM%7Ece>S+w}*?Q8o#i-C5%e)$vW-)!%fz*;Sr=l4Ts z*7rz#Ojbhb1e$G5i**yPAAUsg?geNQjY%}VldBWZ8N9(4PBo0K@!g_EwivV6$QH>( z*_U^JF>QUacj=uKS5if93$F98I3gKw8DdEO{ee7rMlr;hmi=BMP`?1;KU{1$biV&S(Glnj-I?c8{qEq+@{gpcKYcvsh zBy`@>F)xCAx-%9d*q_&rX$CF%^$DNyGb8OV)K&lh literal 0 HcmV?d00001 diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/orderSelectedIcon.imageset/Contents.json b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/orderSelectedIcon.imageset/Contents.json new file mode 100644 index 0000000..47af8f9 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/orderSelectedIcon.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "36.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "36@2x.png", + "scale" : "2x" + }, + { + "scale" : "3x", + "filename" : "36@3x.png", + "idiom" : "universal" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/otherIcon.imageset/46.png b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/otherIcon.imageset/46.png new file mode 100644 index 0000000000000000000000000000000000000000..28b9ba296e63135c1d91bce9723eeea5da1eac4b GIT binary patch literal 288 zcmeAS@N?(olHy`uVBq!ia0vp^vOuiL!VDyRru_c{q_hHjLR{SoeCEuVGk^a4xkv!c z1xf({MCP5~oKT=H-jX1{U^GyL0ii ze%xa1kuH+0biE@vOZtY3h{PmG3B8wQAJz0y3u8|0+0dXkqw%4VN3BNrqr@~uHa1VT zW!wpN>$4>~;^!XN#Be4(FM<8z%(Qb_2{+lE82g&Bew4fs7{koK#x9%YUo%Aq=tc%l LS3j3^P6m!E{AOAybQ!sZCMGbkhG+&?G?%4jxzAWJd)o(l)o({xwWl-+YVH@g z`_k_Dxo;=kLsnh+AN#QE;VwZfy<4*rqeb)ZQ$aZzbXSW-ghf(=IP zFJ^Jc=ge2s-KB54&0O5>c9w-sbm*!p^S_jA`Sw8cW*NK1&#B)8?Gv1?c$r^ZvV7mn zs+;%NqAm)$UyAUtb*<#zU9EM#!lW<5d;cdbH^vWc0o5}mXe%gl-Fju8elGc_HAnLN z4LR+xQYXYOE?`)$d9|YNK>YQ+waXVcHjA>RYsD?uD%sh2ZpF2nB^|eAo#&>;U0>#9 zwA@Z-O? m9^W!nyn6qzzi;-#znnc2viaZ1D?I=PGlQqApUXO@geCx170izS literal 0 HcmV?d00001 diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/otherIcon.imageset/46@3x.png b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/otherIcon.imageset/46@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..3526c70d03cc5ba80550038cc5308e4426197513 GIT binary patch literal 707 zcmV;!0zCbRP)SXotvARot>SZpP!wbot&JUpP!$wu&|t*oSmJWltevX0000BbW%=J0K$Sn>@8=S z4+5{b7Ar+ODck* zm*58hy@ZNh_7Xq9Lod;y5OV3EH!oScxYGBZ^O?`xY&UK1&U2YJJHL5m_RS-}`)oo!oB zw_aZG4NbE)Q+H#XoSdAT{wK|-vwYHf(`vq3fcr~XKNKM-6 zXwh$T<_4`y(9kup>lnodQ2g3#ui#{%8&{ACYa)czXbyNjQ6bL2(E?mXCajANc>n;j zr7&9e9nMwxwPxUgGh62dH6cu-u0V#)lK*!;&blIB@=utd?14<(ro8UH-4IcZs8h5F#0NwuB@!H&m&wie~`k{ zka`>x>mbyBU~019GbF`<@8%)|s)pU>}UL pFsp2B+P?+!BMJ1><~Ey{$$!-mC2UGPidg^v002ovPDHLkV1fVA%WGHk43*j&M| zsS1SFS1_z^00~048!H*M0%bwm$CtU;fg1Trg8YIRjy5<(_Ob&H=JiUXdioEx8k{(@4Kv>lbK&nWodI@YfFEA>qnFLMhpFH4C(vif7j2{=mNTu!PC{xWt~$(696UfXk!2X literal 0 HcmV?d00001 diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/otherSelectedIcon.imageset/38@2x.png b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/otherSelectedIcon.imageset/38@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..755697dbc317afba9d62cc33c63450dc670ddb24 GIT binary patch literal 482 zcmV<80UiE{P)fqc>uC{0I_)huYdruc>uL~0IzrevU&irc>uC{0J3=ivU(fc)SCbR01b3fPE!Ct zz<}xyrZ6B`(2#JlERa+H00CM_L_t(Y$L*A{O2beThED@(tP}zwI9Mn;N}$rNfevC_ z0zs-nK~SP}3dGT^eE^}3ii?9|ppbS3yLfVQqnBC-M0q*N|owQx>!#l7_)4l4BY=p>u}HTNYwS`QLoC+fN!=jhi0OT7E&vC zGMHXQdsw>Wbz04qLo3thHlt|)FN7Yrh;AZu$m;WV+J_h`#;WZW3FDGop;~ws6v)D+ zPdPD1P)zC`zmZj-+JvkJ&d<`^fPyw@!|0v46mF?3sWy)5s+1MRba5p1?5I?nTF~UY z3N<@ll+5T~@gOwY34P|wp`>+e^}xj2Ye<^Kc6T6Y)T2}O15_pD%;;QF?(f=*>i_@%07*qoM6N<$f~D2g)c^nh literal 0 HcmV?d00001 diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/otherSelectedIcon.imageset/38@3x.png b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/otherSelectedIcon.imageset/38@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..bcdb420e5988da835e5df0d0fb9adac13d762aaa GIT binary patch literal 719 zcmV;=0xuC`0Iga8vU*9z_mcns01b3fPE!E9 zK!7dqjav}dpl}02@(rT^00KuzL_t(o!|j&MYZE~j#wXitvTYhLhoxSG*o#6b^h2x{ zAwd)b1qHzmSm>e9Ln#CbJ@m9#JSceRrHZmp=sDo`P3TP#3Q0?>S$Y4LJ~Oj3v)ydc z-aF4_-_88y*_qjQ!^4=Bm6es1)!DkU*VJ|C<_%qYKUTvmTO#V|n=3@0RDGA|NfsU@ z17M;7FtL+`X8>GE>Jik3Y5;Y27+gU8KLt}Byqo774VmxF%(!GdpDv+tBj9A2j61*$qlZ<%|Hcn4@5Sk45L)|23 zIk;+PdhCnH^pJMu_+}jGf@>1A=t!-(LX-PQir7OGhd9|?K)r7=9x6CCFqZiTF|Wi! z*e_}WZYLp5!JSiZ5%4&VdM3wmfWuW4X-zL?*Lg#QS|3r*w9eq!jW-%i0raXxVn&%Al_=7Ir`fd(Mr z3l=N@ia(y}6bsbFTN30K%O^1y-p z$kQSb7G^{3}zK*80&h+&D$ybF#ish{EUG^$R{lP7eI( zU}pQ{#XI-!^1J>^_B=e$(WvL3tINIak7#%Rydb)KH1{%FEl3ytlmLYq^BU2U;i#`eeHkMg9e%^M+KBl%K04*=}qb{2F9%b literal 0 HcmV?d00001 diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/payIcon.imageset/47@3x.png b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/payIcon.imageset/47@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..a7ab66f553b030d8e5ec8fc385467f5c3ef0312d GIT binary patch literal 641 zcmV-{0)G98P)VZo}QkbpP!$fpP!zdo}Qkbo}Qkbo}RT%E0q8M010$bPE!DYLbxA*+i9J#=D3{z z00Id~L_t(o!|j;APr^_T$4e={wt^0_)kckM8q|cXK%!w|X_U=Gmu^Ui37aN5umQ!w z{kOg!ytZl|y|<_#y`e4TllShf*Y~|wP0Q@}%{Z(kSo6}|*sT?ZJxH^+h;F395IEt` zGGs1%I%vUC(0Lg>3uo71o$AOe?yPWSf}ryt(lSNRIWRX@t-qo>?if###~fH_x<^h+ zG)@2W(KI__X(%6oQ-+@w27HC>0Ll5!KvfbSOCsmcv8J*EPN2_UCT}dx7THFh@ zxQNhs&yOTuVjrTLabrD%-0_%MD!tD|dS7)OULIw=XJ(o0oONts4wkNe-!vg;7-H$y z>K^YyQ8ZcW3g9cyng@;o&5k!N+lh2+wqPeFiqj1e=Jh5ybv92AytYJ zf4e8ABm0>4|7EzRk*uUNS@?x?nM7h1RA**Vx+}JwMJkX~o67K#OuxHLfJY&aCJn;T zDqzWbcZ5}U=O5iX3FQvS>!ZxbG>C#((d z)+IRGP8%@a_)Ws>N=i7Sb2P)8uE0Q2Wg(rXlXJQZ(Y9Yg7oxEZ>Dv*~CGZ_%%A1`Q zE0wJ|@k9n`nEf=G)_0B5J=v?&iQbWRb!_logFxjI53JCvZa>R{Ot2z;G2%4d5lw%p beZBDsoJ!th0~@UO00000NkvXXu0mjf>8UD7 literal 0 HcmV?d00001 diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/payIcon.imageset/Contents.json b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/payIcon.imageset/Contents.json new file mode 100644 index 0000000..ad55146 --- /dev/null +++ b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/payIcon.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "47.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "scale" : "2x", + "idiom" : "universal", + "filename" : "47@2x.png" + }, + { + "idiom" : "universal", + "filename" : "47@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/paySelectedIcon.imageset/35.png b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/paySelectedIcon.imageset/35.png new file mode 100644 index 0000000000000000000000000000000000000000..e2fe61a283290bee0369e2ba8a397fc821dda9c7 GIT binary patch literal 367 zcmeAS@N?(olHy`uVBq!ia0vp^azL!c!3-py9KRg^r2GPWLR{Sod^D%WYtM++nVq0L zBY|O4B@l@&&DNWpz`wSLVPl2X%y{joaXK>-plDWt-mC&sV15En8IaAese)ls z6;Q-O<5DWnWQmd>zhH*yR);I!zGyV)eh82|upmF-!NU&%8`d8wS;FB1RI|g=#WBR< z^wmjs`3@=YxLG^?o$%i<Vw)kk8Z1Fg0ZsOT7O>a(|{FOfol6T(UZW+42^GMUL z?Q^798{TG5W5}1Pl4;&lU>S7pK${vHi+kLpC$AqYZ4?(!k_^wD{eRtsTg@RtnGvk# z3*1j^my*l$KCWSTZ0efNdY2iri+|~C$y(B!`|{>yJF^J+6IBaq?SOt@@O1TaS?83{ F1OU{OnZf`7 literal 0 HcmV?d00001 diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/paySelectedIcon.imageset/35@2x.png b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/paySelectedIcon.imageset/35@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..c4fef103bc8a0f86a8df46c3c9d61d671c64ea2d GIT binary patch literal 514 zcmV+d0{#7oP)v+P!{69fRp!<1u*IVR!$| zEV9dBws~OfaGmWx-6Rv9@Pu!Id)tl8+s&g77MDg>-TK;Mwb!DfxxY%yy>F~=a|)Z9 zx<^(;9aCbTBqV7!1c&{Z1~m#3vZi$i#ueXTm zhHR&ngnvb8GGWSW48IM=B#G0+L2=;Cu!==GOqeVUt{JAWsM}$}k{i+tBW7a8QuY8? z?MP-<8-mKxWGWZAB3>+Vkz8p!D?@^GK*qjDNckb)KRd@{*#bJhasU7T07*qoM6N<$ Ef(OppMF0Q* literal 0 HcmV?d00001 diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/paySelectedIcon.imageset/35@3x.png b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/paySelectedIcon.imageset/35@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..37ca1152dfad4e79540d0e0f7edc2680ca20910d GIT binary patch literal 710 zcmV;%0y+JOP)=u0000pP)t-sM{rCk zoM9@SUo4wp0ym>Y42208(tww~|I;p5 z5mY)OEzIofa(*$j{fyy+n-!v~>u(F;aaY4I&{g~nJWK_8K9`)d%#isx6)BHi)HQM} zBu~$4K1So%_7e9nm7;S=W$SWs{%op(7VP52<42E5XrY${zRbrvG%XALZ?ps=kS)-x zj)hVWjdrU-u8!8Bz)f{@96V~HF`~I$)UsKwRrOr+(##+yNmnUe+bt8I&y8<=hMOP`0s3O_{4`gz$j5WQl+_X>O7kE0!h%`8)Jc~^Ju+4c7k5BRAA~n+CjFz45VnBOQ(tq z8nz*i^_+Umd(n#T&|)7`ZHo4H9KS{5#wJM7Tsj9~cGkex*?~+eX{b%707*qoM6N<$f*Z?Jng9R* literal 0 HcmV?d00001 diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/paySelectedIcon.imageset/Contents.json b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/paySelectedIcon.imageset/Contents.json new file mode 100644 index 0000000..e4e6a2d --- /dev/null +++ b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Assets.xcassets/paySelectedIcon.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "35.png", + "scale" : "1x" + }, + { + "scale" : "2x", + "filename" : "35@2x.png", + "idiom" : "universal" + }, + { + "idiom" : "universal", + "filename" : "35@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Screens/Pay/Cells/PayCardCollectionViewCell.swift b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Screens/Pay/Cells/PayCardCollectionViewCell.swift index 8739af6..4c86317 100644 --- a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Screens/Pay/Cells/PayCardCollectionViewCell.swift +++ b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Screens/Pay/Cells/PayCardCollectionViewCell.swift @@ -136,7 +136,6 @@ final class PayCardCollectionViewCell: ASCellNode { super.init() self.automaticallyManagesSubnodes = true self.automaticallyRelayoutOnSafeAreaChanges = true - self.layer.applyCardShadow() } } diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Screens/Pay/NodeControllers/PayNodeController.swift b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Screens/Pay/NodeControllers/PayNodeController.swift index 17a98c3..3db59ff 100644 --- a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Screens/Pay/NodeControllers/PayNodeController.swift +++ b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Screens/Pay/NodeControllers/PayNodeController.swift @@ -110,7 +110,7 @@ extension PayNodeController { let layout = ASStackLayoutSpec( direction: .vertical, spacing: 0, - justifyContent: .spaceBetween, + justifyContent: .spaceAround, alignItems: .center, children: [navigationView, titleLabel, cardCollectionNode, menuView, bannerButton]) return layout @@ -139,6 +139,7 @@ extension PayNodeController: ASCollectionDataSource { return { guard self.cardList.count > indexPath.row else { return ASCellNode() } let payCell = PayCardCollectionViewCell() +// payCell.layer.applyCardShadow() payCell.dataBind( cardImage: self.cardList[indexPath.item].cardImageName, title: self.cardList[indexPath.item].title, diff --git a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Screens/StarbucksTabBarController.swift b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Screens/StarbucksTabBarController.swift index 4d23c50..b4e198c 100644 --- a/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Screens/StarbucksTabBarController.swift +++ b/Starbucks_Clone_TexBrother/Starbucks_Clone_TexBrother/Screens/StarbucksTabBarController.swift @@ -26,12 +26,12 @@ final class StarbucksTabBarController: ASTabBarController { override func viewDidLoad() { super.viewDidLoad() self.selectedIndex = defaultIndex - config() } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) setupTabBar() + config() } } @@ -62,7 +62,29 @@ extension StarbucksTabBarController { } private func config() { + + let icons = ["homeIcon", "payIcon", "orderIcon", "giftIcon", "otherIcon"] + let selectedIcons = [ + "homeSelectedIcon", + "paySelectedIcon", + "orderSelectedIcon", + "giftSelectedIcon", + "otherSelectedIcon" + ] + tabBar.isTranslucent = false tabBar.tintColor = .systemGreen + tabBar.backgroundColor = .systemGray6 + tabBar.barStyle = .default + + for (index, value) in (tabBar.items?.enumerated())! { + let tabBarItem: UITabBarItem = value as UITabBarItem + tabBarItem.title = nil + tabBarItem.image = UIImage(named: icons[index])?.withRenderingMode(.alwaysOriginal) + tabBarItem.selectedImage = UIImage(named: selectedIcons[index])?.withRenderingMode(.alwaysOriginal) + tabBarItem.accessibilityIdentifier = icons[index] + tabBarItem.imageInsets.top = 15 + tabBarItem.imageInsets.bottom = -15 + } } }

-+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�(8+1^H6LN(_?M2^9hBgF^hFo`x4ttecN<^b#?UO$O5T9=G|DP_A6UK z(VGt;6zGl|9bqhUC2dOhsz#la<(12PdGUsu)ew3+m|b~wKZ z*(M0OCiAOxd2RchBgJC{;+eMS^V)RRj4y{f1J8Ko36@ z*afc9s8`vrVx^L0Lo2Ge)24vq%>U!+>I${<>>X7lRk;T!1Xe0V_*QeapBPnSzy*s! zc4Qr1UJJ4vVwgeMKFX)4k+$k$F`0 z%z>wTJXTjIQ8~$y;zO=3Xydj<%SOOenm)EwGqG+p)}J8-^Vv37Jrc#Si6S)T8mkLj zRSnsw&&j1wC{!qtNfmW|oGE#C8U{b7OqW3&=_pWvDvDz|)=`!fMZf&`zSpKDuC0^n zD6b?OGLIQIbB*=AP5!bIa7zVssMeSp6&V#XG+Z4SzC4i6oFb!6L7G$S?w%a+ZY{3E zuq7Qkpdw9qT-KH<-=zDX-i=F=Y(x1eX#0{|YcP`YbAJ9zkSBkhJv%@DctMRF)PspS zaG`2+R2yndwAC9uS>zf!6sgfscK1205sHmlZ<(TSNVcMk#LOgS*f?@-sz&Jp-vfJa zsQ0YLt^+L3g=7)y`D}IY)!2{@^77C%UsfsFfy< zX`J9Vs?*qkSE}4@wOliYMj!Tf%OxD)Qm`3)eR)L(GG!2)D5|rPh1t8jzP?7gmXjWX zPCGQ%+mi$xy(4(%IEA{k(3fIpP&2mfJ5cu`v$mE&NT+{^QAfhct=T%tam-Arc4F4h zz(8;GU;+@29a6GFOdZ&SxP)r~Pj?s&xj!Gz&$rLFw*_(^FJxPW$Vx!c39m^)ZEg)} z(YiGzbfPB$2GI=F5>nF!b*a=WQ1{lL4o6*UWw3Rob(|$qOBVv@5H4C?zsqteQ3uk} zgjFsz6FzH1omZpiha1KO$w0p;d9YP9A?{w&R#0f;8PsVHw|GdCcP7z`s7210@*Jt?y%sC@(I#u~v?* zudj}tuM`dH?ikcfPh)F!T&xSvz>^DgQ$TlsQlL53Ak;O&b{iQVPC;RQ%wP_nyODrX zQJ2ff&qOMiF+oQfKc9nD_sFb!q!aUC>{Po+ZglAGUFmqqcsvtb;8Z$K&}sgXOT^0) z(Dm`1;L5^%!F3N(nC7VER!gGpe;F4x_+*eoQPgR35Ad0}4b&z2>!zobojwyw4(Mz? zPpQF&vK=rQ+BO=(+&2RCGizbHd2sXj^Tox5;7t(sd31mRDue9+2KxsQmBzF)w|uA$8Uo-I7#!7Nc5{SlgNBWVVm&k|>LcRyY0BrUqd z_qFWAp|d99q&7Io0p=SuB#zjiikBD031DRzkIT@jYEE#jJ?*4ntci5%dZbnp`KX*x ze{#|>>_APG<#CeM$?@fKF@4`^y-ZS9<&|CusI@>yQnm?1y}^eMA5LDj_4c82P7(y_ z2N&lCcg&H=t%@ov0!~&E88qlMHGv3`$+9-Ty9w#s>LP|5X@kS8#xFC7Iph&LXbqGV+OxKoKmlEHxKzji4N8H@43Ad_vC; z4eA0KJN9#`{U!At$nZ=kXvkq{4nCnJ=7`AM)8^o2bZjOhs}zaKkT3x^M-L7RLY~Bi zg3dPg!nK8O3vQ3H3=&F{tIVW~j1T7%s%K_(@X#ZZNoEBh$39Y+JEOLadia#nZ8d|P z@N~E9*qJ_bKvU^guk6+~`zI$aU$#54yx#BtsOxujaAUbj`16(4a04$B!2>^nIa%6Z z7&6U65fj&AtGf$fD0stX>pTA;?h0Pw$g}X?_Ub9*)`!;19=aI+Vx@j147+9KgqJMxi7y@(fBtj!JIqmzt?^U&d zWs|)m4&%g3BIU1s-+SMC?|bhh@o@~ossLhhoqLE%KGwldcl!G6?d=;gS%3Wrg93bE z-JH+Aam-Pa!k3XkTX7ud=7kzZ#Y(xw5K6Cq73zMNp8}1YtN9l*-z%&EdEbRc2)wW18%38Buw)}pV=1uH-u z9bzM84chDqZjMGS5+?Z3YQ@BfRk2K;Q~Yq@@#fZkJIe+`thj$vOgd-Vz01IinB(*k zcsiD}LS=tF9bnhmY*pfkLg#R*g;`vD)i={cEgjr-BnL;;q5b;64&J`Au5~{CiLzqN zR(?3UwEC`fcz9Mn*{r&+qdmnu*MIHsR@dU;P(TBU6GPzem|xHu;_O;X{d6q&yaMj{ z!PsHcw|JA7r1&9kScdc&&e$k$r;G1BwC4i{K3Qe_u;BZD4Sbs?L!(Bci`S=^G5(NM za?E6_7VXnz*?Zl*PMRv#(3T&Z|C zE75`-pB=L5pAs+D-76p&Ck{!})$2f!5tby0>r{Mr@1bkQ0(p~AHwOm0ll1LX4`cFw z7aO}IjMHkCpFs}V+&@{VN)Dn)v5i9wL5#R zI&6jiDLfu+32%-Jw!*MlZCdgb?3~9fKE_(%-`_XuCo71R^7R59mZ1B&jOLl)s6WV@ z{K7ahs*7^esVX*+FZr86Psu3cq9&IKyooDVWl91Tpy}AN4w%KT8`tiIo>Z^0e{FVY zZNGW`JN7cN6X_ugY6+X*V6jxru(KPFFZ!L#DdDjhOUg?^sUR>F$l`-2>U!|NkL$@~ z03Y0<1imdPoqD)J+AKV(F~ZT?w{P)yK2Up^7PVNvy1EhddUpd1Hd~MYkn4JvigJs3 zNV;^yhmFJ@_3bdm;jsg)N!H;^k4H2W)RnD8qqeTnE35)>F4ml zxUPYHsGNOO@}S8@3voQM&}wvfVDCH$ReEaK3FFO#syo%{5$7xeX`m*Nk)Qjh)2mU& zoT5_40B_&?V${{bnml^ah#$;;U)`e!`)KR5$|(da8DMc|ZW6-a_+or}HAzvN zbvNqZmbq@Qzo>!#hd~X}5rs->lKE1xDPOGENuz6X#!C7-ot>-6VVJo!yZCr*{|vna ztu^1I16flMadLAuEEd{lvV&$S#YUJ@MLQ;{D}lPp!8Plhzy3-Fr~L0^kVa7~VKz-x zN|9qtG8Y_P|N3@3Hk0KVAI=z6XpZAs+Bt$|`|Qb@_u|gZ%`V8E?zu=U9*%Es$CK=U zxhKVVDSOFlQ^8-uXt0r(0fL8#eV7p$Bt~V(V`LO?6{4mG>NPdxXT8zw?Y|{+hf&~W zz=e{p{;Vvm?X};(f1kD9Z9ZPmmWqJIGSa z%Apj23J_L%TfCweJg@uA0t()SeXc8UPrDgS+x)fC!_Mg->rL#NkFX}n$7I5v%*|@i zL1|(27xc`W5p@y_RVvysoR@d)ZQOqNFv&oR9CXNBFllN=f#sxf!%!C#sFOV)^xApc z*>1VLwwqJ)NwK8VNiHgc{z{?eta>vStA@IBiT&kce7N#jQQv(M8!x)O1?rgnVB*wk zF2>Fo69*`)T=qxT$^etceLynGxSsk;p$6lhZ+eMTurrf&{l*TDs4G9Y$ANWJOIy2V z?&}u3s8E;$uJ$g&px%zJ2ibvW0xCeTiw&oYo^Vz+CrTKp8~%*UP=C<&R3iSIvRUfj zM~a(mlz6p5-IOY5SbKb<82n6E-VM6G2jWv>Y$zEn3RS>R+n7BCPiO` zI*d|zu$^}pY`^+=b3=KU+77-s>b4DaPydw0Cq!MTQeC>g?Nq3{Vp|yA467%I>eO{$ zZoaI1L_I9mi?XFq7rG&&NF0t*Em)DN<6+RqvYW|Ofx4n_SD8~Wx3svtvwMDiezd=} zQf0Ra3DQ)xV5cAV$8SD-xC8HC)TB5-uCCtJ)OFnJmdXd(YFE!4UYq>b8L-k6e7q+z z_V_AMwYN+i$j;j8QXZZxF0Qf{;b?EOs)4hAmn+%X@cnt^GEg_ZLK`p@X89x~@Ps5v zMZ4MC*X<4_lPQP0p7gs3lRQLS>VE-0C9Da4w)7wyT%!S0s3Ytc8LOHu%fug{$pGwKRh1t87XFfjrrnhHi8+N*8vt?|_Yb&E@!2{1LNMq|t-(A?XL?H+f#9kOh+ zH?%d*4Tsl)J|+4h!$g^meLdT7bsul;K22_(qH6a&P`ADPGf;OJ{&9-*E^hVOvqC*s z4lyjsUd?3(3ND&MkdSsR=kVNnnhJI9>!8=ct8vu%{V&%%bP8P%VXvk)G2+s7)RpN^ zl#V0vUaW5H?(XjGY_3#r)fXtzn}QA`q-!_lQ}2*A0j-Z+M4)sg#Jajc+w^sXQGnY+ znui%!S4uP0ftv$PS}S0eHtNNqggi&jZ31SWme=@nTiDk8h-&D2hsVE#0e2(2*S(I* zI?dakH@(7{rY+A$aAeqE0Ec&N0Tkn=#3*H>K~-d`AR6Yxf;kF+aj$#k?5?#=QnKz1 z4a%%8_>82jz1`jYXH>U>ITXd`TrdL$HyC7DCgSSDFv;LT<_gs?)mG&06&S@0c-ie- z(_<7=m8Hyl_Aq9lDe6Hu*+)y4*E}WFUJ{iwx3>0dclT&-OEdgH64uEQ7&?^Ij(9;& zoD%S`rycjFVg71mvhYs70(Ht)^4}kxf4uv2_37s0^SrhE+oEoM`{h%h?x6AR_32L< zZvD&~EX4hf2X3jMo3qv79d-&czbYnlc}3IK!csxZYJRnO|r1; z98JCsMj9lOwNOI8C z40YYh9!`&qO}f^I>OjI8uvB>#5Dvr~p8YWRbP06^7DQSQx7zHKje8xBq5BC}=-vDl2HtRADrm(B|;VqOTtisdLHCLw8{0TE+JUZnE zUFcnQ@q>7yXiT7X-ylcb=e~|76SOpxnl^MWxw<%}@F7O!-6&_2XkHX|fo#0nTKne< zSUld_z?s@sM z5-7$nUm3%^yLt88d>^N0&U{mTIS+zqsQdF_^2h5_*_*O&DFW!sh#VditT0F{roA@0 ziLnQD&7!4937Eo+LixH@jyg}>WA_6SzRVMzqtxfH%Y_WV6m~3_uWCIjxk!a;UyQz~ zN>^ZxCIK(k;u1}8fOWjk+>{{KVtT|~mk^pHuP#|i$lNM%8MTX>;{$AS*r_RroGvWi zcgEn9i(ErhqMyU2ED!0FhLXIod5+v~5GXUH0!PSzq{+0V_tTa+98jx&^Or*LcrQcShovV~HQaWg3luiAPa z&oV4-ypLgCQivc6H3}*o$}2)pN;M)#r8_}_kQfkCh%5s6dH#SwAme3=sf=J45DS5P z=bZ1pStlWrYBHU7t?|{p_uTK-`Ode$-VAb8bm*=l5GB_D^jBP}H)wzV1_=}h(Mp&7 z`SU7)N6@h(Y`<tBLn?IJK3`D6z(&6EMD<)Zb1DNFb_SKuf6sr0} z@$vS!rIA^*#8-19R&;T?_FPBlXQ&J5OH`XeZWVDSGj}8*_OlW5^H?p}SM3Qa!U1&# zJI0=zEe3z-SawBGNj=CBb9H?Y-<14~m=LL?eeBZK67SotX?3-PsA~~|LS5nm7)S;i zn@hP~4|3HM^(bI<_>KNlYIRT#1Cd7Z&K5JB1uwxiiu_A^PHpds{=}=C%JlF}P~6lF ziKecS(eN{C9hv10o9dWjjo}|;@|6L*RUVW~7#9~f+r%$aoxZ{=v?WQkPoTRyTAz*O zM_a&!c0VY_(XPgsKaw!X=5zA^d9*~`fA1w9o_bd+)xzF-<=_aNJ`dS*aCCC?OJp#6 zr-uiJ%jszF)+L0WoRJ8pWITt20QsgsT~3wmNlQ!=1>U$aD3O84ujyGS z>9HiGbVUUVY%VhV!N#w~JJ##J*8mNSXe1@`t6woFam+>GWY}c@PH`^dWd83AL!DX_ zZNt%!Hi1R7QeR%|9wwytSovV*dFTS5*e5bGaiYk_8xUxY(SApZ`t-ZnLW&nWhi2G6Zc5#bP?%6YS8EC@JGgou?@SXJPx^{&+2{@!oQFuMII-0VW zBvq==4a&Fl`Z@aeSjoZA*CT(3EtE?(j|Yzs>!VZSF-k|YFda=sH8&OwYK}TqG-9W? z33aXZ5_r(}jq})+$A-2RZf89#Pv-9F^XI=l#u!_r->TP&Kfbz&rz#`{YHxq)Dp&S@ zC{57)H`Mu3;aPU{e#n(K57lDq$%GF@jb)OnY0+4g(wDEcrrJEIFFWZ$aXQ^MHPkK3 z68+gZzn6m!CM*M8UbX zn-A4j@UG&@)*&+o)V2JX*Vx0jWhe{V30d3Kd{GXE%XT#^B5*3 z?cxmWUbpnYNPVq+nFZC#)5Hf{rXa?2D_dt*tdj-QQD1!GLyF7^q-je9Iw8iu`nkD#`OR<8DfU`V0@7}|CtmQ*aCI#B33MPW+3S+WT2q_D^3v=x)TOj_TwOq& zv>)1(<}=il-leB3-|VWe)%6uUX=o|M9m3`@jq*p0&a0dlRO-;7T=rIbP>Kmf2)d)& zXOu_o(^4&zQWL?}sW&Q>H~>(WQ3!_wyj)4fvS5&h-lMuM>{yKIyTi;y=1VWc%L%q(5aW@MYQUquA5OkE!= z&M6g;k?q?O>JrG{Gq+*4A9{y^hCGM;?V>D8hVdaL3Tbd0&ev5?BI&&u6X$Uf+^cpP zMzVT0c?LFM;^cJ6B@lI8-JD?SnAU`qPRJQ^i3Izt%c?Cir|9l&uN)TQo87$%W>4(u zoV|Sc(pIOpIv$CR%rc0t?)`*z2{#J=#ay~8yc;pd!mXd7j(kGq;|+DWnl#e!M9x`g z$cx>0aPw-^lHOde?9D3C;GeJy(ttRlUVPZR7ccGvonX@BD41JV4tw+KE!ZP;6kse0 zRU#6GpDB+`*SbSK0X@u=R5sRzCfr~THF6b|7Dd&VLitZSTYn~~%ze4p;2&hk`~`#y zLo}?+uTvjJS#`7(>Lx|6K=vR;LY(CG8VNLhwD+S9iOY{~nLueasZw+lejXNeCk@o< z8p9_C2M3Rv&2l;x>gt|fG{aUdZm+l7n{zvw=v~yB+6A2AXqZdk{?_GF zFh33=SrUh-8u7^Pi9PxDG%`p&zuDqwN^$b%G+&MApSJi8;vD1yzYjc9*eW>mZTKX-zq4lokv08l`6)YEe~vV)^Op zb(N>6#r7neug~$A;|bF0vQTue{p^>Go~n~0jeFxi4AiCc$ceg+P-lxlrb(zv#rZ`! za?V=}U32rg&abySdlVWaYbD@N<+ed)y^_E;IKz|RIUZH&nAmBJ?FA_3`|ZWd?K9M|{}{<72FE}h-%z?SLR|+dchp5>2%7t|UT^lFdds#PN))*-w%7A=sRd*8 zVCr0N@<>kXkAmW$5ee^Jr^c-$0G%e$-)@4jIqtCjXL( zDa0J2yaz-bwARhkHJgtOb&r}&b0){~F>C5hEQ(?QKKuuu4nJ8@lob8WJzbV0sOp4#_CcBS!p{D^E>v79y~T=Wa7c*=EL@<+eHz|6Cr(?s14!Gt5<=H z60vT2L*}7^oqmB@E^JU;SwojW!pFN}@ha8YWja!*OIdAVf4flEqEEvB#`!5rmC8Y0 zzS(`)-r@xhEe*9Oc$ti!Y<2D+*)`i-s<}lYPAp1d`^)*h~JzC_+46Kty000h&NklLR1mgDk$vNriH=j=IRb zb12-#dV>=Azf{LTlKOtsHHS@RP?}9n_b}LfQK9bPx>~;&(!U%w;b0yd9G*0X7P2|) zR1C6c8y$^1I|~AAV$t%-?$%nvIV;qeTTjtO=C_$mNkyoy1T|vGag%X?LUtQC73y|q zOrG}N)7sT4&}qoEIdXvj7UNFscDv0W58db!NU}<~vZhdq3{j>^-)o9TC~%B~Iv=kk zk{F*&BsCKELlX_Y_Vzs5I=t8W`4sJT9nZ15%gE^Nw@#I@%Qx6u?snhbtj!j)9@3wT zTPZ2oA(v9j_cwqgMH1*HV$;Y0>2yUI?T`@N4YrPwqljes{Y-nenHR{5#wcL{bvvxC zQ2QPf=WT;7Y3!ie7nUb3DS2+bo9lJXp;#z+Qk$&r*Sd=9Jd|+M zVdHB|$d2$P+G6Ylu-g!ktB1Crt2y?wn?g1fsM}qi$Bf(kXan5Dp`%=xS8Mp5>OSE1 z6s!o@JtMs*;giK}gg~ZHcl6oMzr=OJ)6)i9M!r=FOZrGGhWo*V*n z2fskYyqF`l%SU1s*f$>y*kX~#fmkx0T|#L;%{102m!t;d;EH0^7|E|tD4hKxwo%;KQ8D9U}ZPIWPVIUI!mJQ%+ zEq4wGLQD2Wiv!|eCw;g$66!>d>`Ehyfnfi=@@`n|<(eJczC5*WS4EDRmohK|VvS1$ zF83%4MLs#p&sPD?Cgsg+1aZ2heARZ19{Zz$j*?MqNf>|SFv{+(bk0k zo(VF(L+$~ynGghJ05#|4*`Ilr(7~LIX6N>5evb`s*^ZDykq(Cw6SUK;TY83wt?GQU z5xLrx($U5`S~$L+q3+~Ap^hZ5+fN_=q=JEu*g5%UZ&%OSNU}w}?93W36DV>vk$-^_ zh58GPd4IqN6e$pmk^u*ea5PNXNe9*8WE~h`F`&pSh#kv+;PHZC-~}6Pw(ydMz2}@; z)h$`}dk@xI{!pvCy6V=wb?WopTaLQdw`x@u^9q>5O3G;dmZ&@V3HApzp>9j63fLDXH!RW*A_L*13EfaN!c3CLE% zt{$l0vIuIM+^vl)m{;}I+MHiG1edY!WjtL;68?wwgUP%PI)>a~XNf+>K@6rgx7W