فهرست منبع

阿拉伯语基本适配完毕

100Years 1 ماه پیش
والد
کامیت
653cda331c
22فایلهای تغییر یافته به همراه1600 افزوده شده و 68 حذف شده
  1. 1 0
      Bridging-Header.h
  2. 1 1
      Podfile
  3. 1 1
      Podfile.lock
  4. 26 0
      TSLiveWallpaper.xcodeproj/project.pbxproj
  5. 6 3
      TSLiveWallpaper/AppDelegate.swift
  6. 1 2
      TSLiveWallpaper/Business/BusinessView/TSAppBtnView.swift
  7. 4 4
      TSLiveWallpaper/Business/BusinessView/TSGeneratorView/TSGeneratorErrorView.swift
  8. 7 0
      TSLiveWallpaper/Business/BusinessView/TYCycleImageComparisonView.swift
  9. 1 7
      TSLiveWallpaper/Business/TSAIListVC/TSAIListHistoryVC/TSAIListHistoryVC.swift
  10. 2 8
      TSLiveWallpaper/Business/TSAIListVC/TSAIListVC/TSAIListVC.swift
  11. 1 1
      TSLiveWallpaper/Business/TSAIListVC/TSAIListVC/View/TSAILIstCell.swift
  12. 5 10
      TSLiveWallpaper/Business/TSBootVC/TSBootVC.swift
  13. 25 24
      TSLiveWallpaper/Business/TSPurchaseMembershipVC/TSPurchaseVC.swift
  14. 3 3
      TSLiveWallpaper/Business/TSTabBarController/TSTabBarController.swift
  15. 1 1
      TSLiveWallpaper/Common/Ex/UICollectionView+Ex.swift
  16. 3 3
      TSLiveWallpaper/Common/Purchase/TSPurchaseManager.swift
  17. 74 0
      TSLiveWallpaper/Common/ThirdParty/TYCyclePagerView/TYCyclePagerTransformLayout.h
  18. 319 0
      TSLiveWallpaper/Common/ThirdParty/TYCyclePagerView/TYCyclePagerTransformLayout.m
  19. 180 0
      TSLiveWallpaper/Common/ThirdParty/TYCyclePagerView/TYCyclePagerView.h
  20. 607 0
      TSLiveWallpaper/Common/ThirdParty/TYCyclePagerView/TYCyclePagerView.m
  21. 47 0
      TSLiveWallpaper/Common/ThirdParty/TYCyclePagerView/TYPageControl.h
  22. 285 0
      TSLiveWallpaper/Common/ThirdParty/TYCyclePagerView/TYPageControl.m

+ 1 - 0
Bridging-Header.h

@@ -3,3 +3,4 @@
 //
 
 #import "LivePhotoUtil.h"
+#import "TYCyclePagerView.h"

+ 1 - 1
Podfile

@@ -27,7 +27,7 @@ target 'TSLiveWallpaper' do
   pod 'RealmSwift', '~>10'
   pod 'TSSmalCoacopods', :path => '../TSSmalCoacopods'
   pod "DynamicBlurView"
-  pod 'TYCyclePagerView'
+#  pod 'TYCyclePagerView'
 end
 
 

+ 1 - 1
Podfile.lock

@@ -128,6 +128,6 @@ SPEC CHECKSUMS:
   TYCyclePagerView: 2b051dade0615c70784aa34f40c646feeddb7344
   TZImagePickerController: d084a7b97c82d387e7669dd86dc9a9057500aacf
 
-PODFILE CHECKSUM: 5cbebc045aa8aafa7a1ab7df0b261de22231dead
+PODFILE CHECKSUM: 74c4f0b01b5d032891fb6b6ff41b26a5594adbda
 
 COCOAPODS: 1.16.2

+ 26 - 0
TSLiveWallpaper.xcodeproj/project.pbxproj

@@ -101,6 +101,9 @@
 		A86857DA2DF994600089D222 /* TSAIPhotoDetailsVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = A86857D92DF9945F0089D222 /* TSAIPhotoDetailsVC.swift */; };
 		A86857DD2DF99C200089D222 /* TSImageIPanComparisonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A86857DC2DF99C200089D222 /* TSImageIPanComparisonView.swift */; };
 		A895B5002E01370E004F9B85 /* TSTextToastView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A895B4FF2E01370E004F9B85 /* TSTextToastView.swift */; };
+		A895B5102E0287F2004F9B85 /* TYPageControl.m in Sources */ = {isa = PBXBuildFile; fileRef = A895B50E2E0287F2004F9B85 /* TYPageControl.m */; };
+		A895B5112E0287F2004F9B85 /* TYCyclePagerView.m in Sources */ = {isa = PBXBuildFile; fileRef = A895B50C2E0287F2004F9B85 /* TYCyclePagerView.m */; };
+		A895B5122E0287F2004F9B85 /* TYCyclePagerTransformLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = A895B50A2E0287F2004F9B85 /* TYCyclePagerTransformLayout.m */; };
 		A8C4C0982D242154003C46FC /* LivePhoto.swift in Sources */ = {isa = PBXBuildFile; fileRef = A858EE162D1CF49B004B680F /* LivePhoto.swift */; };
 		A8C4C0A42D24218A003C46FC /* metadata.mov in Resources */ = {isa = PBXBuildFile; fileRef = A8C4C09E2D24218A003C46FC /* metadata.mov */; };
 		A8C4C0A52D24218A003C46FC /* Converter4Video.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8C4C09B2D24218A003C46FC /* Converter4Video.swift */; };
@@ -249,6 +252,12 @@
 		A895B4FF2E01370E004F9B85 /* TSTextToastView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSTextToastView.swift; sourceTree = "<group>"; };
 		A895B5012E025020004F9B85 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/LaunchScreen.strings; sourceTree = "<group>"; };
 		A895B5022E025020004F9B85 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/Localizable.strings; sourceTree = "<group>"; };
+		A895B5092E0287F2004F9B85 /* TYCyclePagerTransformLayout.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TYCyclePagerTransformLayout.h; sourceTree = "<group>"; };
+		A895B50A2E0287F2004F9B85 /* TYCyclePagerTransformLayout.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TYCyclePagerTransformLayout.m; sourceTree = "<group>"; };
+		A895B50B2E0287F2004F9B85 /* TYCyclePagerView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TYCyclePagerView.h; sourceTree = "<group>"; };
+		A895B50C2E0287F2004F9B85 /* TYCyclePagerView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TYCyclePagerView.m; sourceTree = "<group>"; };
+		A895B50D2E0287F2004F9B85 /* TYPageControl.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TYPageControl.h; sourceTree = "<group>"; };
+		A895B50E2E0287F2004F9B85 /* TYPageControl.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TYPageControl.m; sourceTree = "<group>"; };
 		A8C4C0992D24218A003C46FC /* AVAssetExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AVAssetExtension.swift; sourceTree = "<group>"; };
 		A8C4C09A2D24218A003C46FC /* Converter4Image.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Converter4Image.swift; sourceTree = "<group>"; };
 		A8C4C09B2D24218A003C46FC /* Converter4Video.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Converter4Video.swift; sourceTree = "<group>"; };
@@ -651,6 +660,7 @@
 		A858EE182D1CF635004B680F /* ThirdParty */ = {
 			isa = PBXGroup;
 			children = (
+				A895B50F2E0287F2004F9B85 /* TYCyclePagerView */,
 				A8C4C0E42D268D02003C46FC /* LivePhotoCreater.swift */,
 				A8C4C0E52D268D02003C46FC /* VideoRecorder.swift */,
 				A8C4C0A12D24218A003C46FC /* Util */,
@@ -831,6 +841,19 @@
 			path = View;
 			sourceTree = "<group>";
 		};
+		A895B50F2E0287F2004F9B85 /* TYCyclePagerView */ = {
+			isa = PBXGroup;
+			children = (
+				A895B5092E0287F2004F9B85 /* TYCyclePagerTransformLayout.h */,
+				A895B50A2E0287F2004F9B85 /* TYCyclePagerTransformLayout.m */,
+				A895B50B2E0287F2004F9B85 /* TYCyclePagerView.h */,
+				A895B50C2E0287F2004F9B85 /* TYCyclePagerView.m */,
+				A895B50D2E0287F2004F9B85 /* TYPageControl.h */,
+				A895B50E2E0287F2004F9B85 /* TYPageControl.m */,
+			);
+			path = TYCyclePagerView;
+			sourceTree = "<group>";
+		};
 		A8C4C0A12D24218A003C46FC /* Util */ = {
 			isa = PBXGroup;
 			children = (
@@ -1147,6 +1170,9 @@
 				60553FD72D3B54A400BAAD7F /* GradientText.swift in Sources */,
 				60553FD82D3B54A400BAAD7F /* LWBaseNavigationController.swift in Sources */,
 				60553FD92D3B54A400BAAD7F /* GradientBackgroundModifier.swift in Sources */,
+				A895B5102E0287F2004F9B85 /* TYPageControl.m in Sources */,
+				A895B5112E0287F2004F9B85 /* TYCyclePagerView.m in Sources */,
+				A895B5122E0287F2004F9B85 /* TYCyclePagerTransformLayout.m in Sources */,
 				A8FD8F432DFC1656008CAACF /* TSImageComparisonView.swift in Sources */,
 				A868579E2DF915E90089D222 /* TSPurchaseBusiness.swift in Sources */,
 				A81F5B5D2D1A906C00740085 /* TSHomeDataModel.swift in Sources */,

+ 6 - 3
TSLiveWallpaper/AppDelegate.swift

@@ -73,10 +73,13 @@ extension AppDelegate {
     }
     
     func initPlatform() {
-        dePrint("TSRMShared.aiListDB.listModels.count=\(TSRMShared.aiListDB.listModels.count)")
-        TSColorConfigShared.naviMianTextColor = .white
-        _ = TSImageDataCenter.shared
+        TSRTLManage.setUpInit() //阿拉伯语适配
+        dePrint("TSRMShared.aiListDB.listModels.count=\(TSRMShared.aiListDB.listModels.count)")//提前预热数据库
         
+        TSColorConfigShared.naviMianTextColor = .white//设置基类颜色
+//        _ = TSImageDataCenter.shared
+        
+        //设置Kingfisher缓存规则
         let cache = ImageCache(name: "permanent_image_cache")
         cache.diskStorage.config.expiration = .never    // 永不过期
         cache.diskStorage.config.sizeLimit = 0          // 无大小限制

+ 1 - 2
TSLiveWallpaper/Business/BusinessView/TSAppBtnView.swift

@@ -167,8 +167,7 @@ extension TSAppBtnView{
             make.width.equalTo(button.width)
             make.height.equalTo(button.height)
         }
-   
-        
+
         viewH = 48
     }
 }

+ 4 - 4
TSLiveWallpaper/Business/BusinessView/TSGeneratorView/TSGeneratorErrorView.swift

@@ -89,7 +89,7 @@ extension TSGeneratorErrorView {
     
     func sensitiveErrorView() {
         submitBtn.setTitle(isUploadImage ? "Reselect photos".localized : "Got it".localized, for: .normal)
-        errorImageView.image = UIImage(named: "generator_sensitive_error")
+        errorImageView.image = UIImage(named: "generator_sensitive_error")?.mirrored
         
 
         textLabel.text = "Your photo may contain nudity, gore or violence that does not comply with the health policy, please replace the photo and try again.".localized
@@ -98,7 +98,7 @@ extension TSGeneratorErrorView {
     
     func generalErrorView() {
         submitBtn.setTitle("Got it".localized, for: .normal)
-        errorImageView.image = UIImage(named: "generator_failed")
+        errorImageView.image = UIImage(named: "generator_failed")?.mirrored
         
 
         textLabel.text = "Sorry there was a slight problem with the image processing, please try again later.".localized
@@ -107,7 +107,7 @@ extension TSGeneratorErrorView {
     
     func netWorkErrorView() {
         submitBtn.setTitle("Try Again".localized, for: .normal)
-        errorImageView.image = UIImage(named: "generator_network_error")
+        errorImageView.image = UIImage(named: "generator_network_error")?.mirrored
         
 
         textLabel.text = "No network, please check your network and try again.".localized
@@ -115,7 +115,7 @@ extension TSGeneratorErrorView {
     
     func generateTooMuchView() {
         submitBtn.setTitle("Got it".localized, for: .normal)
-        errorImageView.image = UIImage(named: "generator_failed")
+        errorImageView.image = UIImage(named: "generator_failed")?.mirrored
         
         textLabel.text = "Your photo may contain nudity, gore or violence that does not comply with the health policy, please replace the photo and try again.".localized
     }

+ 7 - 0
TSLiveWallpaper/Business/BusinessView/TYCycleImageComparisonView.swift

@@ -62,6 +62,13 @@ class TYCycleImageComparisonView: TSBaseView {
         pagerView.dataSource = self
         pagerView.layout.layoutType = TYCyclePagerTransformLayoutType.linear
         pagerView.register(TSCycleImageComparisonCell.classForCoder(), forCellWithReuseIdentifier: TSCycleImageComparisonCell.reuseIdentifier)
+        
+//        if let layout = pagerView.collectionView?.collectionViewLayout {
+////            if layout.responds(to: Selector(("setFlipsHorizontallyInOppositeLayoutDirection:"))) {
+//                layout.setValue(true, forKey: "flipsHorizontallyInOppositeLayoutDirection")
+////            }
+//        }
+        
         return pagerView
     }()
     

+ 1 - 7
TSLiveWallpaper/Business/TSAIListVC/TSAIListHistoryVC/TSAIListHistoryVC.swift

@@ -59,16 +59,10 @@ class TSAIListHistoryVC: TSBaseVC {
         layout.minimumLineSpacing = 16
         layout.sectionInset = UIEdgeInsets(top: 10, left: 16, bottom: k_Height_TabBar+20, right: 16)
         
-        let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
+        let collectionView = TSBaseCollectionView(frame: .zero, collectionViewLayout: layout)
         collectionView.delegate = self
         collectionView.dataSource = self
-        collectionView.showsVerticalScrollIndicator = false
-        collectionView.showsHorizontalScrollIndicator = false
-        collectionView.backgroundColor = .clear
         collectionView.register(TSAIListHistoryCell.self, forCellWithReuseIdentifier: identifier)
-        if #available(iOS 11.0, *) {
-            collectionView.contentInsetAdjustmentBehavior = .never
-        }
         return collectionView
     }()
     

+ 2 - 8
TSLiveWallpaper/Business/TSAIListVC/TSAIListVC/TSAIListVC.swift

@@ -62,17 +62,11 @@ class TSAIListVC: TSBaseVC {
         layout.sectionInset = UIEdgeInsets(top: 10, left: 16, bottom: k_Height_TabBar+20, right: 16)
         return layout
     }()
-    lazy var collectionView: UICollectionView = {
-        let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
+    lazy var collectionView:TSBaseCollectionView = {
+        let collectionView = TSBaseCollectionView(frame: .zero, collectionViewLayout: layout)
         collectionView.delegate = self
         collectionView.dataSource = self
-        collectionView.showsVerticalScrollIndicator = false
-        collectionView.showsHorizontalScrollIndicator = false
-        collectionView.backgroundColor = .clear
         collectionView.register(TSAILIstCell.self, forCellWithReuseIdentifier: TSAILIstCell.cellID)
-        if #available(iOS 11.0, *) {
-            collectionView.contentInsetAdjustmentBehavior = .never
-        }
         return collectionView
     }()
 

+ 1 - 1
TSLiveWallpaper/Business/TSAIListVC/TSAIListVC/View/TSAILIstCell.swift

@@ -13,7 +13,7 @@ class TSAILIstCell: TSBaseCollectionCell {
     var itemModel:TSBasicItemModel = TSBasicItemModel(){
         didSet{
             if let imageNamed = itemModel.leftImageName{
-                bgImageView.image = UIImage(named: imageNamed)
+                bgImageView.image = UIImage(named: imageNamed)?.mirrored
             }
             titleLab.text = itemModel.leftTitle
             infoLab.text = itemModel.leftSubTitle

+ 5 - 10
TSLiveWallpaper/Business/TSBootVC/TSBootVC.swift

@@ -62,8 +62,8 @@ class TSBootVC: TSBaseVC {
         }
     }
     
-    lazy var layout: UICollectionViewFlowLayout = {
-        let layout = UICollectionViewFlowLayout()
+    lazy var layout: TSBaseCollectionViewFlowLayout = {
+        let layout = TSBaseCollectionViewFlowLayout()
         layout.scrollDirection = .horizontal
         layout.itemSize = CGSize(width: k_ScreenWidth, height: k_ScreenHeight)
         layout.minimumInteritemSpacing = 0.0
@@ -73,19 +73,14 @@ class TSBootVC: TSBaseVC {
     }()
     
     lazy var collectionView: UICollectionView = {
-        let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
+        let collectionView = TSBaseCollectionView(frame: .zero, collectionViewLayout: layout)
         collectionView.delegate = self
         collectionView.dataSource = self
         collectionView.isScrollEnabled = false
         collectionView.isPagingEnabled = true
-        collectionView.showsVerticalScrollIndicator = false
-        collectionView.showsHorizontalScrollIndicator = false
-        collectionView.backgroundColor = .clear
         collectionView.register(TSBootCell.self, forCellWithReuseIdentifier: "TSBootCell")
         collectionView.register(TSBootBaseCell.self, forCellWithReuseIdentifier: "TSBootBaseCell")
-        if #available(iOS 11.0, *) {
-            collectionView.contentInsetAdjustmentBehavior = .never
-        }
+//        collectionView.transform = kIsRTL ? CGAffineTransform(scaleX: -1, y: 1) : .identity
         return collectionView
     }()
 
@@ -99,7 +94,7 @@ class TSBootVC: TSBaseVC {
     }
     
     override func dealThings() {
-
+        currentIndex = 0
     }
 }
 

+ 25 - 24
TSLiveWallpaper/Business/TSPurchaseMembershipVC/TSPurchaseVC.swift

@@ -245,28 +245,28 @@ struct PurchaseView :View {
             }
             
             Spacer().frame(height: 20)
- 
+            
             
             VStack(spacing: 16) {
                 
                 //                if vipType == .none {
-                                    ZStack(alignment: .topTrailing) {
-                //                    //增加月付费
-                //                        PurchaseItemView(title: "One Month".localized, type: .month, selectedType: $viewModel.selectedType).onTapGesture {
-                //                            viewModel.selectedType = .month
-                                        PurchaseItemView(title: "YEARLY".localized, type: .year, selectedType: $viewModel.selectedType).onTapGesture {
-                                            viewModel.selectedType = .year
-                                            viewModel.buyPublisher.send(true)
-                                        }
-                                        TSVipRecView(save: vipType.saveString)
-                                            .offset(x:-30,y:-14)
-                                    }
-                                    
-                                PurchaseItemView(title: "WEEKLY".localized, type: .week, selectedType: $viewModel.selectedType).onTapGesture {
-                                    viewModel.selectedType = .week
-                                        viewModel.buyPublisher.send(true)
-                                    }
-                                    
+                ZStack(alignment: .topTrailing) {
+                    //                    //增加月付费
+                    //                        PurchaseItemView(title: "One Month".localized, type: .month, selectedType: $viewModel.selectedType).onTapGesture {
+                    //                            viewModel.selectedType = .month
+                    PurchaseItemView(title: "YEARLY".localized, type: .year, selectedType: $viewModel.selectedType).onTapGesture {
+                        viewModel.selectedType = .year
+                        viewModel.buyPublisher.send(true)
+                    }
+                    TSVipRecView(save: vipType.saveString)
+                        .offset(x:-30,y:-14)
+                }
+                
+                PurchaseItemView(title: "WEEKLY".localized, type: .week, selectedType: $viewModel.selectedType).onTapGesture {
+                    viewModel.selectedType = .week
+                    viewModel.buyPublisher.send(true)
+                }
+                
                 //                }else{
                 //                    PurchaseItemTypeOneView(title: "One Year".localized, type: .year, selectedType: $viewModel.selectedType).onTapGesture {
                 //                        viewModel.selectedType = .year
@@ -287,22 +287,23 @@ struct PurchaseView :View {
                         Text("Continue")
                             .font(.system(size: 16))
                             .foregroundColor(.hex("#111111"))
-                            
+                        
                     }.frame(maxWidth: .infinity ,maxHeight: 48.0)
                         .cornerRadius(0.0)
                 }
-            
+                
                 HStack {
                     Text("Recurring billing, cancel anytime")
                         .foregroundColor(UIColor.themeColor.color) +
-                        Text(", Payment will be charged to your iTunes account at confirmation of purchase. Subscriptions automatically renew for the same applicable term and price, unless auto-renew is turned off at least 24 hours before the end of the current period.")
+                    Text(", Payment will be charged to your iTunes account at confirmation of purchase. Subscriptions automatically renew for the same applicable term and price, unless auto-renew is turned off at least 24 hours before the end of the current period.")
                         .foregroundColor(UIColor.lesserText.color)
                 }
-                .multilineTextAlignment(.center).font(.font(size: 8))
+                .multilineTextAlignment(.center)
+                .font(.font(size: 8))
                 .onTapGesture {
                     viewModel.privacyPublisher.send(true)
                 }
-
+                
                 HStack(spacing: 8) {
                     Text("Term of us")
                         .onTapGesture {
@@ -322,7 +323,7 @@ struct PurchaseView :View {
             }.padding(.horizontal)
             
             Spacer().frame(height:9+k_Height_safeAreaInsetsBottom())
-        }
+        }.environment(\.layoutDirection, kIsRTL ? .rightToLeft : .leftToRight)
     }
 }
 

+ 3 - 3
TSLiveWallpaper/Business/TSTabBarController/TSTabBarController.swift

@@ -89,7 +89,7 @@ class TSTabBarController: UITabBarController {
         tabBar.addSubview(markView)
         markView.snp.makeConstraints { make in
             make.bottom.equalTo(-k_Height_safeAreaInsetsBottom())
-            make.left.equalTo(0)
+            make.leading.equalTo(0)
             make.width.equalTo(markWidth)
             make.height.equalTo(2)
         }
@@ -110,14 +110,14 @@ extension TSTabBarController {
     
     func updateMarkViewFrame() {
         markView.snp.updateConstraints { make in
-            make.left.equalTo(CGFloat(selectedIndex) * markWidth)
+            make.leading.equalTo(CGFloat(selectedIndex) * markWidth)
         }
     }
     
     
     private func tabBarItem(title: String, image: UIImage?, selectedImage: UIImage?, tag: Int) -> UITabBarItem {
         let tabBarItem = UITabBarItem()
-        tabBarItem.image = image?.withRenderingMode(.alwaysOriginal)
+        tabBarItem.image = image?.withRenderingMode(.alwaysOriginal).mirrored
         tabBarItem.selectedImage = selectedImage?.withRenderingMode(.alwaysOriginal)
         tabBarItem.imageInsets = UIEdgeInsets(top: 0, left: 0, bottom: -8, right: 0) // 向下移动图标
         return tabBarItem

+ 1 - 1
TSLiveWallpaper/Common/Ex/UICollectionView+Ex.swift

@@ -21,7 +21,7 @@ extension UICollectionView {
         let layout = UICollectionViewFlowLayout()
         layout.scrollDirection = .vertical
         
-        let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
+        let collectionView = TSBaseCollectionView(frame: .zero, collectionViewLayout: layout)
         collectionView.delegate = delegate
         collectionView.dataSource = delegate
         collectionView.showsVerticalScrollIndicator = false

+ 3 - 3
TSLiveWallpaper/Common/Purchase/TSPurchaseManager.swift

@@ -75,9 +75,9 @@ public class PurchaseManager: NSObject {
     }
 
     @objc public var isVip: Bool {
-//        #if DEBUG
-//        return true
-//        #endif
+        #if DEBUG
+        return true
+        #endif
         guard let expiresDate = expiredDate else {
             return false
         }

+ 74 - 0
TSLiveWallpaper/Common/ThirdParty/TYCyclePagerView/TYCyclePagerTransformLayout.h

@@ -0,0 +1,74 @@
+//
+//  TYCyclePagerViewLayout.h
+//  TYCyclePagerViewDemo
+//
+//  Created by tany on 2017/6/19.
+//  Copyright © 2017年 tany. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+typedef NS_ENUM(NSUInteger, TYCyclePagerTransformLayoutType) {
+    TYCyclePagerTransformLayoutNormal,
+    TYCyclePagerTransformLayoutLinear,
+    TYCyclePagerTransformLayoutCoverflow,
+};
+
+@class TYCyclePagerTransformLayout;
+@protocol TYCyclePagerTransformLayoutDelegate <NSObject>
+
+// initialize layout attributes
+- (void)pagerViewTransformLayout:(TYCyclePagerTransformLayout *)pagerViewTransformLayout initializeTransformAttributes:(UICollectionViewLayoutAttributes *)attributes;
+
+// apply layout attributes
+- (void)pagerViewTransformLayout:(TYCyclePagerTransformLayout *)pagerViewTransformLayout applyTransformToAttributes:(UICollectionViewLayoutAttributes *)attributes;
+
+@end
+
+
+@interface TYCyclePagerViewLayout : NSObject
+
+@property (nonatomic, assign) CGSize itemSize;
+@property (nonatomic, assign) CGFloat itemSpacing;
+@property (nonatomic, assign) UIEdgeInsets sectionInset;
+
+@property (nonatomic, assign) TYCyclePagerTransformLayoutType layoutType;
+
+@property (nonatomic, assign) CGFloat minimumScale; // sacle default 0.8
+@property (nonatomic, assign) CGFloat minimumAlpha; // alpha default 1.0
+@property (nonatomic, assign) CGFloat maximumAngle; // angle is % default 0.2
+
+@property (nonatomic, assign) BOOL isInfiniteLoop;  // infinte scroll
+@property (nonatomic, assign) CGFloat rateOfChange; // scale and angle change rate
+@property (nonatomic, assign) BOOL adjustSpacingWhenScroling; 
+
+/**
+ pageView cell item vertical centering
+ */
+@property (nonatomic, assign) BOOL itemVerticalCenter;
+
+/**
+ first and last item horizontalc enter, when isInfiniteLoop is NO
+ */
+@property (nonatomic, assign) BOOL itemHorizontalCenter;
+
+// sectionInset
+@property (nonatomic, assign, readonly) UIEdgeInsets onlyOneSectionInset;
+@property (nonatomic, assign, readonly) UIEdgeInsets firstSectionInset;
+@property (nonatomic, assign, readonly) UIEdgeInsets lastSectionInset;
+@property (nonatomic, assign, readonly) UIEdgeInsets middleSectionInset;
+
+@end
+
+
+@interface TYCyclePagerTransformLayout : UICollectionViewFlowLayout
+
+@property (nonatomic, strong) TYCyclePagerViewLayout *layout;
+
+@property (nonatomic, weak, nullable) id<TYCyclePagerTransformLayoutDelegate> delegate;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 319 - 0
TSLiveWallpaper/Common/ThirdParty/TYCyclePagerView/TYCyclePagerTransformLayout.m

@@ -0,0 +1,319 @@
+//
+//  TYCyclePagerViewLayout.m
+//  TYCyclePagerViewDemo
+//
+//  Created by tany on 2017/6/19.
+//  Copyright © 2017年 tany. All rights reserved.
+//
+
+#import "TYCyclePagerTransformLayout.h"
+#import "TSLiveWallpaper-Swift.h"
+typedef NS_ENUM(NSUInteger, TYTransformLayoutItemDirection) {
+    TYTransformLayoutItemLeft,
+    TYTransformLayoutItemCenter,
+    TYTransformLayoutItemRight,
+};
+
+
+@interface TYCyclePagerTransformLayout () {
+    struct {
+        unsigned int applyTransformToAttributes   :1;
+        unsigned int initializeTransformAttributes   :1;
+    }_delegateFlags;
+}
+
+@property (nonatomic, assign) BOOL applyTransformToAttributesDelegate;
+
+@end
+
+
+@interface TYCyclePagerViewLayout ()
+
+@property (nonatomic, weak) UIView *pageView;
+
+@end
+
+
+@implementation TYCyclePagerTransformLayout
+
+
++ (NSString *)appLanguage {
+    id systemLanguages = [[NSUserDefaults standardUserDefaults] valueForKey:@"AppleLanguages"];
+    NSString *currentLanguage = nil;
+    
+    if ([systemLanguages isKindOfClass:[NSArray class]]) {
+        currentLanguage = [systemLanguages firstObject];
+    }
+    else if ([systemLanguages isKindOfClass:[NSString class]]) {
+        currentLanguage = systemLanguages;
+    }
+    
+    return currentLanguage ?: @"";
+}
+
+- (BOOL)flipsHorizontallyInOppositeLayoutDirection {
+    return [[TYCyclePagerTransformLayout appLanguage] compare:@"ar"];
+}
+
+- (instancetype)init {
+    if (self = [super init]) {
+        self.scrollDirection = UICollectionViewScrollDirectionHorizontal;
+    }
+    return self;
+}
+
+- (instancetype)initWithCoder:(NSCoder *)aDecoder {
+    if (self = [super initWithCoder:aDecoder]) {
+        self.scrollDirection = UICollectionViewScrollDirectionHorizontal;
+    }
+    return self;
+}
+
+#pragma mark - getter setter
+
+- (void)setDelegate:(id<TYCyclePagerTransformLayoutDelegate>)delegate {
+    _delegate = delegate;
+    _delegateFlags.initializeTransformAttributes = [delegate respondsToSelector:@selector(pagerViewTransformLayout:initializeTransformAttributes:)];
+    _delegateFlags.applyTransformToAttributes = [delegate respondsToSelector:@selector(pagerViewTransformLayout:applyTransformToAttributes:)];
+}
+
+- (void)setLayout:(TYCyclePagerViewLayout *)layout {
+    _layout = layout;
+    _layout.pageView = self.collectionView;
+    self.itemSize = _layout.itemSize;
+    self.minimumInteritemSpacing = _layout.itemSpacing;
+    self.minimumLineSpacing = _layout.itemSpacing;
+}
+
+- (CGSize)itemSize {
+    if (!_layout) {
+        return [super itemSize];
+    }
+    return _layout.itemSize;
+}
+
+- (CGFloat)minimumLineSpacing {
+    if (!_layout) {
+        return [super minimumLineSpacing];
+    }
+    return _layout.itemSpacing;
+}
+
+- (CGFloat)minimumInteritemSpacing {
+    if (!_layout) {
+        return [super minimumInteritemSpacing];
+    }
+    return _layout.itemSpacing;
+}
+
+- (TYTransformLayoutItemDirection)directionWithCenterX:(CGFloat)centerX {
+    TYTransformLayoutItemDirection direction= TYTransformLayoutItemRight;
+    CGFloat contentCenterX = self.collectionView.contentOffset.x + CGRectGetWidth(self.collectionView.frame)/2;
+    if (ABS(centerX - contentCenterX) < 0.5) {
+        direction = TYTransformLayoutItemCenter;
+    }else if (centerX - contentCenterX < 0) {
+        direction = TYTransformLayoutItemLeft;
+    }
+    return direction;
+}
+
+#pragma mark - layout
+
+-(BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
+{
+    return _layout.layoutType == TYCyclePagerTransformLayoutNormal ? [super shouldInvalidateLayoutForBoundsChange:newBounds] : YES;
+}
+
+- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {
+    if (_delegateFlags.applyTransformToAttributes || _layout.layoutType != TYCyclePagerTransformLayoutNormal) {
+        NSArray *attributesArray = [[NSArray alloc] initWithArray:[super layoutAttributesForElementsInRect:rect] copyItems:YES];
+        CGRect visibleRect = {self.collectionView.contentOffset,self.collectionView.bounds.size};
+        for (UICollectionViewLayoutAttributes *attributes in attributesArray) {
+            if (!CGRectIntersectsRect(visibleRect, attributes.frame)) {
+                continue;
+            }
+            if (_delegateFlags.applyTransformToAttributes) {
+                [_delegate pagerViewTransformLayout:self applyTransformToAttributes:attributes];
+            }else {
+                [self applyTransformToAttributes:attributes layoutType:_layout.layoutType];
+            }
+        }
+        return attributesArray;
+    }
+    return [super layoutAttributesForElementsInRect:rect];
+}
+
+- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
+    UICollectionViewLayoutAttributes *attributes = [super layoutAttributesForItemAtIndexPath:indexPath];
+    if (_delegateFlags.initializeTransformAttributes) {
+        [_delegate pagerViewTransformLayout:self initializeTransformAttributes:attributes];
+    }else if(_layout.layoutType != TYCyclePagerTransformLayoutNormal){
+        [self initializeTransformAttributes:attributes layoutType:_layout.layoutType];
+    }
+    return attributes;
+}
+
+#pragma mark - transform
+
+- (void)initializeTransformAttributes:(UICollectionViewLayoutAttributes *)attributes layoutType:(TYCyclePagerTransformLayoutType)layoutType {
+    switch (layoutType) {
+        case TYCyclePagerTransformLayoutLinear:
+            [self applyLinearTransformToAttributes:attributes scale:_layout.minimumScale alpha:_layout.minimumAlpha];
+            break;
+        case TYCyclePagerTransformLayoutCoverflow:
+        {
+            [self applyCoverflowTransformToAttributes:attributes angle:_layout.maximumAngle alpha:_layout.minimumAlpha];
+            break;
+        }
+        default:
+            break;
+    }
+}
+
+- (void)applyTransformToAttributes:(UICollectionViewLayoutAttributes *)attributes layoutType:(TYCyclePagerTransformLayoutType)layoutType {
+    switch (layoutType) {
+        case TYCyclePagerTransformLayoutLinear:
+            [self applyLinearTransformToAttributes:attributes];
+            break;
+        case TYCyclePagerTransformLayoutCoverflow:
+            [self applyCoverflowTransformToAttributes:attributes];
+            break;
+        default:
+            break;
+    }
+}
+
+#pragma mark - LinearTransform
+
+- (void)applyLinearTransformToAttributes:(UICollectionViewLayoutAttributes *)attributes {
+    CGFloat collectionViewWidth = self.collectionView.frame.size.width;
+    if (collectionViewWidth <= 0) {
+        return;
+    }
+    CGFloat centetX = self.collectionView.contentOffset.x + collectionViewWidth/2;
+    CGFloat delta = ABS(attributes.center.x - centetX);
+    CGFloat scale = MAX(1 - delta/collectionViewWidth*_layout.rateOfChange, _layout.minimumScale);
+    CGFloat alpha = MAX(1 - delta/collectionViewWidth, _layout.minimumAlpha);
+    [self applyLinearTransformToAttributes:attributes scale:scale alpha:alpha];
+}
+
+- (void)applyLinearTransformToAttributes:(UICollectionViewLayoutAttributes *)attributes scale:(CGFloat)scale alpha:(CGFloat)alpha {
+    CGAffineTransform transform = CGAffineTransformMakeScale(scale, scale);
+    if (_layout.adjustSpacingWhenScroling) {
+        TYTransformLayoutItemDirection direction = [self directionWithCenterX:attributes.center.x];
+        CGFloat translate = 0;
+        switch (direction) {
+            case TYTransformLayoutItemLeft:
+                translate = 1.15 * attributes.size.width*(1-scale)/2;
+                break;
+            case TYTransformLayoutItemRight:
+                translate = -1.15 * attributes.size.width*(1-scale)/2;
+                break;
+            default:
+                // center
+                scale = 1.0;
+                alpha = 1.0;
+                break;
+        }
+        transform = CGAffineTransformTranslate(transform,translate, 0);
+    }
+    attributes.transform = transform;
+    attributes.alpha = alpha;
+}
+
+#pragma mark - CoverflowTransform
+
+- (void)applyCoverflowTransformToAttributes:(UICollectionViewLayoutAttributes *)attributes{
+    CGFloat collectionViewWidth = self.collectionView.frame.size.width;
+    if (collectionViewWidth <= 0) {
+        return;
+    }
+    CGFloat centetX = self.collectionView.contentOffset.x + collectionViewWidth/2;
+    CGFloat delta = ABS(attributes.center.x - centetX);
+    CGFloat angle = MIN(delta/collectionViewWidth*(1-_layout.rateOfChange), _layout.maximumAngle);
+    CGFloat alpha = MAX(1 - delta/collectionViewWidth, _layout.minimumAlpha);
+    [self applyCoverflowTransformToAttributes:attributes angle:angle alpha:alpha];
+}
+
+- (void)applyCoverflowTransformToAttributes:(UICollectionViewLayoutAttributes *)attributes angle:(CGFloat)angle alpha:(CGFloat)alpha {
+    TYTransformLayoutItemDirection direction = [self directionWithCenterX:attributes.center.x];
+    CATransform3D transform3D = CATransform3DIdentity;
+    transform3D.m34 = -0.002;
+    CGFloat translate = 0;
+    switch (direction) {
+        case TYTransformLayoutItemLeft:
+            translate = (1-cos(angle*1.2*M_PI))*attributes.size.width;
+            break;
+        case TYTransformLayoutItemRight:
+            translate = -(1-cos(angle*1.2*M_PI))*attributes.size.width;
+            angle = -angle;
+            break;
+        default:
+            // center
+            angle = 0;
+            alpha = 1;
+            break;
+    }
+
+    transform3D = CATransform3DRotate(transform3D, M_PI*angle, 0, 1, 0);
+    if (_layout.adjustSpacingWhenScroling) {
+        transform3D = CATransform3DTranslate(transform3D, translate, 0, 0);
+    }
+    attributes.transform3D = transform3D;
+    attributes.alpha = alpha;
+
+}
+@end
+
+
+@implementation TYCyclePagerViewLayout
+
+- (instancetype)init {
+    if (self = [super init]) {
+        _itemVerticalCenter = YES;
+        _minimumScale = 0.8;
+        _minimumAlpha = 1.0;
+        _maximumAngle = 0.2;
+        _rateOfChange = 0.4;
+        _adjustSpacingWhenScroling = YES;
+    }
+    return self;
+}
+
+#pragma mark - getter
+
+- (UIEdgeInsets)onlyOneSectionInset {
+    CGFloat leftSpace = _pageView && !_isInfiniteLoop && _itemHorizontalCenter ? (CGRectGetWidth(_pageView.frame) - _itemSize.width)/2 : _sectionInset.left;
+    CGFloat rightSpace = _pageView && !_isInfiniteLoop && _itemHorizontalCenter ? (CGRectGetWidth(_pageView.frame) - _itemSize.width)/2 : _sectionInset.right;
+    if (_itemVerticalCenter) {
+        CGFloat verticalSpace = (CGRectGetHeight(_pageView.frame) - _itemSize.height)/2;
+        return UIEdgeInsetsMake(verticalSpace, leftSpace, verticalSpace, rightSpace);
+    }
+    return UIEdgeInsetsMake(_sectionInset.top, leftSpace, _sectionInset.bottom, rightSpace);
+}
+
+- (UIEdgeInsets)firstSectionInset {
+    if (_itemVerticalCenter) {
+        CGFloat verticalSpace = (CGRectGetHeight(_pageView.frame) - _itemSize.height)/2;
+        return UIEdgeInsetsMake(verticalSpace, _sectionInset.left, verticalSpace, _itemSpacing);
+    }
+    return UIEdgeInsetsMake(_sectionInset.top, _sectionInset.left, _sectionInset.bottom, _itemSpacing);
+}
+
+- (UIEdgeInsets)lastSectionInset {
+    if (_itemVerticalCenter) {
+        CGFloat verticalSpace = (CGRectGetHeight(_pageView.frame) - _itemSize.height)/2;
+        return UIEdgeInsetsMake(verticalSpace, 0, verticalSpace, _sectionInset.right);
+    }
+    return UIEdgeInsetsMake(_sectionInset.top, 0, _sectionInset.bottom, _sectionInset.right);
+}
+
+- (UIEdgeInsets)middleSectionInset {
+    if (_itemVerticalCenter) {
+        CGFloat verticalSpace = (CGRectGetHeight(_pageView.frame) - _itemSize.height)/2;
+        return UIEdgeInsetsMake(verticalSpace, 0, verticalSpace, _itemSpacing);
+    }
+    return _sectionInset;
+}
+
+@end

+ 180 - 0
TSLiveWallpaper/Common/ThirdParty/TYCyclePagerView/TYCyclePagerView.h

@@ -0,0 +1,180 @@
+//
+//  TYCyclePagerView.h
+//  TYCyclePagerViewDemo
+//
+//  Created by tany on 2017/6/14.
+//  Copyright © 2017年 tany. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+#import "TYCyclePagerTransformLayout.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+typedef struct {
+    NSInteger index;
+    NSInteger section;
+}TYIndexSection;
+
+// pagerView scrolling direction
+typedef NS_ENUM(NSUInteger, TYPagerScrollDirection) {
+    TYPagerScrollDirectionLeft,
+    TYPagerScrollDirectionRight,
+};
+
+@class TYCyclePagerView;
+@protocol TYCyclePagerViewDataSource <NSObject>
+
+- (NSInteger)numberOfItemsInPagerView:(TYCyclePagerView *)pageView;
+
+- (__kindof UICollectionViewCell *)pagerView:(TYCyclePagerView *)pagerView cellForItemAtIndex:(NSInteger)index;
+
+/**
+ return pagerView layout,and cache layout
+ */
+- (TYCyclePagerViewLayout *)layoutForPagerView:(TYCyclePagerView *)pageView;
+
+@end
+
+@protocol TYCyclePagerViewDelegate <NSObject>
+
+@optional
+
+/**
+ pagerView did scroll to new index page
+ */
+- (void)pagerView:(TYCyclePagerView *)pageView didScrollFromIndex:(NSInteger)fromIndex toIndex:(NSInteger)toIndex;
+
+/**
+ pagerView did selected item cell
+ */
+- (void)pagerView:(TYCyclePagerView *)pageView didSelectedItemCell:(__kindof UICollectionViewCell *)cell atIndex:(NSInteger)index;
+- (void)pagerView:(TYCyclePagerView *)pageView didSelectedItemCell:(__kindof UICollectionViewCell *)cell atIndexSection:(TYIndexSection)indexSection;
+
+// custom layout
+- (void)pagerView:(TYCyclePagerView *)pageView initializeTransformAttributes:(UICollectionViewLayoutAttributes *)attributes;
+
+- (void)pagerView:(TYCyclePagerView *)pageView applyTransformToAttributes:(UICollectionViewLayoutAttributes *)attributes;
+
+
+// scrollViewDelegate
+
+- (void)pagerViewDidScroll:(TYCyclePagerView *)pageView;
+
+- (void)pagerViewWillBeginDragging:(TYCyclePagerView *)pageView;
+
+- (void)pagerViewDidEndDragging:(TYCyclePagerView *)pageView willDecelerate:(BOOL)decelerate;
+
+- (void)pagerViewWillBeginDecelerating:(TYCyclePagerView *)pageView;
+
+- (void)pagerViewDidEndDecelerating:(TYCyclePagerView *)pageView;
+
+- (void)pagerViewWillBeginScrollingAnimation:(TYCyclePagerView *)pageView;
+
+- (void)pagerViewDidEndScrollingAnimation:(TYCyclePagerView *)pageView;
+
+@end
+
+
+@interface TYCyclePagerView : UIView
+
+// will be automatically resized to track the size of the pagerView
+@property (nonatomic, strong, nullable) UIView *backgroundView; 
+
+@property (nonatomic, weak, nullable) id<TYCyclePagerViewDataSource> dataSource;
+@property (nonatomic, weak, nullable) id<TYCyclePagerViewDelegate> delegate;
+
+// pager view, don't set dataSource and delegate
+@property (nonatomic, weak, readonly) UICollectionView *collectionView;
+// pager view layout
+@property (nonatomic, strong, readonly) TYCyclePagerViewLayout *layout;
+
+/**
+ is infinite cycle pageview
+ */
+@property (nonatomic, assign) BOOL isInfiniteLoop;
+
+/**
+ pagerView automatic scroll time interval, default 0,disable automatic
+ */
+@property (nonatomic, assign) CGFloat autoScrollInterval;
+
+@property (nonatomic, assign) BOOL reloadDataNeedResetIndex;
+
+/**
+ current page index
+ */
+@property (nonatomic, assign, readonly) NSInteger curIndex;
+@property (nonatomic, assign, readonly) TYIndexSection indexSection;
+
+// scrollView property
+@property (nonatomic, assign, readonly) CGPoint contentOffset;
+@property (nonatomic, assign, readonly) BOOL tracking;
+@property (nonatomic, assign, readonly) BOOL dragging;
+@property (nonatomic, assign, readonly) BOOL decelerating;
+
+
+/**
+ reload data, !!important!!: will clear layout and call delegate layoutForPagerView
+ */
+- (void)reloadData;
+
+/**
+ update data is reload data, but not clear layuot
+ */
+- (void)updateData;
+
+/**
+ if you only want update layout
+ */
+- (void)setNeedUpdateLayout;
+
+/**
+ will set layout nil and call delegate->layoutForPagerView
+ */
+- (void)setNeedClearLayout;
+
+/**
+ current index cell in pagerView
+ */
+- (__kindof UICollectionViewCell * _Nullable)curIndexCell;
+
+/**
+ visible cells in pageView
+ */
+- (NSArray<__kindof UICollectionViewCell *> *_Nullable)visibleCells;
+
+
+/**
+ visible pageView indexs, maybe repeat index
+ */
+- (NSArray *)visibleIndexs;
+
+/**
+ scroll to item at index
+ */
+- (void)scrollToItemAtIndex:(NSInteger)index animate:(BOOL)animate;
+- (void)scrollToItemAtIndexSection:(TYIndexSection)indexSection animate:(BOOL)animate;
+/**
+ scroll to next or pre item
+ */
+- (void)scrollToNearlyIndexAtDirection:(TYPagerScrollDirection)direction animate:(BOOL)animate;
+
+/**
+ register pager view cell with class
+ */
+- (void)registerClass:(Class)Class forCellWithReuseIdentifier:(NSString *)identifier;
+
+/**
+ register pager view cell with nib
+ */
+- (void)registerNib:(UINib *)nib forCellWithReuseIdentifier:(NSString *)identifier;
+
+/**
+ dequeue reusable cell for pagerView
+ */
+- (__kindof UICollectionViewCell *)dequeueReusableCellWithReuseIdentifier:(NSString *)identifier forIndex:(NSInteger)index;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 607 - 0
TSLiveWallpaper/Common/ThirdParty/TYCyclePagerView/TYCyclePagerView.m

@@ -0,0 +1,607 @@
+//
+//  TYCyclePagerView.m
+//  TYCyclePagerViewDemo
+//
+//  Created by tany on 2017/6/14.
+//  Copyright © 2017年 tany. All rights reserved.
+//
+
+#import "TYCyclePagerView.h"
+
+NS_INLINE BOOL TYEqualIndexSection(TYIndexSection indexSection1,TYIndexSection indexSection2) {
+    return indexSection1.index == indexSection2.index && indexSection1.section == indexSection2.section;
+}
+
+NS_INLINE TYIndexSection TYMakeIndexSection(NSInteger index, NSInteger section) {
+    TYIndexSection indexSection;
+    indexSection.index = index;
+    indexSection.section = section;
+    return indexSection;
+}
+
+@interface TYCyclePagerView () <UICollectionViewDataSource, UICollectionViewDelegateFlowLayout, TYCyclePagerTransformLayoutDelegate> {
+    struct {
+        unsigned int pagerViewDidScroll   :1;
+        unsigned int didScrollFromIndexToNewIndex   :1;
+        unsigned int initializeTransformAttributes   :1;
+        unsigned int applyTransformToAttributes   :1;
+    }_delegateFlags;
+    struct {
+        unsigned int cellForItemAtIndex   :1;
+        unsigned int layoutForPagerView   :1;
+    }_dataSourceFlags;
+}
+
+// UI
+@property (nonatomic, weak) UICollectionView *collectionView;
+@property (nonatomic, strong) TYCyclePagerViewLayout *layout;
+@property (nonatomic, strong) NSTimer *timer;
+
+// Data
+@property (nonatomic, assign) NSInteger numberOfItems;
+
+@property (nonatomic, assign) NSInteger dequeueSection;
+@property (nonatomic, assign) TYIndexSection beginDragIndexSection;
+@property (nonatomic, assign) NSInteger firstScrollIndex;
+
+@property (nonatomic, assign) BOOL needClearLayout;
+@property (nonatomic, assign) BOOL didReloadData;
+@property (nonatomic, assign) BOOL didLayout;
+@property (nonatomic, assign) BOOL needResetIndex;
+
+@end
+
+#define kPagerViewMaxSectionCount 200
+#define kPagerViewMinSectionCount 18
+
+@implementation TYCyclePagerView
+
+#pragma mark - life Cycle
+
+- (instancetype)initWithFrame:(CGRect)frame {
+    if (self = [super initWithFrame:frame]) {
+        [self configureProperty];
+        
+        [self addCollectionView];
+    }
+    return self;
+}
+
+- (instancetype)initWithCoder:(NSCoder *)aDecoder {
+    if (self = [super initWithCoder:aDecoder]) {
+        [self configureProperty];
+        
+        [self addCollectionView];
+    }
+    return self;
+}
+
+- (void)configureProperty {
+    _needResetIndex = NO;
+    _didReloadData = NO;
+    _didLayout = NO;
+    _autoScrollInterval = 0;
+    _isInfiniteLoop = YES;
+    _beginDragIndexSection.index = 0;
+    _beginDragIndexSection.section = 0;
+    _indexSection.index = -1;
+    _indexSection.section = -1;
+    _firstScrollIndex = -1;
+}
+
+- (void)addCollectionView {
+    TYCyclePagerTransformLayout *layout = [[TYCyclePagerTransformLayout alloc]init];
+    UICollectionView *collectionView = [[UICollectionView alloc]initWithFrame:CGRectZero collectionViewLayout:layout];
+    layout.delegate = _delegateFlags.applyTransformToAttributes ? self : nil;;
+    collectionView.backgroundColor = [UIColor clearColor];
+    collectionView.dataSource = self;
+    collectionView.delegate = self;
+    collectionView.pagingEnabled = NO;
+    collectionView.decelerationRate = 1-0.0076;
+    if ([collectionView respondsToSelector:@selector(setPrefetchingEnabled:)]) {
+        collectionView.prefetchingEnabled = NO;
+    }
+    collectionView.showsHorizontalScrollIndicator = NO;
+    collectionView.showsVerticalScrollIndicator = NO;
+    [self addSubview:collectionView];
+    _collectionView = collectionView;
+}
+
+- (void)willMoveToSuperview:(UIView *)newSuperview {
+    if (!newSuperview) {
+        [self removeTimer];
+    }else {
+        [self removeTimer];
+        if (_autoScrollInterval > 0) {
+            [self addTimer];
+        }
+    }
+}
+
+
+#pragma mark - timer
+
+- (void)addTimer {
+    if (_timer || _autoScrollInterval <= 0) {
+        return;
+    }
+    _timer = [NSTimer timerWithTimeInterval:_autoScrollInterval target:self selector:@selector(timerFired:) userInfo:nil repeats:YES];
+    [[NSRunLoop mainRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes];
+}
+
+- (void)removeTimer {
+    if (!_timer) {
+        return;
+    }
+    [_timer invalidate];
+    _timer = nil;
+}
+
+- (void)timerFired:(NSTimer *)timer {
+    if (!self.superview || !self.window || _numberOfItems == 0 || self.tracking) {
+        return;
+    }
+    
+    [self scrollToNearlyIndexAtDirection:TYPagerScrollDirectionRight animate:YES];
+}
+
+#pragma mark - getter
+
+- (TYCyclePagerViewLayout *)layout {
+    if (!_layout) {
+        if (_dataSourceFlags.layoutForPagerView) {
+            _layout = [_dataSource layoutForPagerView:self];
+            _layout.isInfiniteLoop = _isInfiniteLoop;
+        }
+        if (_layout.itemSize.width <= 0 || _layout.itemSize.height <= 0) {
+            _layout = nil;
+        }
+    }
+    return _layout;
+}
+
+- (NSInteger)curIndex {
+    return _indexSection.index;
+}
+
+- (CGPoint)contentOffset {
+    return _collectionView.contentOffset;
+}
+
+- (BOOL)tracking {
+    return _collectionView.tracking;
+}
+
+- (BOOL)dragging {
+    return _collectionView.dragging;
+}
+
+- (BOOL)decelerating {
+    return _collectionView.decelerating;
+}
+
+- (UIView *)backgroundView {
+    return _collectionView.backgroundView;
+}
+
+- (__kindof UICollectionViewCell *)curIndexCell {
+    return [_collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForItem:_indexSection.index inSection:_indexSection.section]];
+}
+
+- (NSArray<__kindof UICollectionViewCell *> *)visibleCells {
+    return _collectionView.visibleCells;
+}
+
+- (NSArray *)visibleIndexs {
+    NSMutableArray *indexs = [NSMutableArray array];
+    for (NSIndexPath *indexPath in _collectionView.indexPathsForVisibleItems) {
+        [indexs addObject:@(indexPath.item)];
+    }
+    return [indexs copy];
+}
+
+#pragma mark - setter
+
+- (void)setBackgroundView:(UIView *)backgroundView {
+    [_collectionView setBackgroundView:backgroundView];
+}
+
+- (void)setAutoScrollInterval:(CGFloat)autoScrollInterval {
+    _autoScrollInterval = autoScrollInterval;
+    [self removeTimer];
+    if (autoScrollInterval > 0 && self.superview) {
+        [self addTimer];
+    }
+}
+
+- (void)setDelegate:(id<TYCyclePagerViewDelegate>)delegate {
+    _delegate = delegate;
+    _delegateFlags.pagerViewDidScroll = [delegate respondsToSelector:@selector(pagerViewDidScroll:)];
+    _delegateFlags.didScrollFromIndexToNewIndex = [delegate respondsToSelector:@selector(pagerView:didScrollFromIndex:toIndex:)];
+    _delegateFlags.initializeTransformAttributes = [delegate respondsToSelector:@selector(pagerView:initializeTransformAttributes:)];
+    _delegateFlags.applyTransformToAttributes = [delegate respondsToSelector:@selector(pagerView:applyTransformToAttributes:)];
+    if (self.collectionView && self.collectionView.collectionViewLayout) {
+        ((TYCyclePagerTransformLayout *)self.collectionView.collectionViewLayout).delegate = _delegateFlags.applyTransformToAttributes ? self : nil;
+    }
+}
+
+- (void)setDataSource:(id<TYCyclePagerViewDataSource>)dataSource {
+    _dataSource = dataSource;
+    _dataSourceFlags.cellForItemAtIndex = [dataSource respondsToSelector:@selector(pagerView:cellForItemAtIndex:)];
+    _dataSourceFlags.layoutForPagerView = [dataSource respondsToSelector:@selector(layoutForPagerView:)];
+}
+
+#pragma mark - public
+
+- (void)reloadData {
+    _didReloadData = YES;
+    _needResetIndex = YES;
+    [self setNeedClearLayout];
+    [self clearLayout];
+    [self updateData];
+}
+
+// not clear layout
+- (void)updateData {
+    [self updateLayout];
+    _numberOfItems = [_dataSource numberOfItemsInPagerView:self];
+    [_collectionView reloadData];
+    if (!_didLayout && !CGRectIsEmpty(self.collectionView.frame) && _indexSection.index < 0) {
+        _didLayout = YES;
+    }
+    BOOL needResetIndex = _needResetIndex && _reloadDataNeedResetIndex;
+    _needResetIndex = NO;
+    if (needResetIndex) {
+        [self removeTimer];
+    }
+    [self resetPagerViewAtIndex:(_indexSection.index < 0 && !CGRectIsEmpty(self.collectionView.frame)) || needResetIndex ? 0 :_indexSection.index];
+    if (needResetIndex) {
+        [self addTimer];
+    }
+}
+
+- (void)scrollToNearlyIndexAtDirection:(TYPagerScrollDirection)direction animate:(BOOL)animate {
+    TYIndexSection indexSection = [self nearlyIndexPathAtDirection:direction];
+    [self scrollToItemAtIndexSection:indexSection animate:animate];
+}
+
+- (void)scrollToItemAtIndex:(NSInteger)index animate:(BOOL)animate {
+    if (!_didLayout && _didReloadData) {
+        _firstScrollIndex = index;
+    }else {
+        _firstScrollIndex = -1;
+    }
+    if (!_isInfiniteLoop) {
+        [self scrollToItemAtIndexSection:TYMakeIndexSection(index, 0) animate:animate];
+        return;
+    }
+
+    [self scrollToItemAtIndexSection:TYMakeIndexSection(index, index >= self.curIndex ? _indexSection.section : _indexSection.section+1) animate:animate];
+}
+
+- (void)scrollToItemAtIndexSection:(TYIndexSection)indexSection animate:(BOOL)animate {
+    if (_numberOfItems <= 0 || ![self isValidIndexSection:indexSection]) {
+        //NSLog(@"scrollToItemAtIndex: item indexSection is invalid!");
+        return;
+    }
+    
+    if (animate && [_delegate respondsToSelector:@selector(pagerViewWillBeginScrollingAnimation:)]) {
+        [_delegate pagerViewWillBeginScrollingAnimation:self];
+    }
+    CGFloat offset = [self caculateOffsetXAtIndexSection:indexSection];
+    [_collectionView setContentOffset:CGPointMake(offset, _collectionView.contentOffset.y) animated:animate];
+}
+
+- (void)registerClass:(Class)Class forCellWithReuseIdentifier:(NSString *)identifier {
+    [_collectionView registerClass:Class forCellWithReuseIdentifier:identifier];
+}
+
+- (void)registerNib:(UINib *)nib forCellWithReuseIdentifier:(NSString *)identifier {
+    [_collectionView registerNib:nib forCellWithReuseIdentifier:identifier];
+}
+
+- (__kindof UICollectionViewCell *)dequeueReusableCellWithReuseIdentifier:(NSString *)identifier forIndex:(NSInteger)index {
+    UICollectionViewCell *cell = [_collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:[NSIndexPath indexPathForItem:index inSection:_dequeueSection]];
+    return cell;
+}
+
+#pragma mark - configure layout
+
+- (void)updateLayout {
+    if (!self.layout) {
+        return;
+    }
+    self.layout.isInfiniteLoop = _isInfiniteLoop;
+    ((TYCyclePagerTransformLayout *)_collectionView.collectionViewLayout).layout = self.layout;
+}
+
+- (void)clearLayout {
+    if (_needClearLayout) {
+        _layout = nil;
+        _needClearLayout = NO;
+    }
+}
+
+- (void)setNeedClearLayout {
+    _needClearLayout = YES;
+}
+
+- (void)setNeedUpdateLayout {
+    if (!self.layout) {
+        return;
+    }
+    [self clearLayout];
+    [self updateLayout];
+    [_collectionView.collectionViewLayout invalidateLayout];
+    [self resetPagerViewAtIndex:_indexSection.index < 0 ? 0 :_indexSection.index];
+}
+
+#pragma mark - pager index
+
+- (BOOL)isValidIndexSection:(TYIndexSection)indexSection {
+    return indexSection.index >= 0 && indexSection.index < _numberOfItems && indexSection.section >= 0 && indexSection.section < kPagerViewMaxSectionCount;
+}
+
+- (TYIndexSection)nearlyIndexPathAtDirection:(TYPagerScrollDirection)direction{
+    return [self nearlyIndexPathForIndexSection:_indexSection direction:direction];
+}
+
+- (TYIndexSection)nearlyIndexPathForIndexSection:(TYIndexSection)indexSection direction:(TYPagerScrollDirection)direction {
+    if (indexSection.index < 0 || indexSection.index >= _numberOfItems) {
+        return indexSection;
+    }
+    
+    if (!_isInfiniteLoop) {
+        if (direction == TYPagerScrollDirectionRight && indexSection.index == _numberOfItems - 1) {
+            return _autoScrollInterval > 0 ? TYMakeIndexSection(0, 0) : indexSection;
+        } else if (direction == TYPagerScrollDirectionRight) {
+            return TYMakeIndexSection(indexSection.index+1, 0);
+        }
+        
+        if (indexSection.index == 0) {
+            return _autoScrollInterval > 0 ? TYMakeIndexSection(_numberOfItems - 1, 0) : indexSection;
+        }
+        return TYMakeIndexSection(indexSection.index-1, 0);
+    }
+    
+    if (direction == TYPagerScrollDirectionRight) {
+        if (indexSection.index < _numberOfItems-1) {
+            return TYMakeIndexSection(indexSection.index+1, indexSection.section);
+        }
+        if (indexSection.section >= kPagerViewMaxSectionCount-1) {
+            return TYMakeIndexSection(indexSection.index, kPagerViewMaxSectionCount-1);
+        }
+        return TYMakeIndexSection(0, indexSection.section+1);
+    }
+    
+    if (indexSection.index > 0) {
+        return TYMakeIndexSection(indexSection.index-1, indexSection.section);
+    }
+    if (indexSection.section <= 0) {
+        return TYMakeIndexSection(indexSection.index, 0);
+    }
+    return TYMakeIndexSection(_numberOfItems-1, indexSection.section-1);
+}
+
+- (TYIndexSection)caculateIndexSectionWithOffsetX:(CGFloat)offsetX {
+    if (_numberOfItems <= 0) {
+        return TYMakeIndexSection(0, 0);
+    }
+     UICollectionViewFlowLayout *layout = (UICollectionViewFlowLayout *)_collectionView.collectionViewLayout;
+    CGFloat leftEdge = _isInfiniteLoop ? _layout.sectionInset.left : _layout.onlyOneSectionInset.left;
+    CGFloat width = CGRectGetWidth(_collectionView.frame);
+    CGFloat middleOffset = offsetX + width/2;
+    CGFloat itemWidth = layout.itemSize.width + layout.minimumInteritemSpacing;
+    NSInteger curIndex = 0;
+    NSInteger curSection = 0;
+    if (middleOffset - leftEdge >= 0) {
+        NSInteger itemIndex = (middleOffset - leftEdge+layout.minimumInteritemSpacing/2)/itemWidth;
+        if (itemIndex < 0) {
+            itemIndex = 0;
+        }else if (itemIndex >= _numberOfItems*kPagerViewMaxSectionCount) {
+            itemIndex = _numberOfItems*kPagerViewMaxSectionCount-1;
+        }
+        curIndex = itemIndex%_numberOfItems;
+        curSection = itemIndex/_numberOfItems;
+    }
+    return TYMakeIndexSection(curIndex, curSection);
+}
+
+- (CGFloat)caculateOffsetXAtIndexSection:(TYIndexSection)indexSection{
+    if (_numberOfItems == 0) {
+        return 0;
+    }
+    UICollectionViewFlowLayout *layout = (UICollectionViewFlowLayout *)_collectionView.collectionViewLayout;
+    UIEdgeInsets edge = _isInfiniteLoop ? _layout.sectionInset : _layout.onlyOneSectionInset;
+    CGFloat leftEdge = edge.left;
+    CGFloat rightEdge = edge.right;
+    CGFloat width = CGRectGetWidth(_collectionView.frame);
+    CGFloat itemWidth = layout.itemSize.width + layout.minimumInteritemSpacing;
+    CGFloat offsetX = 0;
+    if (!_isInfiniteLoop && !_layout.itemHorizontalCenter && indexSection.index == _numberOfItems - 1) {
+        offsetX = leftEdge + itemWidth*(indexSection.index + indexSection.section*_numberOfItems) - (width - itemWidth) -  layout.minimumInteritemSpacing + rightEdge;
+    }else {
+        offsetX = leftEdge + itemWidth*(indexSection.index + indexSection.section*_numberOfItems) - layout.minimumInteritemSpacing/2 - (width - itemWidth)/2;
+    }
+    return MAX(offsetX, 0);
+}
+
+- (void)resetPagerViewAtIndex:(NSInteger)index {
+    if (_didLayout && _firstScrollIndex >= 0) {
+        index = _firstScrollIndex;
+        _firstScrollIndex = -1;
+    }
+    if (index < 0) {
+        return;
+    }
+    if (index >= _numberOfItems) {
+        index = 0;
+    }
+    [self scrollToItemAtIndexSection:TYMakeIndexSection(index, _isInfiniteLoop ? kPagerViewMaxSectionCount/3 : 0) animate:NO];
+    if (!_isInfiniteLoop && _indexSection.index < 0) {
+        [self scrollViewDidScroll:_collectionView];
+    }
+}
+
+- (void)recyclePagerViewIfNeed {
+    if (!_isInfiniteLoop) {
+        return;
+    }
+    if (_indexSection.section > kPagerViewMaxSectionCount - kPagerViewMinSectionCount || _indexSection.section < kPagerViewMinSectionCount) {
+        [self resetPagerViewAtIndex:_indexSection.index];
+    }
+}
+
+#pragma mark - UICollectionViewDataSource
+
+- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
+    return _isInfiniteLoop ? kPagerViewMaxSectionCount : 1;
+}
+
+- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
+    _numberOfItems = [_dataSource numberOfItemsInPagerView:self];
+    return _numberOfItems;
+}
+
+- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
+    _dequeueSection = indexPath.section;
+    if (_dataSourceFlags.cellForItemAtIndex) {
+       return [_dataSource pagerView:self cellForItemAtIndex:indexPath.row];
+    }
+    NSAssert(NO, @"pagerView cellForItemAtIndex: is nil!");
+    return nil;
+}
+
+#pragma mark - UICollectionViewDelegateFlowLayout
+
+- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
+    if (!_isInfiniteLoop) {
+        return _layout.onlyOneSectionInset;
+    }
+    if (section == 0 ) {
+        return _layout.firstSectionInset;
+    }else if (section == kPagerViewMaxSectionCount -1) {
+        return _layout.lastSectionInset;
+    }
+    return _layout.middleSectionInset;
+}
+
+- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
+    UICollectionViewCell *cell = [collectionView cellForItemAtIndexPath:indexPath];
+    if ([_delegate respondsToSelector:@selector(pagerView:didSelectedItemCell:atIndex:)]) {
+        [_delegate pagerView:self didSelectedItemCell:cell atIndex:indexPath.item];
+    }
+    if ([_delegate respondsToSelector:@selector(pagerView:didSelectedItemCell:atIndexSection:)]) {
+        [_delegate pagerView:self didSelectedItemCell:cell atIndexSection:TYMakeIndexSection(indexPath.item, indexPath.section)];
+    }
+}
+
+#pragma mark - UIScrollViewDelegate
+
+- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
+    if (!_didLayout) {
+        return;
+    }
+    TYIndexSection newIndexSection =  [self caculateIndexSectionWithOffsetX:scrollView.contentOffset.x];
+    if (_numberOfItems <= 0 || ![self isValidIndexSection:newIndexSection]) {
+        NSLog(@"inVlaidIndexSection:(%ld,%ld)!",(long)newIndexSection.index,(long)newIndexSection.section);
+        return;
+    }
+    TYIndexSection indexSection = _indexSection;
+    _indexSection = newIndexSection;
+    
+    if (_delegateFlags.pagerViewDidScroll) {
+        [_delegate pagerViewDidScroll:self];
+    }
+    
+    if (_delegateFlags.didScrollFromIndexToNewIndex && !TYEqualIndexSection(_indexSection, indexSection)) {
+        //NSLog(@"curIndex %ld",(long)_indexSection.index);
+        [_delegate pagerView:self didScrollFromIndex:MAX(indexSection.index, 0) toIndex:_indexSection.index];
+    }
+}
+
+- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
+    if (_autoScrollInterval > 0) {
+        [self removeTimer];
+    }
+    _beginDragIndexSection = [self caculateIndexSectionWithOffsetX:scrollView.contentOffset.x];
+    if ([_delegate respondsToSelector:@selector(pagerViewWillBeginDragging:)]) {
+        [_delegate pagerViewWillBeginDragging:self];
+    }
+}
+
+- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
+    if (fabs(velocity.x) < 0.35 || !TYEqualIndexSection(_beginDragIndexSection, _indexSection)) {
+        targetContentOffset->x = [self caculateOffsetXAtIndexSection:_indexSection];
+        return;
+    }
+    TYPagerScrollDirection direction = TYPagerScrollDirectionRight;
+    if ((scrollView.contentOffset.x < 0 && targetContentOffset->x <= 0) || (targetContentOffset->x < scrollView.contentOffset.x && scrollView.contentOffset.x < scrollView.contentSize.width - scrollView.frame.size.width)) {
+        direction = TYPagerScrollDirectionLeft;
+    }
+    TYIndexSection indexSection = [self nearlyIndexPathForIndexSection:_indexSection direction:direction];
+    targetContentOffset->x = [self caculateOffsetXAtIndexSection:indexSection];
+}
+
+- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
+    if (_autoScrollInterval > 0) {
+        [self addTimer];
+    }
+    if ([_delegate respondsToSelector:@selector(pagerViewDidEndDragging:willDecelerate:)]) {
+        [_delegate pagerViewDidEndDragging:self willDecelerate:decelerate];
+    }
+}
+
+- (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView {
+    if ([_delegate respondsToSelector:@selector(pagerViewWillBeginDecelerating:)]) {
+        [_delegate pagerViewWillBeginDecelerating:self];
+    }
+}
+
+- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
+    [self recyclePagerViewIfNeed];
+    if ([_delegate respondsToSelector:@selector(pagerViewDidEndDecelerating:)]) {
+        [_delegate pagerViewDidEndDecelerating:self];
+    }
+}
+
+- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView {
+    [self recyclePagerViewIfNeed];
+    if ([_delegate respondsToSelector:@selector(pagerViewDidEndScrollingAnimation:)]) {
+        [_delegate pagerViewDidEndScrollingAnimation:self];
+    }
+}
+
+#pragma mark - TYCyclePagerTransformLayoutDelegate
+
+- (void)pagerViewTransformLayout:(TYCyclePagerTransformLayout *)pagerViewTransformLayout initializeTransformAttributes:(UICollectionViewLayoutAttributes *)attributes {
+    if (_delegateFlags.initializeTransformAttributes) {
+        [_delegate pagerView:self initializeTransformAttributes:attributes];
+    }
+}
+
+- (void)pagerViewTransformLayout:(TYCyclePagerTransformLayout *)pagerViewTransformLayout applyTransformToAttributes:(UICollectionViewLayoutAttributes *)attributes {
+    if (_delegateFlags.applyTransformToAttributes) {
+        [_delegate pagerView:self applyTransformToAttributes:attributes];
+    }
+}
+
+- (void)layoutSubviews {
+    [super layoutSubviews];
+    BOOL needUpdateLayout = !CGRectEqualToRect(_collectionView.frame, self.bounds);
+    _collectionView.frame = self.bounds;
+    if ((_indexSection.section < 0 || needUpdateLayout) && (_numberOfItems > 0 || _didReloadData)) {
+        _didLayout = YES;
+        [self setNeedUpdateLayout];
+    }
+}
+
+- (void)dealloc {
+    ((TYCyclePagerTransformLayout *)_collectionView.collectionViewLayout).delegate = nil;
+    _collectionView.delegate = nil;
+    _collectionView.dataSource = nil;
+}
+
+@end
+
+

+ 47 - 0
TSLiveWallpaper/Common/ThirdParty/TYCyclePagerView/TYPageControl.h

@@ -0,0 +1,47 @@
+//
+//  TYPageControl.h
+//  TYCyclePagerViewDemo
+//
+//  Created by tany on 2017/6/20.
+//  Copyright © 2017年 tany. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface TYPageControl : UIControl
+
+@property (nonatomic, assign) NSInteger numberOfPages;          // default is 0
+@property (nonatomic, assign) NSInteger currentPage;            // default is 0. value pinned to 0..numberOfPages-1
+
+@property (nonatomic, assign) BOOL hidesForSinglePage;          // hide the the indicator if there is only one page. default is NO
+
+@property (nonatomic, assign) CGFloat pageIndicatorSpaing;
+@property (nonatomic, assign) UIEdgeInsets contentInset; // center will ignore this
+@property (nonatomic, assign ,readonly) CGSize contentSize; // real content size
+
+// override super 
+//@property (nonatomic, assign) UIControlContentVerticalAlignment contentVerticalAlignment;     // how to position content vertically inside control. default is center
+//@property (nonatomic, assign) UIControlContentHorizontalAlignment contentHorizontalAlignment; // how to position content hozontally inside control. default is center
+
+// indicatorTint color
+@property (nullable, nonatomic,strong) UIColor *pageIndicatorTintColor;
+@property (nullable, nonatomic,strong) UIColor *currentPageIndicatorTintColor;
+
+// indicator image
+@property (nullable, nonatomic,strong) UIImage *pageIndicatorImage;
+@property (nullable, nonatomic,strong) UIImage *currentPageIndicatorImage;
+
+@property (nonatomic, assign) UIViewContentMode indicatorImageContentMode; // default is UIViewContentModeCenter
+
+@property (nonatomic, assign) CGSize pageIndicatorSize; // indicator size
+@property (nonatomic, assign) CGSize currentPageIndicatorSize; // default pageIndicatorSize
+
+@property (nonatomic, assign) CGFloat animateDuring; // default 0.3
+
+- (void)setCurrentPage:(NSInteger)currentPage animate:(BOOL)animate;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 285 - 0
TSLiveWallpaper/Common/ThirdParty/TYCyclePagerView/TYPageControl.m

@@ -0,0 +1,285 @@
+//
+//  TYPageControl.m
+//  TYCyclePagerViewDemo
+//
+//  Created by tany on 2017/6/20.
+//  Copyright © 2017年 tany. All rights reserved.
+//
+
+#import "TYPageControl.h"
+
+@interface TYPageControl ()
+// UI
+@property (nonatomic, strong) NSArray<UIImageView *> *indicatorViews;
+
+// Data
+@property (nonatomic, assign) BOOL forceUpdate;
+
+@end
+
+@implementation TYPageControl
+
+#pragma mark - life cycle
+
+- (instancetype)initWithFrame:(CGRect)frame {
+    if (self = [super initWithFrame:frame]) {
+        [self configurePropertys];
+    }
+    return self;
+}
+
+- (instancetype)initWithCoder:(NSCoder *)aDecoder {
+    if (self = [super initWithCoder:aDecoder]) {
+        [self configurePropertys];
+    }
+    return self;
+}
+
+- (void)configurePropertys {
+    self.userInteractionEnabled = NO;
+    _forceUpdate = NO;
+    _animateDuring = 0.3;
+    _pageIndicatorSpaing = 10;
+    _indicatorImageContentMode = UIViewContentModeCenter;
+    _pageIndicatorSize = CGSizeMake(6,6);
+    _currentPageIndicatorSize = _pageIndicatorSize;
+    _pageIndicatorTintColor = [UIColor colorWithRed:128/255. green:128/255. blue:128/255. alpha:1];
+    _currentPageIndicatorTintColor = [UIColor whiteColor];
+}
+
+- (void)willMoveToSuperview:(UIView *)newSuperview {
+    [super willMoveToSuperview:newSuperview];
+    if (newSuperview) {
+        _forceUpdate = YES;
+        [self updateIndicatorViews];
+        _forceUpdate = NO;
+    }
+}
+
+#pragma mark - getter setter
+
+- (CGSize)contentSize {
+    CGFloat width = (_indicatorViews.count - 1) * (_pageIndicatorSize.width + _pageIndicatorSpaing) + _pageIndicatorSize.width + _contentInset.left +_contentInset.right;
+    CGFloat height = _currentPageIndicatorSize.height + _contentInset.top + _contentInset.bottom;
+    return CGSizeMake(width, height);
+}
+
+- (void)setNumberOfPages:(NSInteger)numberOfPages {
+    if (numberOfPages == _numberOfPages) {
+        return;
+    }
+    _numberOfPages = numberOfPages;
+    if (_currentPage >= numberOfPages) {
+        _currentPage = 0;
+    }
+    [self updateIndicatorViews];
+    if (_indicatorViews.count > 0) {
+        [self setNeedsLayout];
+    }
+}
+
+- (void)setCurrentPage:(NSInteger)currentPage {
+    if (_currentPage == currentPage || _indicatorViews.count <= currentPage) {
+        return;
+    }
+    _currentPage = currentPage;
+    if (!CGSizeEqualToSize(_currentPageIndicatorSize, _pageIndicatorSize)) {
+        [self setNeedsLayout];
+    }
+    [self updateIndicatorViewsBehavior];
+    if (self.userInteractionEnabled) {
+        [self sendActionsForControlEvents:UIControlEventValueChanged];
+    }
+}
+
+- (void)setCurrentPage:(NSInteger)currentPage animate:(BOOL)animate {
+    if (animate) {
+        [UIView animateWithDuration:_animateDuring animations:^{
+            [self setCurrentPage:currentPage];
+        }];
+    }else {
+        [self setCurrentPage:currentPage];
+    }
+}
+
+- (void)setPageIndicatorImage:(UIImage *)pageIndicatorImage {
+    _pageIndicatorImage = pageIndicatorImage;
+    [self updateIndicatorViewsBehavior];
+}
+
+- (void)setCurrentPageIndicatorImage:(UIImage *)currentPageIndicatorImage {
+    _currentPageIndicatorImage = currentPageIndicatorImage;
+    [self updateIndicatorViewsBehavior];
+}
+
+- (void)setPageIndicatorTintColor:(UIColor *)pageIndicatorTintColor {
+    _pageIndicatorTintColor = pageIndicatorTintColor;
+    [self updateIndicatorViewsBehavior];
+}
+
+- (void)setCurrentPageIndicatorTintColor:(UIColor *)currentPageIndicatorTintColor {
+    _currentPageIndicatorTintColor = currentPageIndicatorTintColor;
+    [self updateIndicatorViewsBehavior];
+}
+
+- (void)setPageIndicatorSize:(CGSize)pageIndicatorSize {
+    if (CGSizeEqualToSize(_pageIndicatorSize, pageIndicatorSize)) {
+        return;
+    }
+    _pageIndicatorSize = pageIndicatorSize;
+    if (CGSizeEqualToSize(_currentPageIndicatorSize, CGSizeZero) || (_currentPageIndicatorSize.width < pageIndicatorSize.width && _currentPageIndicatorSize.height < pageIndicatorSize.height)) {
+        _currentPageIndicatorSize = pageIndicatorSize;
+    }
+    if (_indicatorViews.count > 0) {
+        [self setNeedsLayout];
+    }
+}
+
+- (void)setPageIndicatorSpaing:(CGFloat)pageIndicatorSpaing {
+    _pageIndicatorSpaing = pageIndicatorSpaing;
+    if (_indicatorViews.count > 0) {
+        [self setNeedsLayout];
+    }
+}
+
+- (void)setCurrentPageIndicatorSize:(CGSize)currentPageIndicatorSize {
+    if (CGSizeEqualToSize(_currentPageIndicatorSize, currentPageIndicatorSize)) {
+        return;
+    }
+    _currentPageIndicatorSize = currentPageIndicatorSize;
+    if (_indicatorViews.count > 0) {
+        [self setNeedsLayout];
+    }
+}
+
+- (void)setContentHorizontalAlignment:(UIControlContentHorizontalAlignment)contentHorizontalAlignment {
+    [super setContentHorizontalAlignment:contentHorizontalAlignment];
+    if (_indicatorViews.count > 0) {
+        [self setNeedsLayout];
+    }
+}
+
+- (void)setContentVerticalAlignment:(UIControlContentVerticalAlignment)contentVerticalAlignment {
+    [super setContentVerticalAlignment:contentVerticalAlignment];
+    if (_indicatorViews.count > 0) {
+        [self setNeedsLayout];
+    }
+}
+
+#pragma mark - update indicator
+
+- (void)updateIndicatorViews {
+    if (!self.superview && !_forceUpdate) {
+        return;
+    }
+    if (_indicatorViews.count == _numberOfPages) {
+        [self updateIndicatorViewsBehavior];
+        return;
+    }
+    NSMutableArray *indicatorViews = _indicatorViews ? [_indicatorViews mutableCopy] :[NSMutableArray array];
+    if (indicatorViews.count < _numberOfPages) {
+        for (NSInteger idx = indicatorViews.count; idx < _numberOfPages; ++idx) {
+            UIImageView *indicatorView = [[UIImageView alloc]init];
+            indicatorView.contentMode = _indicatorImageContentMode;
+            [self addSubview:indicatorView];
+            [indicatorViews addObject:indicatorView];
+        }
+    }else if (indicatorViews.count > _numberOfPages) {
+        for (NSInteger idx = indicatorViews.count - 1; idx >= _numberOfPages; --idx) {
+            UIImageView *indicatorView = indicatorViews[idx];
+            [indicatorView removeFromSuperview];
+            [indicatorViews removeObjectAtIndex:idx];
+        }
+    }
+    _indicatorViews = [indicatorViews copy];
+    [self updateIndicatorViewsBehavior];
+}
+
+- (void)updateIndicatorViewsBehavior {
+    if (_indicatorViews.count == 0 || (!self.superview && !_forceUpdate)) {
+        return;
+    }
+    if (_hidesForSinglePage && _indicatorViews.count == 1) {
+        UIImageView *indicatorView = _indicatorViews.lastObject;
+        indicatorView.hidden = YES;
+        return;
+    }
+    NSInteger index = 0;
+    for (UIImageView *indicatorView in _indicatorViews) {
+        if (_pageIndicatorImage) {
+            indicatorView.contentMode = _indicatorImageContentMode;
+            indicatorView.image = _currentPage == index ? _currentPageIndicatorImage : _pageIndicatorImage;
+        }else {
+            indicatorView.image = nil;
+            indicatorView.backgroundColor = _currentPage == index ? _currentPageIndicatorTintColor : _pageIndicatorTintColor;
+        }
+        indicatorView.hidden = NO;
+        ++index;
+    }
+}
+
+#pragma mark - layout
+
+- (void)layoutIndicatorViews {
+    if (_indicatorViews.count == 0) {
+        return;
+    }
+    CGFloat orignX = 0;
+    CGFloat centerY = 0;
+    CGFloat pageIndicatorSpaing = _pageIndicatorSpaing;
+    switch (self.contentHorizontalAlignment) {
+        case UIControlContentHorizontalAlignmentCenter:
+            // ignore contentInset
+            orignX = (CGRectGetWidth(self.frame) - (_indicatorViews.count - 1) * (_pageIndicatorSize.width + _pageIndicatorSpaing) - _currentPageIndicatorSize.width)/2;
+            break;
+        case UIControlContentHorizontalAlignmentLeft:
+            orignX = _contentInset.left;
+            break;
+        case UIControlContentHorizontalAlignmentRight:
+            orignX = CGRectGetWidth(self.frame) - ((_indicatorViews.count - 1) * (_pageIndicatorSize.width + _pageIndicatorSpaing) + _currentPageIndicatorSize.width) - _contentInset.right;
+            break;
+        case UIControlContentHorizontalAlignmentFill:
+            orignX = _contentInset.left;
+            if (_indicatorViews.count > 1) {
+                pageIndicatorSpaing = (CGRectGetWidth(self.frame) - _contentInset.left - _contentInset.right - _pageIndicatorSize.width - (_indicatorViews.count - 1) * _pageIndicatorSize.width)/(_indicatorViews.count - 1);
+            }
+            break;
+        default:
+            break;
+    }
+    switch (self.contentVerticalAlignment) {
+        case UIControlContentVerticalAlignmentCenter:
+            centerY = CGRectGetHeight(self.frame)/2;
+            break;
+        case UIControlContentVerticalAlignmentTop:
+            centerY = _contentInset.top + _currentPageIndicatorSize.height/2;
+            break;
+        case UIControlContentVerticalAlignmentBottom:
+            centerY = CGRectGetHeight(self.frame) - _currentPageIndicatorSize.height/2 - _contentInset.bottom;
+            break;
+        case UIControlContentVerticalAlignmentFill:
+            centerY = (CGRectGetHeight(self.frame) - _contentInset.top - _contentInset.bottom)/2 + _contentInset.top;
+            break;
+        default:
+            break;
+    }
+    NSInteger index = 0;
+    for (UIImageView *indicatorView in _indicatorViews) {
+        if (_pageIndicatorImage) {
+            indicatorView.layer.cornerRadius = 0;
+        }else {
+            indicatorView.layer.cornerRadius = _currentPage == index ? _currentPageIndicatorSize.height/2 : _pageIndicatorSize.height/2;
+        }
+        CGSize size = index == _currentPage ? _currentPageIndicatorSize : _pageIndicatorSize;
+        indicatorView.frame = CGRectMake(orignX, centerY - size.height/2, size.width, size.height);
+        orignX += size.width + pageIndicatorSpaing;
+        ++index;
+    }
+}
+
+- (void)layoutSubviews {
+    [super layoutSubviews];
+    [self layoutIndicatorViews];
+}
+
+@end