首页开发,tabbar样式开发,网络框架搭建
This commit is contained in:
parent
54b5f08e23
commit
a687f01794
1
Podfile
1
Podfile
@ -27,5 +27,6 @@ target 'Veloria' do
|
|||||||
pod 'HWPanModal' #底部弹出控制器
|
pod 'HWPanModal' #底部弹出控制器
|
||||||
pod 'Kingfisher' #图片加载
|
pod 'Kingfisher' #图片加载
|
||||||
pod "ESTabBarController-swift"
|
pod "ESTabBarController-swift"
|
||||||
|
pod 'WMZPageController' #分页控制器
|
||||||
|
|
||||||
end
|
end
|
||||||
|
@ -16,6 +16,7 @@ PODS:
|
|||||||
- SmartCodable/Core (5.0.9)
|
- SmartCodable/Core (5.0.9)
|
||||||
- SnapKit (5.7.1)
|
- SnapKit (5.7.1)
|
||||||
- Toast (4.1.1)
|
- Toast (4.1.1)
|
||||||
|
- WMZPageController (1.5.5)
|
||||||
- YYKit (1.0.9):
|
- YYKit (1.0.9):
|
||||||
- YYKit/no-arc (= 1.0.9)
|
- YYKit/no-arc (= 1.0.9)
|
||||||
- YYKit/no-arc (1.0.9)
|
- YYKit/no-arc (1.0.9)
|
||||||
@ -33,6 +34,7 @@ DEPENDENCIES:
|
|||||||
- SmartCodable
|
- SmartCodable
|
||||||
- SnapKit
|
- SnapKit
|
||||||
- Toast
|
- Toast
|
||||||
|
- WMZPageController
|
||||||
- YYKit
|
- YYKit
|
||||||
- ZFPlayer/AVPlayer
|
- ZFPlayer/AVPlayer
|
||||||
|
|
||||||
@ -49,6 +51,7 @@ SPEC REPOS:
|
|||||||
- SmartCodable
|
- SmartCodable
|
||||||
- SnapKit
|
- SnapKit
|
||||||
- Toast
|
- Toast
|
||||||
|
- WMZPageController
|
||||||
- YYKit
|
- YYKit
|
||||||
- ZFPlayer
|
- ZFPlayer
|
||||||
|
|
||||||
@ -64,9 +67,10 @@ SPEC CHECKSUMS:
|
|||||||
SmartCodable: 68b3598438181a938eed8ee5623e58ef3e3ea443
|
SmartCodable: 68b3598438181a938eed8ee5623e58ef3e3ea443
|
||||||
SnapKit: d612e99e678a2d3b95bf60b0705ed0a35c03484a
|
SnapKit: d612e99e678a2d3b95bf60b0705ed0a35c03484a
|
||||||
Toast: 1f5ea13423a1e6674c4abdac5be53587ae481c4e
|
Toast: 1f5ea13423a1e6674c4abdac5be53587ae481c4e
|
||||||
|
WMZPageController: 87dd82d1e3528cd362de19b9a74fd6890d6e1906
|
||||||
YYKit: 7cda43304a8dc3696c449041e2cb3107b4e236e7
|
YYKit: 7cda43304a8dc3696c449041e2cb3107b4e236e7
|
||||||
ZFPlayer: 5cf39e8d9f0c2394a014b0db4767b5b5a6bffe13
|
ZFPlayer: 5cf39e8d9f0c2394a014b0db4767b5b5a6bffe13
|
||||||
|
|
||||||
PODFILE CHECKSUM: 41b6b69056545708f3956ded0b47fda298f175b8
|
PODFILE CHECKSUM: e5cac55fd92698becba7d142dc17ec724cd6313f
|
||||||
|
|
||||||
COCOAPODS: 1.16.2
|
COCOAPODS: 1.16.2
|
||||||
|
@ -33,6 +33,36 @@
|
|||||||
1B056E772DDB3641007EE38D /* VPTabBarItemNormalVew.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B056E762DDB3641007EE38D /* VPTabBarItemNormalVew.swift */; };
|
1B056E772DDB3641007EE38D /* VPTabBarItemNormalVew.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B056E762DDB3641007EE38D /* VPTabBarItemNormalVew.swift */; };
|
||||||
1B056E792DDB365A007EE38D /* VPTabBarItemSelectedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B056E782DDB365A007EE38D /* VPTabBarItemSelectedView.swift */; };
|
1B056E792DDB365A007EE38D /* VPTabBarItemSelectedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B056E782DDB365A007EE38D /* VPTabBarItemSelectedView.swift */; };
|
||||||
1B056E7B2DDB37BA007EE38D /* VPGradientView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B056E7A2DDB37BA007EE38D /* VPGradientView.swift */; };
|
1B056E7B2DDB37BA007EE38D /* VPGradientView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B056E7A2DDB37BA007EE38D /* VPGradientView.swift */; };
|
||||||
|
BF0FA6D52DDC5B5D00C9E5F2 /* VPApi.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0FA6D42DDC5B5D00C9E5F2 /* VPApi.swift */; };
|
||||||
|
BF0FA6D72DDC5BE100C9E5F2 /* VPURLPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0FA6D62DDC5BE100C9E5F2 /* VPURLPath.swift */; };
|
||||||
|
BF0FA6DA2DDC5CB600C9E5F2 /* VPLoginManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0FA6D92DDC5CB600C9E5F2 /* VPLoginManager.swift */; };
|
||||||
|
BF0FA6DC2DDC5CD700C9E5F2 /* VPTokenModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0FA6DB2DDC5CD700C9E5F2 /* VPTokenModel.swift */; };
|
||||||
|
BF0FA6DF2DDC5E4D00C9E5F2 /* String+VPAdd.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0FA6DE2DDC5E4D00C9E5F2 /* String+VPAdd.swift */; };
|
||||||
|
BF0FA6EE2DDC5F8700C9E5F2 /* JXUUID.m in Sources */ = {isa = PBXBuildFile; fileRef = BF0FA6EA2DDC5F8700C9E5F2 /* JXUUID.m */; };
|
||||||
|
BF0FA6EF2DDC5F8700C9E5F2 /* PDKeyChain.m in Sources */ = {isa = PBXBuildFile; fileRef = BF0FA6EC2DDC5F8700C9E5F2 /* PDKeyChain.m */; };
|
||||||
|
BF0FA6F12DDC600200C9E5F2 /* VPNetwork.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0FA6F02DDC600200C9E5F2 /* VPNetwork.swift */; };
|
||||||
|
BF0FA6F42DDC604500C9E5F2 /* VPHUD.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0FA6F32DDC604500C9E5F2 /* VPHUD.swift */; };
|
||||||
|
BF0FA6F62DDC616300C9E5F2 /* VPToast.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0FA6F52DDC616300C9E5F2 /* VPToast.swift */; };
|
||||||
|
BF0FA6F92DDC64E700C9E5F2 /* VPHomeAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0FA6F82DDC64E700C9E5F2 /* VPHomeAPI.swift */; };
|
||||||
|
BF0FA6FC2DDC657500C9E5F2 /* VPHomeDataModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0FA6FB2DDC657500C9E5F2 /* VPHomeDataModel.swift */; };
|
||||||
|
BF0FA7002DDC665300C9E5F2 /* VPShortModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0FA6FF2DDC665300C9E5F2 /* VPShortModel.swift */; };
|
||||||
|
BF0FA7022DDC667C00C9E5F2 /* VPVideoInfoModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0FA7012DDC667C00C9E5F2 /* VPVideoInfoModel.swift */; };
|
||||||
|
BF0FA7052DDC67AC00C9E5F2 /* VPHomeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0FA7042DDC67AC00C9E5F2 /* VPHomeViewModel.swift */; };
|
||||||
|
BF0FA7072DDC687C00C9E5F2 /* VPHomeDataItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0FA7062DDC687C00C9E5F2 /* VPHomeDataItem.swift */; };
|
||||||
|
BF0FA70A2DDC69C800C9E5F2 /* VPTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0FA7092DDC69C800C9E5F2 /* VPTableViewCell.swift */; };
|
||||||
|
BF0FA70C2DDC6A3800C9E5F2 /* VPHomeBannerContentCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0FA70B2DDC6A3800C9E5F2 /* VPHomeBannerContentCell.swift */; };
|
||||||
|
BF0FA70E2DDC6ACC00C9E5F2 /* VPHomeItemContentCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0FA70D2DDC6ACC00C9E5F2 /* VPHomeItemContentCell.swift */; };
|
||||||
|
BF0FA7102DDC6CA200C9E5F2 /* VPListModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0FA70F2DDC6CA200C9E5F2 /* VPListModel.swift */; };
|
||||||
|
BF0FA7122DDC6D2C00C9E5F2 /* VPHomeModuleItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0FA7112DDC6D2C00C9E5F2 /* VPHomeModuleItem.swift */; };
|
||||||
|
BF0FA7162DDC78FF00C9E5F2 /* ZKCycleScrollViewFlowLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0FA7142DDC78FF00C9E5F2 /* ZKCycleScrollViewFlowLayout.swift */; };
|
||||||
|
BF0FA7172DDC78FF00C9E5F2 /* ZKCycleScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0FA7132DDC78FF00C9E5F2 /* ZKCycleScrollView.swift */; };
|
||||||
|
BF0FA7192DDC7F4900C9E5F2 /* VPHomeBannerCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0FA7182DDC7F4900C9E5F2 /* VPHomeBannerCell.swift */; };
|
||||||
|
BF0FA71B2DDC7FF200C9E5F2 /* VPImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0FA71A2DDC7FF200C9E5F2 /* VPImageView.swift */; };
|
||||||
|
BF0FA71D2DDC807200C9E5F2 /* UIImageView+VPAdd.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0FA71C2DDC807200C9E5F2 /* UIImageView+VPAdd.swift */; };
|
||||||
|
BF0FA7202DDC83AE00C9E5F2 /* JXButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0FA71E2DDC83AE00C9E5F2 /* JXButton.swift */; };
|
||||||
|
BF0FA7222DDC859D00C9E5F2 /* NSNumber+VPAdd.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0FA7212DDC859D00C9E5F2 /* NSNumber+VPAdd.swift */; };
|
||||||
|
BF0FA7242DDC888F00C9E5F2 /* VPHomeRecommandContentCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0FA7232DDC888F00C9E5F2 /* VPHomeRecommandContentCell.swift */; };
|
||||||
|
BF0FA7262DDC8F7600C9E5F2 /* VPCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0FA7252DDC8F7600C9E5F2 /* VPCollectionView.swift */; };
|
||||||
F939C04AD4003BA127F15C28 /* Pods_Veloria.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 34F57E87E765BF8D72A43DCA /* Pods_Veloria.framework */; };
|
F939C04AD4003BA127F15C28 /* Pods_Veloria.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 34F57E87E765BF8D72A43DCA /* Pods_Veloria.framework */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
@ -70,6 +100,38 @@
|
|||||||
34F57E87E765BF8D72A43DCA /* Pods_Veloria.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Veloria.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
34F57E87E765BF8D72A43DCA /* Pods_Veloria.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Veloria.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
831DCE145EAEF22088BB06E4 /* Pods-Veloria.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Veloria.debug.xcconfig"; path = "Target Support Files/Pods-Veloria/Pods-Veloria.debug.xcconfig"; sourceTree = "<group>"; };
|
831DCE145EAEF22088BB06E4 /* Pods-Veloria.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Veloria.debug.xcconfig"; path = "Target Support Files/Pods-Veloria/Pods-Veloria.debug.xcconfig"; sourceTree = "<group>"; };
|
||||||
AA827CC8ACC3AE70E33A0EEB /* Pods-Veloria.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Veloria.release.xcconfig"; path = "Target Support Files/Pods-Veloria/Pods-Veloria.release.xcconfig"; sourceTree = "<group>"; };
|
AA827CC8ACC3AE70E33A0EEB /* Pods-Veloria.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Veloria.release.xcconfig"; path = "Target Support Files/Pods-Veloria/Pods-Veloria.release.xcconfig"; sourceTree = "<group>"; };
|
||||||
|
BF0FA6D42DDC5B5D00C9E5F2 /* VPApi.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPApi.swift; sourceTree = "<group>"; };
|
||||||
|
BF0FA6D62DDC5BE100C9E5F2 /* VPURLPath.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPURLPath.swift; sourceTree = "<group>"; };
|
||||||
|
BF0FA6D92DDC5CB600C9E5F2 /* VPLoginManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPLoginManager.swift; sourceTree = "<group>"; };
|
||||||
|
BF0FA6DB2DDC5CD700C9E5F2 /* VPTokenModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPTokenModel.swift; sourceTree = "<group>"; };
|
||||||
|
BF0FA6DE2DDC5E4D00C9E5F2 /* String+VPAdd.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+VPAdd.swift"; sourceTree = "<group>"; };
|
||||||
|
BF0FA6E92DDC5F8700C9E5F2 /* JXUUID.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JXUUID.h; sourceTree = "<group>"; };
|
||||||
|
BF0FA6EA2DDC5F8700C9E5F2 /* JXUUID.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JXUUID.m; sourceTree = "<group>"; };
|
||||||
|
BF0FA6EB2DDC5F8700C9E5F2 /* PDKeyChain.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PDKeyChain.h; sourceTree = "<group>"; };
|
||||||
|
BF0FA6EC2DDC5F8700C9E5F2 /* PDKeyChain.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PDKeyChain.m; sourceTree = "<group>"; };
|
||||||
|
BF0FA6F02DDC600200C9E5F2 /* VPNetwork.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNetwork.swift; sourceTree = "<group>"; };
|
||||||
|
BF0FA6F32DDC604500C9E5F2 /* VPHUD.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPHUD.swift; sourceTree = "<group>"; };
|
||||||
|
BF0FA6F52DDC616300C9E5F2 /* VPToast.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPToast.swift; sourceTree = "<group>"; };
|
||||||
|
BF0FA6F82DDC64E700C9E5F2 /* VPHomeAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPHomeAPI.swift; sourceTree = "<group>"; };
|
||||||
|
BF0FA6FB2DDC657500C9E5F2 /* VPHomeDataModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPHomeDataModel.swift; sourceTree = "<group>"; };
|
||||||
|
BF0FA6FF2DDC665300C9E5F2 /* VPShortModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPShortModel.swift; sourceTree = "<group>"; };
|
||||||
|
BF0FA7012DDC667C00C9E5F2 /* VPVideoInfoModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPVideoInfoModel.swift; sourceTree = "<group>"; };
|
||||||
|
BF0FA7042DDC67AC00C9E5F2 /* VPHomeViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPHomeViewModel.swift; sourceTree = "<group>"; };
|
||||||
|
BF0FA7062DDC687C00C9E5F2 /* VPHomeDataItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPHomeDataItem.swift; sourceTree = "<group>"; };
|
||||||
|
BF0FA7092DDC69C800C9E5F2 /* VPTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPTableViewCell.swift; sourceTree = "<group>"; };
|
||||||
|
BF0FA70B2DDC6A3800C9E5F2 /* VPHomeBannerContentCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPHomeBannerContentCell.swift; sourceTree = "<group>"; };
|
||||||
|
BF0FA70D2DDC6ACC00C9E5F2 /* VPHomeItemContentCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPHomeItemContentCell.swift; sourceTree = "<group>"; };
|
||||||
|
BF0FA70F2DDC6CA200C9E5F2 /* VPListModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPListModel.swift; sourceTree = "<group>"; };
|
||||||
|
BF0FA7112DDC6D2C00C9E5F2 /* VPHomeModuleItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPHomeModuleItem.swift; sourceTree = "<group>"; };
|
||||||
|
BF0FA7132DDC78FF00C9E5F2 /* ZKCycleScrollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZKCycleScrollView.swift; sourceTree = "<group>"; };
|
||||||
|
BF0FA7142DDC78FF00C9E5F2 /* ZKCycleScrollViewFlowLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZKCycleScrollViewFlowLayout.swift; sourceTree = "<group>"; };
|
||||||
|
BF0FA7182DDC7F4900C9E5F2 /* VPHomeBannerCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPHomeBannerCell.swift; sourceTree = "<group>"; };
|
||||||
|
BF0FA71A2DDC7FF200C9E5F2 /* VPImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPImageView.swift; sourceTree = "<group>"; };
|
||||||
|
BF0FA71C2DDC807200C9E5F2 /* UIImageView+VPAdd.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImageView+VPAdd.swift"; sourceTree = "<group>"; };
|
||||||
|
BF0FA71E2DDC83AE00C9E5F2 /* JXButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JXButton.swift; sourceTree = "<group>"; };
|
||||||
|
BF0FA7212DDC859D00C9E5F2 /* NSNumber+VPAdd.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSNumber+VPAdd.swift"; sourceTree = "<group>"; };
|
||||||
|
BF0FA7232DDC888F00C9E5F2 /* VPHomeRecommandContentCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPHomeRecommandContentCell.swift; sourceTree = "<group>"; };
|
||||||
|
BF0FA7252DDC8F7600C9E5F2 /* VPCollectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPCollectionView.swift; sourceTree = "<group>"; };
|
||||||
E0BDA3570E00C90877E45AA0 /* Pods-VideoPlayer.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-VideoPlayer.debug.xcconfig"; path = "Target Support Files/Pods-VideoPlayer/Pods-VideoPlayer.debug.xcconfig"; sourceTree = "<group>"; };
|
E0BDA3570E00C90877E45AA0 /* Pods-VideoPlayer.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-VideoPlayer.debug.xcconfig"; path = "Target Support Files/Pods-VideoPlayer/Pods-VideoPlayer.debug.xcconfig"; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
@ -110,7 +172,7 @@
|
|||||||
1B056E372DDAC1EF007EE38D /* Base */,
|
1B056E372DDAC1EF007EE38D /* Base */,
|
||||||
1B056E362DDAC1E8007EE38D /* Class */,
|
1B056E362DDAC1E8007EE38D /* Class */,
|
||||||
1B056E352DDAC1DE007EE38D /* Libs */,
|
1B056E352DDAC1DE007EE38D /* Libs */,
|
||||||
1B056E342DDAC1C7007EE38D /* Thirdparty */,
|
BF0FA6E82DDC5F6F00C9E5F2 /* Thirdparty */,
|
||||||
1B056E332DDAC1AB007EE38D /* Source */,
|
1B056E332DDAC1AB007EE38D /* Source */,
|
||||||
);
|
);
|
||||||
path = Veloria;
|
path = Veloria;
|
||||||
@ -137,16 +199,11 @@
|
|||||||
path = Source;
|
path = Source;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
1B056E342DDAC1C7007EE38D /* Thirdparty */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
);
|
|
||||||
path = Thirdparty;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
1B056E352DDAC1DE007EE38D /* Libs */ = {
|
1B056E352DDAC1DE007EE38D /* Libs */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
BF0FA6F22DDC603200C9E5F2 /* HUD */,
|
||||||
|
BF0FA6D82DDC5C8200C9E5F2 /* Login */,
|
||||||
1B056E652DDAD098007EE38D /* LocalizedManager */,
|
1B056E652DDAD098007EE38D /* LocalizedManager */,
|
||||||
1B056E472DDAC3CB007EE38D /* AppTool */,
|
1B056E472DDAC3CB007EE38D /* AppTool */,
|
||||||
);
|
);
|
||||||
@ -157,6 +214,7 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
1B056E522DDACBF4007EE38D /* Home */,
|
1B056E522DDACBF4007EE38D /* Home */,
|
||||||
|
BF0FA6FD2DDC65F300C9E5F2 /* Player */,
|
||||||
);
|
);
|
||||||
path = Class;
|
path = Class;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -189,6 +247,9 @@
|
|||||||
children = (
|
children = (
|
||||||
1B056E752DDB35C5007EE38D /* TabBar */,
|
1B056E752DDB35C5007EE38D /* TabBar */,
|
||||||
1B056E7A2DDB37BA007EE38D /* VPGradientView.swift */,
|
1B056E7A2DDB37BA007EE38D /* VPGradientView.swift */,
|
||||||
|
BF0FA7092DDC69C800C9E5F2 /* VPTableViewCell.swift */,
|
||||||
|
BF0FA71A2DDC7FF200C9E5F2 /* VPImageView.swift */,
|
||||||
|
BF0FA7252DDC8F7600C9E5F2 /* VPCollectionView.swift */,
|
||||||
);
|
);
|
||||||
path = View;
|
path = View;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -197,6 +258,7 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
1B056E402DDAC30A007EE38D /* VPModel.swift */,
|
1B056E402DDAC30A007EE38D /* VPModel.swift */,
|
||||||
|
BF0FA70F2DDC6CA200C9E5F2 /* VPListModel.swift */,
|
||||||
);
|
);
|
||||||
path = Model;
|
path = Model;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -204,6 +266,7 @@
|
|||||||
1B056E3B2DDAC22D007EE38D /* Networking */ = {
|
1B056E3B2DDAC22D007EE38D /* Networking */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
BF0FA6F72DDC64CF00C9E5F2 /* API */,
|
||||||
1B056E3D2DDAC283007EE38D /* Base */,
|
1B056E3D2DDAC283007EE38D /* Base */,
|
||||||
);
|
);
|
||||||
path = Networking;
|
path = Networking;
|
||||||
@ -221,6 +284,9 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
1B056E3E2DDAC2DB007EE38D /* VPCryptorService.swift */,
|
1B056E3E2DDAC2DB007EE38D /* VPCryptorService.swift */,
|
||||||
|
BF0FA6D42DDC5B5D00C9E5F2 /* VPApi.swift */,
|
||||||
|
BF0FA6D62DDC5BE100C9E5F2 /* VPURLPath.swift */,
|
||||||
|
BF0FA6F02DDC600200C9E5F2 /* VPNetwork.swift */,
|
||||||
);
|
);
|
||||||
path = Base;
|
path = Base;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -233,6 +299,9 @@
|
|||||||
1B056E5A2DDACD80007EE38D /* UIColor+VPAdd.swift */,
|
1B056E5A2DDACD80007EE38D /* UIColor+VPAdd.swift */,
|
||||||
1B056E5C2DDACD8E007EE38D /* UIFont+VPAdd.swift */,
|
1B056E5C2DDACD8E007EE38D /* UIFont+VPAdd.swift */,
|
||||||
1B056E732DDB2DD7007EE38D /* UIView+VPAdd.swift */,
|
1B056E732DDB2DD7007EE38D /* UIView+VPAdd.swift */,
|
||||||
|
BF0FA6DE2DDC5E4D00C9E5F2 /* String+VPAdd.swift */,
|
||||||
|
BF0FA71C2DDC807200C9E5F2 /* UIImageView+VPAdd.swift */,
|
||||||
|
BF0FA7212DDC859D00C9E5F2 /* NSNumber+VPAdd.swift */,
|
||||||
);
|
);
|
||||||
path = Extension;
|
path = Extension;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -249,26 +318,13 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
1B056E552DDACC08007EE38D /* Controller */,
|
1B056E552DDACC08007EE38D /* Controller */,
|
||||||
1B056E542DDACC03007EE38D /* View */,
|
BF0FA7082DDC699A00C9E5F2 /* View */,
|
||||||
1B056E532DDACBFA007EE38D /* Model */,
|
BF0FA6FA2DDC652000C9E5F2 /* Model */,
|
||||||
|
BF0FA7032DDC679B00C9E5F2 /* ViewModel */,
|
||||||
);
|
);
|
||||||
path = Home;
|
path = Home;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
1B056E532DDACBFA007EE38D /* Model */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
);
|
|
||||||
path = Model;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
1B056E542DDACC03007EE38D /* View */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
);
|
|
||||||
path = View;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
1B056E552DDACC08007EE38D /* Controller */ = {
|
1B056E552DDACC08007EE38D /* Controller */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@ -317,6 +373,116 @@
|
|||||||
name = Frameworks;
|
name = Frameworks;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
BF0FA6D82DDC5C8200C9E5F2 /* Login */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
BF0FA6D92DDC5CB600C9E5F2 /* VPLoginManager.swift */,
|
||||||
|
BF0FA6DB2DDC5CD700C9E5F2 /* VPTokenModel.swift */,
|
||||||
|
);
|
||||||
|
path = Login;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
BF0FA6E82DDC5F6F00C9E5F2 /* Thirdparty */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
BF0FA6ED2DDC5F8700C9E5F2 /* JXUUID */,
|
||||||
|
BF0FA7152DDC78FF00C9E5F2 /* ZKCycleScrollView-Swift */,
|
||||||
|
BF0FA71F2DDC83AE00C9E5F2 /* JXButton */,
|
||||||
|
);
|
||||||
|
path = Thirdparty;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
BF0FA6ED2DDC5F8700C9E5F2 /* JXUUID */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
BF0FA6E92DDC5F8700C9E5F2 /* JXUUID.h */,
|
||||||
|
BF0FA6EA2DDC5F8700C9E5F2 /* JXUUID.m */,
|
||||||
|
BF0FA6EB2DDC5F8700C9E5F2 /* PDKeyChain.h */,
|
||||||
|
BF0FA6EC2DDC5F8700C9E5F2 /* PDKeyChain.m */,
|
||||||
|
);
|
||||||
|
path = JXUUID;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
BF0FA6F22DDC603200C9E5F2 /* HUD */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
BF0FA6F32DDC604500C9E5F2 /* VPHUD.swift */,
|
||||||
|
BF0FA6F52DDC616300C9E5F2 /* VPToast.swift */,
|
||||||
|
);
|
||||||
|
path = HUD;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
BF0FA6F72DDC64CF00C9E5F2 /* API */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
BF0FA6F82DDC64E700C9E5F2 /* VPHomeAPI.swift */,
|
||||||
|
);
|
||||||
|
path = API;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
BF0FA6FA2DDC652000C9E5F2 /* Model */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
BF0FA6FB2DDC657500C9E5F2 /* VPHomeDataModel.swift */,
|
||||||
|
BF0FA7062DDC687C00C9E5F2 /* VPHomeDataItem.swift */,
|
||||||
|
BF0FA7112DDC6D2C00C9E5F2 /* VPHomeModuleItem.swift */,
|
||||||
|
);
|
||||||
|
path = Model;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
BF0FA6FD2DDC65F300C9E5F2 /* Player */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
BF0FA6FE2DDC660300C9E5F2 /* Model */,
|
||||||
|
);
|
||||||
|
path = Player;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
BF0FA6FE2DDC660300C9E5F2 /* Model */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
BF0FA6FF2DDC665300C9E5F2 /* VPShortModel.swift */,
|
||||||
|
BF0FA7012DDC667C00C9E5F2 /* VPVideoInfoModel.swift */,
|
||||||
|
);
|
||||||
|
path = Model;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
BF0FA7032DDC679B00C9E5F2 /* ViewModel */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
BF0FA7042DDC67AC00C9E5F2 /* VPHomeViewModel.swift */,
|
||||||
|
);
|
||||||
|
path = ViewModel;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
BF0FA7082DDC699A00C9E5F2 /* View */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
BF0FA70D2DDC6ACC00C9E5F2 /* VPHomeItemContentCell.swift */,
|
||||||
|
BF0FA70B2DDC6A3800C9E5F2 /* VPHomeBannerContentCell.swift */,
|
||||||
|
BF0FA7232DDC888F00C9E5F2 /* VPHomeRecommandContentCell.swift */,
|
||||||
|
BF0FA7182DDC7F4900C9E5F2 /* VPHomeBannerCell.swift */,
|
||||||
|
);
|
||||||
|
path = View;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
BF0FA7152DDC78FF00C9E5F2 /* ZKCycleScrollView-Swift */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
BF0FA7132DDC78FF00C9E5F2 /* ZKCycleScrollView.swift */,
|
||||||
|
BF0FA7142DDC78FF00C9E5F2 /* ZKCycleScrollViewFlowLayout.swift */,
|
||||||
|
);
|
||||||
|
path = "ZKCycleScrollView-Swift";
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
BF0FA71F2DDC83AE00C9E5F2 /* JXButton */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
BF0FA71E2DDC83AE00C9E5F2 /* JXButton.swift */,
|
||||||
|
);
|
||||||
|
path = JXButton;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
/* End PBXGroup section */
|
/* End PBXGroup section */
|
||||||
|
|
||||||
/* Begin PBXNativeTarget section */
|
/* Begin PBXNativeTarget section */
|
||||||
@ -436,8 +602,14 @@
|
|||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
1B056E742DDB2DD7007EE38D /* UIView+VPAdd.swift in Sources */,
|
1B056E742DDB2DD7007EE38D /* UIView+VPAdd.swift in Sources */,
|
||||||
|
BF0FA6F12DDC600200C9E5F2 /* VPNetwork.swift in Sources */,
|
||||||
1B056E572DDACC6B007EE38D /* VPHomePageViewController.swift in Sources */,
|
1B056E572DDACC6B007EE38D /* VPHomePageViewController.swift in Sources */,
|
||||||
|
BF0FA6DF2DDC5E4D00C9E5F2 /* String+VPAdd.swift in Sources */,
|
||||||
|
BF0FA7002DDC665300C9E5F2 /* VPShortModel.swift in Sources */,
|
||||||
1B056E7B2DDB37BA007EE38D /* VPGradientView.swift in Sources */,
|
1B056E7B2DDB37BA007EE38D /* VPGradientView.swift in Sources */,
|
||||||
|
BF0FA7262DDC8F7600C9E5F2 /* VPCollectionView.swift in Sources */,
|
||||||
|
BF0FA71D2DDC807200C9E5F2 /* UIImageView+VPAdd.swift in Sources */,
|
||||||
|
BF0FA6DC2DDC5CD700C9E5F2 /* VPTokenModel.swift in Sources */,
|
||||||
1B056E772DDB3641007EE38D /* VPTabBarItemNormalVew.swift in Sources */,
|
1B056E772DDB3641007EE38D /* VPTabBarItemNormalVew.swift in Sources */,
|
||||||
1B056E4B2DDAC6BA007EE38D /* VPDefine.swift in Sources */,
|
1B056E4B2DDAC6BA007EE38D /* VPDefine.swift in Sources */,
|
||||||
1B056E702DDB019B007EE38D /* VPTabBarItemContainer.swift in Sources */,
|
1B056E702DDB019B007EE38D /* VPTabBarItemContainer.swift in Sources */,
|
||||||
@ -445,19 +617,43 @@
|
|||||||
1B056E2B2DDAC0FD007EE38D /* AppDelegate.swift in Sources */,
|
1B056E2B2DDAC0FD007EE38D /* AppDelegate.swift in Sources */,
|
||||||
1B056E6C2DDADAA1007EE38D /* VPTabBar.swift in Sources */,
|
1B056E6C2DDADAA1007EE38D /* VPTabBar.swift in Sources */,
|
||||||
1B056E5D2DDACD8E007EE38D /* UIFont+VPAdd.swift in Sources */,
|
1B056E5D2DDACD8E007EE38D /* UIFont+VPAdd.swift in Sources */,
|
||||||
|
BF0FA6F92DDC64E700C9E5F2 /* VPHomeAPI.swift in Sources */,
|
||||||
|
BF0FA6F42DDC604500C9E5F2 /* VPHUD.swift in Sources */,
|
||||||
|
BF0FA7222DDC859D00C9E5F2 /* NSNumber+VPAdd.swift in Sources */,
|
||||||
|
BF0FA71B2DDC7FF200C9E5F2 /* VPImageView.swift in Sources */,
|
||||||
1B056E2C2DDAC0FD007EE38D /* SceneDelegate.swift in Sources */,
|
1B056E2C2DDAC0FD007EE38D /* SceneDelegate.swift in Sources */,
|
||||||
1B056E462DDAC370007EE38D /* UIScreen+VPAdd.swift in Sources */,
|
1B056E462DDAC370007EE38D /* UIScreen+VPAdd.swift in Sources */,
|
||||||
|
BF0FA6EE2DDC5F8700C9E5F2 /* JXUUID.m in Sources */,
|
||||||
|
BF0FA7052DDC67AC00C9E5F2 /* VPHomeViewModel.swift in Sources */,
|
||||||
|
BF0FA6EF2DDC5F8700C9E5F2 /* PDKeyChain.m in Sources */,
|
||||||
1B056E4D2DDAC7C1007EE38D /* VPTabBarController.swift in Sources */,
|
1B056E4D2DDAC7C1007EE38D /* VPTabBarController.swift in Sources */,
|
||||||
|
BF0FA6DA2DDC5CB600C9E5F2 /* VPLoginManager.swift in Sources */,
|
||||||
1B056E4F2DDAC7FC007EE38D /* VPNavigationController.swift in Sources */,
|
1B056E4F2DDAC7FC007EE38D /* VPNavigationController.swift in Sources */,
|
||||||
1B056E3F2DDAC2DB007EE38D /* VPCryptorService.swift in Sources */,
|
1B056E3F2DDAC2DB007EE38D /* VPCryptorService.swift in Sources */,
|
||||||
|
BF0FA7202DDC83AE00C9E5F2 /* JXButton.swift in Sources */,
|
||||||
|
BF0FA7072DDC687C00C9E5F2 /* VPHomeDataItem.swift in Sources */,
|
||||||
|
BF0FA70E2DDC6ACC00C9E5F2 /* VPHomeItemContentCell.swift in Sources */,
|
||||||
|
BF0FA6D72DDC5BE100C9E5F2 /* VPURLPath.swift in Sources */,
|
||||||
1B056E5B2DDACD80007EE38D /* UIColor+VPAdd.swift in Sources */,
|
1B056E5B2DDACD80007EE38D /* UIColor+VPAdd.swift in Sources */,
|
||||||
1B056E6A2DDAD0BF007EE38D /* VPLocalizedManager.swift in Sources */,
|
1B056E6A2DDAD0BF007EE38D /* VPLocalizedManager.swift in Sources */,
|
||||||
|
BF0FA7242DDC888F00C9E5F2 /* VPHomeRecommandContentCell.swift in Sources */,
|
||||||
|
BF0FA7192DDC7F4900C9E5F2 /* VPHomeBannerCell.swift in Sources */,
|
||||||
|
BF0FA7022DDC667C00C9E5F2 /* VPVideoInfoModel.swift in Sources */,
|
||||||
1B056E792DDB365A007EE38D /* VPTabBarItemSelectedView.swift in Sources */,
|
1B056E792DDB365A007EE38D /* VPTabBarItemSelectedView.swift in Sources */,
|
||||||
1B056E722DDB022F007EE38D /* VPTabBarItem.swift in Sources */,
|
1B056E722DDB022F007EE38D /* VPTabBarItem.swift in Sources */,
|
||||||
1B056E412DDAC30A007EE38D /* VPModel.swift in Sources */,
|
1B056E412DDAC30A007EE38D /* VPModel.swift in Sources */,
|
||||||
|
BF0FA70A2DDC69C800C9E5F2 /* VPTableViewCell.swift in Sources */,
|
||||||
1B056E442DDAC355007EE38D /* UIDevice+VPAdd.swift in Sources */,
|
1B056E442DDAC355007EE38D /* UIDevice+VPAdd.swift in Sources */,
|
||||||
1B056E512DDACBE5007EE38D /* VPViewController.swift in Sources */,
|
1B056E512DDACBE5007EE38D /* VPViewController.swift in Sources */,
|
||||||
|
BF0FA7122DDC6D2C00C9E5F2 /* VPHomeModuleItem.swift in Sources */,
|
||||||
|
BF0FA7162DDC78FF00C9E5F2 /* ZKCycleScrollViewFlowLayout.swift in Sources */,
|
||||||
|
BF0FA7172DDC78FF00C9E5F2 /* ZKCycleScrollView.swift in Sources */,
|
||||||
|
BF0FA6D52DDC5B5D00C9E5F2 /* VPApi.swift in Sources */,
|
||||||
|
BF0FA6FC2DDC657500C9E5F2 /* VPHomeDataModel.swift in Sources */,
|
||||||
|
BF0FA6F62DDC616300C9E5F2 /* VPToast.swift in Sources */,
|
||||||
1B056E492DDAC3DF007EE38D /* VPAppTool.swift in Sources */,
|
1B056E492DDAC3DF007EE38D /* VPAppTool.swift in Sources */,
|
||||||
|
BF0FA70C2DDC6A3800C9E5F2 /* VPHomeBannerContentCell.swift in Sources */,
|
||||||
|
BF0FA7102DDC6CA200C9E5F2 /* VPListModel.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
@ -51,9 +51,11 @@ class VPTabBarController: UITabBarController {
|
|||||||
tabBar.delegate = self
|
tabBar.delegate = self
|
||||||
self.setValue(tabBar, forKey: "tabBar")
|
self.setValue(tabBar, forKey: "tabBar")
|
||||||
|
|
||||||
|
view.backgroundColor = .color000000()
|
||||||
|
|
||||||
// ESTabBarController
|
// ESTabBarController
|
||||||
vp_setup()
|
vp_setup()
|
||||||
// self.tabBar.isTranslucent = false
|
self.tabBar.isTranslucent = true
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,9 +77,10 @@ extension VPTabBarController {
|
|||||||
let appearance = UITabBarAppearance()
|
let appearance = UITabBarAppearance()
|
||||||
|
|
||||||
appearance.backgroundImage = UIImage(color: .clear)
|
appearance.backgroundImage = UIImage(color: .clear)
|
||||||
appearance.backgroundColor = .red
|
appearance.backgroundColor = .colorFFFFFF(alpha: 0.1)
|
||||||
appearance.shadowImage = UIImage()
|
appearance.shadowImage = UIImage()
|
||||||
appearance.shadowColor = .clear
|
appearance.shadowColor = .clear
|
||||||
|
appearance.backgroundEffect = UIBlurEffect(style: .dark)
|
||||||
tabBar.standardAppearance = appearance
|
tabBar.standardAppearance = appearance
|
||||||
tabBar.scrollEdgeAppearance = appearance
|
tabBar.scrollEdgeAppearance = appearance
|
||||||
|
|
||||||
|
@ -9,11 +9,22 @@ import UIKit
|
|||||||
|
|
||||||
class VPViewController: UIViewController {
|
class VPViewController: UIViewController {
|
||||||
|
|
||||||
|
private(set) lazy var bgImageView: UIImageView = {
|
||||||
|
let imageView = UIImageView(image: UIImage(named: "bg_image_01"))
|
||||||
|
return imageView
|
||||||
|
}()
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
||||||
view.backgroundColor = .backgroundColor()
|
view.backgroundColor = .backgroundColor()
|
||||||
|
|
||||||
|
|
||||||
|
view.addSubview(bgImageView)
|
||||||
|
|
||||||
|
bgImageView.snp.makeConstraints { make in
|
||||||
|
make.edges.equalToSuperview()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -8,6 +8,18 @@
|
|||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
|
|
||||||
|
//MARK:-------------- 系统版本号 --------------
|
||||||
|
///当前系统版本号
|
||||||
|
let kVPOsVersion: String = UIDevice.current.systemVersion
|
||||||
|
let kVPAPPBundleIdentifier: String = (Bundle.main.infoDictionary!["CFBundleIdentifier"] as? String) ?? "0"
|
||||||
|
|
||||||
|
///app版本号
|
||||||
|
public let kVPAPPVersion: String = (Bundle.main.infoDictionary!["CFBundleShortVersionString"] as? String) ?? "0"
|
||||||
|
public let kVPAPPBundleVersion: String = (Bundle.main.infoDictionary!["CFBundleVersion"] as? String) ?? "0"
|
||||||
|
|
||||||
|
public let kVPAPPBundleName: String = (Bundle.main.infoDictionary!["CFBundleName"] as? String) ?? ""
|
||||||
|
public let kVPAPPName: String = (Bundle.main.infoDictionary!["CFBundleDisplayName"] as? String) ?? ""
|
||||||
|
|
||||||
|
|
||||||
//MARK: ------- 打印信息 ----------
|
//MARK: ------- 打印信息 ----------
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
|
25
Veloria/Base/Extension/NSNumber+VPAdd.swift
Normal file
25
Veloria/Base/Extension/NSNumber+VPAdd.swift
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
//
|
||||||
|
// NSNumber+VPAdd.swift
|
||||||
|
// Veloria
|
||||||
|
//
|
||||||
|
// Created by Veloria on 2025/5/20.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
extension NSNumber {
|
||||||
|
|
||||||
|
func toString(maximumFractionDigits: Int = 10, minimumFractionDigits: Int? = nil, roundingMode: NumberFormatter.RoundingMode? = nil) -> String {
|
||||||
|
let formatter = NumberFormatter()
|
||||||
|
formatter.minimumIntegerDigits = 1
|
||||||
|
formatter.maximumFractionDigits = maximumFractionDigits
|
||||||
|
if let minimumFractionDigits = minimumFractionDigits {
|
||||||
|
formatter.minimumFractionDigits = minimumFractionDigits
|
||||||
|
}
|
||||||
|
if let roundingMode = roundingMode {
|
||||||
|
formatter.roundingMode = roundingMode
|
||||||
|
}
|
||||||
|
formatter.numberStyle = .none
|
||||||
|
return formatter.string(from: self) ?? "0"
|
||||||
|
}
|
||||||
|
}
|
33
Veloria/Base/Extension/String+VPAdd.swift
Normal file
33
Veloria/Base/Extension/String+VPAdd.swift
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
//
|
||||||
|
// String+VPAdd.swift
|
||||||
|
// Veloria
|
||||||
|
//
|
||||||
|
// Created by Veloria on 2025/5/20.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import SmartCodable
|
||||||
|
|
||||||
|
extension String: SmartCodable {
|
||||||
|
func length() -> Int {
|
||||||
|
return self.ocString().length
|
||||||
|
}
|
||||||
|
|
||||||
|
func ocString() -> NSString {
|
||||||
|
return self as NSString
|
||||||
|
}
|
||||||
|
|
||||||
|
static func timeZone() -> String {
|
||||||
|
let timeZone = NSTimeZone.local as NSTimeZone
|
||||||
|
let timeZoneSecondsFromGMT = timeZone.secondsFromGMT / 3600
|
||||||
|
return String(format: "GMT+0%d:00", timeZoneSecondsFromGMT)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension String {
|
||||||
|
///获取文字Size
|
||||||
|
func size(font: UIFont, size: CGSize = CGSize(width: CGFloat(MAXFLOAT), height: CGFloat(MAXFLOAT))) -> CGSize{
|
||||||
|
let string: NSString = self as NSString
|
||||||
|
return string.size(for: font, size: size, mode: .byWordWrapping)
|
||||||
|
}
|
||||||
|
}
|
@ -33,4 +33,12 @@ extension UIColor {
|
|||||||
static func color353537(alpha: CGFloat = 1) -> UIColor {
|
static func color353537(alpha: CGFloat = 1) -> UIColor {
|
||||||
return UIColor(rgb: 0x353537, alpha: alpha)
|
return UIColor(rgb: 0x353537, alpha: alpha)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static func color828282(alpha: CGFloat = 1) -> UIColor {
|
||||||
|
return UIColor(rgb: 0x828282, alpha: alpha)
|
||||||
|
}
|
||||||
|
|
||||||
|
static func colorAFAFAF(alpha: CGFloat = 1) -> UIColor {
|
||||||
|
return UIColor(rgb: 0xAFAFAF, alpha: alpha)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,3 +8,67 @@
|
|||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
|
|
||||||
|
extension UIDevice {
|
||||||
|
|
||||||
|
//http://theiphonewiki.com/wiki/Models
|
||||||
|
static func sp_machineModelName() -> String {
|
||||||
|
guard let machineModel = UIDevice.current.machineModel else { return "" }
|
||||||
|
let map = [
|
||||||
|
"iPhone1,1" : "iPhone",
|
||||||
|
"iPhone1,2" : "iPhone 3G",
|
||||||
|
"iPhone2,1" : "iPhone 3GS",
|
||||||
|
"iPhone3,1" : "iPhone 4",
|
||||||
|
"iPhone3,2" : "iPhone 4",
|
||||||
|
"iPhone3,3" : "iPhone 4",
|
||||||
|
"iPhone4,1" : "iPhone4,1",
|
||||||
|
"iPhone5,1" : "iPhone 5",
|
||||||
|
"iPhone5,2" : "iPhone 5",
|
||||||
|
"iPhone5,3" : "iPhone 5c",
|
||||||
|
"iPhone5,4" : "iPhone 5c",
|
||||||
|
"iPhone6,1" : "iPhone 5s",
|
||||||
|
"iPhone6,2" : "iPhone 5s",
|
||||||
|
"iPhone7,2" : "iPhone 6",
|
||||||
|
"iPhone7,1" : "iPhone 6 Plus",
|
||||||
|
"iPhone8,1" : "iPhone 6s",
|
||||||
|
"iPhone8,2" : "iPhone 6s Plus",
|
||||||
|
"iPhone8,4" : "iPhone SE (1st generation)",
|
||||||
|
"iPhone9,1" : "iPhone 7",
|
||||||
|
"iPhone9,3" : "iPhone 7",
|
||||||
|
"iPhone9,2" : "iPhone 7 Plus",
|
||||||
|
"iPhone9,4" : "iPhone 7 Plus",
|
||||||
|
"iPhone10,1" : "iPhone 8",
|
||||||
|
"iPhone10,4" : "iPhone 8",
|
||||||
|
"iPhone10,2" : "iPhone 8 Plus",
|
||||||
|
"iPhone10,5" : "iPhone 8 Plus",
|
||||||
|
"iPhone10,3" : "iPhone X",
|
||||||
|
"iPhone10,6" : "iPhone X",
|
||||||
|
"iPhone11,8" : "iPhone XR",
|
||||||
|
"iPhone11,2" : "iPhone11,2",
|
||||||
|
"iPhone11,6" : "iPhone XS Max",
|
||||||
|
"iPhone11,4" : "iPhone XS Max",
|
||||||
|
"iPhone12,1" : "iPhone 11",
|
||||||
|
"iPhone12,3" : "iPhone 11 Pro",
|
||||||
|
"iPhone12,5" : "iPhone 11 Pro Max",
|
||||||
|
"iPhone12,8" : "iPhone SE (2nd generation)",
|
||||||
|
"iPhone13,1" : "iPhone 12 mini",
|
||||||
|
"iPhone13,2" : "iPhone13,2",
|
||||||
|
"iPhone13,3" : "iPhone 12 Pro",
|
||||||
|
"iPhone13,4" : "iPhone 12 Pro Max",
|
||||||
|
"iPhone14,4" : "iPhone 13 mini",
|
||||||
|
"iPhone14,5" : "iPhone 13",
|
||||||
|
"iPhone14,2" : "iPhone 13 Pro",
|
||||||
|
"iPhone14,3" : "iPhone 13 Pro Max",
|
||||||
|
"iPhone14,6" : "iPhone SE (3rd generation)",
|
||||||
|
"iPhone14,7" : "iPhone 14",
|
||||||
|
"iPhone14,8" : "iPhone 14 Plus",
|
||||||
|
"iPhone15,2" : "iPhone 14 Pro",
|
||||||
|
"iPhone15,3" : "iPhone 14 Pro Max",
|
||||||
|
]
|
||||||
|
if let name = map[machineModel] {
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
return machineModel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
24
Veloria/Base/Extension/UIImageView+VPAdd.swift
Normal file
24
Veloria/Base/Extension/UIImageView+VPAdd.swift
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
//
|
||||||
|
// UIImageView+VPAdd.swift
|
||||||
|
// Veloria
|
||||||
|
//
|
||||||
|
// Created by Veloria on 2025/5/20.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import Kingfisher
|
||||||
|
|
||||||
|
extension UIImageView {
|
||||||
|
func vp_setImage(url: String?, placeholder: UIImage? = nil, completer: ((_ image: UIImage?, _ url: URL?) -> Void)? = nil) {
|
||||||
|
|
||||||
|
self.kf.setImage(with: URL(string: url ?? ""), placeholder: placeholder, options: nil) { result in
|
||||||
|
switch result {
|
||||||
|
case .success(let value):
|
||||||
|
completer?(value.image, value.source.url)
|
||||||
|
default :
|
||||||
|
completer?(nil, nil)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
22
Veloria/Base/Model/VPListModel.swift
Normal file
22
Veloria/Base/Model/VPListModel.swift
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
//
|
||||||
|
// VPListModel.swift
|
||||||
|
// Veloria
|
||||||
|
//
|
||||||
|
// Created by Veloria on 2025/5/20.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import SmartCodable
|
||||||
|
|
||||||
|
class VPListModel<T: SmartCodable>: VPModel, SmartCodable {
|
||||||
|
var list: [T]?
|
||||||
|
var pagination: VPListPaginationModel?
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class VPListPaginationModel: VPModel, SmartCodable {
|
||||||
|
var current_page: Int?
|
||||||
|
var page_size: Int?
|
||||||
|
var page_total: Int?
|
||||||
|
var total_size: Int?
|
||||||
|
}
|
@ -9,7 +9,7 @@ import UIKit
|
|||||||
|
|
||||||
class VPModel: NSObject {
|
class VPModel: NSObject {
|
||||||
|
|
||||||
override init() {
|
required override init() {
|
||||||
super.init()
|
super.init()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
22
Veloria/Base/Networking/API/VPHomeAPI.swift
Normal file
22
Veloria/Base/Networking/API/VPHomeAPI.swift
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
//
|
||||||
|
// VPHomeAPI.swift
|
||||||
|
// Veloria
|
||||||
|
//
|
||||||
|
// Created by Veloria on 2025/5/20.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class VPHomeAPI: NSObject {
|
||||||
|
|
||||||
|
///首页数据
|
||||||
|
static func requestHomeData(completer: ((_ list: [VPHomeModuleItem]?) -> Void)?) {
|
||||||
|
var param = VPNetworkParameters(path: "/home/all-modules")
|
||||||
|
param.method = .get
|
||||||
|
|
||||||
|
|
||||||
|
VPNetwork.request(parameters: param) { (response: VPNetworkResponse<VPListModel<VPHomeModuleItem>>) in
|
||||||
|
completer?(response.data?.list)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
116
Veloria/Base/Networking/Base/VPApi.swift
Normal file
116
Veloria/Base/Networking/Base/VPApi.swift
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
//
|
||||||
|
// VPApi.swift
|
||||||
|
// Veloria
|
||||||
|
//
|
||||||
|
// Created by Veloria on 2025/5/20.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import Moya
|
||||||
|
import SmartCodable
|
||||||
|
|
||||||
|
struct VPNetworkData<T: SmartCodable> {
|
||||||
|
var parameters: VPNetworkParameters?
|
||||||
|
var completion: ((_ response: VPNetworkResponse<T>) -> Void)?
|
||||||
|
}
|
||||||
|
|
||||||
|
struct VPNetworkParameters {
|
||||||
|
var baseURL: URL?
|
||||||
|
var parameters: [String : Any]?
|
||||||
|
var method: Moya.Method = .post
|
||||||
|
var path: String
|
||||||
|
var isLoding: Bool = false
|
||||||
|
var isToast: Bool = true
|
||||||
|
}
|
||||||
|
|
||||||
|
struct VPNetworkResponse<T: SmartCodable>: SmartCodable {
|
||||||
|
var code: Int?
|
||||||
|
var data: T?
|
||||||
|
var msg: String?
|
||||||
|
|
||||||
|
///原始数据
|
||||||
|
@IgnoredKey
|
||||||
|
var rawData: Any?
|
||||||
|
}
|
||||||
|
|
||||||
|
enum VPApi {
|
||||||
|
case request(parameters: VPNetworkParameters)
|
||||||
|
case uploadFile(parameters: VPNetworkParameters)
|
||||||
|
case downloadFile(parameters: VPNetworkParameters)
|
||||||
|
}
|
||||||
|
|
||||||
|
extension VPApi: TargetType {
|
||||||
|
|
||||||
|
|
||||||
|
var baseURL: URL {
|
||||||
|
return URL(string: VPBaseURL)!
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var path: String {
|
||||||
|
switch self {
|
||||||
|
case .request(let parameters):
|
||||||
|
return VPURLPathPrefix + parameters.path
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var method: Moya.Method {
|
||||||
|
switch self {
|
||||||
|
case .request(let parameters):
|
||||||
|
return parameters.method
|
||||||
|
default:
|
||||||
|
return .post
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var task: Moya.Task {
|
||||||
|
switch self {
|
||||||
|
case .request(let parameters):
|
||||||
|
let p = parameters.parameters ?? [:]
|
||||||
|
return .requestParameters(parameters: p, encoding: getEncoding())
|
||||||
|
default:
|
||||||
|
return .requestParameters(parameters: [:], encoding: getEncoding())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var headers: [String : String]? {
|
||||||
|
let userToken = VPLoginManager.manager.token?.token ?? ""
|
||||||
|
|
||||||
|
var dic: [String : String] = [
|
||||||
|
"system-version" : kVPOsVersion,
|
||||||
|
"lang-key" : VPLocalizedManager.shared.currentLocalizedKey,//当前语言
|
||||||
|
"time-zone" : String.timeZone(), //时区
|
||||||
|
"app-version" : kVPAPPVersion,
|
||||||
|
// "device-id" : JXUUID.systemUUID(), //设备id
|
||||||
|
"device-id" : JXUUID.uuid(), //设备id
|
||||||
|
"brand" : "apple", //品牌
|
||||||
|
"app-name" : "",
|
||||||
|
"system-type" : "ios",
|
||||||
|
"idfa" : JXUUID.idfa(),
|
||||||
|
"model" : UIDevice.sp_machineModelName(),
|
||||||
|
// "security" : "false",
|
||||||
|
]
|
||||||
|
//登录信息
|
||||||
|
dic["authorization"] = userToken
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return dic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension TargetType {
|
||||||
|
var sampleData: Data { return "".data(using: String.Encoding.utf8)! }
|
||||||
|
|
||||||
|
func getEncoding() -> ParameterEncoding {
|
||||||
|
switch self.method {
|
||||||
|
case .get, .delete:
|
||||||
|
return URLEncoding.default
|
||||||
|
default:
|
||||||
|
return JSONEncoding.default
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
224
Veloria/Base/Networking/Base/VPNetwork.swift
Normal file
224
Veloria/Base/Networking/Base/VPNetwork.swift
Normal file
@ -0,0 +1,224 @@
|
|||||||
|
//
|
||||||
|
// VPNetwork.swift
|
||||||
|
// Veloria
|
||||||
|
//
|
||||||
|
// Created by Veloria on 2025/5/20.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import Moya
|
||||||
|
import SmartCodable
|
||||||
|
|
||||||
|
///获取数据成功
|
||||||
|
let VPNetworkCodeSucceed = 200
|
||||||
|
|
||||||
|
class VPNetwork: NSObject {
|
||||||
|
|
||||||
|
///用来获取token的线程
|
||||||
|
private static let operationQueue = OperationQueue()
|
||||||
|
private static var tokenOperation: BlockOperation?
|
||||||
|
|
||||||
|
static let provider = MoyaProvider<VPApi>(requestClosure: CustomApiTimeoutClosure)
|
||||||
|
|
||||||
|
static func request<T>(parameters: VPNetworkParameters, completion: ((_ response: VPNetworkResponse<T>) -> Void)?) {
|
||||||
|
|
||||||
|
if VPLoginManager.manager.token == nil {
|
||||||
|
self.requestToken(completer: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if let tokenOperation = self.tokenOperation, parameters.path != "/customer/register" {
|
||||||
|
|
||||||
|
let requestOperation = BlockOperation {
|
||||||
|
let semaphore = DispatchSemaphore(value: 0)
|
||||||
|
_request(parameters: parameters) { (response: VPNetworkResponse<T>) in
|
||||||
|
semaphore.signal()
|
||||||
|
completion?(response)
|
||||||
|
}
|
||||||
|
semaphore.wait()
|
||||||
|
}
|
||||||
|
///设置依赖关系
|
||||||
|
requestOperation.addDependency(tokenOperation)
|
||||||
|
|
||||||
|
operationQueue.addOperation(requestOperation)
|
||||||
|
} else {
|
||||||
|
_request(parameters: parameters, completion: completion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@discardableResult
|
||||||
|
static func _request<T>(parameters: VPNetworkParameters, completion: ((_ response: VPNetworkResponse<T>) -> Void)?) -> Cancellable {
|
||||||
|
|
||||||
|
if parameters.isLoding {
|
||||||
|
VPHUD.show()
|
||||||
|
}
|
||||||
|
return provider.requestCustomJson(.request(parameters: parameters)) { (result) in
|
||||||
|
|
||||||
|
if parameters.isLoding {
|
||||||
|
VPHUD.dismiss()
|
||||||
|
}
|
||||||
|
guard let completion = completion else {return}
|
||||||
|
|
||||||
|
|
||||||
|
_resultDispose(parameters: parameters, result: result, completion: completion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static func _resultDispose<T>(parameters: VPNetworkParameters, result: Result<Moya.Response, MoyaError>, completion: ((_ response: VPNetworkResponse<T>) -> Void)?) {
|
||||||
|
|
||||||
|
switch result {
|
||||||
|
case .success(let response):
|
||||||
|
let code = response.statusCode
|
||||||
|
if code == 401 || code == 402 || code == 403 {
|
||||||
|
|
||||||
|
if parameters.path == "/customer/register" {
|
||||||
|
var res = VPNetworkResponse<T>()
|
||||||
|
res.code = -1
|
||||||
|
if parameters.isToast {
|
||||||
|
VPToast.show(text: "movia_error".localized)
|
||||||
|
}
|
||||||
|
completion?(res)
|
||||||
|
} else {
|
||||||
|
///重新获取token
|
||||||
|
self.requestToken(completer: nil)
|
||||||
|
|
||||||
|
///将请求失败数据重新请求
|
||||||
|
if let tokenOperation = self.tokenOperation, parameters.path != "/customer/register" {
|
||||||
|
|
||||||
|
let requestOperation = BlockOperation {
|
||||||
|
let semaphore = DispatchSemaphore(value: 0)
|
||||||
|
_request(parameters: parameters) { (response: VPNetworkResponse<T>) in
|
||||||
|
semaphore.signal()
|
||||||
|
completion?(response)
|
||||||
|
}
|
||||||
|
semaphore.wait()
|
||||||
|
}
|
||||||
|
///设置依赖关系
|
||||||
|
requestOperation.addDependency(tokenOperation)
|
||||||
|
|
||||||
|
operationQueue.addOperation(requestOperation)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
let tempData = try response.mapString()
|
||||||
|
vpLog(message: parameters.parameters)
|
||||||
|
vpLog(message: parameters.path)
|
||||||
|
|
||||||
|
|
||||||
|
DispatchQueue.global().async {
|
||||||
|
let response: VPNetworkResponse<T> = _deserialize(data: tempData)
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
if response.code != VPNetworkCodeSucceed {
|
||||||
|
if parameters.isToast {
|
||||||
|
VPToast.show(text: response.msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
completion?(response)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch {
|
||||||
|
var res = VPNetworkResponse<T>()
|
||||||
|
res.code = -1
|
||||||
|
if parameters.isToast {
|
||||||
|
VPToast.show(text: "movia_error".localized)
|
||||||
|
}
|
||||||
|
completion?(res)
|
||||||
|
}
|
||||||
|
case .failure(let error):
|
||||||
|
vpLog(message: error)
|
||||||
|
var res = VPNetworkResponse<T>()
|
||||||
|
res.code = -1
|
||||||
|
if parameters.isToast {
|
||||||
|
VPToast.show(text: "movia_network_toast_01".localized)
|
||||||
|
}
|
||||||
|
completion?(res)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
///解析数据
|
||||||
|
static private func _deserialize<T>(data: String) -> VPNetworkResponse<T> {
|
||||||
|
var response: VPNetworkResponse<T>?
|
||||||
|
|
||||||
|
let time = Date().timeIntervalSince1970
|
||||||
|
let decrypted = VPCryptorService.decrypt(data: data)
|
||||||
|
vpLog(message: decrypted)
|
||||||
|
response = VPNetworkResponse<T>.deserialize(from: decrypted)
|
||||||
|
response?.rawData = decrypted
|
||||||
|
|
||||||
|
vpLog(message: Date().timeIntervalSince1970 - time)
|
||||||
|
|
||||||
|
if let response = response {
|
||||||
|
return response
|
||||||
|
} else {
|
||||||
|
var response = VPNetworkResponse<T>()
|
||||||
|
response.code = -1
|
||||||
|
response.msg = "movia_error".localized
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension VPNetwork {
|
||||||
|
///获取token
|
||||||
|
static func requestToken(completer: ((_ token: VPTokenModel?) -> Void)?) {
|
||||||
|
guard self.tokenOperation == nil else {
|
||||||
|
completer?(nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
self.tokenOperation = BlockOperation(block: {
|
||||||
|
let semaphore = DispatchSemaphore(value: 0)
|
||||||
|
let param = VPNetworkParameters(path: "/customer/register")
|
||||||
|
VPNetwork.request(parameters: param) { (response: VPNetworkResponse<VPTokenModel>) in
|
||||||
|
if let token = response.data {
|
||||||
|
VPLoginManager.manager.setLoginToken(token: token)
|
||||||
|
}
|
||||||
|
do { semaphore.signal() }
|
||||||
|
self.tokenOperation = nil
|
||||||
|
completer?(response.data)
|
||||||
|
}
|
||||||
|
semaphore.wait()
|
||||||
|
})
|
||||||
|
operationQueue.addOperation(self.tokenOperation!)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension MoyaProvider {
|
||||||
|
|
||||||
|
@discardableResult
|
||||||
|
func requestCustomJson(_ target: Target, callbackQueue: DispatchQueue? = nil, completion: Completion?) -> Cancellable {
|
||||||
|
return request(target, callbackQueue: callbackQueue) { (result) in
|
||||||
|
guard let completion = completion else {return}
|
||||||
|
completion(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let CustomApiTimeoutClosure = {(endpoint: Endpoint, closure: MoyaProvider<VPApi>.RequestResultClosure) -> Void in
|
||||||
|
|
||||||
|
if var urlRequest = try? endpoint.urlRequest() {
|
||||||
|
///总是获取新数据
|
||||||
|
urlRequest.cachePolicy = .reloadIgnoringCacheData
|
||||||
|
urlRequest.timeoutInterval = 30
|
||||||
|
closure(.success(urlRequest))
|
||||||
|
} else {
|
||||||
|
closure(.failure(MoyaError.requestMapping(endpoint.url)))
|
||||||
|
}
|
||||||
|
|
||||||
|
#if DEBUG ///
|
||||||
|
//print(try? endpoint.urlRequest() )
|
||||||
|
#endif
|
||||||
|
}
|
46
Veloria/Base/Networking/Base/VPURLPath.swift
Normal file
46
Veloria/Base/Networking/Base/VPURLPath.swift
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
//
|
||||||
|
// VPURLPath.swift
|
||||||
|
// Veloria
|
||||||
|
//
|
||||||
|
// Created by Veloria on 2025/5/20.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
let VPBaseURL = "https://api-qjwl168.qjwl168.com"
|
||||||
|
let VPURLPathPrefix = "/velo"
|
||||||
|
|
||||||
|
let VPWebBaseURL = "https://www.thimratv.com"
|
||||||
|
let VPCampaignWebURL = "https://campaign.moviatv.com"
|
||||||
|
#else
|
||||||
|
let VPBaseURL = "https://api-qjwl168.qjwl168.com"
|
||||||
|
let VPURLPathPrefix = "/velo"
|
||||||
|
|
||||||
|
let VPWebBaseURL = "https://www.thimratv.com"
|
||||||
|
let VPCampaignWebURL = "https://campaign.moviatv.com"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
///用户协议
|
||||||
|
let VPUserAgreementWebUrl = VPWebBaseURL + "/user_policy"
|
||||||
|
///隐私协议
|
||||||
|
let VPPrivacyPolicyWebUrl = VPWebBaseURL + "/private"
|
||||||
|
///儿童个人信息保护规则
|
||||||
|
let VPInformationProtectionWebUrl = VPWebBaseURL + "/information_protection"
|
||||||
|
///第三方共享清单
|
||||||
|
let VPInformationSharingWebUrl = VPWebBaseURL + "/information_sharing"
|
||||||
|
///收集个人信息明示清单
|
||||||
|
let VPPersoInforDisclosureWebUrl = VPWebBaseURL + "/persoInfor_disclosure"
|
||||||
|
///全国青少年互联网文明公约
|
||||||
|
let VPCivizatioConventionWebUrl = VPWebBaseURL + "/civizatio_convention"
|
||||||
|
///会员服务协议
|
||||||
|
let VPMemberShipAgreement = VPWebBaseURL + "/member_ship_agreement"
|
||||||
|
|
||||||
|
///反馈首页
|
||||||
|
let VPFeedBackHomeWebUrl = VPCampaignWebURL + "/pages/leave/index"
|
||||||
|
///反馈列表
|
||||||
|
let VPFeedBackListWebUrl = VPCampaignWebURL + "/pages/leave/list"
|
||||||
|
///反馈详情
|
||||||
|
let VPFeedBackDetailWebUrl = VPCampaignWebURL + "/pages/leave/detail"
|
||||||
|
///活动页面
|
||||||
|
let VPRewardsWebUrl = VPCampaignWebURL
|
22
Veloria/Base/View/VPCollectionView.swift
Normal file
22
Veloria/Base/View/VPCollectionView.swift
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
//
|
||||||
|
// VPCollectionView.swift
|
||||||
|
// Veloria
|
||||||
|
//
|
||||||
|
// Created by Veloria on 2025/5/20.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class VPCollectionView: UICollectionView {
|
||||||
|
|
||||||
|
override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) {
|
||||||
|
super.init(frame: frame, collectionViewLayout: layout)
|
||||||
|
self.backgroundColor = .clear
|
||||||
|
self.contentInsetAdjustmentBehavior = .never
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
54
Veloria/Base/View/VPImageView.swift
Normal file
54
Veloria/Base/View/VPImageView.swift
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
//
|
||||||
|
// VPImageView.swift
|
||||||
|
// Veloria
|
||||||
|
//
|
||||||
|
// Created by Veloria on 2025/5/20.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class VPImageView: UIImageView {
|
||||||
|
|
||||||
|
var placeholderColor = UIColor.gray
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
super.init(frame: frame)
|
||||||
|
_init()
|
||||||
|
}
|
||||||
|
|
||||||
|
override init(image: UIImage?) {
|
||||||
|
super.init(image: image)
|
||||||
|
_init()
|
||||||
|
}
|
||||||
|
|
||||||
|
override init(image: UIImage?, highlightedImage: UIImage?) {
|
||||||
|
super.init(image: image, highlightedImage: highlightedImage)
|
||||||
|
_init()
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func _init() {
|
||||||
|
self.contentMode = .scaleAspectFill
|
||||||
|
self.layer.masksToBounds = true
|
||||||
|
if image == nil {
|
||||||
|
self.backgroundColor = self.placeholderColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override var image: UIImage? {
|
||||||
|
didSet {
|
||||||
|
if self.backgroundColor == nil && image == nil {
|
||||||
|
self.backgroundColor = self.placeholderColor
|
||||||
|
} else if image != nil {
|
||||||
|
if self.backgroundColor == self.placeholderColor {
|
||||||
|
self.backgroundColor = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
43
Veloria/Base/View/VPTableViewCell.swift
Normal file
43
Veloria/Base/View/VPTableViewCell.swift
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
//
|
||||||
|
// VPTableViewCell.swift
|
||||||
|
// Veloria
|
||||||
|
//
|
||||||
|
// Created by Veloria on 2025/5/20.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class VPTableViewCell: UITableViewCell {
|
||||||
|
|
||||||
|
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||||
|
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||||
|
|
||||||
|
self.layer.rasterizationScale = UIScreen.main.scale
|
||||||
|
self.layer.shouldRasterize = true
|
||||||
|
self.selectionStyle = .none
|
||||||
|
self.backgroundColor = .clear
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override func awakeFromNib() {
|
||||||
|
super.awakeFromNib()
|
||||||
|
// Initialization code
|
||||||
|
}
|
||||||
|
|
||||||
|
override func setSelected(_ selected: Bool, animated: Bool) {
|
||||||
|
super.setSelected(selected, animated: animated)
|
||||||
|
|
||||||
|
// Configure the view for the selected state
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension UITableViewCell {
|
||||||
|
|
||||||
|
var vp_tableView: UITableView? {
|
||||||
|
return self.value(forKey: "_tableView") as? UITableView
|
||||||
|
}
|
||||||
|
}
|
@ -7,16 +7,132 @@
|
|||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
class VPHomePageViewController: UIViewController {
|
class VPHomePageViewController: VPViewController {
|
||||||
|
|
||||||
|
|
||||||
|
private lazy var viewModel = VPHomeViewModel()
|
||||||
|
|
||||||
|
private lazy var pageParam: WMZPageParam = {
|
||||||
|
let param = WMZPageParam()
|
||||||
|
param.wTitleArr = ["All".localized, "Romance", "Revenge"]
|
||||||
|
param.wViewController = { [weak self] index in
|
||||||
|
return VPViewController()
|
||||||
|
}
|
||||||
|
|
||||||
|
param.wTopSuspension = true
|
||||||
|
//顶部可下拉
|
||||||
|
param.wBounces = true
|
||||||
|
param.wMenuTitleColor = .color828282()
|
||||||
|
param.wMenuTitleSelectColor = .colorFFFFFF()
|
||||||
|
param.wMenuTitleUIFont = .fontRegular(ofSize: 12)
|
||||||
|
param.wMenuTitleSelectUIFont = .fontMedium(ofSize: 12)
|
||||||
|
param.wMenuBgColor = .clear
|
||||||
|
param.wBgColor = .clear
|
||||||
|
param.wCustomMenuTitle = { [weak self] buttons in
|
||||||
|
|
||||||
|
vpLog(message: "++++++++++\(buttons ?? [])")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
param.wCustomMenuSelectTitle = { buttons in
|
||||||
|
vpLog(message: "----------\(buttons ?? [])")
|
||||||
|
}
|
||||||
|
|
||||||
|
param.wCustomNaviBarY = { _ in
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
param.wCustomTabbarY = { _ in
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return param
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var pageView: WMZPageView = {
|
||||||
|
let y = UIScreen.statusBarHeight
|
||||||
|
let width = UIScreen.width
|
||||||
|
let height = UIScreen.height - y
|
||||||
|
let pageView = WMZPageView(frame: CGRect(x: 0, y: y, width: width, height: height), param: pageParam, parentReponder: self)
|
||||||
|
pageView.param = pageParam
|
||||||
|
pageView.backgroundColor = .clear
|
||||||
|
pageView.downSc?.backgroundColor = .clear
|
||||||
|
// pageView.downSc?.delegate = self
|
||||||
|
pageView.downSc?.dataSource = self
|
||||||
|
pageView.downSc?.isScrollEnabled = true
|
||||||
|
pageView.downSc?.register(VPHomeItemContentCell.self, forCellReuseIdentifier: "cell")
|
||||||
|
pageView.downSc?.register(VPHomeBannerContentCell.self, forCellReuseIdentifier: VPHomeBannerContentCell.moduleKey.rawValue)
|
||||||
|
pageView.downSc?.register(VPHomeRecommandContentCell.self, forCellReuseIdentifier: VPHomeRecommandContentCell.moduleKey.rawValue)
|
||||||
|
// pageView.downSc?.rowHeight = UITableView.automaticDimension
|
||||||
|
// pageView.downSc?.estimatedRowHeight = 100
|
||||||
|
|
||||||
|
return pageView
|
||||||
|
}()
|
||||||
|
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
||||||
|
requestHomeData()
|
||||||
|
|
||||||
|
vp_setupUI()
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewWillAppear(_ animated: Bool) {
|
||||||
|
super.viewWillAppear(animated)
|
||||||
|
self.navigationController?.setNavigationBarHidden(true, animated: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func createView(title: String) -> WMZPageNaviBtn {
|
||||||
|
let button = WMZPageNaviBtn(type: .custom)
|
||||||
|
button.setTitle(title, for: .normal)
|
||||||
|
button.setTitleColor(.red, for: .normal)
|
||||||
|
button.setTitleColor(.green, for: .selected)
|
||||||
|
return button
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension VPHomePageViewController {
|
||||||
|
|
||||||
|
private func vp_setupUI() {
|
||||||
|
|
||||||
|
view.addSubview(pageView)
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//MARK: -------------- UITableViewDataSource --------------
|
||||||
|
extension VPHomePageViewController: UITableViewDataSource, UITableViewDelegate {
|
||||||
|
|
||||||
|
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||||
|
let item = self.viewModel.newModuleList[indexPath.row]
|
||||||
|
|
||||||
|
let cell = tableView.dequeueReusableCell(withIdentifier: item.module_key?.rawValue ?? "cell") as! VPHomeItemContentCell
|
||||||
|
cell.item = item
|
||||||
|
return cell
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||||
|
return self.viewModel.newModuleList.count
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension VPHomePageViewController {
|
||||||
|
|
||||||
|
private func requestHomeData() {
|
||||||
|
|
||||||
|
VPHomeAPI.requestHomeData { [weak self] list in
|
||||||
|
guard let self = self else { return }
|
||||||
|
if let list = list {
|
||||||
|
self.viewModel.oldModuleList = list
|
||||||
|
self.pageView.downSc?.reloadData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
20
Veloria/Class/Home/Model/VPHomeDataItem.swift
Normal file
20
Veloria/Class/Home/Model/VPHomeDataItem.swift
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
//
|
||||||
|
// VPHomeDataItem.swift
|
||||||
|
// Veloria
|
||||||
|
//
|
||||||
|
// Created by Veloria on 2025/5/20.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class VPHomeDataItem: VPModel {
|
||||||
|
enum ItemType: String {
|
||||||
|
case banner
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var type: ItemType = .banner
|
||||||
|
var list: [VPShortModel]?
|
||||||
|
|
||||||
|
|
||||||
|
}
|
27
Veloria/Class/Home/Model/VPHomeDataModel.swift
Normal file
27
Veloria/Class/Home/Model/VPHomeDataModel.swift
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
//
|
||||||
|
// VPHomeDataModel.swift
|
||||||
|
// Veloria
|
||||||
|
//
|
||||||
|
// Created by Veloria on 2025/5/20.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import SmartCodable
|
||||||
|
|
||||||
|
class VPHomeDataModel: VPModel, SmartCodable {
|
||||||
|
|
||||||
|
|
||||||
|
var bannerData: [VPShortModel]?
|
||||||
|
///首页九宫格数据
|
||||||
|
var nineSquare: VPNineSquareModel?
|
||||||
|
///新剧列表
|
||||||
|
var manualNewestRecommand: [VPShortModel]?
|
||||||
|
///热门排行
|
||||||
|
var hotData: [VPShortModel]?
|
||||||
|
}
|
||||||
|
|
||||||
|
class VPNineSquareModel: VPModel, SmartCodable {
|
||||||
|
|
||||||
|
var list: [VPShortModel]?
|
||||||
|
var title: String?
|
||||||
|
}
|
38
Veloria/Class/Home/Model/VPHomeModuleItem.swift
Normal file
38
Veloria/Class/Home/Model/VPHomeModuleItem.swift
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
//
|
||||||
|
// VPHomeModuleItem.swift
|
||||||
|
// Veloria
|
||||||
|
//
|
||||||
|
// Created by Veloria on 2025/5/20.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import SmartCodable
|
||||||
|
|
||||||
|
class VPHomeModuleItem: VPModel, SmartCodable {
|
||||||
|
|
||||||
|
enum ModuleKey: String, SmartCaseDefaultable {
|
||||||
|
case banner = "home_banner"
|
||||||
|
case v3_recommand = "home_v3_recommand"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var module_key: ModuleKey?
|
||||||
|
var title: String?
|
||||||
|
var list: [VPShortModel]?
|
||||||
|
|
||||||
|
@SmartAny
|
||||||
|
var data: Any?
|
||||||
|
|
||||||
|
|
||||||
|
func didFinishMapping() {
|
||||||
|
if let data = data as? [[String : Any]] {
|
||||||
|
self.list = [VPShortModel].deserialize(from: data)
|
||||||
|
} else if let data = data as? [String : Any] {
|
||||||
|
title = data["title"] as? String
|
||||||
|
list = [VPShortModel].deserialize(from: data["list"] as? [[String : Any]])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
98
Veloria/Class/Home/View/VPHomeBannerCell.swift
Normal file
98
Veloria/Class/Home/View/VPHomeBannerCell.swift
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
//
|
||||||
|
// VPHomeBannerCell.swift
|
||||||
|
// Veloria
|
||||||
|
//
|
||||||
|
// Created by Veloria on 2025/5/20.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class VPHomeBannerCell: ZKCycleScrollViewCell {
|
||||||
|
|
||||||
|
var model: VPShortModel? {
|
||||||
|
didSet {
|
||||||
|
coverImageView.vp_setImage(url: model?.horizontally_img)
|
||||||
|
titleLabel.text = model?.name
|
||||||
|
|
||||||
|
let watchCount = model?.watch_total ?? 0
|
||||||
|
if watchCount > 1000 {
|
||||||
|
let numStr = NSNumber(floatLiteral: CGFloat(watchCount) / 1000).toString(maximumFractionDigits: 1)
|
||||||
|
hotView.setTitle("\(numStr)K", for: .normal)
|
||||||
|
} else {
|
||||||
|
hotView.setTitle("\(watchCount)", for: .normal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private lazy var coverImageView: VPImageView = {
|
||||||
|
let imageView = VPImageView()
|
||||||
|
return imageView
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var bottomView: UIView = {
|
||||||
|
let view = VPGradientView()
|
||||||
|
view.locations = [0, 1]
|
||||||
|
view.colors = [UIColor.color000000(alpha: 0).cgColor, UIColor.color000000().cgColor]
|
||||||
|
view.startPoint = .init(x: 0.5, y: 0)
|
||||||
|
view.endPoint = .init(x: 0.5, y: 1)
|
||||||
|
return view
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var titleLabel: UILabel = {
|
||||||
|
let label = UILabel()
|
||||||
|
label.font = .fontMedium(ofSize: 12)
|
||||||
|
label.textColor = .colorFFFFFF()
|
||||||
|
return label
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var hotView: UIButton = {
|
||||||
|
let button = JXButton(type: .custom)
|
||||||
|
button.isUserInteractionEnabled = false
|
||||||
|
button.jx_font = .fontRegular(ofSize: 10)
|
||||||
|
button.setTitleColor(.colorAFAFAF(), for: .normal)
|
||||||
|
button.setImage(UIImage(named: "hot_icon_01"), for: .normal)
|
||||||
|
return button
|
||||||
|
}()
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
super.init(frame: frame)
|
||||||
|
|
||||||
|
vp_setupUI()
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
extension VPHomeBannerCell {
|
||||||
|
|
||||||
|
private func vp_setupUI() {
|
||||||
|
contentView.addSubview(coverImageView)
|
||||||
|
contentView.addSubview(bottomView)
|
||||||
|
contentView.addSubview(hotView)
|
||||||
|
contentView.addSubview(titleLabel)
|
||||||
|
|
||||||
|
coverImageView.snp.makeConstraints { make in
|
||||||
|
make.edges.equalToSuperview()
|
||||||
|
}
|
||||||
|
|
||||||
|
bottomView.snp.makeConstraints { make in
|
||||||
|
make.left.right.bottom.equalToSuperview()
|
||||||
|
make.height.equalTo(62)
|
||||||
|
}
|
||||||
|
|
||||||
|
hotView.snp.makeConstraints { make in
|
||||||
|
make.left.equalToSuperview().offset(10)
|
||||||
|
make.bottom.equalToSuperview().offset(-11)
|
||||||
|
}
|
||||||
|
|
||||||
|
titleLabel.snp.makeConstraints { make in
|
||||||
|
make.left.equalToSuperview().offset(10)
|
||||||
|
make.bottom.equalToSuperview().offset(-27)
|
||||||
|
make.right.lessThanOrEqualToSuperview().offset(-100)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
74
Veloria/Class/Home/View/VPHomeBannerContentCell.swift
Normal file
74
Veloria/Class/Home/View/VPHomeBannerContentCell.swift
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
//
|
||||||
|
// VPHomeBannerContentCell.swift
|
||||||
|
// Veloria
|
||||||
|
//
|
||||||
|
// Created by Veloria on 2025/5/20.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class VPHomeBannerContentCell: VPHomeItemContentCell {
|
||||||
|
|
||||||
|
override class var moduleKey: VPHomeModuleItem.ModuleKey {
|
||||||
|
return .banner
|
||||||
|
}
|
||||||
|
|
||||||
|
override var item: VPHomeModuleItem? {
|
||||||
|
didSet {
|
||||||
|
bannerView.reloadData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private lazy var bannerView: ZKCycleScrollView = {
|
||||||
|
let view = ZKCycleScrollView(frame: .zero, shouldInfiniteLoop: true)
|
||||||
|
view.delegate = self
|
||||||
|
view.dataSource = self
|
||||||
|
view.hidesPageControl = true
|
||||||
|
view.layer.cornerRadius = 10
|
||||||
|
view.layer.masksToBounds = true
|
||||||
|
view.register(VPHomeBannerCell.self, forCellWithReuseIdentifier: "cell")
|
||||||
|
return view
|
||||||
|
}()
|
||||||
|
|
||||||
|
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||||
|
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||||
|
|
||||||
|
vp_setupUI()
|
||||||
|
}
|
||||||
|
|
||||||
|
@MainActor required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension VPHomeBannerContentCell {
|
||||||
|
|
||||||
|
private func vp_setupUI() {
|
||||||
|
containerView.addSubview(bannerView)
|
||||||
|
|
||||||
|
bannerView.snp.makeConstraints { make in
|
||||||
|
make.left.equalToSuperview().offset(15)
|
||||||
|
make.centerX.equalToSuperview()
|
||||||
|
make.top.equalToSuperview()
|
||||||
|
make.bottom.equalToSuperview()
|
||||||
|
make.height.equalTo(182)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//MARK: -------------- ZKCycleScrollViewDataSource ZKCycleScrollViewDelegate --------------
|
||||||
|
extension VPHomeBannerContentCell: ZKCycleScrollViewDataSource, ZKCycleScrollViewDelegate {
|
||||||
|
|
||||||
|
func numberOfItems(in cycleScrollView: ZKCycleScrollView) -> Int {
|
||||||
|
return self.item?.list?.count ?? 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func cycleScrollView(_ cycleScrollView: ZKCycleScrollView, cellForItemAt index: Int) -> ZKCycleScrollViewCell {
|
||||||
|
let cell = cycleScrollView.dequeueReusableCell(withReuseIdentifier: "cell", for: index) as! VPHomeBannerCell
|
||||||
|
cell.model = self.item?.list?[index]
|
||||||
|
return cell
|
||||||
|
}
|
||||||
|
}
|
39
Veloria/Class/Home/View/VPHomeItemContentCell.swift
Normal file
39
Veloria/Class/Home/View/VPHomeItemContentCell.swift
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
//
|
||||||
|
// VPHomeItemContentCell.swift
|
||||||
|
// Veloria
|
||||||
|
//
|
||||||
|
// Created by Veloria on 2025/5/20.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class VPHomeItemContentCell: VPTableViewCell {
|
||||||
|
|
||||||
|
class var moduleKey: VPHomeModuleItem.ModuleKey {
|
||||||
|
return .banner
|
||||||
|
}
|
||||||
|
|
||||||
|
var item: VPHomeModuleItem?
|
||||||
|
|
||||||
|
|
||||||
|
private(set) lazy var containerView: UIView = {
|
||||||
|
let view = UIView()
|
||||||
|
return view
|
||||||
|
}()
|
||||||
|
|
||||||
|
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||||
|
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||||
|
contentView.addSubview(containerView)
|
||||||
|
|
||||||
|
containerView.snp.makeConstraints { make in
|
||||||
|
make.left.right.top.equalToSuperview()
|
||||||
|
make.bottom.equalToSuperview().offset(-40)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@MainActor required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
85
Veloria/Class/Home/View/VPHomeRecommandContentCell.swift
Normal file
85
Veloria/Class/Home/View/VPHomeRecommandContentCell.swift
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
//
|
||||||
|
// VPHomeRecommandContentCell.swift
|
||||||
|
// Veloria
|
||||||
|
//
|
||||||
|
// Created by Veloria on 2025/5/20.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class VPHomeRecommandContentCell: VPHomeItemContentCell {
|
||||||
|
|
||||||
|
override class var moduleKey: VPHomeModuleItem.ModuleKey {
|
||||||
|
return .v3_recommand
|
||||||
|
}
|
||||||
|
|
||||||
|
override var item: VPHomeModuleItem? {
|
||||||
|
didSet {
|
||||||
|
titleLabel.text = item?.title
|
||||||
|
collectionView.reloadData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private lazy var titleLabel: UILabel = {
|
||||||
|
let label = UILabel()
|
||||||
|
label.font = .fontMedium(ofSize: 15)
|
||||||
|
label.textColor = .colorFFFFFF()
|
||||||
|
return label
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var collectionViewLayout: UICollectionViewFlowLayout = {
|
||||||
|
let layout = UICollectionViewFlowLayout()
|
||||||
|
layout.scrollDirection = .horizontal
|
||||||
|
layout.itemSize = CGSize(width: 138, height: 152 + 30)
|
||||||
|
return layout
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var collectionView: VPCollectionView = {
|
||||||
|
let collectionView = VPCollectionView(frame: .zero, collectionViewLayout: collectionViewLayout)
|
||||||
|
return collectionView
|
||||||
|
}()
|
||||||
|
|
||||||
|
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||||
|
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||||
|
|
||||||
|
vp_setupUI()
|
||||||
|
}
|
||||||
|
|
||||||
|
@MainActor required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension VPHomeRecommandContentCell {
|
||||||
|
|
||||||
|
private func vp_setupUI() {
|
||||||
|
containerView.addSubview(titleLabel)
|
||||||
|
containerView.addSubview(collectionView)
|
||||||
|
|
||||||
|
titleLabel.snp.makeConstraints { make in
|
||||||
|
make.left.equalToSuperview().offset(15)
|
||||||
|
make.top.equalToSuperview()
|
||||||
|
}
|
||||||
|
|
||||||
|
collectionView.snp.makeConstraints { make in
|
||||||
|
make.left.right.equalToSuperview()
|
||||||
|
make.bottom.equalToSuperview()
|
||||||
|
make.top.equalToSuperview().offset(32)
|
||||||
|
make.height.equalTo(collectionViewLayout.itemSize.height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//MARK: -------------- UICollectionViewDelegate UICollectionViewDataSource --------------
|
||||||
|
//extension VPHomeRecommandContentCell: UICollectionViewDelegate, UICollectionViewDataSource {
|
||||||
|
//
|
||||||
|
// func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||||
|
// return self.item?.list?.count ?? 0
|
||||||
|
// }
|
||||||
|
//}
|
56
Veloria/Class/Home/ViewModel/VPHomeViewModel.swift
Normal file
56
Veloria/Class/Home/ViewModel/VPHomeViewModel.swift
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
//
|
||||||
|
// VPHomeViewModel.swift
|
||||||
|
// Veloria
|
||||||
|
//
|
||||||
|
// Created by Veloria on 2025/5/20.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class VPHomeViewModel: VPModel {
|
||||||
|
|
||||||
|
var homeDataModel: VPHomeDataModel? {
|
||||||
|
didSet {
|
||||||
|
updateDataList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///原始模版列表 不可直接使用,需要过滤
|
||||||
|
var oldModuleList: [VPHomeModuleItem]? {
|
||||||
|
didSet {
|
||||||
|
filterModuleData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
///筛选后的数据
|
||||||
|
private(set) lazy var newModuleList: [VPHomeModuleItem] = []
|
||||||
|
|
||||||
|
|
||||||
|
private(set) lazy var homeDataList: [VPHomeDataItem] = []
|
||||||
|
|
||||||
|
|
||||||
|
///筛选模版数据
|
||||||
|
private func filterModuleData() {
|
||||||
|
newModuleList.removeAll()
|
||||||
|
|
||||||
|
oldModuleList?.forEach({
|
||||||
|
if let key = $0.module_key {
|
||||||
|
if key == .banner {
|
||||||
|
newModuleList.insert($0, at: 0)
|
||||||
|
} else {
|
||||||
|
newModuleList.append($0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private func updateDataList() {
|
||||||
|
homeDataList.removeAll()
|
||||||
|
|
||||||
|
if let bannerData = homeDataModel?.bannerData {
|
||||||
|
let item = VPHomeDataItem()
|
||||||
|
item.type = .banner
|
||||||
|
item.list = bannerData
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
52
Veloria/Class/Player/Model/VPShortModel.swift
Normal file
52
Veloria/Class/Player/Model/VPShortModel.swift
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
//
|
||||||
|
// VPShortModel.swift
|
||||||
|
// Veloria
|
||||||
|
//
|
||||||
|
// Created by Veloria on 2025/5/20.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import SmartCodable
|
||||||
|
|
||||||
|
class VPShortModel: VPModel, SmartCodable {
|
||||||
|
|
||||||
|
enum TagType: String, SmartCaseDefaultable {
|
||||||
|
case hot = "hot"
|
||||||
|
case new = "new"
|
||||||
|
}
|
||||||
|
|
||||||
|
var id: String?
|
||||||
|
var all_coins: String?
|
||||||
|
var buy_type: String?
|
||||||
|
var collect_total: Int?
|
||||||
|
var sp_description: String?
|
||||||
|
var episode_total: Int?
|
||||||
|
var horizontally_img: String?
|
||||||
|
var image_url: String?
|
||||||
|
var is_collect: Bool?
|
||||||
|
var name: String?
|
||||||
|
var process: String?
|
||||||
|
var search_click_total: String?
|
||||||
|
var short_id: String?
|
||||||
|
var short_play_id: String?
|
||||||
|
var short_play_video_id: String?
|
||||||
|
var tag_type: TagType?
|
||||||
|
var video_info: VPVideoInfoModel?
|
||||||
|
var category: [String]?
|
||||||
|
///观看数
|
||||||
|
var watch_total: Int?
|
||||||
|
var current_episode: String?
|
||||||
|
var video_url: String?
|
||||||
|
|
||||||
|
@IgnoredKey
|
||||||
|
var titleAttributedString: NSAttributedString?
|
||||||
|
@IgnoredKey
|
||||||
|
var sp_isSelected: Bool?
|
||||||
|
|
||||||
|
|
||||||
|
static func mappingForKey() -> [SmartKeyTransformer]? {
|
||||||
|
return [
|
||||||
|
CodingKeys.sp_description <--- ["description"]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
30
Veloria/Class/Player/Model/VPVideoInfoModel.swift
Normal file
30
Veloria/Class/Player/Model/VPVideoInfoModel.swift
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
//
|
||||||
|
// VPVideoInfoModel.swift
|
||||||
|
// Veloria
|
||||||
|
//
|
||||||
|
// Created by Veloria on 2025/5/20.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import SmartCodable
|
||||||
|
|
||||||
|
class VPVideoInfoModel: VPModel, SmartCodable {
|
||||||
|
|
||||||
|
var coins: Int?
|
||||||
|
var vip_coins: Int?
|
||||||
|
var episode: String?
|
||||||
|
var id: String?
|
||||||
|
var image_url: String?
|
||||||
|
///1:会员,2: 非会员
|
||||||
|
var is_vip: Int?
|
||||||
|
///是否锁定,购买后解锁
|
||||||
|
var is_lock: Bool?
|
||||||
|
var promise_view_ad: Int?
|
||||||
|
// var revolution: []
|
||||||
|
var short_id: String?
|
||||||
|
var short_play_id: String?
|
||||||
|
var short_play_video_id: String?
|
||||||
|
var video_url: String?
|
||||||
|
///播放进度,毫秒
|
||||||
|
var play_seconds: Int?
|
||||||
|
}
|
@ -10,4 +10,51 @@ import UIKit
|
|||||||
class VPAppTool: NSObject {
|
class VPAppTool: NSObject {
|
||||||
|
|
||||||
static var windowScene: UIWindowScene?
|
static var windowScene: UIWindowScene?
|
||||||
|
|
||||||
|
static var keyWindow: UIWindow? {
|
||||||
|
return windowScene?.keyWindow
|
||||||
|
}
|
||||||
|
|
||||||
|
static var rootViewController: UIViewController? {
|
||||||
|
return keyWindow?.rootViewController
|
||||||
|
}
|
||||||
|
|
||||||
|
static var topViewController: UIViewController? {
|
||||||
|
var resultVC: UIViewController? = self.rootViewController
|
||||||
|
if let rootNav = resultVC as? UINavigationController {
|
||||||
|
resultVC = rootNav.topViewController
|
||||||
|
}
|
||||||
|
|
||||||
|
resultVC = self._topViewController(resultVC)
|
||||||
|
while resultVC?.presentedViewController != nil {
|
||||||
|
resultVC = self._topViewController(resultVC?.presentedViewController)
|
||||||
|
}
|
||||||
|
return resultVC
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func _topViewController(_ vc: UIViewController?) -> UIViewController? {
|
||||||
|
if vc is UINavigationController {
|
||||||
|
return _topViewController((vc as? UINavigationController)?.topViewController)
|
||||||
|
} else if vc is UITabBarController {
|
||||||
|
return _topViewController((vc as? UITabBarController)?.selectedViewController)
|
||||||
|
} else {
|
||||||
|
return vc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension VPAppTool {
|
||||||
|
|
||||||
|
///打开消息通知设置页面
|
||||||
|
static func openApnsSetting() {
|
||||||
|
if #available(iOS 16.0, *) {
|
||||||
|
if let url = URL(string: UIApplication.openNotificationSettingsURLString) {
|
||||||
|
UIApplication.shared.open(url)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if let url = URL(string: UIApplication.openSettingsURLString) {
|
||||||
|
UIApplication.shared.open(url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
23
Veloria/Libs/HUD/VPHUD.swift
Normal file
23
Veloria/Libs/HUD/VPHUD.swift
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
//
|
||||||
|
// VPHUD.swift
|
||||||
|
// Veloria
|
||||||
|
//
|
||||||
|
// Created by Veloria on 2025/5/20.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class VPHUD: NSObject {
|
||||||
|
static func config() {
|
||||||
|
// SVProgressHUD.setDefaultMaskType(.clear)
|
||||||
|
}
|
||||||
|
|
||||||
|
static func show() {
|
||||||
|
// SVProgressHUD.setDefaultMaskType(.clear)
|
||||||
|
// SVProgressHUD.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
static func dismiss() {
|
||||||
|
// SVProgressHUD.dismiss()
|
||||||
|
}
|
||||||
|
}
|
22
Veloria/Libs/HUD/VPToast.swift
Normal file
22
Veloria/Libs/HUD/VPToast.swift
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
//
|
||||||
|
// VPToast.swift
|
||||||
|
// Veloria
|
||||||
|
//
|
||||||
|
// Created by Veloria on 2025/5/20.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class VPToast: NSObject {
|
||||||
|
|
||||||
|
static func config() {
|
||||||
|
CSToastManager.setTapToDismissEnabled(false)
|
||||||
|
CSToastManager.setDefaultDuration(2)
|
||||||
|
CSToastManager.setDefaultPosition(CSToastPositionCenter)
|
||||||
|
}
|
||||||
|
|
||||||
|
static func show(text: String?) {
|
||||||
|
guard let text = text else { return }
|
||||||
|
// SPAPPTool.getKeyWindow()?.makeToast(text)
|
||||||
|
}
|
||||||
|
}
|
25
Veloria/Libs/Login/VPLoginManager.swift
Normal file
25
Veloria/Libs/Login/VPLoginManager.swift
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
//
|
||||||
|
// VPLoginManager.swift
|
||||||
|
// Veloria
|
||||||
|
//
|
||||||
|
// Created by Veloria on 2025/5/20.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class VPLoginManager: NSObject {
|
||||||
|
|
||||||
|
|
||||||
|
static let manager = VPLoginManager()
|
||||||
|
|
||||||
|
private(set) var token: VPTokenModel?
|
||||||
|
|
||||||
|
///是否正在刷新token
|
||||||
|
private(set) var isRefreshingToken = false
|
||||||
|
|
||||||
|
|
||||||
|
func setLoginToken(token: VPTokenModel?) {
|
||||||
|
self.token = token
|
||||||
|
// UserDefaults.jx_setObject(token, forKey: kSPLoginTokenDefaultsKey)
|
||||||
|
}
|
||||||
|
}
|
41
Veloria/Libs/Login/VPTokenModel.swift
Normal file
41
Veloria/Libs/Login/VPTokenModel.swift
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
//
|
||||||
|
// VPTokenModel.swift
|
||||||
|
// Veloria
|
||||||
|
//
|
||||||
|
// Created by Veloria on 2025/5/20.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import SmartCodable
|
||||||
|
|
||||||
|
class VPTokenModel: VPModel, SmartCodable, NSSecureCoding {
|
||||||
|
|
||||||
|
var token: String?
|
||||||
|
var customer_id: String?
|
||||||
|
var auto_login: Int?
|
||||||
|
|
||||||
|
required init() { }
|
||||||
|
|
||||||
|
static var supportsSecureCoding: Bool {
|
||||||
|
get {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func encode(with coder: NSCoder) {
|
||||||
|
coder.encode(token, forKey: "token")
|
||||||
|
coder.encode(customer_id, forKey: "customer_id")
|
||||||
|
coder.encode(auto_login, forKey: "auto_login")
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
super.init()
|
||||||
|
|
||||||
|
token = coder.decodeObject(of: NSString.self, forKey: "token") as? String
|
||||||
|
customer_id = coder.decodeObject(of: NSString.self, forKey: "customer_id") as? String
|
||||||
|
auto_login = coder.decodeObject(of: NSNumber.self, forKey: "auto_login")?.intValue
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
22
Veloria/Source/Assets.xcassets/icon/hot_icon_01.imageset/Contents.json
vendored
Normal file
22
Veloria/Source/Assets.xcassets/icon/hot_icon_01.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "Frame@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "Frame@3x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
Veloria/Source/Assets.xcassets/icon/hot_icon_01.imageset/Frame@2x.png
vendored
Normal file
BIN
Veloria/Source/Assets.xcassets/icon/hot_icon_01.imageset/Frame@2x.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 891 B |
BIN
Veloria/Source/Assets.xcassets/icon/hot_icon_01.imageset/Frame@3x.png
vendored
Normal file
BIN
Veloria/Source/Assets.xcassets/icon/hot_icon_01.imageset/Frame@3x.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
6
Veloria/Source/Assets.xcassets/image/Contents.json
Normal file
6
Veloria/Source/Assets.xcassets/image/Contents.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
22
Veloria/Source/Assets.xcassets/image/bg_image_01.imageset/Contents.json
vendored
Normal file
22
Veloria/Source/Assets.xcassets/image/bg_image_01.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "背景@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "背景@3x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
Veloria/Source/Assets.xcassets/image/bg_image_01.imageset/背景@2x.png
vendored
Normal file
BIN
Veloria/Source/Assets.xcassets/image/bg_image_01.imageset/背景@2x.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 921 KiB |
BIN
Veloria/Source/Assets.xcassets/image/bg_image_01.imageset/背景@3x.png
vendored
Normal file
BIN
Veloria/Source/Assets.xcassets/image/bg_image_01.imageset/背景@3x.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.0 MiB |
@ -3,3 +3,6 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#import <YYKit/YYKit.h>
|
#import <YYKit/YYKit.h>
|
||||||
|
#import <WMZPageController/WMZPageController.h>
|
||||||
|
#import "JXUUID.h"
|
||||||
|
#import <Toast.h>
|
||||||
|
@ -7,3 +7,4 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
"Home" = "Home";
|
"Home" = "Home";
|
||||||
|
"All" = "All";
|
||||||
|
252
Veloria/Thirdparty/JXButton/JXButton.swift
vendored
Normal file
252
Veloria/Thirdparty/JXButton/JXButton.swift
vendored
Normal file
@ -0,0 +1,252 @@
|
|||||||
|
//
|
||||||
|
// JXButton.swift
|
||||||
|
// BoJia
|
||||||
|
//
|
||||||
|
// Created by 火山传媒 on 2024/5/31.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class JXButton: UIButton {
|
||||||
|
lazy var jx_font: UIFont? = self.titleLabel?.font {
|
||||||
|
didSet {
|
||||||
|
self.titleLabel?.font = jx_font
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var maxTitleWidth: CGFloat = 0
|
||||||
|
var titleDirection: UITextLayoutDirection?
|
||||||
|
///左右边距
|
||||||
|
var leftAndRightMargin: CGFloat = 0
|
||||||
|
|
||||||
|
///文字与图片的间距
|
||||||
|
var space: CGFloat = 0
|
||||||
|
|
||||||
|
private var imageRect: CGRect = .zero
|
||||||
|
private var titleRect: CGRect = .zero
|
||||||
|
|
||||||
|
|
||||||
|
override class var layerClass: AnyClass {
|
||||||
|
return CAGradientLayer.self
|
||||||
|
}
|
||||||
|
|
||||||
|
var gradientLayer: CAGradientLayer {
|
||||||
|
return self.layer as! CAGradientLayer
|
||||||
|
}
|
||||||
|
|
||||||
|
var locations: [NSNumber]? {
|
||||||
|
didSet {
|
||||||
|
self.gradientLayer.locations = locations
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var colors: [CGColor]? {
|
||||||
|
didSet {
|
||||||
|
self.gradientLayer.colors = colors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var startPoint: CGPoint = .zero {
|
||||||
|
didSet {
|
||||||
|
self.gradientLayer.startPoint = startPoint
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var endPoint: CGPoint = .zero {
|
||||||
|
didSet {
|
||||||
|
self.gradientLayer.endPoint = endPoint
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override var intrinsicContentSize: CGSize {
|
||||||
|
|
||||||
|
var width: CGFloat = 0
|
||||||
|
var height: CGFloat = 0
|
||||||
|
switch titleDirection {
|
||||||
|
case .left:
|
||||||
|
width = imageRect.width + titleRect.width + space
|
||||||
|
if imageRect.height > titleRect.height {
|
||||||
|
height = imageRect.height
|
||||||
|
} else {
|
||||||
|
height = titleRect.height
|
||||||
|
}
|
||||||
|
|
||||||
|
case .up:
|
||||||
|
if imageRect.width > titleRect.width {
|
||||||
|
width = imageRect.width
|
||||||
|
} else {
|
||||||
|
width = titleRect.width
|
||||||
|
}
|
||||||
|
height = titleRect.height + imageRect.height + space
|
||||||
|
|
||||||
|
case .down:
|
||||||
|
if imageRect.width > titleRect.width {
|
||||||
|
width = imageRect.width
|
||||||
|
} else {
|
||||||
|
width = titleRect.width
|
||||||
|
}
|
||||||
|
height = titleRect.height + imageRect.height + space
|
||||||
|
|
||||||
|
default:
|
||||||
|
width = imageRect.width + titleRect.width + space
|
||||||
|
if imageRect.height > titleRect.height {
|
||||||
|
height = imageRect.height
|
||||||
|
} else {
|
||||||
|
height = titleRect.height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let size = CGSize(width: width + leftAndRightMargin * 2, height: height)
|
||||||
|
return size
|
||||||
|
}
|
||||||
|
|
||||||
|
override func imageRect(forContentRect contentRect: CGRect) -> CGRect {
|
||||||
|
let imageSize = currentImage?.size ?? .zero
|
||||||
|
let textSize = self._textSize()
|
||||||
|
let contentWidth = self.frame.size.width
|
||||||
|
let contentHeight = self.frame.size.height
|
||||||
|
|
||||||
|
var x: CGFloat = 0
|
||||||
|
var y: CGFloat = 0
|
||||||
|
var width: CGFloat = 0
|
||||||
|
var height: CGFloat = 0
|
||||||
|
|
||||||
|
switch titleDirection {
|
||||||
|
case .left:
|
||||||
|
x = (contentWidth - space) / 2 - imageSize.width / 2 + textSize.width / 2 + space
|
||||||
|
y = contentHeight / 2 - imageSize.height / 2
|
||||||
|
width = imageSize.width
|
||||||
|
height = imageSize.height
|
||||||
|
|
||||||
|
case .up:
|
||||||
|
x = contentWidth / 2 - imageSize.width / 2
|
||||||
|
y = (contentHeight - space) / 2 - imageSize.height / 2 + textSize.height / 2 + space
|
||||||
|
width = imageSize.width
|
||||||
|
height = imageSize.height
|
||||||
|
|
||||||
|
case .down:
|
||||||
|
x = contentWidth / 2 - imageSize.width / 2
|
||||||
|
y = (contentHeight - space) / 2 - imageSize.height / 2 - textSize.height / 2
|
||||||
|
width = imageSize.width
|
||||||
|
height = imageSize.height
|
||||||
|
|
||||||
|
default:
|
||||||
|
x = (contentWidth - space) / 2 - imageSize.width / 2 - textSize.width / 2
|
||||||
|
y = contentHeight / 2 - imageSize.height / 2
|
||||||
|
width = imageSize.width
|
||||||
|
height = imageSize.height
|
||||||
|
}
|
||||||
|
self.imageRect = CGRect(x: x, y: y, width: width, height: height)
|
||||||
|
|
||||||
|
return self.imageRect
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override func titleRect(forContentRect contentRect: CGRect) -> CGRect {
|
||||||
|
let imageSize = currentImage?.size ?? .zero
|
||||||
|
let textSize = self._textSize()
|
||||||
|
let contentWidth = self.frame.size.width
|
||||||
|
let contentHeight = self.frame.size.height
|
||||||
|
|
||||||
|
var x: CGFloat = 0
|
||||||
|
var y: CGFloat = 0
|
||||||
|
var width: CGFloat = 0
|
||||||
|
var height: CGFloat = 0
|
||||||
|
|
||||||
|
switch titleDirection {
|
||||||
|
case .left:
|
||||||
|
x = (contentWidth - space) / 2 - imageSize.width / 2 - textSize.width / 2
|
||||||
|
y = contentHeight / 2 - textSize.height / 2
|
||||||
|
width = textSize.width
|
||||||
|
height = textSize.height
|
||||||
|
|
||||||
|
case .up:
|
||||||
|
x = contentWidth / 2 - textSize.width / 2
|
||||||
|
y = (contentHeight - space) / 2 - textSize.height / 2 - imageSize.height / 2
|
||||||
|
width = textSize.width
|
||||||
|
height = textSize.height
|
||||||
|
|
||||||
|
case .down:
|
||||||
|
x = contentWidth / 2 - textSize.width / 2
|
||||||
|
y = (contentHeight - space) / 2 - textSize.height / 2 + imageSize.height / 2 + space
|
||||||
|
width = textSize.width
|
||||||
|
height = textSize.height
|
||||||
|
|
||||||
|
default:
|
||||||
|
x = (contentWidth - space) / 2 + imageSize.width / 2 - textSize.width / 2 + space
|
||||||
|
y = contentHeight / 2 - textSize.height / 2
|
||||||
|
width = textSize.width
|
||||||
|
height = textSize.height
|
||||||
|
}
|
||||||
|
|
||||||
|
self.titleRect = CGRect(x: x, y: y, width: width, height: height)
|
||||||
|
return self.titleRect
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private var borderColors: [UInt : UIColor] = [:]
|
||||||
|
|
||||||
|
func jx_setBorderColor(_ color: UIColor?, for state: UIControl.State) {
|
||||||
|
var arr = self.borderColors
|
||||||
|
let index = state.rawValue
|
||||||
|
arr[index] = color
|
||||||
|
if state == .selected {
|
||||||
|
let i = index + UIControl.State.highlighted.rawValue
|
||||||
|
arr[i] = color
|
||||||
|
}
|
||||||
|
self.borderColors = arr
|
||||||
|
updateStatus()
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateStatus() {
|
||||||
|
var color = self.borderColors[self.state.rawValue]?.cgColor
|
||||||
|
if (color == nil) {
|
||||||
|
color = self.borderColors[UIControl.State.normal.rawValue]?.cgColor
|
||||||
|
}
|
||||||
|
self.layer.borderColor = color
|
||||||
|
}
|
||||||
|
|
||||||
|
override var isSelected: Bool {
|
||||||
|
didSet {
|
||||||
|
updateStatus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override var isEnabled: Bool {
|
||||||
|
didSet {
|
||||||
|
updateStatus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
extension JXButton {
|
||||||
|
|
||||||
|
private func _textSize() -> CGSize {
|
||||||
|
// let size = CGSize(width: self.bounds.size.width, height: self.bounds.size.height)
|
||||||
|
|
||||||
|
var attr: [NSAttributedString.Key : Any] = [:]
|
||||||
|
attr[NSAttributedString.Key.font] = jx_font
|
||||||
|
|
||||||
|
let paragraphStyle = NSMutableParagraphStyle()
|
||||||
|
paragraphStyle.lineBreakMode = .byTruncatingMiddle
|
||||||
|
attr[NSAttributedString.Key.paragraphStyle] = paragraphStyle
|
||||||
|
|
||||||
|
// var rect = self.currentTitle?.ocString.boundingRect(with: size, options: [.usesLineFragmentOrigin, .usesFontLeading], attributes: attr, context: nil) ?? .zero
|
||||||
|
|
||||||
|
if let font = jx_font {
|
||||||
|
var size = self.currentTitle?.size(font: font) ?? .zero
|
||||||
|
|
||||||
|
if maxTitleWidth != 0 && maxTitleWidth < size.width {
|
||||||
|
size = CGSize(width: maxTitleWidth, height: size.height)
|
||||||
|
}
|
||||||
|
// if maxTitleWidth != 0 && maxTitleWidth < rect.size.width {
|
||||||
|
// rect.size = CGSize(width: maxTitleWidth, height: rect.size.height)
|
||||||
|
// }
|
||||||
|
return size
|
||||||
|
} else {
|
||||||
|
return .zero
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
20
Veloria/Thirdparty/JXUUID/JXUUID.h
vendored
Normal file
20
Veloria/Thirdparty/JXUUID/JXUUID.h
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
//
|
||||||
|
// JXUUID.h
|
||||||
|
// 设备标识符
|
||||||
|
//
|
||||||
|
// Created by 曾觉新 on 2017/8/24.
|
||||||
|
// Copyright © 2017年 曾觉新. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
@interface JXUUID : NSObject
|
||||||
|
|
||||||
|
+ (nonnull NSString *)uuid;
|
||||||
|
+ (nonnull NSString *)idfa;
|
||||||
|
/**
|
||||||
|
重新安装app后,会发生变化
|
||||||
|
*/
|
||||||
|
+ (nonnull NSString *)systemUUID;
|
||||||
|
|
||||||
|
@end
|
47
Veloria/Thirdparty/JXUUID/JXUUID.m
vendored
Normal file
47
Veloria/Thirdparty/JXUUID/JXUUID.m
vendored
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
//
|
||||||
|
// JXUUID.m
|
||||||
|
// 设备标识符
|
||||||
|
//
|
||||||
|
// Created by 曾觉新 on 2017/8/24.
|
||||||
|
// Copyright © 2017年 曾觉新. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "JXUUID.h"
|
||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
#import "PDKeyChain.h"
|
||||||
|
#import <AdSupport/AdSupport.h>
|
||||||
|
|
||||||
|
static NSString *const uuidKey = @"com.JXUUID";
|
||||||
|
|
||||||
|
@implementation JXUUID
|
||||||
|
|
||||||
|
+ (nonnull NSString *)uuid
|
||||||
|
{
|
||||||
|
static NSString *uuid;
|
||||||
|
static dispatch_once_t onceToken;
|
||||||
|
dispatch_once(&onceToken, ^{
|
||||||
|
uuid = [PDKeyChain objectForKey:uuidKey];
|
||||||
|
if (uuid && uuid.length > 0) {
|
||||||
|
} else {
|
||||||
|
uuid = [[NSUUID UUID] UUIDString];
|
||||||
|
[PDKeyChain setObject:uuid forKey:uuidKey];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
+ (nonnull NSString *)idfa
|
||||||
|
{
|
||||||
|
static NSString *idfa;
|
||||||
|
static dispatch_once_t onceToken;
|
||||||
|
dispatch_once(&onceToken, ^{
|
||||||
|
idfa = [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];
|
||||||
|
});
|
||||||
|
return idfa;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (nonnull NSString *)systemUUID
|
||||||
|
{
|
||||||
|
return [UIDevice currentDevice].identifierForVendor.UUIDString;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
31
Veloria/Thirdparty/JXUUID/PDKeyChain.h
vendored
Executable file
31
Veloria/Thirdparty/JXUUID/PDKeyChain.h
vendored
Executable file
@ -0,0 +1,31 @@
|
|||||||
|
//
|
||||||
|
// PDKeyChain.h
|
||||||
|
// PDKeyChain
|
||||||
|
//
|
||||||
|
// Created by Panda on 16/8/23.
|
||||||
|
// Copyright © 2016年 v2panda. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#import <Security/Security.h>
|
||||||
|
|
||||||
|
@interface PDKeyChain : NSObject
|
||||||
|
/**
|
||||||
|
* 从 KeyChain 中读取存储的数据
|
||||||
|
*
|
||||||
|
* @return NSDictionary
|
||||||
|
*/
|
||||||
|
+ (NSDictionary *)getKeyChainData;
|
||||||
|
|
||||||
|
+ (id)objectForKey:(NSString *)key;
|
||||||
|
+ (void)setObject:(id)object forKey:(NSString *)key;
|
||||||
|
+ (void)removeObjectForKey:(NSString *)key;
|
||||||
|
+ (void)removeAllObjects;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除 KeyChain 信息
|
||||||
|
*/
|
||||||
|
+ (void)keyChainDelete;
|
||||||
|
|
||||||
|
@end
|
100
Veloria/Thirdparty/JXUUID/PDKeyChain.m
vendored
Executable file
100
Veloria/Thirdparty/JXUUID/PDKeyChain.m
vendored
Executable file
@ -0,0 +1,100 @@
|
|||||||
|
//
|
||||||
|
// PDKeyChain.m
|
||||||
|
// PDKeyChain
|
||||||
|
//
|
||||||
|
// Created by Panda on 16/8/23.
|
||||||
|
// Copyright © 2016年 v2panda. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "PDKeyChain.h"
|
||||||
|
|
||||||
|
static NSString * const kPDKeyChainKey = @"com.veloria.keychainKey";
|
||||||
|
|
||||||
|
@implementation PDKeyChain
|
||||||
|
|
||||||
|
+ (void)keyChainDelete{
|
||||||
|
[self delete:kPDKeyChainKey];
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (NSDictionary *)getKeyChainData
|
||||||
|
{
|
||||||
|
NSDictionary *dic = [self load:kPDKeyChainKey];
|
||||||
|
if (!dic) {
|
||||||
|
dic = [NSDictionary dictionary];
|
||||||
|
}
|
||||||
|
return dic;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (void)setObject:(id)object forKey:(NSString *)key
|
||||||
|
{
|
||||||
|
NSMutableDictionary *tempDic = [[self getKeyChainData] mutableCopy];
|
||||||
|
[tempDic setObject:object forKey:key];
|
||||||
|
[self save:kPDKeyChainKey data:tempDic];
|
||||||
|
}
|
||||||
|
+ (id)objectForKey:(NSString *)key
|
||||||
|
{
|
||||||
|
NSDictionary *tempDic = [self getKeyChainData];
|
||||||
|
return tempDic[key];
|
||||||
|
}
|
||||||
|
+ (void)removeObjectForKey:(NSString *)key
|
||||||
|
{
|
||||||
|
NSMutableDictionary *tempDic = [[self getKeyChainData] mutableCopy];
|
||||||
|
[tempDic removeObjectForKey:key];
|
||||||
|
[self save:kPDKeyChainKey data:tempDic];
|
||||||
|
}
|
||||||
|
+ (void)removeAllObjects
|
||||||
|
{
|
||||||
|
NSMutableDictionary *tempDic = [[self getKeyChainData] mutableCopy];
|
||||||
|
[tempDic removeAllObjects];
|
||||||
|
[self save:kPDKeyChainKey data:tempDic];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
+ (NSMutableDictionary *)getKeychainQuery:(NSString *)service {
|
||||||
|
return [NSMutableDictionary dictionaryWithObjectsAndKeys:
|
||||||
|
(id)kSecClassGenericPassword,(id)kSecClass,
|
||||||
|
service, (id)kSecAttrService,
|
||||||
|
service, (id)kSecAttrAccount,
|
||||||
|
(id)kSecAttrAccessibleAfterFirstUnlock,(id)kSecAttrAccessible,
|
||||||
|
nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (void)save:(NSString *)service data:(id)data {
|
||||||
|
//Get search dictionary
|
||||||
|
NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
|
||||||
|
//Delete old item before add new item
|
||||||
|
SecItemDelete((CFDictionaryRef)keychainQuery);
|
||||||
|
//Add new object to search dictionary(Attention:the data format)
|
||||||
|
[keychainQuery setObject:[NSKeyedArchiver archivedDataWithRootObject:data] forKey:(id)kSecValueData];
|
||||||
|
//Add item to keychain with the search dictionary
|
||||||
|
SecItemAdd((CFDictionaryRef)keychainQuery, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (id)load:(NSString *)service {
|
||||||
|
id ret = nil;
|
||||||
|
NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
|
||||||
|
//Configure the search setting
|
||||||
|
//Since in our simple case we are expecting only a single attribute to be returned (the password) we can set the attribute kSecReturnData to kCFBooleanTrue
|
||||||
|
[keychainQuery setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData];
|
||||||
|
[keychainQuery setObject:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit];
|
||||||
|
CFDataRef keyData = NULL;
|
||||||
|
if (SecItemCopyMatching((CFDictionaryRef)keychainQuery, (CFTypeRef *)&keyData) == noErr) {
|
||||||
|
@try {
|
||||||
|
ret = [NSKeyedUnarchiver unarchiveObjectWithData:(__bridge NSData *)keyData];
|
||||||
|
} @catch (NSException *e) {
|
||||||
|
NSLog(@"Unarchive of %@ failed: %@", service, e);
|
||||||
|
} @finally {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (keyData)
|
||||||
|
CFRelease(keyData);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (void)delete:(NSString *)service {
|
||||||
|
NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
|
||||||
|
SecItemDelete((CFDictionaryRef)keychainQuery);
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
591
Veloria/Thirdparty/ZKCycleScrollView-Swift/ZKCycleScrollView.swift
vendored
Normal file
591
Veloria/Thirdparty/ZKCycleScrollView-Swift/ZKCycleScrollView.swift
vendored
Normal file
@ -0,0 +1,591 @@
|
|||||||
|
//
|
||||||
|
// ZKCycleScrollView.swift
|
||||||
|
// ZKCycleScrollViewDemo
|
||||||
|
//
|
||||||
|
// Created by bestdew on 2019/3/8.
|
||||||
|
// Copyright © 2019 bestdew. All rights reserved.
|
||||||
|
//
|
||||||
|
// d*##$.
|
||||||
|
// zP"""""$e. $" $o
|
||||||
|
//4$ '$ $" $
|
||||||
|
//'$ '$ J$ $F
|
||||||
|
// 'b $k $> $
|
||||||
|
// $k $r J$ d$
|
||||||
|
// '$ $ $" $~
|
||||||
|
// '$ "$ '$E $
|
||||||
|
// $ $L $" $F ...
|
||||||
|
// $. 4B $ $$$*"""*b
|
||||||
|
// '$ $. $$ $$ $F
|
||||||
|
// "$ R$ $F $" $
|
||||||
|
// $k ?$ u* dF .$
|
||||||
|
// ^$. $$" z$ u$$$$e
|
||||||
|
// #$b $E.dW@e$" ?$
|
||||||
|
// #$ .o$$# d$$$$c ?F
|
||||||
|
// $ .d$$#" . zo$> #$r .uF
|
||||||
|
// $L .u$*" $&$$$k .$$d$$F
|
||||||
|
// $$" ""^"$$$P"$P9$
|
||||||
|
// JP .o$$$$u:$P $$
|
||||||
|
// $ ..ue$" "" $"
|
||||||
|
// d$ $F $
|
||||||
|
// $$ ....udE 4B
|
||||||
|
// #$ """"` $r @$
|
||||||
|
// ^$L '$ $F
|
||||||
|
// RN 4N $
|
||||||
|
// *$b d$
|
||||||
|
// $$k $F
|
||||||
|
// $$b $F
|
||||||
|
// $"" $F
|
||||||
|
// '$ $
|
||||||
|
// $L $
|
||||||
|
// '$ $
|
||||||
|
// $ $
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
public typealias ZKCycleScrollViewCell = UICollectionViewCell
|
||||||
|
|
||||||
|
public enum ZKScrollDirection: Int {
|
||||||
|
case horizontal
|
||||||
|
case vertical
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc public protocol ZKCycleScrollViewDataSource: NSObjectProtocol {
|
||||||
|
/// Return number of pages
|
||||||
|
func numberOfItems(in cycleScrollView: ZKCycleScrollView) -> Int
|
||||||
|
/// The cell that is returned must be retrieved from a call to -dequeueReusableCellWithReuseIdentifier:forIndex:
|
||||||
|
func cycleScrollView(_ cycleScrollView: ZKCycleScrollView, cellForItemAt index: Int) -> ZKCycleScrollViewCell
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc public protocol ZKCycleScrollViewDelegate: NSObjectProtocol {
|
||||||
|
/// Called when the cell is clicked
|
||||||
|
@objc optional func cycleScrollView(_ cycleScrollView: ZKCycleScrollView, didSelectItemAt index: Int)
|
||||||
|
/// Called when the offset changes. The progress range is from 0 to the maximum index value, which means the progress value for a round of scrolling
|
||||||
|
@objc optional func cycleScrollViewDidScroll(_ cycleScrollView: ZKCycleScrollView, progress: Double)
|
||||||
|
/// Called when scrolling to a new index page
|
||||||
|
@objc optional func cycleScrollView(_ cycleScrollView: ZKCycleScrollView, didScrollFromIndex fromIndex: Int, toIndex: Int)
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBDesignable open class ZKCycleScrollView: UIView {
|
||||||
|
|
||||||
|
@IBOutlet open weak var delegate: ZKCycleScrollViewDelegate?
|
||||||
|
@IBOutlet open weak var dataSource: ZKCycleScrollViewDataSource?
|
||||||
|
|
||||||
|
|
||||||
|
var currentIdx = 0
|
||||||
|
var stepLength: CGFloat? // 自定义每个时间间隔移动的步长
|
||||||
|
|
||||||
|
#if TARGET_INTERFACE_BUILDER
|
||||||
|
@IBInspectable open var scrollDirection: Int = 0
|
||||||
|
#else
|
||||||
|
/// default horizontal. scroll direction
|
||||||
|
open var scrollDirection: ZKScrollDirection = .horizontal {
|
||||||
|
didSet {
|
||||||
|
switch scrollDirection {
|
||||||
|
case .vertical:
|
||||||
|
flowLayout?.scrollDirection = .vertical
|
||||||
|
default:
|
||||||
|
flowLayout?.scrollDirection = .horizontal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
/// default 3.f. automatic scroll time interval
|
||||||
|
@IBInspectable open var autoScrollInterval: TimeInterval = 3 {
|
||||||
|
didSet {
|
||||||
|
addTimer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@IBInspectable open var isAutoScroll: Bool = true {
|
||||||
|
didSet {
|
||||||
|
addTimer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// default true. turn off any dragging temporarily
|
||||||
|
@IBInspectable open var allowsDragging: Bool = true {
|
||||||
|
didSet {
|
||||||
|
collectionView.isScrollEnabled = allowsDragging
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// default the view size
|
||||||
|
@IBInspectable open var itemSize: CGSize = CGSize.zero {
|
||||||
|
didSet {
|
||||||
|
itemSizeFlag = true
|
||||||
|
flowLayout.itemSize = itemSize
|
||||||
|
flowLayout.headerReferenceSize = CGSize(width: (bounds.width - itemSize.width) / 2, height: (bounds.height - itemSize.height) / 2)
|
||||||
|
flowLayout.footerReferenceSize = CGSize(width: (bounds.width - itemSize.width) / 2, height: (bounds.height - itemSize.height) / 2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// default 0.0
|
||||||
|
@IBInspectable open var itemSpacing: CGFloat = 0.0 {
|
||||||
|
didSet {
|
||||||
|
flowLayout.minimumLineSpacing = itemSpacing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// default 1.f(no scaling), it ranges from 0.f to 1.f
|
||||||
|
@IBInspectable open var itemZoomScale: CGFloat = 1.0 {
|
||||||
|
didSet {
|
||||||
|
flowLayout.zoomScale = itemZoomScale
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///item渐隐效果
|
||||||
|
open var itemAlpha = false {
|
||||||
|
didSet {
|
||||||
|
flowLayout.itemAlpha = itemAlpha
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///旋转角度
|
||||||
|
open var rotationAngle = 0.0 {
|
||||||
|
didSet {
|
||||||
|
flowLayout.rotationAngle = rotationAngle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBInspectable open var hidesPageControl: Bool = false {
|
||||||
|
didSet {
|
||||||
|
pageControl?.isHidden = hidesPageControl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@IBInspectable open var pageIndicatorTintColor: UIColor = UIColor.gray {
|
||||||
|
didSet {
|
||||||
|
pageControl?.pageIndicatorTintColor = pageIndicatorTintColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@IBInspectable open var currentPageIndicatorTintColor: UIColor = UIColor.white {
|
||||||
|
didSet {
|
||||||
|
pageControl?.currentPageIndicatorTintColor = currentPageIndicatorTintColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// current page index
|
||||||
|
open var pageIndex: Int {
|
||||||
|
return changeIndex(currentIndex())
|
||||||
|
}
|
||||||
|
/// current content offset
|
||||||
|
open var contentOffset: CGPoint {
|
||||||
|
let num = CGFloat(numberOfAddedCells() / 2)
|
||||||
|
switch scrollDirection {
|
||||||
|
case .vertical:
|
||||||
|
return CGPoint(x: 0.0, y: max(0.0, collectionView.contentOffset.y - (flowLayout.itemSize.height + flowLayout.minimumLineSpacing) * num))
|
||||||
|
default:
|
||||||
|
return CGPoint(x: max(0.0, collectionView.contentOffset.x - (flowLayout.itemSize.width + flowLayout.minimumLineSpacing) * num), y: 0.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// infinite cycle
|
||||||
|
@IBInspectable open private(set) var isInfiniteLoop: Bool = true
|
||||||
|
/// load completed callback
|
||||||
|
open var loadCompletion: (() -> Void)? = nil
|
||||||
|
open var pageControlFrame: CGRect?
|
||||||
|
var pageControl: UIPageControl!
|
||||||
|
var collectionView: UICollectionView!
|
||||||
|
private var flowLayout: ZKCycleScrollViewFlowLayout!
|
||||||
|
private var timer: Timer?
|
||||||
|
private var numberOfItems: Int = 0
|
||||||
|
private var fromIndex: Int = 0
|
||||||
|
private var itemSizeFlag: Bool = false
|
||||||
|
private var indexOffset: Int = 0
|
||||||
|
private var configuredFlag: Bool = false
|
||||||
|
private var tempIndex: Int = 0
|
||||||
|
|
||||||
|
// MARK: - Open Func
|
||||||
|
open func register(_ cellClass: AnyClass?, forCellWithReuseIdentifier identifier: String) {
|
||||||
|
collectionView.register(cellClass, forCellWithReuseIdentifier: identifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
open func register(_ nib: UINib?, forCellWithReuseIdentifier identifier: String) {
|
||||||
|
collectionView.register(nib, forCellWithReuseIdentifier: identifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
open func dequeueReusableCell(withReuseIdentifier identifier: String, for index: Int) -> ZKCycleScrollViewCell {
|
||||||
|
let indexPath = IndexPath(item: changeIndex(index), section: 0)
|
||||||
|
return collectionView.dequeueReusableCell(withReuseIdentifier: identifier, for: indexPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
open func reloadData() {
|
||||||
|
removeTimer()
|
||||||
|
UIView.performWithoutAnimation {
|
||||||
|
self.collectionView.reloadData()
|
||||||
|
}
|
||||||
|
collectionView.performBatchUpdates(nil) { _ in
|
||||||
|
self.configuration()
|
||||||
|
self.loadCompletion?()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Call -beginUpdates and -endUpdates to update layout
|
||||||
|
/// Allows multiple scrollDirection/itemSize/itemSpacing/itemZoomScale to be set simultaneously.
|
||||||
|
open func beginUpdates() {
|
||||||
|
tempIndex = pageIndex
|
||||||
|
removeTimer()
|
||||||
|
}
|
||||||
|
|
||||||
|
open func endUpdates() {
|
||||||
|
flowLayout.invalidateLayout()
|
||||||
|
scrollToItem(at: tempIndex, animated: false)
|
||||||
|
addTimer()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Scroll to page
|
||||||
|
open func scrollToItem(at index: Int, animated: Bool) {
|
||||||
|
let num = numberOfAddedCells()
|
||||||
|
guard index >= 0 && index <= numberOfItems - 1 - num else {
|
||||||
|
print("⚠️attempt to scroll to invalid index:\(index)")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
removeTimer()
|
||||||
|
let idx = index + num / 2
|
||||||
|
let position = scrollPosition()
|
||||||
|
let indexPath = IndexPath(item: idx, section: 0)
|
||||||
|
collectionView.scrollToItem(at: indexPath, at: position, animated: animated)
|
||||||
|
|
||||||
|
addTimer()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the visible cell object at the specified index
|
||||||
|
open func cellForItem(at index: Int) -> ZKCycleScrollViewCell? {
|
||||||
|
let num = numberOfAddedCells()
|
||||||
|
guard index >= 0 && index <= numberOfItems - 1 - num else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
let idx = index + num / 2
|
||||||
|
let indexPath = IndexPath(item: idx, section: 0)
|
||||||
|
let cell = collectionView.cellForItem(at: indexPath)
|
||||||
|
return cell
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Init
|
||||||
|
public init(frame: CGRect, shouldInfiniteLoop infiniteLoop: Bool? = nil) {
|
||||||
|
super.init(frame: frame)
|
||||||
|
|
||||||
|
isInfiniteLoop = infiniteLoop ?? true
|
||||||
|
initialization()
|
||||||
|
}
|
||||||
|
|
||||||
|
required public init?(coder aDecoder: NSCoder) {
|
||||||
|
super.init(coder: aDecoder)
|
||||||
|
|
||||||
|
initialization()
|
||||||
|
}
|
||||||
|
|
||||||
|
override open func layoutSubviews() {
|
||||||
|
super.layoutSubviews()
|
||||||
|
|
||||||
|
if itemSizeFlag {
|
||||||
|
flowLayout.itemSize = itemSize
|
||||||
|
flowLayout.headerReferenceSize = CGSize(width: (bounds.width - itemSize.width) / 2, height: (bounds.height - itemSize.height) / 2)
|
||||||
|
flowLayout.footerReferenceSize = CGSize(width: (bounds.width - itemSize.width) / 2, height: (bounds.height - itemSize.height) / 2)
|
||||||
|
} else {
|
||||||
|
flowLayout.itemSize = bounds.size
|
||||||
|
flowLayout.headerReferenceSize = CGSize.zero
|
||||||
|
flowLayout.footerReferenceSize = CGSize.zero
|
||||||
|
}
|
||||||
|
collectionView.frame = bounds
|
||||||
|
if let pageControlFrame = pageControlFrame {
|
||||||
|
pageControl.frame = pageControlFrame
|
||||||
|
} else {
|
||||||
|
pageControl.frame = CGRect(x: 0.0, y: bounds.height - 15.0, width: bounds.width, height: 15.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override open func willMove(toSuperview newSuperview: UIView?) {
|
||||||
|
if newSuperview == nil { removeTimer() }
|
||||||
|
}
|
||||||
|
|
||||||
|
override open func setValue(_ value: Any?, forUndefinedKey key: String) {
|
||||||
|
guard key == "scrollDirection" else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let direction = value as! Int
|
||||||
|
if direction == 1 {
|
||||||
|
scrollDirection = .vertical
|
||||||
|
} else {
|
||||||
|
scrollDirection = .horizontal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
collectionView.delegate = nil
|
||||||
|
collectionView.dataSource = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Private Func
|
||||||
|
private func initialization() {
|
||||||
|
flowLayout = ZKCycleScrollViewFlowLayout()
|
||||||
|
flowLayout.minimumLineSpacing = 0
|
||||||
|
flowLayout.minimumInteritemSpacing = 0
|
||||||
|
flowLayout.scrollDirection = .horizontal
|
||||||
|
|
||||||
|
collectionView = UICollectionView(frame: CGRect.zero, collectionViewLayout: flowLayout)
|
||||||
|
collectionView.backgroundColor = nil
|
||||||
|
collectionView.delegate = self
|
||||||
|
collectionView.dataSource = self
|
||||||
|
collectionView.scrollsToTop = false
|
||||||
|
collectionView.bounces = false
|
||||||
|
collectionView.showsVerticalScrollIndicator = false
|
||||||
|
collectionView.showsHorizontalScrollIndicator = false
|
||||||
|
addSubview(collectionView)
|
||||||
|
|
||||||
|
pageControl = UIPageControl()
|
||||||
|
pageControl.isEnabled = false
|
||||||
|
pageControl.hidesForSinglePage = true
|
||||||
|
pageControl.pageIndicatorTintColor = UIColor.gray
|
||||||
|
pageControl.currentPageIndicatorTintColor = UIColor.white
|
||||||
|
addSubview(pageControl);
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.configuration()
|
||||||
|
self.loadCompletion?()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func configuration() {
|
||||||
|
fromIndex = 0
|
||||||
|
indexOffset = 0
|
||||||
|
configuredFlag = false
|
||||||
|
|
||||||
|
guard numberOfItems > 1 else { return }
|
||||||
|
|
||||||
|
let position = scrollPosition()
|
||||||
|
if isInfiniteLoop {
|
||||||
|
let indexPath = IndexPath(item: 2, section: 0)
|
||||||
|
collectionView.scrollToItem(at: indexPath, at: position, animated: false)
|
||||||
|
}
|
||||||
|
|
||||||
|
addTimer()
|
||||||
|
updatePageControl()
|
||||||
|
|
||||||
|
configuredFlag = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func addTimer() {
|
||||||
|
removeTimer()
|
||||||
|
|
||||||
|
if numberOfItems < 2 || !isAutoScroll || autoScrollInterval <= 0.0 { return }
|
||||||
|
timer = Timer.scheduledTimer(timeInterval: autoScrollInterval, target: YYWeakProxy(target: self), selector: #selector(automaticScroll), userInfo: nil, repeats: true)
|
||||||
|
RunLoop.main.add(timer!, forMode: .common)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func updatePageControl() {
|
||||||
|
let num = numberOfAddedCells()
|
||||||
|
pageControl.currentPage = 0
|
||||||
|
pageControl.numberOfPages = max(0, numberOfItems - num)
|
||||||
|
pageControl.isHidden = (hidesPageControl || pageControl.numberOfPages < 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func numberOfAddedCells() -> Int {
|
||||||
|
return isInfiniteLoop ? 4 : 0
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func automaticScroll() {
|
||||||
|
var index = currentIndex() + 1
|
||||||
|
if !isInfiniteLoop && index >= numberOfItems {
|
||||||
|
index = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if let stepLength = stepLength {
|
||||||
|
let offsetX = self.collectionView.contentOffset.x
|
||||||
|
let width = flowLayout.itemSize.width + flowLayout.minimumLineSpacing
|
||||||
|
self.collectionView.contentOffset = CGPoint(x: offsetX+stepLength, y: self.contentOffset.y)
|
||||||
|
|
||||||
|
let position = scrollPosition()
|
||||||
|
if self.currentIndex() == 1 {
|
||||||
|
let indexPath = IndexPath(item: numberOfItems - 3, section: 0)
|
||||||
|
collectionView.scrollToItem(at: indexPath, at: position, animated: false)
|
||||||
|
} else if self.currentIndex() == numberOfItems - 2 {
|
||||||
|
let offsetx = (width)*CGFloat(2) - width*0.5
|
||||||
|
self.collectionView.contentOffset = CGPoint(x: offsetx, y: self.contentOffset.y)
|
||||||
|
self.collectionView.reloadData()
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
let position = scrollPosition()
|
||||||
|
let indexPath = IndexPath(item: index, section: 0)
|
||||||
|
collectionView.scrollToItem(at: indexPath, at: position, animated: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeTimer() {
|
||||||
|
timer?.invalidate()
|
||||||
|
timer = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
private func scrollPosition() -> UICollectionView.ScrollPosition {
|
||||||
|
switch scrollDirection {
|
||||||
|
case .vertical:
|
||||||
|
return .centeredVertically
|
||||||
|
default:
|
||||||
|
return .centeredHorizontally
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func currentIndex() -> Int {
|
||||||
|
guard numberOfItems > 0 else {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
var index = 0
|
||||||
|
var minimumIndex = 0
|
||||||
|
var maximumIndex = numberOfItems - 1
|
||||||
|
|
||||||
|
if numberOfItems == 1 {
|
||||||
|
return index
|
||||||
|
}
|
||||||
|
|
||||||
|
if isInfiniteLoop {
|
||||||
|
minimumIndex = 1
|
||||||
|
maximumIndex = numberOfItems - 2
|
||||||
|
}
|
||||||
|
|
||||||
|
switch scrollDirection {
|
||||||
|
case .vertical:
|
||||||
|
let height = flowLayout.itemSize.height + flowLayout.minimumLineSpacing
|
||||||
|
index = Int((collectionView.contentOffset.y + height / 2) / height)
|
||||||
|
default:
|
||||||
|
let width = flowLayout.itemSize.width + flowLayout.minimumLineSpacing
|
||||||
|
index = Int((collectionView.contentOffset.x + width / 2) / width)
|
||||||
|
}
|
||||||
|
return min(maximumIndex, max(minimumIndex, index))
|
||||||
|
}
|
||||||
|
|
||||||
|
private func changeIndex(_ index: Int) -> Int {
|
||||||
|
guard isInfiniteLoop && numberOfItems > 1 else {
|
||||||
|
return index
|
||||||
|
}
|
||||||
|
|
||||||
|
var idx = index
|
||||||
|
|
||||||
|
if index == 0 {
|
||||||
|
idx = numberOfItems - 6
|
||||||
|
} else if index == 1 {
|
||||||
|
idx = numberOfItems - 5
|
||||||
|
} else if index == numberOfItems - 2 {
|
||||||
|
idx = 0
|
||||||
|
} else if index == numberOfItems - 1 {
|
||||||
|
idx = 1
|
||||||
|
} else {
|
||||||
|
idx = index - 2
|
||||||
|
}
|
||||||
|
return idx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ZKCycleScrollView: UICollectionViewDelegate {
|
||||||
|
|
||||||
|
public func collectionView(_ collectionView: UICollectionView, shouldHighlightItemAt indexPath: IndexPath) -> Bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
public func collectionView(_ collectionView: UICollectionView, didHighlightItemAt indexPath: IndexPath) {
|
||||||
|
if let delegate = delegate, delegate.responds(to: #selector(ZKCycleScrollViewDelegate.cycleScrollView(_:didSelectItemAt:))) {
|
||||||
|
removeTimer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func collectionView(_ collectionView: UICollectionView, didUnhighlightItemAt indexPath: IndexPath) {
|
||||||
|
if let delegate = delegate, delegate.responds(to: #selector(ZKCycleScrollViewDelegate.cycleScrollView(_:didSelectItemAt:))) {
|
||||||
|
addTimer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
||||||
|
if let delegate = delegate, delegate.responds(to: #selector(ZKCycleScrollViewDelegate.cycleScrollView(_:didSelectItemAt:))) {
|
||||||
|
delegate.cycleScrollView!(self, didSelectItemAt: changeIndex(indexPath.item))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||||
|
pageControl.currentPage = pageIndex
|
||||||
|
if let delegate = delegate, delegate.responds(to: #selector(ZKCycleScrollViewDelegate.cycleScrollViewDidScroll(_:progress:))) {
|
||||||
|
var total: CGFloat = 0.0
|
||||||
|
var offset: CGFloat = 0.0
|
||||||
|
let num = numberOfAddedCells()
|
||||||
|
|
||||||
|
switch scrollDirection {
|
||||||
|
case .vertical:
|
||||||
|
total = CGFloat(numberOfItems - 1 - num) * (flowLayout.itemSize.height + flowLayout.minimumLineSpacing)
|
||||||
|
offset = contentOffset.y.truncatingRemainder(dividingBy:((flowLayout.itemSize.height + flowLayout.minimumLineSpacing) * CGFloat(numberOfItems - num)))
|
||||||
|
default:
|
||||||
|
total = CGFloat(numberOfItems - 1 - num) * (flowLayout.itemSize.width + flowLayout.minimumLineSpacing)
|
||||||
|
offset = contentOffset.x.truncatingRemainder(dividingBy:((flowLayout.itemSize.width + flowLayout.minimumLineSpacing) * CGFloat(numberOfItems - num)))
|
||||||
|
}
|
||||||
|
let percent = Double(offset / total)
|
||||||
|
let progress = percent * Double(numberOfItems - 1 - num)
|
||||||
|
delegate.cycleScrollViewDidScroll!(self, progress: progress)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
|
||||||
|
removeTimer()
|
||||||
|
}
|
||||||
|
|
||||||
|
public func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
|
||||||
|
if let _ = stepLength {
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
|
||||||
|
self.addTimer()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
addTimer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
|
||||||
|
let index = currentIndex()
|
||||||
|
|
||||||
|
if isInfiniteLoop {
|
||||||
|
let position = scrollPosition()
|
||||||
|
if index == 1 {
|
||||||
|
let indexPath = IndexPath(item: numberOfItems - 3, section: 0)
|
||||||
|
collectionView.scrollToItem(at: indexPath, at: position, animated: false)
|
||||||
|
} else if index == numberOfItems - 2 {
|
||||||
|
let indexPath = IndexPath(item: 2, section: 0)
|
||||||
|
collectionView.scrollToItem(at: indexPath, at: position, animated: false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let toIndex = changeIndex(index)
|
||||||
|
if let delegate = delegate, delegate.responds(to: #selector(ZKCycleScrollViewDelegate.cycleScrollView(_:didScrollFromIndex:toIndex:))) {
|
||||||
|
delegate.cycleScrollView!(self, didScrollFromIndex: fromIndex, toIndex: toIndex)
|
||||||
|
}
|
||||||
|
fromIndex = toIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
public func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
|
||||||
|
guard pageIndex == fromIndex else { return }
|
||||||
|
|
||||||
|
let sum = velocity.x + velocity.y
|
||||||
|
if sum > 0 {
|
||||||
|
indexOffset = 1
|
||||||
|
} else if sum < 0 {
|
||||||
|
indexOffset = -1
|
||||||
|
} else {
|
||||||
|
indexOffset = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func scrollViewWillBeginDecelerating(_ scrollView: UIScrollView) {
|
||||||
|
let position = scrollPosition()
|
||||||
|
var index = currentIndex() + indexOffset
|
||||||
|
index = max(0, index)
|
||||||
|
index = min(numberOfItems - 1, index)
|
||||||
|
let indexPath = IndexPath(item: index, section: 0)
|
||||||
|
collectionView.scrollToItem(at: indexPath, at: position, animated: true)
|
||||||
|
indexOffset = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ZKCycleScrollView: UICollectionViewDataSource {
|
||||||
|
|
||||||
|
public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||||
|
numberOfItems = dataSource?.numberOfItems(in: self) ?? 0
|
||||||
|
if isInfiniteLoop && numberOfItems > 1 {
|
||||||
|
numberOfItems += numberOfAddedCells()
|
||||||
|
}
|
||||||
|
return numberOfItems
|
||||||
|
}
|
||||||
|
|
||||||
|
public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
||||||
|
let index = changeIndex(indexPath.item)
|
||||||
|
return (dataSource?.cycleScrollView(self, cellForItemAt: index))!
|
||||||
|
}
|
||||||
|
}
|
168
Veloria/Thirdparty/ZKCycleScrollView-Swift/ZKCycleScrollViewFlowLayout.swift
vendored
Normal file
168
Veloria/Thirdparty/ZKCycleScrollView-Swift/ZKCycleScrollViewFlowLayout.swift
vendored
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
//
|
||||||
|
// ZKCycleScrollViewFlowLayout.swift
|
||||||
|
// ZKCycleScrollViewDemo
|
||||||
|
//
|
||||||
|
// Created by bestdew on 2019/3/22.
|
||||||
|
// Copyright © 2019 bestdew. All rights reserved.
|
||||||
|
//
|
||||||
|
// d*##$.
|
||||||
|
// zP"""""$e. $" $o
|
||||||
|
//4$ '$ $" $
|
||||||
|
//'$ '$ J$ $F
|
||||||
|
// 'b $k $> $
|
||||||
|
// $k $r J$ d$
|
||||||
|
// '$ $ $" $~
|
||||||
|
// '$ "$ '$E $
|
||||||
|
// $ $L $" $F ...
|
||||||
|
// $. 4B $ $$$*"""*b
|
||||||
|
// '$ $. $$ $$ $F
|
||||||
|
// "$ R$ $F $" $
|
||||||
|
// $k ?$ u* dF .$
|
||||||
|
// ^$. $$" z$ u$$$$e
|
||||||
|
// #$b $E.dW@e$" ?$
|
||||||
|
// #$ .o$$# d$$$$c ?F
|
||||||
|
// $ .d$$#" . zo$> #$r .uF
|
||||||
|
// $L .u$*" $&$$$k .$$d$$F
|
||||||
|
// $$" ""^"$$$P"$P9$
|
||||||
|
// JP .o$$$$u:$P $$
|
||||||
|
// $ ..ue$" "" $"
|
||||||
|
// d$ $F $
|
||||||
|
// $$ ....udE 4B
|
||||||
|
// #$ """"` $r @$
|
||||||
|
// ^$L '$ $F
|
||||||
|
// RN 4N $
|
||||||
|
// *$b d$
|
||||||
|
// $$k $F
|
||||||
|
// $$b $F
|
||||||
|
// $"" $F
|
||||||
|
// '$ $
|
||||||
|
// $L $
|
||||||
|
// '$ $
|
||||||
|
// $ $
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
open class ZKCycleScrollViewFlowLayout: UICollectionViewFlowLayout {
|
||||||
|
|
||||||
|
open var zoomScale: CGFloat = 1.0
|
||||||
|
///item渐隐效果
|
||||||
|
open var itemAlpha = false
|
||||||
|
///旋转角度
|
||||||
|
open var rotationAngle = 0.0
|
||||||
|
|
||||||
|
override open func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override open func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
|
||||||
|
let attributes: [UICollectionViewLayoutAttributes] = NSArray(array: super.layoutAttributesForElements(in: rect) ?? [], copyItems: true) as! [UICollectionViewLayoutAttributes]
|
||||||
|
if let collectionView = collectionView {
|
||||||
|
switch scrollDirection {
|
||||||
|
case .vertical:
|
||||||
|
let offset = collectionView.bounds.midY;
|
||||||
|
let distanceForScale = itemSize.height + minimumLineSpacing;
|
||||||
|
|
||||||
|
for attr in attributes {
|
||||||
|
var scale: CGFloat = 0.0;
|
||||||
|
let distance = abs(offset - attr.center.y)
|
||||||
|
if distance >= distanceForScale {
|
||||||
|
scale = zoomScale;
|
||||||
|
} else if distance == 0.0 {
|
||||||
|
scale = 1.0
|
||||||
|
attr.zIndex = 1
|
||||||
|
} else {
|
||||||
|
scale = zoomScale + (distanceForScale - distance) * (1.0 - zoomScale) / distanceForScale
|
||||||
|
}
|
||||||
|
attr.transform = CGAffineTransform(scaleX: scale, y: scale)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
let offset = collectionView.bounds.midX;
|
||||||
|
let distanceForScale = itemSize.width + minimumLineSpacing;
|
||||||
|
|
||||||
|
|
||||||
|
for attr in attributes {
|
||||||
|
var scale: CGFloat = 0.0;
|
||||||
|
let distance = offset - attr.center.x
|
||||||
|
let abs_distance = abs(distance)
|
||||||
|
let screenScale = distance / (collectionView.bounds.width)
|
||||||
|
|
||||||
|
if abs_distance >= distanceForScale {
|
||||||
|
scale = zoomScale;
|
||||||
|
} else if abs_distance == 0.0 {
|
||||||
|
scale = 1.0
|
||||||
|
attr.zIndex = 1
|
||||||
|
} else {
|
||||||
|
scale = zoomScale + (distanceForScale - abs_distance) * (1.0 - zoomScale) / distanceForScale
|
||||||
|
}
|
||||||
|
|
||||||
|
if itemAlpha {
|
||||||
|
var alpha = 1 - abs(screenScale) + 0.1
|
||||||
|
if alpha < 0 {
|
||||||
|
alpha = 0
|
||||||
|
} else if alpha > 1 {
|
||||||
|
alpha = 1
|
||||||
|
}
|
||||||
|
attr.alpha = alpha
|
||||||
|
} else {
|
||||||
|
attr.alpha = 1
|
||||||
|
}
|
||||||
|
//缩放
|
||||||
|
let scaleTransform = CGAffineTransform(scaleX: scale, y: scale)
|
||||||
|
|
||||||
|
//旋转
|
||||||
|
// let rotationAngle = kSPAngleToRadians(angle: -screenScale * self.rotationAngle)
|
||||||
|
// let rotationTransform = CGAffineTransform(rotationAngle: rotationAngle)
|
||||||
|
//
|
||||||
|
// // 组合旋转和缩放变换
|
||||||
|
// let combinedTransform = rotationTransform.concatenating(scaleTransform)
|
||||||
|
// 应用变换
|
||||||
|
attr.transform = scaleTransform
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return attributes
|
||||||
|
}
|
||||||
|
|
||||||
|
override open func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {
|
||||||
|
var point = proposedContentOffset
|
||||||
|
if let collectionView = collectionView {
|
||||||
|
switch scrollDirection {
|
||||||
|
case .vertical:
|
||||||
|
/// 计算出最终显示的矩形框
|
||||||
|
let rect = CGRect(x: 0.0, y: point.y, width: collectionView.bounds.width, height: collectionView.bounds.height)
|
||||||
|
/// 计算collectionView最中心点的y值
|
||||||
|
let centerY = point.y + collectionView.bounds.size.height * 0.5
|
||||||
|
/// 存放最小的间距值
|
||||||
|
var minDelta = CGFloat(MAXFLOAT)
|
||||||
|
/// 获得super已经计算好的布局属性
|
||||||
|
if let attributes = super.layoutAttributesForElements(in: rect) {
|
||||||
|
for attr in attributes {
|
||||||
|
if abs(minDelta) > abs(attr.center.y - centerY) {
|
||||||
|
minDelta = attr.center.y - centerY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// 修改原有的偏移量
|
||||||
|
point.y += minDelta
|
||||||
|
default:
|
||||||
|
/// 计算出最终显示的矩形框
|
||||||
|
let rect = CGRect(x: point.x, y: 0.0, width: collectionView.bounds.width, height: collectionView.bounds.height)
|
||||||
|
/// 计算collectionView最中心点的y值
|
||||||
|
let centerX = point.x + collectionView.bounds.size.width * 0.5
|
||||||
|
/// 存放最小的间距值
|
||||||
|
var minDelta = CGFloat(MAXFLOAT)
|
||||||
|
/// 获得super已经计算好的布局属性
|
||||||
|
if let attributes = super.layoutAttributesForElements(in: rect) {
|
||||||
|
for attr in attributes {
|
||||||
|
if abs(minDelta) > abs(attr.center.x - centerX) {
|
||||||
|
minDelta = attr.center.x - centerX
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// 修改原有的偏移量
|
||||||
|
point.x += minDelta
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return point
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user