Repository: DragonTnT/appstore-clone Branch: master Commit: 7166ceba69b6 Files: 102 Total size: 261.2 KB Directory structure: gitextract_vi5lwdol/ ├── AppStoreDemo/ │ ├── AppDelegate.swift │ ├── Assets.xcassets/ │ │ ├── AppIcon.appiconset/ │ │ │ └── Contents.json │ │ ├── Contents.json │ │ ├── DS_Store │ │ ├── app_logo/ │ │ │ ├── Contents.json │ │ │ ├── logo_broadcast.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── logo_car.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── logo_game.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── logo_jump.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── logo_smile.imageset/ │ │ │ │ └── Contents.json │ │ │ └── logo_weibo.imageset/ │ │ │ └── Contents.json │ │ ├── common/ │ │ │ ├── Contents.json │ │ │ ├── circle_download.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── close_button.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── demo_icon.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── detail_download.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── detail_more.imageset/ │ │ │ │ └── Contents.json │ │ │ └── navigation_back.imageset/ │ │ │ └── Contents.json │ │ ├── cover/ │ │ │ ├── Contents.json │ │ │ ├── cover_1.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── cover_2.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── cover_3.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── cover_4.imageset/ │ │ │ │ └── Contents.json │ │ │ └── cover_5.imageset/ │ │ │ └── Contents.json │ │ ├── cover_detail/ │ │ │ ├── Contents.json │ │ │ ├── cover_detail.imageset/ │ │ │ │ └── Contents.json │ │ │ ├── cover_detail1.imageset/ │ │ │ │ └── Contents.json │ │ │ └── cover_detail2.imageset/ │ │ │ └── Contents.json │ │ └── tabBar/ │ │ ├── Contents.json │ │ ├── tabbar_apps.imageset/ │ │ │ └── Contents.json │ │ ├── tabbar_games.imageset/ │ │ │ └── Contents.json │ │ ├── tabbar_search.imageset/ │ │ │ └── Contents.json │ │ ├── tabbar_today.imageset/ │ │ │ └── Contents.json │ │ └── tabbar_updates.imageset/ │ │ └── Contents.json │ ├── Base.lproj/ │ │ └── LaunchScreen.storyboard │ ├── Common/ │ │ ├── CommonCollectionFlowLayout.swift │ │ └── CommonSectionHeaderView.swift │ ├── Extension/ │ │ ├── String+Extension.swift │ │ ├── UIColor+Extension.swift │ │ ├── UITableView+Extension.swift │ │ ├── UIView+Extension.swift │ │ └── UIViewController+Extension.swift │ ├── Game/ │ │ ├── Controller/ │ │ │ ├── DetailViewController.swift │ │ │ ├── DownloadPresentationController.swift │ │ │ ├── DownloadViewController.swift │ │ │ └── GameTableViewController.swift │ │ ├── Model/ │ │ │ ├── DownloadTransitioning.swift │ │ │ ├── GameRecommandModel.swift │ │ │ └── GameTopicModel.swift │ │ └── View/ │ │ ├── Detail/ │ │ │ ├── DetailInfomationTableViewCell.swift │ │ │ ├── DetailInformationCell.swift │ │ │ ├── DetailNavigationView.swift │ │ │ ├── DetailNewFeaturesCell.swift │ │ │ ├── DetailNewFeaturesCell.xib │ │ │ ├── DetailPreviewCell.swift │ │ │ ├── DetailPreviewCollectionView.swift │ │ │ ├── DetailPreviewCollectionViewCell.swift │ │ │ ├── DetailTopInfoCell.swift │ │ │ └── DetailTopInfoCell.xib │ │ ├── Download/ │ │ │ ├── DownloadBottomView.swift │ │ │ ├── DownloadBottomView.xib │ │ │ ├── DownloadClickView.swift │ │ │ └── DownloadClickView.xib │ │ ├── Link/ │ │ │ ├── GameLinkTableView.swift │ │ │ └── GameLinkTableViewCell.swift │ │ ├── Recommand/ │ │ │ ├── GameRecommandCollectionView.swift │ │ │ ├── GameRecommandTableViewCell.swift │ │ │ ├── RecommandCollectionViewCell.swift │ │ │ └── RecommandCollectionViewCell.xib │ │ └── Topic/ │ │ ├── GameTopicCollectionView.swift │ │ ├── GameTopicCollectionViewCell.swift │ │ ├── GameTopicCollectionViewCell.xib │ │ └── GameTopicTableViewCell.swift │ ├── Info.plist │ ├── Main.storyboard │ ├── Search/ │ │ ├── Controller/ │ │ │ └── SearchTableViewController.swift │ │ └── View/ │ │ └── SearchTableViewCell.swift │ ├── Today/ │ │ ├── Controller/ │ │ │ ├── CardDetailViewController.swift │ │ │ ├── CardPresentationController.swift │ │ │ └── TodayViewController.swift │ │ ├── Model/ │ │ │ ├── DS_Store │ │ │ └── TodayAnimationTransition.swift │ │ └── View/ │ │ ├── DetailScrollView.swift │ │ ├── TodayTableHeaderView.swift │ │ └── TodayTableViewCell.swift │ ├── Update/ │ │ ├── Controller/ │ │ │ └── UpdateTableViewController.swift │ │ ├── Model/ │ │ │ └── UpdateModel.swift │ │ └── View/ │ │ ├── UpdateTableViewCell.swift │ │ └── UpdateTableViewCell.xib │ ├── User/ │ │ ├── Controller/ │ │ │ └── UserTableViewController.swift │ │ └── View/ │ │ └── User.storyboard │ └── Utils/ │ ├── GlobalConstants.swift │ ├── GlobalFunctions.swift │ └── StarView.swift ├── AppStoreDemo.xcodeproj/ │ ├── project.pbxproj │ ├── project.xcworkspace/ │ │ ├── contents.xcworkspacedata │ │ ├── xcshareddata/ │ │ │ └── IDEWorkspaceChecks.plist │ │ └── xcuserdata/ │ │ └── fengbufang.xcuserdatad/ │ │ └── UserInterfaceState.xcuserstate │ └── xcuserdata/ │ └── fengbufang.xcuserdatad/ │ ├── xcdebugger/ │ │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes/ │ └── xcschememanagement.plist ├── README.md └── 中文简介.md ================================================ FILE CONTENTS ================================================ ================================================ FILE: AppStoreDemo/AppDelegate.swift ================================================ // // AppDelegate.swift // AppStoreDemo // // Created by Allen long on 2019/7/31. // Copyright © 2019 Utimes. All rights reserved. // import UIKit @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { setStatusBarColor(.white) return true } } ================================================ FILE: AppStoreDemo/Assets.xcassets/AppIcon.appiconset/Contents.json ================================================ { "images" : [ { "idiom" : "iphone", "size" : "20x20", "scale" : "2x" }, { "idiom" : "iphone", "size" : "20x20", "scale" : "3x" }, { "idiom" : "iphone", "size" : "29x29", "scale" : "2x" }, { "idiom" : "iphone", "size" : "29x29", "scale" : "3x" }, { "idiom" : "iphone", "size" : "40x40", "scale" : "2x" }, { "idiom" : "iphone", "size" : "40x40", "scale" : "3x" }, { "size" : "60x60", "idiom" : "iphone", "filename" : "AppStore-120.png", "scale" : "2x" }, { "size" : "60x60", "idiom" : "iphone", "filename" : "AppStore-180.png", "scale" : "3x" }, { "idiom" : "ipad", "size" : "20x20", "scale" : "1x" }, { "idiom" : "ipad", "size" : "20x20", "scale" : "2x" }, { "idiom" : "ipad", "size" : "29x29", "scale" : "1x" }, { "idiom" : "ipad", "size" : "29x29", "scale" : "2x" }, { "idiom" : "ipad", "size" : "40x40", "scale" : "1x" }, { "idiom" : "ipad", "size" : "40x40", "scale" : "2x" }, { "idiom" : "ipad", "size" : "76x76", "scale" : "1x" }, { "idiom" : "ipad", "size" : "76x76", "scale" : "2x" }, { "idiom" : "ipad", "size" : "83.5x83.5", "scale" : "2x" }, { "idiom" : "ios-marketing", "size" : "1024x1024", "scale" : "1x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: AppStoreDemo/Assets.xcassets/Contents.json ================================================ { "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: AppStoreDemo/Assets.xcassets/app_logo/Contents.json ================================================ { "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: AppStoreDemo/Assets.xcassets/app_logo/logo_broadcast.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "filename" : "logo_broadcast.jpeg", "scale" : "1x" }, { "idiom" : "universal", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: AppStoreDemo/Assets.xcassets/app_logo/logo_car.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "filename" : "logo_car.jpg", "scale" : "1x" }, { "idiom" : "universal", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: AppStoreDemo/Assets.xcassets/app_logo/logo_game.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "filename" : "logo_game.jpg", "scale" : "1x" }, { "idiom" : "universal", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: AppStoreDemo/Assets.xcassets/app_logo/logo_jump.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "filename" : "logo_jump.jpg", "scale" : "1x" }, { "idiom" : "universal", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: AppStoreDemo/Assets.xcassets/app_logo/logo_smile.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "filename" : "logo_smile.jpg", "scale" : "1x" }, { "idiom" : "universal", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: AppStoreDemo/Assets.xcassets/app_logo/logo_weibo.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "filename" : "logo_weibo.jpeg", "scale" : "1x" }, { "idiom" : "universal", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: AppStoreDemo/Assets.xcassets/common/Contents.json ================================================ { "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: AppStoreDemo/Assets.xcassets/common/circle_download.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "scale" : "1x" }, { "idiom" : "universal", "filename" : "circle_download@2x.png", "scale" : "2x" }, { "idiom" : "universal", "filename" : "circle_download@3x.png", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: AppStoreDemo/Assets.xcassets/common/close_button.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "scale" : "1x" }, { "idiom" : "universal", "filename" : "close_button@2x.png", "scale" : "2x" }, { "idiom" : "universal", "filename" : "close_button@3x.png", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: AppStoreDemo/Assets.xcassets/common/demo_icon.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "filename" : "demo_icon.png", "scale" : "1x" }, { "idiom" : "universal", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: AppStoreDemo/Assets.xcassets/common/detail_download.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "scale" : "1x" }, { "idiom" : "universal", "filename" : "detail_download@2x.png", "scale" : "2x" }, { "idiom" : "universal", "filename" : "detail_download@3x.png", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: AppStoreDemo/Assets.xcassets/common/detail_more.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "scale" : "1x" }, { "idiom" : "universal", "filename" : "detail_more@2x.png", "scale" : "2x" }, { "idiom" : "universal", "filename" : "detail_more@3x.png", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: AppStoreDemo/Assets.xcassets/common/navigation_back.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "scale" : "1x" }, { "idiom" : "universal", "scale" : "2x" }, { "idiom" : "universal", "filename" : "navigation_back.png", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: AppStoreDemo/Assets.xcassets/cover/Contents.json ================================================ { "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: AppStoreDemo/Assets.xcassets/cover/cover_1.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "filename" : "cover_1.jpeg", "scale" : "1x" }, { "idiom" : "universal", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: AppStoreDemo/Assets.xcassets/cover/cover_2.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "filename" : "cover_2.jpeg", "scale" : "1x" }, { "idiom" : "universal", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: AppStoreDemo/Assets.xcassets/cover/cover_3.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "filename" : "cover_3.jpeg", "scale" : "1x" }, { "idiom" : "universal", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: AppStoreDemo/Assets.xcassets/cover/cover_4.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "filename" : "cover_4.jpeg", "scale" : "1x" }, { "idiom" : "universal", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: AppStoreDemo/Assets.xcassets/cover/cover_5.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "filename" : "cover_5.jpeg", "scale" : "1x" }, { "idiom" : "universal", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: AppStoreDemo/Assets.xcassets/cover_detail/Contents.json ================================================ { "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: AppStoreDemo/Assets.xcassets/cover_detail/cover_detail.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "filename" : "cover_detail.jpg", "scale" : "1x" }, { "idiom" : "universal", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: AppStoreDemo/Assets.xcassets/cover_detail/cover_detail1.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "filename" : "cover_detail1.jpg", "scale" : "1x" }, { "idiom" : "universal", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: AppStoreDemo/Assets.xcassets/cover_detail/cover_detail2.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "filename" : "cover_detail2.jpg", "scale" : "1x" }, { "idiom" : "universal", "scale" : "2x" }, { "idiom" : "universal", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: AppStoreDemo/Assets.xcassets/tabBar/Contents.json ================================================ { "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: AppStoreDemo/Assets.xcassets/tabBar/tabbar_apps.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "scale" : "1x" }, { "idiom" : "universal", "filename" : "tabbar_apps@2x.png", "scale" : "2x" }, { "idiom" : "universal", "filename" : "tabbar_apps@3x.png", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: AppStoreDemo/Assets.xcassets/tabBar/tabbar_games.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "scale" : "1x" }, { "idiom" : "universal", "filename" : "tabbar_games@2x.png", "scale" : "2x" }, { "idiom" : "universal", "filename" : "tabbar_games@3x.png", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: AppStoreDemo/Assets.xcassets/tabBar/tabbar_search.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "scale" : "1x" }, { "idiom" : "universal", "filename" : "tabbar_search@2x.png", "scale" : "2x" }, { "idiom" : "universal", "filename" : "tabbar_search@3x.png", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: AppStoreDemo/Assets.xcassets/tabBar/tabbar_today.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "scale" : "1x" }, { "idiom" : "universal", "filename" : "tabbar_today@2x.png", "scale" : "2x" }, { "idiom" : "universal", "filename" : "tabbar_today@3x.png", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: AppStoreDemo/Assets.xcassets/tabBar/tabbar_updates.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "scale" : "1x" }, { "idiom" : "universal", "filename" : "tabbar_updates@2x.png", "scale" : "2x" }, { "idiom" : "universal", "filename" : "tabbar_updates@3x.png", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: AppStoreDemo/Base.lproj/LaunchScreen.storyboard ================================================ ================================================ FILE: AppStoreDemo/Common/CommonCollectionFlowLayout.swift ================================================ // // GameCollectionFlowLayout.swift // AppStoreDemo // // Created by Allen long on 2019/8/6. // Copyright © 2019 Utimes. All rights reserved. // import UIKit class CommonCollectionFlowLayout: UICollectionViewFlowLayout { init(itemSize: CGSize) { super.init() self.itemSize = itemSize setupUI() } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } private func setupUI() { scrollDirection = UICollectionView.ScrollDirection.horizontal minimumInteritemSpacing = 0 minimumLineSpacing = 10 sectionInset = UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 20) } override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint { // Page width used for estimating and calculating paging. let pageWidth = itemSize.width + minimumLineSpacing // Make an estimation of the current page position. let approximatePage = collectionView!.contentOffset.x/pageWidth // Determine the current page based on velocity. let currentPage = (velocity.x < 0.0) ? floor(approximatePage) : ceil(approximatePage) // Create custom flickVelocity. let flickVelocity = velocity.x * 0.3 // Check how many pages the user flicked, if <= 1 then flickedPages should return 0. let flickedPages = (abs(round(flickVelocity)) <= 1) ? 0 : round(flickVelocity) // Calculate newHorizontalOffset. let newHorizontalOffset = ((currentPage + flickedPages) * pageWidth) - self.collectionView!.contentInset.left return CGPoint(x: newHorizontalOffset, y: proposedContentOffset.y) } } ================================================ FILE: AppStoreDemo/Common/CommonSectionHeaderView.swift ================================================ // // CommonSectionHeaderView.swift // AppStoreDemo // // Created by Allen long on 2019/8/9. // Copyright © 2019 Utimes. All rights reserved. // import UIKit class CommonSectionHeaderView: UIView { let topicLabel = UILabel() let seeAllBtn = UIButton() let lineView = UIView() override init(frame: CGRect) { super.init(frame: frame) setupUI() } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } private func setupUI() { lineView.backgroundColor = GlobalConstants.speratorLineColor lineView.frame = CGRect(x: GlobalConstants.leftMargin, y: 0, width: kScreenW - 2 * GlobalConstants.leftMargin, height: 0.8) topicLabel.frame = CGRect(x: 20, y: 13, width: 200, height: 24) topicLabel.font = UIFont.systemFont(ofSize: 22.0, weight: .medium) topicLabel.textColor = UIColor.black seeAllBtn.setTitleColor(GlobalConstants.textBlueColor, for: .normal) seeAllBtn.setTitle("See All", for: .normal) seeAllBtn.titleLabel?.font = UIFont.systemFont(ofSize: 17) seeAllBtn.frame = CGRect(x: kScreenW - GlobalConstants.leftMargin - 72, y: 16, width: 72, height: 22) addSubview(lineView) addSubview(topicLabel) addSubview(seeAllBtn) } func changeSectionTitle(with title: String) { topicLabel.text = title } } ================================================ FILE: AppStoreDemo/Extension/String+Extension.swift ================================================ // // String+Extension.swift // AppStoreDemo // // Created by Allen long on 2019/8/2. // Copyright © 2019 Utimes. All rights reserved. // import UIKit extension String { /// Calculate text's height from `width` and `front`. func calculateHeightWith(width: CGFloat, font: UIFont)-> CGFloat { let attr = [NSAttributedString.Key.font: font] let maxSize: CGSize = CGSize(width: width, height: CGFloat(MAXFLOAT)) let option = NSStringDrawingOptions.usesLineFragmentOrigin return self.boundingRect(with: (maxSize), options: option, attributes: attr, context: nil).size.height } } ================================================ FILE: AppStoreDemo/Extension/UIColor+Extension.swift ================================================ // // UIColor+Extension.swift // AppStoreDemo // // Created by Allen long on 2019/8/5. // Copyright © 2019 Utimes. All rights reserved. // import UIKit extension UIColor { convenience init(r: CGFloat, g: CGFloat, b: CGFloat, alpha: CGFloat = 1.0) { self.init(red: r/255.0, green: g/255.0, blue: b/255.0, alpha: alpha) } } ================================================ FILE: AppStoreDemo/Extension/UITableView+Extension.swift ================================================ // // UITableView+Extension.swift // AppStoreDemo // // Created by Allen long on 2019/8/5. // Copyright © 2019 Utimes. All rights reserved. // import UIKit extension UITableView { // 注册有nib的cell func ut_registerNibCell(_ cellType: T.Type) where T: UITableViewCell { let nib = UINib(nibName: "\(cellType)", bundle: nil) let identifier = "\(cellType)" register(nib, forCellReuseIdentifier: identifier) } // 注册无nib的cell func ut_registerClassCell(_ cellType: T.Type) where T: UITableViewCell { let identifier = "\(cellType)" register(cellType, forCellReuseIdentifier: identifier) } // 从缓存池池出队已经存在的 cell func ut_dequeueReusable(_ cell: T.Type, for indexPath: IndexPath) -> T { let cell = dequeueReusableCell(withIdentifier: "\(T.self)", for: indexPath) as! T return cell } // 注册有nib的headerFooterView func ut_registerNibHeaderFooterView(_ viewType: T.Type) where T: UITableViewHeaderFooterView { let nib = UINib(nibName: "\(viewType)", bundle: nil) let identifier = "\(viewType)" register(nib, forHeaderFooterViewReuseIdentifier: identifier) } // 注册无nib的headerFooterView func ut_registerClassHeaderFooterView(_ viewType: T.Type) where T: UITableViewHeaderFooterView { let identifier = "\(viewType)" register(viewType, forHeaderFooterViewReuseIdentifier: identifier) } // 从缓存池里取出已注册的headerFooterView func ut_dequeueReusableHeaderFooterView(_ view: T.Type) -> T { let headerFooterView = dequeueReusableHeaderFooterView(withIdentifier: "\(T.self)") as! T return headerFooterView } } extension UICollectionView { // 注册有nib的cell func ut_registerNibCell(_ cellType: T.Type) where T: UICollectionViewCell { let nib = UINib(nibName: "\(cellType)", bundle: nil) let identifier = "\(cellType)" register(nib, forCellWithReuseIdentifier: identifier) } // 注册无nib的cell func ut_registerClassCell(_ cellType: T.Type) where T: UICollectionViewCell { let identifier = "\(cellType)" register(cellType, forCellWithReuseIdentifier: identifier) } // 从缓存池池出队已经存在的 cell func ut_dequeueReusable(_ cell: T.Type, for indexPath: IndexPath) -> T { let cell = dequeueReusableCell(withReuseIdentifier: "\(T.self)", for: indexPath) as! T return cell } } ================================================ FILE: AppStoreDemo/Extension/UIView+Extension.swift ================================================ // // UIView+Extension.swift // AppStoreDemo // // Created by Allen long on 2019/8/2. // Copyright © 2019 Utimes. All rights reserved. // import UIKit protocol NibLoadable {} extension NibLoadable where Self: UIView { static func loadViewFromNib() -> Self { return Bundle.main.loadNibNamed("\(self)", owner: nil, options: nil)?.first as! Self } } extension UIView { // Constrain 4 edges of `self` to specified `view`. func edges(to view: UIView, top: CGFloat=0, left: CGFloat=0, bottom: CGFloat=0, right: CGFloat=0) { NSLayoutConstraint.activate([ self.leftAnchor.constraint(equalTo: view.leftAnchor, constant: left), self.rightAnchor.constraint(equalTo: view.rightAnchor, constant: right), self.topAnchor.constraint(equalTo: view.topAnchor, constant: top), self.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: bottom) ]) } // Set view's cornerRadius @IBInspectable var cornerRadius: CGFloat { get { return layer.cornerRadius } set { layer.cornerRadius = newValue layer.masksToBounds = newValue > 0 } } @IBInspectable var borderWidth: CGFloat { get { return layer.borderWidth } set { layer.borderWidth = newValue } } @IBInspectable var borderColor: UIColor { get { return UIColor(cgColor: layer.borderColor ?? UIColor.black.cgColor) } set { layer.borderColor = newValue.cgColor } } } ================================================ FILE: AppStoreDemo/Extension/UIViewController+Extension.swift ================================================ // // UIViewController+Extension.swift // AppStoreDemo // // Created by Erwin on 2019/8/4. // Copyright © 2019 Utimes. All rights reserved. // import UIKit extension UIViewController { private struct associateKeys { static var iconButtonKey = "UIViewController+Extension+iconButton" } // We use `runtime` to add a iconButton for every UIViewController we want var iconButton: UIButton? { get { return objc_getAssociatedObject(self, &associateKeys.iconButtonKey) as? UIButton } set { objc_setAssociatedObject(self, &associateKeys.iconButtonKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } func setNavigationBarBottomLineHidden(_ isHidden: Bool) { navigationController?.navigationBar.setValue(isHidden, forKey: "hidesShadow") } /** Add icon button on navigationBar. We can not use `UIBarButtonItem` here for adding a button on navigationBar, because it will cause an unsuitable layout. So I use a custom button instead. */ func addIconButtonOnNavigationBar() { guard let navController = navigationController else { return } guard let classType = NSClassFromString("_UINavigationBarLargeTitleView") else { return } if iconButton == nil { iconButton = createIconButtonForNavigationBar() } for subView in navController.navigationBar.subviews { if subView.isKind(of: classType) { subView.addSubview(iconButton!) guard let largeTitleLabel = subView.subviews.first as? UILabel else { return } iconButton!.translatesAutoresizingMaskIntoConstraints = false iconButton!.trailingAnchor.constraint(equalTo: subView.trailingAnchor, constant: -25).isActive = true iconButton!.centerYAnchor.constraint(equalTo: largeTitleLabel.centerYAnchor, constant: -5).isActive = true iconButton!.widthAnchor.constraint(equalToConstant: 35).isActive = true iconButton!.heightAnchor.constraint(equalToConstant: 35).isActive = true } } } // change navigationBar backgroundColor for iOS 13 func adjustNavigationForiOS13() { if #available(iOS 13.0, *) { let navBarAppearance = UINavigationBarAppearance() navBarAppearance.configureWithOpaqueBackground() navBarAppearance.backgroundColor = .white navBarAppearance.shadowColor = nil navigationController?.navigationBar.standardAppearance = navBarAppearance navigationController?.navigationBar.scrollEdgeAppearance = navBarAppearance } } // creat a button for navigationBar private func createIconButtonForNavigationBar()-> UIButton { let btn = UIButton() btn.setImage(#imageLiteral(resourceName: "demo_icon"), for: .normal) btn.setImage(#imageLiteral(resourceName: "demo_icon"), for: .highlighted) btn.translatesAutoresizingMaskIntoConstraints = false btn.layer.borderColor = GlobalConstants.iconBorderColor btn.layer.borderWidth = GlobalConstants.iconBorderWidth btn.layer.cornerRadius = GlobalConstants.iconCornerRadius btn.addTarget(self, action: #selector(presentUserTableViewController), for: .touchUpInside) return btn } @objc func presentUserTableViewController() { let navController = UIStoryboard(name: "User", bundle: nil).instantiateViewController(withIdentifier: "UserNavigationControllerID") present(navController, animated: true, completion: nil) } } extension UIResponder { func setStatusBarColor(_ color: UIColor) { if #available(iOS 13.0, *) { let tag = 38482 let keyWindow = UIApplication.shared.windows.filter {$0.isKeyWindow}.first if let statusBar = keyWindow?.viewWithTag(tag) { statusBar.backgroundColor = color } else { guard let statusBarFrame = keyWindow?.windowScene?.statusBarManager?.statusBarFrame else { return } let statusBarView = UIView(frame: statusBarFrame) statusBarView.tag = tag statusBarView.backgroundColor = color keyWindow?.addSubview(statusBarView) } } else if let statusBar = UIApplication.shared.value(forKeyPath: "statusBarWindow.statusBar") as? UIView { statusBar.backgroundColor = color } } } ================================================ FILE: AppStoreDemo/Game/Controller/DetailViewController.swift ================================================ // // DetailViewController.swift // AppStoreDemo // // Created by Allen long on 2019/8/7. // Copyright © 2019 Utimes. All rights reserved. // import UIKit fileprivate let navigationViewH: CGFloat = statusBarH + navigationBarH fileprivate let tableViewTopInset: CGFloat = 200 fileprivate let topImageViewHeight: CGFloat = 288 fileprivate let originalContentOffSetY: CGFloat = tableViewTopInset + statusBarH fileprivate let alphaChangeProgress: CGFloat = (hasTopNotch() ? 112 : 84) class DetailViewController: UIViewController { lazy var navigationView: DetailNavigationView = { let it = DetailNavigationView() it.frame = CGRect(x: 0, y: 0, width: kScreenW, height: navigationViewH) return it }() lazy var tableView: UITableView = { let it = UITableView() it.frame = CGRect(x: 0, y: -statusBarH, width: kScreenW, height: kScreenH + statusBarH) it.backgroundColor = .white it.delegate = self it.dataSource = self it.contentInset = UIEdgeInsets(top: tableViewTopInset, left: 0, bottom: 0, right: 0) it.separatorInset = UIEdgeInsets(top: 0, left: GlobalConstants.leftMargin, bottom: 0, right: GlobalConstants.leftMargin) it.ut_registerNibCell(DetailTopInfoCell.self) it.ut_registerNibCell(DetailNewFeaturesCell.self) it.ut_registerClassCell(DetailPreviewCell.self) it.ut_registerClassCell(DetailInformationCell.self) return it }() lazy var topImageView: UIImageView = { let it = UIImageView() it.image = #imageLiteral(resourceName: "cover_detail") it.contentMode = .scaleAspectFill it.frame = CGRect(x: 0, y: -(topImageViewHeight) , width: kScreenW, height: topImageViewHeight) it.layer.masksToBounds = true return it }() override func viewDidLoad() { super.viewDidLoad() navigationController?.interactivePopGestureRecognizer?.delegate = nil tableView.addSubview(topImageView) view.addSubview(tableView) view.addSubview(navigationView) navigationView.goBackClosure = { [weak self] in guard let StrongSelf = self else { return } StrongSelf.navigationController?.popViewController(animated: true) } } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) navigationController?.setNavigationBarHidden(true, animated: true) setStatusBarColor(UIColor.white.withAlphaComponent(0)) } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) navigationController?.setNavigationBarHidden(false, animated: true) } override func viewDidDisappear(_ animated: Bool) { super.viewDidDisappear(animated) setStatusBarColor(.white) } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) navigationController?.interactivePopGestureRecognizer?.isEnabled = true } } extension DetailViewController: UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 4 } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { switch indexPath.row { case 0: let cell = tableView.ut_dequeueReusable(DetailTopInfoCell.self, for: indexPath) cell.selectionStyle = .none return cell case 1: let cell = tableView.ut_dequeueReusable(DetailNewFeaturesCell.self, for: indexPath) cell.selectionStyle = .none return cell case 2: let cell = tableView.ut_dequeueReusable(DetailPreviewCell.self, for: indexPath) cell.selectionStyle = .none return cell case 3: let cell = tableView.ut_dequeueReusable(DetailInformationCell.self, for: indexPath) cell.selectionStyle = .none return cell default: return UITableViewCell() } } } extension DetailViewController: UITableViewDelegate { func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { switch indexPath.row { case 0: return 230 case 1: return 168 case 2: return 227 case 3: return 520 default: return 0 } } func scrollViewDidScroll(_ scrollView: UIScrollView) { var offSetY = scrollView.contentOffset.y // change topImageView frame if offSetY < -originalContentOffSetY { topImageView.frame.origin.y = offSetY - (hasTopNotch() ? 44 : 64) topImageView.frame.size.height = -(offSetY - (hasTopNotch() ? 44 : 64)) } // change navigationView backgroundColor and goBackBtn color //deal with offsetY to make sure alpha from 0 to 1 if offSetY > -(originalContentOffSetY - alphaChangeProgress) { offSetY = -(originalContentOffSetY - alphaChangeProgress) } else if offSetY < -originalContentOffSetY { offSetY = -originalContentOffSetY } let calculateY = offSetY + originalContentOffSetY navigationView.backgroundColor = UIColor.white.withAlphaComponent(calculateY/alphaChangeProgress) // values of targetRed and targetGreen are from GlobalConstants.textBlueColor // blue is still 255.0, so we do not neet to change it let targetRed: CGFloat = 0 let targetGreen: CGFloat = 122 let color = UIColor(red: (255 - ((255.0 - targetRed)/alphaChangeProgress) * calculateY) / 255.0, green: (255 - ((255.0 - targetGreen)/alphaChangeProgress) * calculateY) / 255.0, blue: 255.0/255.0, alpha: 1) navigationView.goBackBtn.setTitleColor(color, for: .normal) navigationView.goBackBtn.tintColor = color } } ================================================ FILE: AppStoreDemo/Game/Controller/DownloadPresentationController.swift ================================================ // // DownloadPresentationController.swift // AppStoreDemo // // Created by Allen long on 2019/9/5. // Copyright © 2019 Utimes. All rights reserved. // import UIKit class DownloadPresentationController: UIPresentationController { private lazy var dimmingView: UIView = { let view = UIView() view.backgroundColor = UIColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.7) view.alpha = 0 return view }() override var shouldRemovePresentersView: Bool { return false } override func presentationTransitionWillBegin() { guard let containerView = containerView, let presentedView = presentedView else { return } dimmingView.frame = containerView.bounds containerView.addSubview(dimmingView) containerView.addSubview(presentedView) presentingViewController.beginAppearanceTransition(false, animated: false) presentedViewController.transitionCoordinator!.animate(alongsideTransition: { (ctx) in self.dimmingView.alpha = 1 }) { (ctx) in } } override func presentationTransitionDidEnd(_ completed: Bool) { presentingViewController.endAppearanceTransition() } override func dismissalTransitionWillBegin() { presentingViewController.beginAppearanceTransition(true, animated: true) presentedViewController.transitionCoordinator!.animate(alongsideTransition: { (ctx) in self.dimmingView.alpha = 0.0 }, completion: nil) } override func dismissalTransitionDidEnd(_ completed: Bool) { presentingViewController.endAppearanceTransition() if completed { dimmingView.removeFromSuperview() } } } ================================================ FILE: AppStoreDemo/Game/Controller/DownloadViewController.swift ================================================ // // DownloadViewController.swift // AppStoreDemo // // Created by Allen long on 2019/9/5. // Copyright © 2019 Utimes. All rights reserved. // import UIKit class DownloadViewController: UIViewController { var model: GameTopicModel? override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { super.init(nibName: nil, bundle: nil) modalPresentationStyle = .custom transitioningDelegate = self } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) setStatusBarColor(UIColor.white.withAlphaComponent(0)) } override func viewDidDisappear(_ animated: Bool) { super.viewDidDisappear(animated) setStatusBarColor(.white) } override func viewDidLoad() { super.viewDidLoad() if let model = model { bottomView.model = model } view.addSubview(bottomView) if #available(iOS 13.0, *) { //在iOS13上,获取keywindow后,并不能将clickView加到屏幕最上方,因此将它加到view上。但这时的弹出动画就与系统不一样了。 //如果有知道如何做的朋友,麻烦给我提个issue,谢了 view.addSubview(clickView) } else { keyWindow.addSubview(clickView) } } private lazy var bottomView: DownloadBottomView = { let view = DownloadBottomView.loadViewFromNib() let height = 260 + tabbarExtraH view.frame = CGRect(x: 0, y: kScreenH - height, width: kScreenW, height: height) view.delegate = self return view }() lazy var clickView: DownloadClickView = { let view = DownloadClickView.loadViewFromNib() view.frame = CGRect(x: kScreenW, y: 168, width: GlobalConstants.doubleClickViewW, height: 110) return view }() } extension DownloadViewController: DownloadBottomViewDelegate { func downloadBottomViewDidClickCancel(_ bottomView: DownloadBottomView) { dismiss(animated: true, completion: nil) } } extension DownloadViewController: UIViewControllerTransitioningDelegate { func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? { if presented == self { return DownloadPresentationController(presentedViewController: presented, presenting: presenting) } return nil } func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? { return DownloadTransitioning(isPresenting: true, transitionDuration: 0.3) } func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { return DownloadTransitioning(isPresenting: false, transitionDuration: 0.3) } } ================================================ FILE: AppStoreDemo/Game/Controller/GameTableViewController.swift ================================================ // // GameTableViewController.swift // AppStoreDemo // // Created by Allen long on 2019/8/6. // Copyright © 2019 Utimes. All rights reserved. // import UIKit class GameTableViewController: UITableViewController { override func viewDidLoad() { super.viewDidLoad() adjustNavigationForiOS13() setNavigationBarBottomLineHidden(true) addIconButtonOnNavigationBar() registerCells() } private func registerCells() { tableView.ut_registerClassCell(GameRecommandTableViewCell.self) tableView.ut_registerClassCell(GameTopicTableViewCell.self) tableView.ut_registerClassCell(GameLinkTableViewCell.self) } private func pushToDetailController() { navigationController?.pushViewController(DetailViewController(), animated: true) } // MARK: - Table view data source and delegate override func numberOfSections(in tableView: UITableView) -> Int { return 3 } override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { if indexPath.section == 0 { return GlobalConstants.recommandHeight } else if indexPath.section == 1 { return GlobalConstants.topicHeight } else if indexPath.section == 2 { return GlobalConstants.linkHeight } return 0 } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { // #warning Incomplete implementation, return the number of rows if section == 0 || section == 1 || section == 2 { return 1 } return 0 } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { if indexPath.section == 0 { let cell = tableView.ut_dequeueReusable(GameRecommandTableViewCell.self, for: indexPath) cell.detailClosure = { self.pushToDetailController() } return cell } else if indexPath.section == 1 { let cell = tableView.ut_dequeueReusable(GameTopicTableViewCell.self, for: indexPath) cell.detailClosure = { self.pushToDetailController() } cell.downloadClosure = { model in let vc = DownloadViewController() vc.model = model self.present(vc, animated: true, completion: nil) } return cell } else if indexPath.section == 2 { let cell = tableView.ut_dequeueReusable(GameLinkTableViewCell.self, for: indexPath) return cell } return UITableViewCell() } override func scrollViewDidScroll(_ scrollView: UIScrollView) { if scrollView.contentOffset.y > 0 { setNavigationBarBottomLineHidden(false) } else { setNavigationBarBottomLineHidden(true) } } } ================================================ FILE: AppStoreDemo/Game/Model/DownloadTransitioning.swift ================================================ // // DownloadTransitioning.swift // AppStoreDemo // // Created by Allen long on 2019/9/5. // Copyright © 2019 Utimes. All rights reserved. // import UIKit class DownloadTransitioning: NSObject { let isPresenting: Bool let transitionDuration: TimeInterval //a height make animation more smoother when dismissing let dismissHeight: CGFloat? init(isPresenting: Bool, transitionDuration: TimeInterval, dismissHeight: CGFloat? = nil) { self.isPresenting = isPresenting self.transitionDuration = transitionDuration self.dismissHeight = dismissHeight super.init() } } extension DownloadTransitioning: UIViewControllerAnimatedTransitioning { func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { return transitionDuration } func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { if isPresenting { animationForPresent(using: transitionContext) } else { animationForDismiss(using: transitionContext) } } } // MARK: - Helper extension DownloadTransitioning { private func animationForPresent(using transitionContext: UIViewControllerContextTransitioning) { guard let presentedVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to) as? DownloadViewController else { return } guard let presentedView = transitionContext.view(forKey: UITransitionContextViewKey.to) else { return } let containerView = transitionContext.containerView // Position the presented view off the top of the container view presentedView.frame = transitionContext.finalFrame(for: presentedVC) presentedView.center.y += containerView.bounds.size.height containerView.addSubview(presentedView) UIView.animate(withDuration: transitionDuration, animations: { presentedView.center.y -= containerView.bounds.size.height presentedVC.clickView.frame.origin.x -= clickViewShowH }) { (completed) in transitionContext.completeTransition(completed) } delay(0.5) { presentedVC.clickView.startMoving() } } private func animationForDismiss(using transitionContext: UIViewControllerContextTransitioning) { guard let presentedVC = transitionContext.viewController(forKey: .from) as? DownloadViewController else { return } guard let presentedView = transitionContext.view(forKey: UITransitionContextViewKey.from) else { return } let containerView = transitionContext.containerView UIView.animate(withDuration: transitionDuration, animations: { if let height = self.dismissHeight { presentedView.center.y += height } else { presentedView.center.y += containerView.bounds.size.height } presentedVC.clickView.frame.origin.x += clickViewShowH }) { (completed) in transitionContext.completeTransition(completed) presentedVC.clickView.removeFromSuperview() } presentedVC.clickView.endMoving() } } fileprivate let clickViewShowH: CGFloat = GlobalConstants.doubleClickViewW - GlobalConstants.clickBarWidth ================================================ FILE: AppStoreDemo/Game/Model/GameRecommandModel.swift ================================================ // // GameRecommandModel.swift // AppStoreDemo // // Created by Allen long on 2019/8/6. // Copyright © 2019 Utimes. All rights reserved. // import Foundation struct GameRecommandModel { let feature: String let name: String let desc: String let coverImageName: String } ================================================ FILE: AppStoreDemo/Game/Model/GameTopicModel.swift ================================================ // // GameTopicModel.swift // AppStoreDemo // // Created by Allen long on 2019/8/6. // Copyright © 2019 Utimes. All rights reserved. // import Foundation struct GameTopicModel { let name: String let desc: String let iconImageName: String } ================================================ FILE: AppStoreDemo/Game/View/Detail/DetailInfomationTableViewCell.swift ================================================ // // DetailInfomationTableViewCell.swift // AppStoreDemo // // Created by Allen long on 2019/8/9. // Copyright © 2019 Utimes. All rights reserved. // import UIKit class DetailInfomationTableViewCell: UITableViewCell { let nameLabel = UILabel() let contentLabel = UILabel() override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) setupUI() } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } private func setupUI() { nameLabel.textColor = .lightGray nameLabel.font = UIFont.systemFont(ofSize: 15) nameLabel.textAlignment = .left contentLabel.textColor = UIColor.black contentLabel.font = UIFont.systemFont(ofSize: 15) contentLabel.textAlignment = .right nameLabel.translatesAutoresizingMaskIntoConstraints = false contentView.addSubview(nameLabel) contentLabel.translatesAutoresizingMaskIntoConstraints = false contentView.addSubview(contentLabel) nameLabel.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true nameLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20).isActive = true contentLabel.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true contentLabel.rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: -20).isActive = true contentLabel.leadingAnchor.constraint(greaterThanOrEqualTo: nameLabel.trailingAnchor, constant: 30).isActive = true nameLabel.setContentHuggingPriority(UILayoutPriority(rawValue: 252), for: .horizontal) contentLabel.setContentCompressionResistancePriority(UILayoutPriority(749), for: .horizontal) } } ================================================ FILE: AppStoreDemo/Game/View/Detail/DetailInformationCell.swift ================================================ // // DetailInformationCell.swift // AppStoreDemo // // Created by Allen long on 2019/8/9. // Copyright © 2019 Utimes. All rights reserved. // import UIKit class DetailInformationCell: UITableViewCell { override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) contentView.addSubview(headerView) contentView.addSubview(tableView) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } private lazy var headerView: CommonSectionHeaderView = { let frame = CGRect(x: 0, y: 0, width: kScreenW, height: 52) let it = CommonSectionHeaderView(frame: frame) it.changeSectionTitle(with: "Information") it.lineView.isHidden = true return it }() private lazy var tableView: UITableView = { let frame = CGRect(x: 0, y: self.headerView.frame.height, width: kScreenW, height: 400) let it = UITableView(frame: frame) it.separatorInset = UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 20) it.dataSource = self it.ut_registerClassCell(DetailInfomationTableViewCell.self) it.rowHeight = 50 return it }() } extension DetailInformationCell: UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return dataSource.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.ut_dequeueReusable(DetailInfomationTableViewCell.self, for: indexPath) cell.nameLabel.text = dataSource[indexPath.row].0 cell.contentLabel.text = dataSource[indexPath.row].1 cell.selectionStyle = .none return cell } } fileprivate let dataSource: [(String,String)] = [ ("Seller", "Hangzhou NetEase Leihuo Technology Co., Ltd."), ("Size", "2.5GB"), ("Category", "Games: Strategy"), ("Compatibility", "Works on this iphone"), ("Languages", "Simplified Chinese"), ("Age Rating", "9+"), ("In-App Purchases", "Yes"), ("Copyright", "©1997-2019 网易公司版权所有") ] ================================================ FILE: AppStoreDemo/Game/View/Detail/DetailNavigationView.swift ================================================ // // DetailNavigationView.swift // AppStoreDemo // // Created by Allen long on 2019/8/7. // Copyright © 2019 Utimes. All rights reserved. // import UIKit class DetailNavigationView: UIView { var goBackClosure: (()->())? let goBackBtn = UIButton() override init(frame: CGRect) { super.init(frame: frame) setupUI() backgroundColor = UIColor.white.withAlphaComponent(0) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } private func setupUI() { goBackBtn.setTitle("Games", for: .normal) goBackBtn.setTitleColor(UIColor.white, for: .normal) let image = UIImage(named: "navigation_back")?.withRenderingMode(.alwaysTemplate) goBackBtn.setImage(image, for: .normal) goBackBtn.tintColor = UIColor.white goBackBtn.frame = CGRect(x: 5, y: statusBarH + 5, width: 80, height: 30) goBackBtn.addTarget(self, action: #selector(goBackAction), for: .touchUpInside) addSubview(goBackBtn) } @objc private func goBackAction() { goBackClosure?() } } ================================================ FILE: AppStoreDemo/Game/View/Detail/DetailNewFeaturesCell.swift ================================================ // // DetailNewFeaturesCell.swift // AppStoreDemo // // Created by Allen long on 2019/8/8. // Copyright © 2019 Utimes. All rights reserved. // import UIKit class DetailNewFeaturesCell: UITableViewCell { 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 } } ================================================ FILE: AppStoreDemo/Game/View/Detail/DetailNewFeaturesCell.xib ================================================ ================================================ FILE: AppStoreDemo/Game/View/Detail/DetailPreviewCell.swift ================================================ // // DetailPreviewCell.swift // AppStoreDemo // // Created by Allen long on 2019/8/8. // Copyright © 2019 Utimes. All rights reserved. // import UIKit class DetailPreviewCell: UITableViewCell { override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) setupUI() } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } private func setupUI() { contentView.addSubview(headerView) contentView.addSubview(collectionView) } private lazy var headerView: CommonSectionHeaderView = { let frame = CGRect(x: 0, y: 0, width: kScreenW, height: 42) let it = CommonSectionHeaderView(frame: frame) it.changeSectionTitle(with: "Review") it.lineView.isHidden = true return it }() private lazy var collectionView: DetailPreviewCollectionView = { let itemSize = CGSize(width: kScreenW - 2 * GlobalConstants.leftMargin, height: GlobalConstants.detailPreviewImageH) let frame = CGRect(x: 0, y: 52, width: kScreenW, height: GlobalConstants.detailPreviewImageH) let layout = CommonCollectionFlowLayout(itemSize: itemSize) let collectionView = DetailPreviewCollectionView(frame: frame, collectionViewLayout: layout) collectionView.dataSource = self collectionView.delegate = self return collectionView }() } extension DetailPreviewCell: UICollectionViewDataSource,UICollectionViewDelegate { func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return dataSource.count } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.ut_dequeueReusable(DetailPreviewCollectionViewCell.self, for: indexPath) cell.coverImageView.image = UIImage(named: dataSource[indexPath.item]) return cell } func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { } } fileprivate let dataSource: [String] = [ "cover_detail1", "cover_detail2", "cover_detail" ] ================================================ FILE: AppStoreDemo/Game/View/Detail/DetailPreviewCollectionView.swift ================================================ // // DetailPreviewCollectionView.swift // AppStoreDemo // // Created by Allen long on 2019/8/8. // Copyright © 2019 Utimes. All rights reserved. // import UIKit class DetailPreviewCollectionView: UICollectionView { override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) { super.init(frame: frame, collectionViewLayout: layout) config() } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } private func config() { backgroundColor = .white decelerationRate = UIScrollView.DecelerationRate.fast ut_registerClassCell(DetailPreviewCollectionViewCell.self) showsHorizontalScrollIndicator = false } } ================================================ FILE: AppStoreDemo/Game/View/Detail/DetailPreviewCollectionViewCell.swift ================================================ // // DetailPreviewCollectionViewCell.swift // AppStoreDemo // // Created by Allen long on 2019/8/8. // Copyright © 2019 Utimes. All rights reserved. // import UIKit class DetailPreviewCollectionViewCell: UICollectionViewCell { let coverImageView = UIImageView() override init(frame: CGRect) { super.init(frame: frame) coverImageView.frame = CGRect(x: 0, y: 0, width: self.frame.width, height: GlobalConstants.detailPreviewImageH) coverImageView.cornerRadius = 14.0 coverImageView.contentMode = .scaleAspectFill contentView.addSubview(coverImageView) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } } ================================================ FILE: AppStoreDemo/Game/View/Detail/DetailTopInfoCell.swift ================================================ // // DetailTopInfoCell.swift // AppStoreDemo // // Created by Allen long on 2019/8/8. // Copyright © 2019 Utimes. All rights reserved. // import UIKit class DetailTopInfoCell: UITableViewCell { override func awakeFromNib() { super.awakeFromNib() setupUI() } private func setupUI() { for index in 0...3 { let star = StarView(frame: CGRect(x: index * (15 + 3), y: 0, width: 15, height: 15)) starBackView.addSubview(star) } } @IBOutlet weak var starBackView: UIView! } ================================================ FILE: AppStoreDemo/Game/View/Detail/DetailTopInfoCell.xib ================================================ ================================================ FILE: AppStoreDemo/Game/View/Download/DownloadBottomView.swift ================================================ // // DownloadBottomView.swift // AppStoreDemo // // Created by Allen long on 2019/9/5. // Copyright © 2019 Utimes. All rights reserved. // import UIKit protocol DownloadBottomViewDelegate: class { func downloadBottomViewDidClickCancel(_ bottomView: DownloadBottomView) } class DownloadBottomView: UIView,NibLoadable { weak var delegate: DownloadBottomViewDelegate? @IBAction func cancelAction(_ sender: UIButton) { delegate?.downloadBottomViewDidClickCancel(self) } @IBOutlet weak var nameLabel: UILabel! @IBOutlet weak var descLabel: UILabel! @IBOutlet weak var iconImageView: UIImageView! var model: GameTopicModel! { didSet { nameLabel.text = model.name descLabel.text = model.desc iconImageView.image = UIImage(named: model.iconImageName) } } } ================================================ FILE: AppStoreDemo/Game/View/Download/DownloadBottomView.xib ================================================ ================================================ FILE: AppStoreDemo/Game/View/Download/DownloadClickView.swift ================================================ // // DownloadClickView.swift // AppStoreDemo // // Created by Allen long on 2019/9/5. // Copyright © 2019 Utimes. All rights reserved. // import UIKit class DownloadClickView: UIView,NibLoadable { var timer: Timer? override func awakeFromNib() { super.awakeFromNib() } func startMoving() { timer = Timer(timeInterval: 1.5, repeats: true, block: { _ in self.animations() }) timer?.fire() RunLoop.main.add(timer!, forMode: .default) } func endMoving() { timer?.invalidate() timer = nil } private func animations() { UIView.animateKeyframes(withDuration: 1, delay: 0, options: [], animations: { UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.25, animations: { self.frame.origin.x -= GlobalConstants.clickBarWidth }) UIView.addKeyframe(withRelativeStartTime: 0.25, relativeDuration: 0.5, animations: { self.frame.origin.x += GlobalConstants.clickBarWidth }) UIView.addKeyframe(withRelativeStartTime: 0.5, relativeDuration: 0.75, animations: { self.frame.origin.x -= GlobalConstants.clickBarWidth }) UIView.addKeyframe(withRelativeStartTime: 0.75, relativeDuration: 1.0, animations: { self.frame.origin.x += GlobalConstants.clickBarWidth }) }, completion: nil) } } ================================================ FILE: AppStoreDemo/Game/View/Download/DownloadClickView.xib ================================================ ================================================ FILE: AppStoreDemo/Game/View/Link/GameLinkTableView.swift ================================================ // // GameLinkTableView.swift // AppStoreDemo // // Created by Allen long on 2019/8/7. // Copyright © 2019 Utimes. All rights reserved. // import UIKit class GameLinkTableView: UITableView { override init(frame: CGRect, style: UITableView.Style) { super.init(frame: frame, style: style) setupUI() } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } private func setupUI() { rowHeight = GlobalConstants.linkCellHeight register(UITableViewCell.self, forCellReuseIdentifier: UITableViewCellID) dataSource = self separatorInset = UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 20) } } extension GameLinkTableView: UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return linkDataSource.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = dequeueReusableCell(withIdentifier: UITableViewCellID, for: indexPath) cell.textLabel?.font = UIFont.systemFont(ofSize: 22) cell.textLabel?.text = linkDataSource[indexPath.row] cell.textLabel?.textColor = GlobalConstants.textBlueColor return cell } } fileprivate let UITableViewCellID = "UITableViewCellID" fileprivate let linkDataSource: [String] = [ "Add a Payment Method", "Parents' Guide to the App", "About In-App Purchases", "About Apps & Games for Your Kids", "About Personalisation", "New to the App Store", ] ================================================ FILE: AppStoreDemo/Game/View/Link/GameLinkTableViewCell.swift ================================================ // // GameLinkTableViewCell.swift // AppStoreDemo // // Created by Allen long on 2019/8/7. // Copyright © 2019 Utimes. All rights reserved. // import UIKit class GameLinkTableViewCell: UITableViewCell { private lazy var headerView: CommonSectionHeaderView = { let frame = CGRect(x: 0, y: 0, width: kScreenW, height: 42) let it = CommonSectionHeaderView(frame: frame) it.changeSectionTitle(with: "Quick Links") return it }() private lazy var tableView: GameLinkTableView = { let frame = CGRect(x: 0, y: 42, width: kScreenW, height: GlobalConstants.linkCellHeight * 6) let it = GameLinkTableView(frame: frame) return it }() override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) setupUI() } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } private func setupUI() { contentView.addSubview(headerView) contentView.addSubview(tableView) } } ================================================ FILE: AppStoreDemo/Game/View/Recommand/GameRecommandCollectionView.swift ================================================ // // GameRecommandCollectionView.swift // AppStoreDemo // // Created by Allen long on 2019/8/6. // Copyright © 2019 Utimes. All rights reserved. // import UIKit class GameRecommandCollectionView: UICollectionView { override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) { super.init(frame: frame, collectionViewLayout: layout) config() } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } private func config() { backgroundColor = .white decelerationRate = UIScrollView.DecelerationRate.fast ut_registerNibCell(RecommandCollectionViewCell.self) showsHorizontalScrollIndicator = false } } ================================================ FILE: AppStoreDemo/Game/View/Recommand/GameRecommandTableViewCell.swift ================================================ // // GameRecommandTableViewCell.swift // AppStoreDemo // // Created by Allen long on 2019/8/6. // Copyright © 2019 Utimes. All rights reserved. // import UIKit class GameRecommandTableViewCell: UITableViewCell { var detailClosure: (()->())? override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) setupUI() } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } private func setupUI() { contentView.addSubview(recommandCollectionView) } private lazy var recommandCollectionView: GameRecommandCollectionView = { let itemSize = CGSize(width: kScreenW - 2 * GlobalConstants.leftMargin, height: GlobalConstants.recommandHeight) let frame = CGRect(x: 0, y: 0, width: kScreenW, height: GlobalConstants.recommandHeight) let layout = CommonCollectionFlowLayout(itemSize: itemSize) let collectionView = GameRecommandCollectionView(frame: frame, collectionViewLayout: layout) collectionView.dataSource = self collectionView.delegate = self return collectionView }() } extension GameRecommandTableViewCell: UICollectionViewDataSource,UICollectionViewDelegate { func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { if collectionView == recommandCollectionView { return RecommandDataSource.count } return 0 } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { if collectionView == recommandCollectionView { let cell = collectionView.ut_dequeueReusable(RecommandCollectionViewCell.self, for: indexPath) cell.model = RecommandDataSource[indexPath.row] return cell } return UICollectionViewCell() } func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { detailClosure?() } } fileprivate let RecommandDataSource: [GameRecommandModel] = [ GameRecommandModel(feature: "MAJOR UPDATE", name: "Onmyoji", desc: "Cards", coverImageName: "cover_1"), GameRecommandModel(feature: "NEW GAME", name: "Clash Royale", desc: "Strategy", coverImageName: "cover_2"), GameRecommandModel(feature: "REDISCOVER THIS", name: "Fantasy Westward Journey", desc: "Adventure", coverImageName: "cover_3") ] ================================================ FILE: AppStoreDemo/Game/View/Recommand/RecommandCollectionViewCell.swift ================================================ // // RecommandCollectionViewCell.swift // AppStoreDemo // // Created by Allen long on 2019/8/6. // Copyright © 2019 Utimes. All rights reserved. // import UIKit class RecommandCollectionViewCell: UICollectionViewCell { @IBOutlet weak var featureLabel: UILabel! @IBOutlet weak var nameLabel: UILabel! @IBOutlet weak var descLabel: UILabel! @IBOutlet weak var coverImageView: UIImageView! override func awakeFromNib() { super.awakeFromNib() // Initialization code } var model: GameRecommandModel! { didSet { featureLabel.text = model.feature nameLabel.text = model.name descLabel.text = model.desc coverImageView.image = UIImage(named: model.coverImageName) } } } ================================================ FILE: AppStoreDemo/Game/View/Recommand/RecommandCollectionViewCell.xib ================================================ ================================================ FILE: AppStoreDemo/Game/View/Topic/GameTopicCollectionView.swift ================================================ // // GameTopicCollectionView.swift // AppStoreDemo // // Created by Allen long on 2019/8/6. // Copyright © 2019 Utimes. All rights reserved. // import UIKit class GameTopicCollectionView: UICollectionView { override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) { super.init(frame: frame, collectionViewLayout: layout) config() } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } private func config() { backgroundColor = .white decelerationRate = UIScrollView.DecelerationRate.fast ut_registerNibCell(GameTopicCollectionViewCell.self) showsHorizontalScrollIndicator = false } } ================================================ FILE: AppStoreDemo/Game/View/Topic/GameTopicCollectionViewCell.swift ================================================ // // GameTopicCollectionViewCell.swift // AppStoreDemo // // Created by Allen long on 2019/8/6. // Copyright © 2019 Utimes. All rights reserved. // import UIKit protocol GameTopicCollectionViewCellDelegate: class { func gameTopicCollectionViewCellDidClickGet(_ topicCell: GameTopicCollectionViewCell) } class GameTopicCollectionViewCell: UICollectionViewCell { @IBOutlet weak var iconImageView: UIImageView! @IBOutlet weak var nameLabel: UILabel! @IBOutlet weak var descLabel: UILabel! @IBOutlet weak var lineView: UIView! weak var delegate: GameTopicCollectionViewCellDelegate? var model: GameTopicModel! { didSet { iconImageView.image = UIImage(named: model.iconImageName) nameLabel.text = model.name descLabel.text = model.desc } } @IBAction func getAction(_ sender: UIButton) { delegate?.gameTopicCollectionViewCellDidClickGet(self) } } ================================================ FILE: AppStoreDemo/Game/View/Topic/GameTopicCollectionViewCell.xib ================================================ ================================================ FILE: AppStoreDemo/Game/View/Topic/GameTopicTableViewCell.swift ================================================ // // GameTopicTableViewCell.swift // AppStoreDemo // // Created by Allen long on 2019/8/6. // Copyright © 2019 Utimes. All rights reserved. // import UIKit class GameTopicTableViewCell: UITableViewCell { var detailClosure: (()->())? var downloadClosure: ((_ model: GameTopicModel)->())? override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) setupUI() } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } private func setupUI() { contentView.addSubview(headerView) contentView.addSubview(topicCollectionView) } private lazy var headerView: CommonSectionHeaderView = { let frame = CGRect(x: 0, y: 0, width: kScreenW, height: 42) let it = CommonSectionHeaderView(frame: frame) it.changeSectionTitle(with: "What We're Playing") return it }() private lazy var topicCollectionView: GameTopicCollectionView = { let itemSize = CGSize(width: kScreenW - 2 * GlobalConstants.leftMargin, height: 80) let frame = CGRect(x: 0, y: 42, width: kScreenW, height: 80 * 3) let layout = CommonCollectionFlowLayout(itemSize: itemSize) let collectionView = GameTopicCollectionView(frame: frame, collectionViewLayout: layout) collectionView.dataSource = self collectionView.delegate = self return collectionView }() } extension GameTopicTableViewCell: UICollectionViewDataSource,UICollectionViewDelegate { func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { if collectionView == topicCollectionView { return TopicDataSource.count } return 0 } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { if collectionView == topicCollectionView { let cell = collectionView.ut_dequeueReusable(GameTopicCollectionViewCell.self, for: indexPath) cell.delegate = self cell.model = TopicDataSource[indexPath.row] cell.lineView.isHidden = (((indexPath.row + 1) % 3 == 0) || (indexPath.row == TopicDataSource.count - 1)) return cell } return UICollectionViewCell() } func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { detailClosure?() } } extension GameTopicTableViewCell: GameTopicCollectionViewCellDelegate { func gameTopicCollectionViewCellDidClickGet(_ topicCell: GameTopicCollectionViewCell) { guard let indexPath = topicCollectionView.indexPath(for: topicCell) else { return } downloadClosure?(TopicDataSource[indexPath.item]) } } fileprivate let TopicDataSource: [GameTopicModel] = [ GameTopicModel(name: "Bullet Hell", desc: "Casual", iconImageName: "logo_broadcast"), GameTopicModel(name: "Hot Wheels", desc: "Strategy", iconImageName: "logo_car"), GameTopicModel(name: "SpellForce - Heroes", desc: "Card", iconImageName: "logo_jump"), GameTopicModel(name: "Farm Punks", desc: "Role-Playing", iconImageName: "logo_smile"), GameTopicModel(name: "Super Spinball", desc: "A musical journey awaits", iconImageName: "logo_weibo"), ] ================================================ FILE: AppStoreDemo/Info.plist ================================================ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType APPL CFBundleShortVersionString 1.0 CFBundleVersion 1 LSRequiresIPhoneOS UILaunchStoryboardName LaunchScreen UIMainStoryboardFile Main UIRequiredDeviceCapabilities armv7 UISupportedInterfaceOrientations UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UISupportedInterfaceOrientations~ipad UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIUserInterfaceStyle Light ================================================ FILE: AppStoreDemo/Main.storyboard ================================================ ================================================ FILE: AppStoreDemo/Search/Controller/SearchTableViewController.swift ================================================ // // SearchTableViewController.swift // AppStoreDemo // // Created by Allen long on 2019/8/2. // Copyright © 2019 Utimes. All rights reserved. // import UIKit class SearchTableViewController: UITableViewController { private var searchController: UISearchController! override func viewDidLoad() { super.viewDidLoad() setNavigationBarBottomLineHidden(true) setupSearchController() tableView.ut_registerClassCell(SearchTableViewCell.self) } private func setupSearchController() { searchController = UISearchController(searchResultsController: nil) searchController.searchBar.autocapitalizationType = .none searchController.searchBar.placeholder = "App Store" searchController.isActive = true definesPresentationContext = true if #available(iOS 11.0, *) { // For iOS 11 and later, place the search bar in the navigation bar. navigationItem.searchController = searchController // Make the search bar always visible. navigationItem.hidesSearchBarWhenScrolling = false } else { // For iOS 10 and earlier, place the search controller's search bar in the table view's header. tableView.tableHeaderView = searchController.searchBar } } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return dataList.count } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.ut_dequeueReusable(SearchTableViewCell.self, for: indexPath) cell.textLabel?.text = dataList[indexPath.row] cell.canChangeHightlighted = (indexPath.row != 0) return cell } } fileprivate let dataList: [String] = [ "Hot Search", "Daily life", "League of Legends", "Wechat", "Game of Thrones", "Hupu JRS", "Game Center", "QQ Music" ] ================================================ FILE: AppStoreDemo/Search/View/SearchTableViewCell.swift ================================================ // // SearchTableViewCell.swift // AppStoreDemo // // Created by Allen long on 2019/8/2. // Copyright © 2019 Utimes. All rights reserved. // import UIKit class SearchTableViewCell: UITableViewCell { var canChangeHightlighted: Bool = false { didSet { if !canChangeHightlighted { textLabel?.textColor = .black } } } override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) setupUI() } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func setHighlighted(_ highlighted: Bool, animated: Bool) { super.setHighlighted(highlighted, animated: animated) if !canChangeHightlighted { return } if highlighted { textLabel?.textColor = .white backgroundColor = GlobalConstants.textBlueColor } else { textLabel?.textColor = GlobalConstants.textBlueColor backgroundColor = .white } } private func setupUI() { textLabel?.textColor = GlobalConstants.textBlueColor textLabel?.font = UIFont.systemFont(ofSize: 19) selectionStyle = .none } } ================================================ FILE: AppStoreDemo/Today/Controller/CardDetailViewController.swift ================================================ // // CardDetailViewController.swift // AppStoreDemo // // Created by Allen long on 2019/7/31. // Copyright © 2019 Utimes. All rights reserved. // import UIKit class CardDetailViewController: UIViewController { override var prefersStatusBarHidden: Bool { return true } var dismissClosure: (()->())? //the point when start to interactive var interactiveStartingPoint: CGPoint? = nil var draggingDownToDismiss = false let cell: TodayTableViewCell! private lazy var dismissPanGesture: UIPanGestureRecognizer = { let ges = UIPanGestureRecognizer() ges.maximumNumberOfTouches = 1 ges.addTarget(self, action: #selector(handleDismissPan(gesture:))) ges.delegate = self return ges }() lazy var scrollView: DetailScrollView = { let frame = self.view.bounds let view = DetailScrollView(frame: frame) view.delegate = self return view }() lazy var closeBtn: UIButton = { let btn = UIButton() btn.frame = CGRect(x: kScreenW - 20 - 30, y: 20, width: 30, height: 30) btn.setImage(#imageLiteral(resourceName: "close_button"), for: .normal) btn.addTarget(self, action: #selector(closeAction), for: .touchUpInside) return btn }() init(cell: TodayTableViewCell) { self.cell = cell super.init(nibName: nil, bundle: nil) self.setupTranstion() } private func setupTranstion() { modalPresentationStyle = .custom transitioningDelegate = self } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func viewDidLoad() { super.viewDidLoad() setupUI() getImageFromCell() } private func setupUI() { self.view.backgroundColor = .white self.view.layer.masksToBounds = true view.addSubview(scrollView) view.addSubview(closeBtn) view.addGestureRecognizer(dismissPanGesture) if #available(iOS 11.0, *) { scrollView.contentInsetAdjustmentBehavior = .never } else { automaticallyAdjustsScrollViewInsets = false } } private func getImageFromCell() { scrollView.imageView.image = cell.bgImageView.image } @objc private func closeAction() { dismiss(animated: true, completion: nil) dismissClosure?() } @objc private func handleDismissPan(gesture: UIPanGestureRecognizer) { if !draggingDownToDismiss { return } let startingPoint: CGPoint if let p = interactiveStartingPoint { startingPoint = p } else { startingPoint = gesture.location(in: nil) interactiveStartingPoint = startingPoint } let currentLocation = gesture.location(in: nil) var progress = (currentLocation.y - startingPoint.y) / 100 //prevent viewController bigger when scrolling up if currentLocation.y <= startingPoint.y { progress = 0 } if progress >= 1.0 { dismiss(animated: true, completion: nil) dismissClosure?() stopDismissPanGesture(gesture) return } let targetShrinkScale: CGFloat = 0.86 let currentScale: CGFloat = 1 - (1 - targetShrinkScale) * progress switch gesture.state { case .began,.changed: scrollView.isScrollEnabled = false gesture.view?.transform = CGAffineTransform(scaleX: currentScale, y: currentScale) gesture.view?.layer.cornerRadius = GlobalConstants.toDayCardCornerRadius * (progress) scrollView.showsVerticalScrollIndicator = false case .cancelled,.ended: scrollView.isScrollEnabled = true stopDismissPanGesture(gesture) default: break } } //当下拉Offset超过100或取消下拉手势时,执行此方法 private func stopDismissPanGesture(_ gesture: UIPanGestureRecognizer) { draggingDownToDismiss = false interactiveStartingPoint = nil scrollView.showsVerticalScrollIndicator = true UIView.animate(withDuration: 0.2) { gesture.view?.transform = CGAffineTransform.identity } } } extension CardDetailViewController: UIViewControllerTransitioningDelegate { func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? { return TodayAnimationTransition(animationType: .present) } func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { return TodayAnimationTransition(animationType: .dismiss) } func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? { return CardPresentationController(presentedViewController: presented, presenting: presenting) } } extension CardDetailViewController: UIScrollViewDelegate { func scrollViewDidScroll(_ scrollView: UIScrollView) { if scrollView.contentOffset.y < 0 { scrollView.contentOffset = .zero draggingDownToDismiss = true } } } extension CardDetailViewController: UIGestureRecognizerDelegate { func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { return true } } ================================================ FILE: AppStoreDemo/Today/Controller/CardPresentationController.swift ================================================ // // TodayPresentationController.swift // AppStoreDemo // // Created by Allen long on 2019/8/2. // Copyright © 2019 Utimes. All rights reserved. // import UIKit class CardPresentationController: UIPresentationController { private lazy var blurView = UIVisualEffectView(effect: nil) override var shouldRemovePresentersView: Bool { return false } override func presentationTransitionWillBegin() { let container = containerView! blurView.translatesAutoresizingMaskIntoConstraints = false container.addSubview(blurView) blurView.edges(to: container) blurView.alpha = 0.0 presentingViewController.beginAppearanceTransition(false, animated: false) presentedViewController.transitionCoordinator!.animate(alongsideTransition: { (ctx) in self.blurView.effect = UIBlurEffect(style: .light) self.blurView.alpha = 1 }) { (ctx) in } } override func presentationTransitionDidEnd(_ completed: Bool) { presentingViewController.endAppearanceTransition() } override func dismissalTransitionWillBegin() { presentingViewController.beginAppearanceTransition(true, animated: true) presentedViewController.transitionCoordinator!.animate(alongsideTransition: { (ctx) in self.blurView.alpha = 0.0 }, completion: nil) } override func dismissalTransitionDidEnd(_ completed: Bool) { presentingViewController.endAppearanceTransition() if completed { blurView.removeFromSuperview() } } } ================================================ FILE: AppStoreDemo/Today/Controller/TodayViewController.swift ================================================ // // TodayViewController.swift // AppStoreDemo // // Created by Allen long on 2019/7/31. // Copyright © 2019 Utimes. All rights reserved. // import UIKit class TodayViewController: UITableViewController { var selectedCell: TodayTableViewCell? var statusBarShouldBeHidden = false //we need to set `View controller-based status bar appearance = YES` in info.plist. //so we can be able to hide statusBar. override var prefersStatusBarHidden: Bool { return statusBarShouldBeHidden } override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation { return .slide } lazy var headerView: TodayTableHeaderView = { let frame = CGRect(x: 0, y: 0, width: kScreenW, height: 96) let view = TodayTableHeaderView(frame: frame) view.iconButtonClosure = { [weak self] in guard let StrongSelf = self else { return } StrongSelf.presentUserTableViewController() } return view }() override func viewDidLoad() { super.viewDidLoad() setupTableView() } private func setupTableView() { tableView.ut_registerClassCell(TodayTableViewCell.self) tableView.separatorStyle = .none tableView.rowHeight = GlobalConstants.toDayCardRowH tableView.tableHeaderView = headerView } private func updateStatusBarAndTabbarFrame(visible: Bool) { statusBarShouldBeHidden = !visible UIView.animate(withDuration: 0.25) { self.setNeedsStatusBarAppearanceUpdate() } } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 2 } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.ut_dequeueReusable(TodayTableViewCell.self, for: indexPath) cell.selectionStyle = .none if indexPath.row == 0 { cell.bgImageView.image = #imageLiteral(resourceName: "cover_4") } else { cell.bgImageView.image = #imageLiteral(resourceName: "cover_5") } return cell } override func tableView(_ tableView: UITableView, didHighlightRowAt indexPath: IndexPath) { guard let row = tableView.cellForRow(at: indexPath) as? TodayTableViewCell else { return } UIView.animate(withDuration: 0.1) { row.bgBackView.transform = CGAffineTransform(scaleX: 0.95, y: 0.95) } } override func tableView(_ tableView: UITableView, didUnhighlightRowAt indexPath: IndexPath) { guard let row = tableView.cellForRow(at: indexPath) as? TodayTableViewCell else { return } UIView.animate(withDuration: 0.3) { row.bgBackView.transform = CGAffineTransform(scaleX: 1.0, y: 1.0) } } override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { guard let cell = tableView.cellForRow(at: indexPath) as? TodayTableViewCell else { return } selectedCell = cell let detailVC = CardDetailViewController(cell: cell) detailVC.dismissClosure = { [weak self] in guard let StrongSelf = self else { return } StrongSelf.updateStatusBarAndTabbarFrame(visible: true) } updateStatusBarAndTabbarFrame(visible: false) present(detailVC, animated: true, completion: nil) } } ================================================ FILE: AppStoreDemo/Today/Model/TodayAnimationTransition.swift ================================================ // // TodayAnimationTransition.swift // AppStoreDemo // // Created by Allen long on 2019/7/31. // Copyright © 2019 Utimes. All rights reserved. // import UIKit fileprivate let transitonDuration: TimeInterval = 1.0 enum AnimationType { case present case dismiss } class TodayAnimationTransition: NSObject { let animationType: AnimationType! init(animationType: AnimationType) { self.animationType = animationType super.init() } } extension TodayAnimationTransition: UIViewControllerAnimatedTransitioning { func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { return transitonDuration } func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { if animationType == .present { animationForPresent(using: transitionContext) } else { animationForDismiss(using: transitionContext) } } func animationForPresent(using transitionContext: UIViewControllerContextTransitioning) { let containerView = transitionContext.containerView //1.Get fromVC and toVC guard let fromVC = transitionContext.viewController(forKey: .from) as? UITabBarController else { return } guard let tableViewController = fromVC.viewControllers?.first as? TodayViewController else { return } guard let toVC = transitionContext.viewController(forKey: .to) as? CardDetailViewController else { return } guard let selectedCell = tableViewController.selectedCell else { return } let frame = selectedCell.convert(selectedCell.bgBackView.frame, to: fromVC.view) //2.Set presentation original size. toVC.view.frame = frame toVC.scrollView.imageView.frame.size.width = GlobalConstants.todayCardSize.width toVC.scrollView.imageView.frame.size.height = GlobalConstants.todayCardSize.height containerView.addSubview(toVC.view) //3.Change original size to final size with animation. UIView.animate(withDuration: transitonDuration, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0, options: [], animations: { toVC.view.frame = UIScreen.main.bounds toVC.scrollView.imageView.frame.size.width = kScreenW toVC.scrollView.imageView.frame.size.height = GlobalConstants.cardDetailTopImageH toVC.closeBtn.alpha = 1 fromVC.tabBar.frame.origin.y = kScreenH }) { (completed) in transitionContext.completeTransition(completed) } } func animationForDismiss(using transitionContext: UIViewControllerContextTransitioning) { guard let fromVC = transitionContext.viewController(forKey: .from) as? CardDetailViewController else { return } guard let toVC = transitionContext.viewController(forKey: .to) as? UITabBarController else { return } guard let tableViewController = toVC.viewControllers?.first as? TodayViewController else { return } guard let selectedCell = tableViewController.selectedCell else { return } UIView.animate(withDuration: transitonDuration - 0.3, delay: 0, usingSpringWithDamping: 0.8, initialSpringVelocity: 0, options: [], animations: { let frame = selectedCell.convert(selectedCell.bgBackView.frame, to: toVC.view) fromVC.view.frame = frame fromVC.view.layer.cornerRadius = GlobalConstants.toDayCardCornerRadius fromVC.scrollView.imageView.frame.size.width = GlobalConstants.todayCardSize.width fromVC.scrollView.imageView.frame.size.height = GlobalConstants.todayCardSize.height fromVC.closeBtn.alpha = 0 toVC.tabBar.frame.origin.y = kScreenH - toVC.tabBar.frame.height }) { (completed) in transitionContext.completeTransition(completed) toVC.view.addSubview(toVC.tabBar) } } } ================================================ FILE: AppStoreDemo/Today/View/DetailScrollView.swift ================================================ // // DetailScrollView.swift // AppStoreDemo // // Created by Allen long on 2019/7/31. // Copyright © 2019 Utimes. All rights reserved. // import UIKit class DetailScrollView: UIScrollView { let bgBackView = UIView() let imageView = UIImageView() let textView = UITextView() override init(frame: CGRect) { super.init(frame: frame) setupUI() } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } private func setupUI() { bgBackView.frame = CGRect(x: 0, y: 0, width: kScreenW, height: GlobalConstants.cardDetailTopImageH) bgBackView.layer.masksToBounds = true imageView.frame = bgBackView.bounds imageView.isUserInteractionEnabled = true imageView.contentMode = .scaleAspectFill let textViewWidth = kScreenW - 2 * textViewLeftMargin let font = UIFont.boldSystemFont(ofSize: 15) let textHeight = textViewText.calculateHeightWith(width: textViewWidth, font: font) textView.frame = CGRect(x: textViewLeftMargin, y: bgBackView.frame.height + textViewTopMargin, width: textViewWidth, height: textHeight + textViewBottomMargin) textView.text = textViewText textView.font = font textView.textColor = .gray bgBackView.addSubview(imageView) addSubview(bgBackView) addSubview(textView) contentSize = CGSize(width: kScreenW, height: bgBackView.frame.height + textViewTopMargin + textView.frame.height + textViewBottomMargin) } } fileprivate let textViewLeftMargin: CGFloat = 20 fileprivate let textViewTopMargin: CGFloat = 40 fileprivate let textViewBottomMargin: CGFloat = 50 fileprivate let textViewText = "Thank you. I'm honored to be with you today for your commencement from one of the finest universities in the world. Truth be told, i never graduated from college and this is the closest I've ever gotten to a college gradution. \n\nToday i want to tell you three stories from my life. That's it. No big deal. Just three stories. The first story is about connecting the dots. \n\ndropped out of Reed College after the first 6 months, but then stayed around as a drop-in for another 18 months or so before I really quit. So why did I drop out? \n\nIt started before I was born. My biological mother was a young,unwed college graduate student, and she decided to put me up for adoption. She felt very strongly that I should be adopted by college graduates, so everything was all set for me to be adopted at birth by a lawyer and his wife. Except that when I popped out they decided at the last minute that they really wanted a girl. So my parents, who were on a waiting list, got a call in the middle of the night asking: 'We got an unexpected baby boy; do you want him?' They said: 'Of course.' My biological mother found out later that my mother had never graduated from college and my father had never graduated from high school. She refused to sign the final adoption papers. She only relented a few months later when my parents promised that I would go to college." ================================================ FILE: AppStoreDemo/Today/View/TodayTableHeaderView.swift ================================================ // // TodayTableHeaderView.swift // AppStoreDemo // // Created by Allen long on 2019/7/31. // Copyright © 2019 Utimes. All rights reserved. // import UIKit class TodayTableHeaderView: UIView { let dateLabel = UILabel() let todayLabel = UILabel() let iconButton = UIButton() var iconButtonClosure: (()->())? override init(frame: CGRect) { super.init(frame: frame) setupUI() } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } private func setupUI() { dateLabel.text = "TUESDAY, JULY 10" dateLabel.textColor = .gray dateLabel.font = UIFont.boldSystemFont(ofSize: 13) dateLabel.frame = CGRect(x: 20, y: 33, width: 200, height: 15) todayLabel.text = "Today" todayLabel.font = UIFont.boldSystemFont(ofSize: 34) todayLabel.frame = CGRect(x: 20, y: 48, width: 200, height: 40) iconButton.setImage(#imageLiteral(resourceName: "demo_icon"), for: .normal) iconButton.setImage(#imageLiteral(resourceName: "demo_icon"), for: .highlighted) iconButton.frame = CGRect(x: kScreenW - 20 - 40, y: 46, width: 35, height: 35) iconButton.layer.cornerRadius = GlobalConstants.iconCornerRadius iconButton.layer.borderColor = GlobalConstants.iconBorderColor iconButton.layer.borderWidth = GlobalConstants.iconBorderWidth iconButton.addTarget(self, action: #selector(iconButtonAction), for: .touchUpInside) addSubview(dateLabel) addSubview(todayLabel) addSubview(iconButton) } @objc private func iconButtonAction() { iconButtonClosure?() } } ================================================ FILE: AppStoreDemo/Today/View/TodayTableViewCell.swift ================================================ // // TodayTableViewCell.swift // AppStoreDemo // // Created by Allen long on 2019/7/31. // Copyright © 2019 Utimes. All rights reserved. // import UIKit class TodayTableViewCell: UITableViewCell { let bgBackView = UIView() let bgImageView = UIImageView() let emptyView = UIView() override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) setupUI() } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } private func setupUI() { bgBackView.addSubview(bgImageView) contentView.addSubview(bgBackView) contentView.addSubview(emptyView) bgBackView.frame = CGRect(x: GlobalConstants.leftMargin, y: 0, width: GlobalConstants.todayCardSize.width, height: GlobalConstants.todayCardSize.height) bgBackView.layer.shadowColor = UIColor.black.cgColor bgBackView.layer.shadowOpacity = 0.4 bgBackView.layer.shadowOffset = CGSize(width: 0, height: 1) bgImageView.frame = bgBackView.bounds bgImageView.contentMode = .scaleAspectFill bgImageView.layer.cornerRadius = GlobalConstants.toDayCardCornerRadius bgImageView.layer.masksToBounds = true emptyView.backgroundColor = UIColor.white.withAlphaComponent(0) emptyView.frame = CGRect(x: 0, y: bgImageView.frame.size.height, width: GlobalConstants.todayCardSize.width, height: GlobalConstants.toDayCardRowH - GlobalConstants.todayCardSize.height) } } ================================================ FILE: AppStoreDemo/Update/Controller/UpdateTableViewController.swift ================================================ // // UpdateTableViewController.swift // AppStoreDemo // // Created by Erwin on 2019/8/4. // Copyright © 2019 Utimes. All rights reserved. // import UIKit class UpdateTableViewController: UITableViewController { var dataSource = DataSource //record cellHeight to prevent flickering when tapped `more` var cellHeights: [IndexPath: CGFloat] = [:] override func viewDidLoad() { super.viewDidLoad() adjustNavigationForiOS13() setNavigationBarBottomLineHidden(true) addIconButtonOnNavigationBar() configTableView() } private func configTableView() { tableView.ut_registerNibCell(UpdateTableViewCell.self) } //add iconButton to LargeTitleView @objc func rightButtonTapped(_ btn: UIButton) { presentUserTableViewController() } // MARK: - Table view data source override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { // #warning Incomplete implementation, return the number of rows return dataSource.count } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.ut_dequeueReusable(UpdateTableViewCell.self, for: indexPath) cell.isFirstCell = (indexPath.row == 0) cell.updateClosure = { [weak self] tappedCell in guard let StrongSelf = self else { return } StrongSelf.dataSource[indexPath.row].showMore = true StrongSelf.tableView.reloadRows(at: [indexPath], with: .none) print(indexPath) } cell.model = dataSource[indexPath.row] return cell } override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { cellHeights[indexPath] = cell.frame.size.height } override func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat { if let height = cellHeights[indexPath] { return height } else { return UITableView.automaticDimension } } override func scrollViewDidScroll(_ scrollView: UIScrollView) { if scrollView.contentOffset.y > 0 { setNavigationBarBottomLineHidden(false) } else { setNavigationBarBottomLineHidden(true) } } } fileprivate let DataSource: [UpdateModel] = [ UpdateModel(appName: "Huajiao Live", updateDate: "Today", iconImageStr: "logo_broadcast", updateContent: "[Play] Music radio broadcasting page revision, more immersed in music exploration \n\n[Mine] Rewriting sets the position of the night mode \n\n[Radio] Let's go with DJ and get up!", version: "2.0.0", size: 35.7, showMore: false), UpdateModel(appName: "Sina Weibo", updateDate: "Today", iconImageStr: "logo_weibo", updateContent: "-Performance improvements and bug fixed", version: "5.3.3", size: 32.5, showMore: false), UpdateModel(appName: "Sougou-input", updateDate: "Yesterday", iconImageStr: "logo_smile", updateContent: "Fix bug and to be better for you", version: "2.1.1", size: 42.2, showMore: false), UpdateModel(appName: "Guazi Car", updateDate: "Yesterday", iconImageStr: "logo_car", updateContent: "Sometimes, a polish is all you need. No big chages, just a shine", version: "1.5.0", size: 28.0, showMore: false), UpdateModel(appName: "Fly-chat", updateDate: "2019/08/02", iconImageStr: "logo_jump", updateContent: "This update includes bug fixed and user interface improvements", version: "1.5.6", size: 33.0, showMore: false), ] ================================================ FILE: AppStoreDemo/Update/Model/UpdateModel.swift ================================================ // // UpdateModel.swift // AppStoreDemo // // Created by Allen long on 2019/8/5. // Copyright © 2019 Utimes. All rights reserved. // import Foundation struct UpdateModel { let appName: String let updateDate: String let iconImageStr: String let updateContent: String let version: String let size: Float var showMore: Bool } ================================================ FILE: AppStoreDemo/Update/View/UpdateTableViewCell.swift ================================================ // // UpdateTableViewCell.swift // AppStoreDemo // // Created by Allen long on 2019/8/5. // Copyright © 2019 Utimes. All rights reserved. // import UIKit class UpdateTableViewCell: UITableViewCell { // MARK: - IBOutlets @IBOutlet weak var topLeftLabel: UILabel! @IBOutlet weak var updateAllBtn: UIButton! @IBOutlet weak var contentLabel: UILabel! @IBOutlet weak var showMoreBtn: UIButton! @IBOutlet weak var iconImageView: UIImageView! @IBOutlet weak var nameLabel: UILabel! @IBOutlet weak var dateLabel: UILabel! @IBOutlet weak var versionAndSizeLabel: UILabel! // MARK: - Constraint @IBOutlet weak var iconImageViewTopMargin: NSLayoutConstraint! @IBOutlet weak var contentLabelBottomMargin: NSLayoutConstraint! // MARK: - Properties // if the cell is the first cell in tableView, it would show more content than other cells var isFirstCell: Bool = false { didSet { iconImageViewTopMargin.constant = isFirstCell ? 48 : 16 topLeftLabel.isHidden = !isFirstCell updateAllBtn.isHidden = !isFirstCell } } var model: UpdateModel! { didSet { iconImageView.image = UIImage(named: model.iconImageStr) nameLabel.text = model.appName dateLabel.text = model.updateDate contentLabel.text = model.updateContent versionAndSizeLabel.text = "Version " + model.version + " · " + String(model.size) + " MB" showMoreBtn.isHidden = model.showMore contentLabel.numberOfLines = model.showMore ? 0 : 1 contentLabelBottomMargin.constant = model.showMore ? 60 : 20 versionAndSizeLabel.isHidden = !model.showMore } } var updateClosure: ((UpdateTableViewCell)->())? // MARK: - IBAction @IBAction func updateAllBtnAction(_ sender: UIButton) { } @IBAction func showMoreBtnAction(_ sender: UIButton) { updateClosure?(self) } override func awakeFromNib() { super.awakeFromNib() } } ================================================ FILE: AppStoreDemo/Update/View/UpdateTableViewCell.xib ================================================ ================================================ FILE: AppStoreDemo/User/Controller/UserTableViewController.swift ================================================ // // UserTableViewController.swift // AppStoreDemo // // Created by Allen long on 2019/8/5. // Copyright © 2019 Utimes. All rights reserved. // import UIKit class UserTableViewController: UITableViewController { @IBAction func completeAction(_ sender: UIBarButtonItem) { dismiss(animated: true, completion: nil) } override func viewDidLoad() { super.viewDidLoad() setNavigationBarBottomLineHidden(true) } override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { return (section == 0 ? 36 : 29) } override func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat { return 0.1 } override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { tableView.deselectRow(at: indexPath, animated: true) } } ================================================ FILE: AppStoreDemo/User/View/User.storyboard ================================================ ================================================ FILE: AppStoreDemo/Utils/GlobalConstants.swift ================================================ // // GlobalConstants.swift // AppStoreDemo // // Created by Allen long on 2019/8/2. // Copyright © 2019 Utimes. All rights reserved. // import UIKit enum GlobalConstants { static let leftMargin: CGFloat = 20 static let toDayCardRowH: CGFloat = 440 static let toDayCardCornerRadius: CGFloat = 15.0 static let todayCardSize: CGSize = CGSize(width: kScreenW - 2 * 20, height: 410) static let cardDetailTopImageH: CGFloat = 500 static let iconBorderColor: CGColor = UIColor(red: 239/255.0, green: 240/255.0, blue: 241/255.0, alpha: 1).cgColor static let iconBorderWidth: CGFloat = 0.8 static let iconCornerRadius: CGFloat = 17.5 static let textBlueColor = UIColor(red: 0/255.0, green: 122/255.0, blue: 255/255.0, alpha: 1.0) //007AFF static let speratorLineColor = UIColor(red: 224/255.0, green: 224/255.0, blue: 224/255.0, alpha: 1) //GameTableViewController static let recommandHeight: CGFloat = 336 static let topicHeight: CGFloat = 282 static let linkHeight: CGFloat = 312 static let sectionHeaderH: CGFloat = 42 static let linkCellHeight: CGFloat = 45 //DetailViewController static let detailPreviewImageH: CGFloat = 155 //DownloadViewController static let doubleClickViewW: CGFloat = 140 static let clickBarWidth: CGFloat = 6 } let kScreenH = UIScreen.main.bounds.size.height let kScreenW = UIScreen.main.bounds.size.width let statusBarH = UIApplication.shared.statusBarFrame.height let navigationBarH: CGFloat = 44 let tabbarExtraH: CGFloat = hasTopNotch() ? 34 : 0 var keyWindow: UIWindow { if #available(iOS 13.0, *) { return UIApplication.shared.windows.first { $0.isKeyWindow }! } else { return UIApplication.shared.keyWindow! } } ================================================ FILE: AppStoreDemo/Utils/GlobalFunctions.swift ================================================ // // GlobalFunctions.swift // AppStoreDemo // // Created by Allen long on 2019/8/2. // Copyright © 2019 Utimes. All rights reserved. // import UIKit /// Judge whether the phone has a top notch func hasTopNotch()-> Bool { if #available(iOS 11.0, *) { let top = UIApplication.shared.delegate?.window??.safeAreaInsets.top ?? 0 return top < CGFloat(24) ? false : true } else { return false } } /// delay action func delay(_ timeInterval: TimeInterval, closure: @escaping()->()) { DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + timeInterval) { closure() } } ================================================ FILE: AppStoreDemo/Utils/StarView.swift ================================================ // // StarView.swift // AppStoreDemo // // Created by Allen long on 2019/8/8. // Copyright © 2019 Utimes. All rights reserved. // import UIKit class StarView: UIView { override init(frame: CGRect) { super.init(frame: frame) backgroundColor = .white } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func draw(_ rect: CGRect) { super.draw(rect) self.backgroundColor = UIColor.clear var points: [CGPoint] = [] let context = UIGraphicsGetCurrentContext() context?.setStrokeColor(UIColor.clear.cgColor) let grayColor = UIColor(red: 123/255.0, green: 124/255.0, blue: 128/255.0, alpha: 1) context?.setFillColor(grayColor.cgColor) context?.setLineWidth(1) let radius = Float(self.bounds.width / 2) let angel = Double.pi * 2 / 5 for i in 1...5 { //这里是获取五角星的五个定点的坐标点位置 let x = Float(self.bounds.width / 2) - sinf(Float(i) * Float(angel)) * radius let y = Float(self.bounds.height / 2) - cosf(Float(i) * Float(angel)) * radius points.append(CGPoint(x: CGFloat(x), y: CGFloat(y))) } context?.move(to: points.first!) for i in 1...5 { let index = (2 * i) % 5 context?.addLine(to: points[index]) } context?.closePath() context?.drawPath(using: CGPathDrawingMode.fillStroke) } } ================================================ FILE: AppStoreDemo.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 50; objects = { /* Begin PBXBuildFile section */ 06B0FB9A2320A6EF0004A4E4 /* DownloadViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06B0FB992320A6EF0004A4E4 /* DownloadViewController.swift */; }; 06B0FB9E2320ABB10004A4E4 /* DownloadPresentationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06B0FB9D2320ABB10004A4E4 /* DownloadPresentationController.swift */; }; 06B0FBA02320AE730004A4E4 /* DownloadTransitioning.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06B0FB9F2320AE730004A4E4 /* DownloadTransitioning.swift */; }; 06B0FBA32320B3120004A4E4 /* DownloadBottomView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06B0FBA22320B3120004A4E4 /* DownloadBottomView.swift */; }; 06B0FBA52320B3490004A4E4 /* DownloadBottomView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 06B0FBA42320B3490004A4E4 /* DownloadBottomView.xib */; }; 06B0FBA72320EC0B0004A4E4 /* DownloadClickView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06B0FBA62320EC0B0004A4E4 /* DownloadClickView.swift */; }; 06B0FBA92320EC140004A4E4 /* DownloadClickView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 06B0FBA82320EC140004A4E4 /* DownloadClickView.xib */; }; 06C2460E23161330005B6EB7 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 06C2460C23161330005B6EB7 /* LaunchScreen.storyboard */; }; 06C2466323161377005B6EB7 /* UIColor+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C2461623161377005B6EB7 /* UIColor+Extension.swift */; }; 06C2466423161377005B6EB7 /* UIView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C2461723161377005B6EB7 /* UIView+Extension.swift */; }; 06C2466523161377005B6EB7 /* UIViewController+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C2461823161377005B6EB7 /* UIViewController+Extension.swift */; }; 06C2466623161377005B6EB7 /* String+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C2461923161377005B6EB7 /* String+Extension.swift */; }; 06C2466723161377005B6EB7 /* UITableView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C2461A23161377005B6EB7 /* UITableView+Extension.swift */; }; 06C2466823161377005B6EB7 /* SearchTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C2461D23161377005B6EB7 /* SearchTableViewController.swift */; }; 06C2466923161377005B6EB7 /* SearchTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C2461F23161377005B6EB7 /* SearchTableViewCell.swift */; }; 06C2466A23161377005B6EB7 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C2462023161377005B6EB7 /* AppDelegate.swift */; }; 06C2466B23161377005B6EB7 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 06C2462123161377005B6EB7 /* Main.storyboard */; }; 06C2466C23161377005B6EB7 /* StarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C2462323161377005B6EB7 /* StarView.swift */; }; 06C2466D23161377005B6EB7 /* GlobalConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C2462423161377005B6EB7 /* GlobalConstants.swift */; }; 06C2466E23161377005B6EB7 /* GlobalFunctions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C2462523161377005B6EB7 /* GlobalFunctions.swift */; }; 06C2466F23161377005B6EB7 /* CardDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C2462823161377005B6EB7 /* CardDetailViewController.swift */; }; 06C2467023161377005B6EB7 /* TodayViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C2462923161377005B6EB7 /* TodayViewController.swift */; }; 06C2467123161377005B6EB7 /* CardPresentationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C2462A23161377005B6EB7 /* CardPresentationController.swift */; }; 06C2467223161377005B6EB7 /* TodayAnimationTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C2462C23161377005B6EB7 /* TodayAnimationTransition.swift */; }; 06C2467323161377005B6EB7 /* DS_Store in Resources */ = {isa = PBXBuildFile; fileRef = 06C2462D23161377005B6EB7 /* DS_Store */; }; 06C2467423161377005B6EB7 /* DetailScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C2462F23161377005B6EB7 /* DetailScrollView.swift */; }; 06C2467523161377005B6EB7 /* TodayTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C2463023161377005B6EB7 /* TodayTableViewCell.swift */; }; 06C2467623161377005B6EB7 /* TodayTableHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C2463123161377005B6EB7 /* TodayTableHeaderView.swift */; }; 06C2467723161377005B6EB7 /* GameTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C2463423161377005B6EB7 /* GameTableViewController.swift */; }; 06C2467823161377005B6EB7 /* DetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C2463523161377005B6EB7 /* DetailViewController.swift */; }; 06C2467923161377005B6EB7 /* GameTopicModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C2463723161377005B6EB7 /* GameTopicModel.swift */; }; 06C2467A23161377005B6EB7 /* GameRecommandModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C2463823161377005B6EB7 /* GameRecommandModel.swift */; }; 06C2467B23161377005B6EB7 /* RecommandCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 06C2463B23161377005B6EB7 /* RecommandCollectionViewCell.xib */; }; 06C2467C23161377005B6EB7 /* GameRecommandTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C2463C23161377005B6EB7 /* GameRecommandTableViewCell.swift */; }; 06C2467D23161377005B6EB7 /* RecommandCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C2463D23161377005B6EB7 /* RecommandCollectionViewCell.swift */; }; 06C2467E23161377005B6EB7 /* GameRecommandCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C2463E23161377005B6EB7 /* GameRecommandCollectionView.swift */; }; 06C2467F23161377005B6EB7 /* GameTopicCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 06C2464023161377005B6EB7 /* GameTopicCollectionViewCell.xib */; }; 06C2468023161377005B6EB7 /* GameTopicCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C2464123161377005B6EB7 /* GameTopicCollectionView.swift */; }; 06C2468123161377005B6EB7 /* GameTopicCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C2464223161377005B6EB7 /* GameTopicCollectionViewCell.swift */; }; 06C2468223161377005B6EB7 /* GameTopicTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C2464323161377005B6EB7 /* GameTopicTableViewCell.swift */; }; 06C2468323161377005B6EB7 /* DetailTopInfoCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 06C2464523161377005B6EB7 /* DetailTopInfoCell.xib */; }; 06C2468423161377005B6EB7 /* DetailNewFeaturesCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C2464623161377005B6EB7 /* DetailNewFeaturesCell.swift */; }; 06C2468523161377005B6EB7 /* DetailPreviewCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C2464723161377005B6EB7 /* DetailPreviewCollectionViewCell.swift */; }; 06C2468623161377005B6EB7 /* DetailInfomationTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C2464823161377005B6EB7 /* DetailInfomationTableViewCell.swift */; }; 06C2468723161377005B6EB7 /* DetailPreviewCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C2464923161377005B6EB7 /* DetailPreviewCollectionView.swift */; }; 06C2468823161377005B6EB7 /* DetailTopInfoCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C2464A23161377005B6EB7 /* DetailTopInfoCell.swift */; }; 06C2468923161377005B6EB7 /* DetailNewFeaturesCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 06C2464B23161377005B6EB7 /* DetailNewFeaturesCell.xib */; }; 06C2468A23161377005B6EB7 /* DetailInformationCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C2464C23161377005B6EB7 /* DetailInformationCell.swift */; }; 06C2468B23161377005B6EB7 /* DetailPreviewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C2464D23161377005B6EB7 /* DetailPreviewCell.swift */; }; 06C2468C23161377005B6EB7 /* DetailNavigationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C2464E23161377005B6EB7 /* DetailNavigationView.swift */; }; 06C2468D23161377005B6EB7 /* GameLinkTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C2465023161377005B6EB7 /* GameLinkTableViewCell.swift */; }; 06C2468E23161377005B6EB7 /* GameLinkTableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C2465123161377005B6EB7 /* GameLinkTableView.swift */; }; 06C2468F23161377005B6EB7 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 06C2465223161377005B6EB7 /* Assets.xcassets */; }; 06C2469023161377005B6EB7 /* CommonSectionHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C2465423161377005B6EB7 /* CommonSectionHeaderView.swift */; }; 06C2469123161377005B6EB7 /* CommonCollectionFlowLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C2465523161377005B6EB7 /* CommonCollectionFlowLayout.swift */; }; 06C2469223161377005B6EB7 /* UpdateTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C2465823161377005B6EB7 /* UpdateTableViewController.swift */; }; 06C2469323161377005B6EB7 /* UpdateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C2465A23161377005B6EB7 /* UpdateModel.swift */; }; 06C2469423161377005B6EB7 /* UpdateTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C2465C23161377005B6EB7 /* UpdateTableViewCell.swift */; }; 06C2469523161377005B6EB7 /* UpdateTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 06C2465D23161377005B6EB7 /* UpdateTableViewCell.xib */; }; 06C2469623161377005B6EB7 /* UserTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C2466023161377005B6EB7 /* UserTableViewController.swift */; }; 06C2469723161377005B6EB7 /* User.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 06C2466223161377005B6EB7 /* User.storyboard */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ 06B0FB992320A6EF0004A4E4 /* DownloadViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DownloadViewController.swift; sourceTree = ""; }; 06B0FB9D2320ABB10004A4E4 /* DownloadPresentationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DownloadPresentationController.swift; sourceTree = ""; }; 06B0FB9F2320AE730004A4E4 /* DownloadTransitioning.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DownloadTransitioning.swift; sourceTree = ""; }; 06B0FBA22320B3120004A4E4 /* DownloadBottomView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DownloadBottomView.swift; sourceTree = ""; }; 06B0FBA42320B3490004A4E4 /* DownloadBottomView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = DownloadBottomView.xib; sourceTree = ""; }; 06B0FBA62320EC0B0004A4E4 /* DownloadClickView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DownloadClickView.swift; sourceTree = ""; }; 06B0FBA82320EC140004A4E4 /* DownloadClickView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = DownloadClickView.xib; sourceTree = ""; }; 06C246002316132F005B6EB7 /* AppStoreDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AppStoreDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 06C2460D23161330005B6EB7 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 06C2460F23161330005B6EB7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 06C2461623161377005B6EB7 /* UIColor+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIColor+Extension.swift"; sourceTree = ""; }; 06C2461723161377005B6EB7 /* UIView+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIView+Extension.swift"; sourceTree = ""; }; 06C2461823161377005B6EB7 /* UIViewController+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIViewController+Extension.swift"; sourceTree = ""; }; 06C2461923161377005B6EB7 /* String+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+Extension.swift"; sourceTree = ""; }; 06C2461A23161377005B6EB7 /* UITableView+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UITableView+Extension.swift"; sourceTree = ""; }; 06C2461D23161377005B6EB7 /* SearchTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SearchTableViewController.swift; sourceTree = ""; }; 06C2461F23161377005B6EB7 /* SearchTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SearchTableViewCell.swift; sourceTree = ""; }; 06C2462023161377005B6EB7 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 06C2462123161377005B6EB7 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = ""; }; 06C2462323161377005B6EB7 /* StarView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StarView.swift; sourceTree = ""; }; 06C2462423161377005B6EB7 /* GlobalConstants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GlobalConstants.swift; sourceTree = ""; }; 06C2462523161377005B6EB7 /* GlobalFunctions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GlobalFunctions.swift; sourceTree = ""; }; 06C2462823161377005B6EB7 /* CardDetailViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CardDetailViewController.swift; sourceTree = ""; }; 06C2462923161377005B6EB7 /* TodayViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TodayViewController.swift; sourceTree = ""; }; 06C2462A23161377005B6EB7 /* CardPresentationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CardPresentationController.swift; sourceTree = ""; }; 06C2462C23161377005B6EB7 /* TodayAnimationTransition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TodayAnimationTransition.swift; sourceTree = ""; }; 06C2462D23161377005B6EB7 /* DS_Store */ = {isa = PBXFileReference; lastKnownFileType = file; path = DS_Store; sourceTree = ""; }; 06C2462F23161377005B6EB7 /* DetailScrollView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DetailScrollView.swift; sourceTree = ""; }; 06C2463023161377005B6EB7 /* TodayTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TodayTableViewCell.swift; sourceTree = ""; }; 06C2463123161377005B6EB7 /* TodayTableHeaderView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TodayTableHeaderView.swift; sourceTree = ""; }; 06C2463423161377005B6EB7 /* GameTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GameTableViewController.swift; sourceTree = ""; }; 06C2463523161377005B6EB7 /* DetailViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DetailViewController.swift; sourceTree = ""; }; 06C2463723161377005B6EB7 /* GameTopicModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GameTopicModel.swift; sourceTree = ""; }; 06C2463823161377005B6EB7 /* GameRecommandModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GameRecommandModel.swift; sourceTree = ""; }; 06C2463B23161377005B6EB7 /* RecommandCollectionViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = RecommandCollectionViewCell.xib; sourceTree = ""; }; 06C2463C23161377005B6EB7 /* GameRecommandTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GameRecommandTableViewCell.swift; sourceTree = ""; }; 06C2463D23161377005B6EB7 /* RecommandCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecommandCollectionViewCell.swift; sourceTree = ""; }; 06C2463E23161377005B6EB7 /* GameRecommandCollectionView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GameRecommandCollectionView.swift; sourceTree = ""; }; 06C2464023161377005B6EB7 /* GameTopicCollectionViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = GameTopicCollectionViewCell.xib; sourceTree = ""; }; 06C2464123161377005B6EB7 /* GameTopicCollectionView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GameTopicCollectionView.swift; sourceTree = ""; }; 06C2464223161377005B6EB7 /* GameTopicCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GameTopicCollectionViewCell.swift; sourceTree = ""; }; 06C2464323161377005B6EB7 /* GameTopicTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GameTopicTableViewCell.swift; sourceTree = ""; }; 06C2464523161377005B6EB7 /* DetailTopInfoCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = DetailTopInfoCell.xib; sourceTree = ""; }; 06C2464623161377005B6EB7 /* DetailNewFeaturesCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DetailNewFeaturesCell.swift; sourceTree = ""; }; 06C2464723161377005B6EB7 /* DetailPreviewCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DetailPreviewCollectionViewCell.swift; sourceTree = ""; }; 06C2464823161377005B6EB7 /* DetailInfomationTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DetailInfomationTableViewCell.swift; sourceTree = ""; }; 06C2464923161377005B6EB7 /* DetailPreviewCollectionView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DetailPreviewCollectionView.swift; sourceTree = ""; }; 06C2464A23161377005B6EB7 /* DetailTopInfoCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DetailTopInfoCell.swift; sourceTree = ""; }; 06C2464B23161377005B6EB7 /* DetailNewFeaturesCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = DetailNewFeaturesCell.xib; sourceTree = ""; }; 06C2464C23161377005B6EB7 /* DetailInformationCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DetailInformationCell.swift; sourceTree = ""; }; 06C2464D23161377005B6EB7 /* DetailPreviewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DetailPreviewCell.swift; sourceTree = ""; }; 06C2464E23161377005B6EB7 /* DetailNavigationView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DetailNavigationView.swift; sourceTree = ""; }; 06C2465023161377005B6EB7 /* GameLinkTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GameLinkTableViewCell.swift; sourceTree = ""; }; 06C2465123161377005B6EB7 /* GameLinkTableView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GameLinkTableView.swift; sourceTree = ""; }; 06C2465223161377005B6EB7 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 06C2465423161377005B6EB7 /* CommonSectionHeaderView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CommonSectionHeaderView.swift; sourceTree = ""; }; 06C2465523161377005B6EB7 /* CommonCollectionFlowLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CommonCollectionFlowLayout.swift; sourceTree = ""; }; 06C2465823161377005B6EB7 /* UpdateTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UpdateTableViewController.swift; sourceTree = ""; }; 06C2465A23161377005B6EB7 /* UpdateModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UpdateModel.swift; sourceTree = ""; }; 06C2465C23161377005B6EB7 /* UpdateTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UpdateTableViewCell.swift; sourceTree = ""; }; 06C2465D23161377005B6EB7 /* UpdateTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = UpdateTableViewCell.xib; sourceTree = ""; }; 06C2466023161377005B6EB7 /* UserTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserTableViewController.swift; sourceTree = ""; }; 06C2466223161377005B6EB7 /* User.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = User.storyboard; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 06C245FD2316132F005B6EB7 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 06B0FBA12320B2F30004A4E4 /* Download */ = { isa = PBXGroup; children = ( 06B0FBA22320B3120004A4E4 /* DownloadBottomView.swift */, 06B0FBA42320B3490004A4E4 /* DownloadBottomView.xib */, 06B0FBA62320EC0B0004A4E4 /* DownloadClickView.swift */, 06B0FBA82320EC140004A4E4 /* DownloadClickView.xib */, ); path = Download; sourceTree = ""; }; 06C245F72316132F005B6EB7 = { isa = PBXGroup; children = ( 06C246022316132F005B6EB7 /* AppStoreDemo */, 06C246012316132F005B6EB7 /* Products */, ); sourceTree = ""; }; 06C246012316132F005B6EB7 /* Products */ = { isa = PBXGroup; children = ( 06C246002316132F005B6EB7 /* AppStoreDemo.app */, ); name = Products; sourceTree = ""; }; 06C246022316132F005B6EB7 /* AppStoreDemo */ = { isa = PBXGroup; children = ( 06C2462023161377005B6EB7 /* AppDelegate.swift */, 06C2465223161377005B6EB7 /* Assets.xcassets */, 06C2465323161377005B6EB7 /* Common */, 06C2461523161377005B6EB7 /* Extension */, 06C2463223161377005B6EB7 /* Game */, 06C2462123161377005B6EB7 /* Main.storyboard */, 06C2461B23161377005B6EB7 /* Search */, 06C2462623161377005B6EB7 /* Today */, 06C2465623161377005B6EB7 /* Update */, 06C2465E23161377005B6EB7 /* User */, 06C2462223161377005B6EB7 /* Utils */, 06C2460C23161330005B6EB7 /* LaunchScreen.storyboard */, 06C2460F23161330005B6EB7 /* Info.plist */, ); path = AppStoreDemo; sourceTree = ""; }; 06C2461523161377005B6EB7 /* Extension */ = { isa = PBXGroup; children = ( 06C2461623161377005B6EB7 /* UIColor+Extension.swift */, 06C2461723161377005B6EB7 /* UIView+Extension.swift */, 06C2461823161377005B6EB7 /* UIViewController+Extension.swift */, 06C2461923161377005B6EB7 /* String+Extension.swift */, 06C2461A23161377005B6EB7 /* UITableView+Extension.swift */, ); path = Extension; sourceTree = ""; }; 06C2461B23161377005B6EB7 /* Search */ = { isa = PBXGroup; children = ( 06C2461C23161377005B6EB7 /* Controller */, 06C2461E23161377005B6EB7 /* View */, ); path = Search; sourceTree = ""; }; 06C2461C23161377005B6EB7 /* Controller */ = { isa = PBXGroup; children = ( 06C2461D23161377005B6EB7 /* SearchTableViewController.swift */, ); path = Controller; sourceTree = ""; }; 06C2461E23161377005B6EB7 /* View */ = { isa = PBXGroup; children = ( 06C2461F23161377005B6EB7 /* SearchTableViewCell.swift */, ); path = View; sourceTree = ""; }; 06C2462223161377005B6EB7 /* Utils */ = { isa = PBXGroup; children = ( 06C2462323161377005B6EB7 /* StarView.swift */, 06C2462423161377005B6EB7 /* GlobalConstants.swift */, 06C2462523161377005B6EB7 /* GlobalFunctions.swift */, ); path = Utils; sourceTree = ""; }; 06C2462623161377005B6EB7 /* Today */ = { isa = PBXGroup; children = ( 06C2462723161377005B6EB7 /* Controller */, 06C2462B23161377005B6EB7 /* Model */, 06C2462E23161377005B6EB7 /* View */, ); path = Today; sourceTree = ""; }; 06C2462723161377005B6EB7 /* Controller */ = { isa = PBXGroup; children = ( 06C2462923161377005B6EB7 /* TodayViewController.swift */, 06C2462823161377005B6EB7 /* CardDetailViewController.swift */, 06C2462A23161377005B6EB7 /* CardPresentationController.swift */, ); path = Controller; sourceTree = ""; }; 06C2462B23161377005B6EB7 /* Model */ = { isa = PBXGroup; children = ( 06C2462C23161377005B6EB7 /* TodayAnimationTransition.swift */, 06C2462D23161377005B6EB7 /* DS_Store */, ); path = Model; sourceTree = ""; }; 06C2462E23161377005B6EB7 /* View */ = { isa = PBXGroup; children = ( 06C2462F23161377005B6EB7 /* DetailScrollView.swift */, 06C2463023161377005B6EB7 /* TodayTableViewCell.swift */, 06C2463123161377005B6EB7 /* TodayTableHeaderView.swift */, ); path = View; sourceTree = ""; }; 06C2463223161377005B6EB7 /* Game */ = { isa = PBXGroup; children = ( 06C2463323161377005B6EB7 /* Controller */, 06C2463623161377005B6EB7 /* Model */, 06C2463923161377005B6EB7 /* View */, ); path = Game; sourceTree = ""; }; 06C2463323161377005B6EB7 /* Controller */ = { isa = PBXGroup; children = ( 06C2463423161377005B6EB7 /* GameTableViewController.swift */, 06C2463523161377005B6EB7 /* DetailViewController.swift */, 06B0FB992320A6EF0004A4E4 /* DownloadViewController.swift */, 06B0FB9D2320ABB10004A4E4 /* DownloadPresentationController.swift */, ); path = Controller; sourceTree = ""; }; 06C2463623161377005B6EB7 /* Model */ = { isa = PBXGroup; children = ( 06C2463723161377005B6EB7 /* GameTopicModel.swift */, 06C2463823161377005B6EB7 /* GameRecommandModel.swift */, 06B0FB9F2320AE730004A4E4 /* DownloadTransitioning.swift */, ); path = Model; sourceTree = ""; }; 06C2463923161377005B6EB7 /* View */ = { isa = PBXGroup; children = ( 06B0FBA12320B2F30004A4E4 /* Download */, 06C2463A23161377005B6EB7 /* Recommand */, 06C2463F23161377005B6EB7 /* Topic */, 06C2464423161377005B6EB7 /* Detail */, 06C2464F23161377005B6EB7 /* Link */, ); path = View; sourceTree = ""; }; 06C2463A23161377005B6EB7 /* Recommand */ = { isa = PBXGroup; children = ( 06C2463B23161377005B6EB7 /* RecommandCollectionViewCell.xib */, 06C2463C23161377005B6EB7 /* GameRecommandTableViewCell.swift */, 06C2463D23161377005B6EB7 /* RecommandCollectionViewCell.swift */, 06C2463E23161377005B6EB7 /* GameRecommandCollectionView.swift */, ); path = Recommand; sourceTree = ""; }; 06C2463F23161377005B6EB7 /* Topic */ = { isa = PBXGroup; children = ( 06C2464223161377005B6EB7 /* GameTopicCollectionViewCell.swift */, 06C2464023161377005B6EB7 /* GameTopicCollectionViewCell.xib */, 06C2464123161377005B6EB7 /* GameTopicCollectionView.swift */, 06C2464323161377005B6EB7 /* GameTopicTableViewCell.swift */, ); path = Topic; sourceTree = ""; }; 06C2464423161377005B6EB7 /* Detail */ = { isa = PBXGroup; children = ( 06C2464A23161377005B6EB7 /* DetailTopInfoCell.swift */, 06C2464523161377005B6EB7 /* DetailTopInfoCell.xib */, 06C2464623161377005B6EB7 /* DetailNewFeaturesCell.swift */, 06C2464723161377005B6EB7 /* DetailPreviewCollectionViewCell.swift */, 06C2464823161377005B6EB7 /* DetailInfomationTableViewCell.swift */, 06C2464923161377005B6EB7 /* DetailPreviewCollectionView.swift */, 06C2464B23161377005B6EB7 /* DetailNewFeaturesCell.xib */, 06C2464C23161377005B6EB7 /* DetailInformationCell.swift */, 06C2464D23161377005B6EB7 /* DetailPreviewCell.swift */, 06C2464E23161377005B6EB7 /* DetailNavigationView.swift */, ); path = Detail; sourceTree = ""; }; 06C2464F23161377005B6EB7 /* Link */ = { isa = PBXGroup; children = ( 06C2465023161377005B6EB7 /* GameLinkTableViewCell.swift */, 06C2465123161377005B6EB7 /* GameLinkTableView.swift */, ); path = Link; sourceTree = ""; }; 06C2465323161377005B6EB7 /* Common */ = { isa = PBXGroup; children = ( 06C2465423161377005B6EB7 /* CommonSectionHeaderView.swift */, 06C2465523161377005B6EB7 /* CommonCollectionFlowLayout.swift */, ); path = Common; sourceTree = ""; }; 06C2465623161377005B6EB7 /* Update */ = { isa = PBXGroup; children = ( 06C2465723161377005B6EB7 /* Controller */, 06C2465923161377005B6EB7 /* Model */, 06C2465B23161377005B6EB7 /* View */, ); path = Update; sourceTree = ""; }; 06C2465723161377005B6EB7 /* Controller */ = { isa = PBXGroup; children = ( 06C2465823161377005B6EB7 /* UpdateTableViewController.swift */, ); path = Controller; sourceTree = ""; }; 06C2465923161377005B6EB7 /* Model */ = { isa = PBXGroup; children = ( 06C2465A23161377005B6EB7 /* UpdateModel.swift */, ); path = Model; sourceTree = ""; }; 06C2465B23161377005B6EB7 /* View */ = { isa = PBXGroup; children = ( 06C2465C23161377005B6EB7 /* UpdateTableViewCell.swift */, 06C2465D23161377005B6EB7 /* UpdateTableViewCell.xib */, ); path = View; sourceTree = ""; }; 06C2465E23161377005B6EB7 /* User */ = { isa = PBXGroup; children = ( 06C2465F23161377005B6EB7 /* Controller */, 06C2466123161377005B6EB7 /* View */, ); path = User; sourceTree = ""; }; 06C2465F23161377005B6EB7 /* Controller */ = { isa = PBXGroup; children = ( 06C2466023161377005B6EB7 /* UserTableViewController.swift */, ); path = Controller; sourceTree = ""; }; 06C2466123161377005B6EB7 /* View */ = { isa = PBXGroup; children = ( 06C2466223161377005B6EB7 /* User.storyboard */, ); path = View; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ 06C245FF2316132F005B6EB7 /* AppStoreDemo */ = { isa = PBXNativeTarget; buildConfigurationList = 06C2461223161330005B6EB7 /* Build configuration list for PBXNativeTarget "AppStoreDemo" */; buildPhases = ( 06C245FC2316132F005B6EB7 /* Sources */, 06C245FD2316132F005B6EB7 /* Frameworks */, 06C245FE2316132F005B6EB7 /* Resources */, ); buildRules = ( ); dependencies = ( ); name = AppStoreDemo; productName = AppStoreDemo; productReference = 06C246002316132F005B6EB7 /* AppStoreDemo.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 06C245F82316132F005B6EB7 /* Project object */ = { isa = PBXProject; attributes = { LastSwiftUpdateCheck = 1030; LastUpgradeCheck = 1030; ORGANIZATIONNAME = Utimes; TargetAttributes = { 06C245FF2316132F005B6EB7 = { CreatedOnToolsVersion = 10.3; LastSwiftMigration = 1030; }; }; }; buildConfigurationList = 06C245FB2316132F005B6EB7 /* Build configuration list for PBXProject "AppStoreDemo" */; compatibilityVersion = "Xcode 9.3"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = 06C245F72316132F005B6EB7; productRefGroup = 06C246012316132F005B6EB7 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( 06C245FF2316132F005B6EB7 /* AppStoreDemo */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ 06C245FE2316132F005B6EB7 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 06C2460E23161330005B6EB7 /* LaunchScreen.storyboard in Resources */, 06C2469723161377005B6EB7 /* User.storyboard in Resources */, 06C2467B23161377005B6EB7 /* RecommandCollectionViewCell.xib in Resources */, 06C2468323161377005B6EB7 /* DetailTopInfoCell.xib in Resources */, 06C2467F23161377005B6EB7 /* GameTopicCollectionViewCell.xib in Resources */, 06C2469523161377005B6EB7 /* UpdateTableViewCell.xib in Resources */, 06C2466B23161377005B6EB7 /* Main.storyboard in Resources */, 06C2467323161377005B6EB7 /* DS_Store in Resources */, 06C2468F23161377005B6EB7 /* Assets.xcassets in Resources */, 06C2468923161377005B6EB7 /* DetailNewFeaturesCell.xib in Resources */, 06B0FBA52320B3490004A4E4 /* DownloadBottomView.xib in Resources */, 06B0FBA92320EC140004A4E4 /* DownloadClickView.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ 06C245FC2316132F005B6EB7 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 06C2468523161377005B6EB7 /* DetailPreviewCollectionViewCell.swift in Sources */, 06C2468A23161377005B6EB7 /* DetailInformationCell.swift in Sources */, 06C2467C23161377005B6EB7 /* GameRecommandTableViewCell.swift in Sources */, 06C2468C23161377005B6EB7 /* DetailNavigationView.swift in Sources */, 06C2467A23161377005B6EB7 /* GameRecommandModel.swift in Sources */, 06C2468023161377005B6EB7 /* GameTopicCollectionView.swift in Sources */, 06C2466E23161377005B6EB7 /* GlobalFunctions.swift in Sources */, 06C2468823161377005B6EB7 /* DetailTopInfoCell.swift in Sources */, 06C2468123161377005B6EB7 /* GameTopicCollectionViewCell.swift in Sources */, 06C2466F23161377005B6EB7 /* CardDetailViewController.swift in Sources */, 06C2466C23161377005B6EB7 /* StarView.swift in Sources */, 06B0FBA32320B3120004A4E4 /* DownloadBottomView.swift in Sources */, 06C2469023161377005B6EB7 /* CommonSectionHeaderView.swift in Sources */, 06B0FBA72320EC0B0004A4E4 /* DownloadClickView.swift in Sources */, 06C2468723161377005B6EB7 /* DetailPreviewCollectionView.swift in Sources */, 06B0FB9A2320A6EF0004A4E4 /* DownloadViewController.swift in Sources */, 06C2467923161377005B6EB7 /* GameTopicModel.swift in Sources */, 06C2466A23161377005B6EB7 /* AppDelegate.swift in Sources */, 06C2466D23161377005B6EB7 /* GlobalConstants.swift in Sources */, 06C2466623161377005B6EB7 /* String+Extension.swift in Sources */, 06C2469423161377005B6EB7 /* UpdateTableViewCell.swift in Sources */, 06C2468223161377005B6EB7 /* GameTopicTableViewCell.swift in Sources */, 06C2467523161377005B6EB7 /* TodayTableViewCell.swift in Sources */, 06C2468623161377005B6EB7 /* DetailInfomationTableViewCell.swift in Sources */, 06C2469223161377005B6EB7 /* UpdateTableViewController.swift in Sources */, 06C2469123161377005B6EB7 /* CommonCollectionFlowLayout.swift in Sources */, 06C2469323161377005B6EB7 /* UpdateModel.swift in Sources */, 06B0FB9E2320ABB10004A4E4 /* DownloadPresentationController.swift in Sources */, 06C2467823161377005B6EB7 /* DetailViewController.swift in Sources */, 06C2466823161377005B6EB7 /* SearchTableViewController.swift in Sources */, 06C2467023161377005B6EB7 /* TodayViewController.swift in Sources */, 06C2467223161377005B6EB7 /* TodayAnimationTransition.swift in Sources */, 06C2467623161377005B6EB7 /* TodayTableHeaderView.swift in Sources */, 06C2466523161377005B6EB7 /* UIViewController+Extension.swift in Sources */, 06C2469623161377005B6EB7 /* UserTableViewController.swift in Sources */, 06C2466723161377005B6EB7 /* UITableView+Extension.swift in Sources */, 06C2466423161377005B6EB7 /* UIView+Extension.swift in Sources */, 06C2466323161377005B6EB7 /* UIColor+Extension.swift in Sources */, 06C2467D23161377005B6EB7 /* RecommandCollectionViewCell.swift in Sources */, 06C2468423161377005B6EB7 /* DetailNewFeaturesCell.swift in Sources */, 06C2467123161377005B6EB7 /* CardPresentationController.swift in Sources */, 06C2467E23161377005B6EB7 /* GameRecommandCollectionView.swift in Sources */, 06C2467723161377005B6EB7 /* GameTableViewController.swift in Sources */, 06C2467423161377005B6EB7 /* DetailScrollView.swift in Sources */, 06C2466923161377005B6EB7 /* SearchTableViewCell.swift in Sources */, 06C2468B23161377005B6EB7 /* DetailPreviewCell.swift in Sources */, 06B0FBA02320AE730004A4E4 /* DownloadTransitioning.swift in Sources */, 06C2468D23161377005B6EB7 /* GameLinkTableViewCell.swift in Sources */, 06C2468E23161377005B6EB7 /* GameLinkTableView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXVariantGroup section */ 06C2460C23161330005B6EB7 /* LaunchScreen.storyboard */ = { isa = PBXVariantGroup; children = ( 06C2460D23161330005B6EB7 /* Base */, ); name = LaunchScreen.storyboard; sourceTree = ""; }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ 06C2461023161330005B6EB7 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 12.4; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; name = Debug; }; 06C2461123161330005B6EB7 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 12.4; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = iphoneos; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; VALIDATE_PRODUCT = YES; }; name = Release; }; 06C2461323161330005B6EB7 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = FXRUS8AN42; INFOPLIST_FILE = AppStoreDemo/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 10.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); PRODUCT_BUNDLE_IDENTIFIER = cc.utimes.AppStoreDemo; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 4.2; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; 06C2461423161330005B6EB7 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = FXRUS8AN42; INFOPLIST_FILE = AppStoreDemo/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 10.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); PRODUCT_BUNDLE_IDENTIFIER = cc.utimes.AppStoreDemo; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 4.2; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 06C245FB2316132F005B6EB7 /* Build configuration list for PBXProject "AppStoreDemo" */ = { isa = XCConfigurationList; buildConfigurations = ( 06C2461023161330005B6EB7 /* Debug */, 06C2461123161330005B6EB7 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 06C2461223161330005B6EB7 /* Build configuration list for PBXNativeTarget "AppStoreDemo" */ = { isa = XCConfigurationList; buildConfigurations = ( 06C2461323161330005B6EB7 /* Debug */, 06C2461423161330005B6EB7 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = 06C245F82316132F005B6EB7 /* Project object */; } ================================================ FILE: AppStoreDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: AppStoreDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist ================================================ IDEDidComputeMac32BitWarning ================================================ FILE: AppStoreDemo.xcodeproj/xcuserdata/fengbufang.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist ================================================ ================================================ FILE: AppStoreDemo.xcodeproj/xcuserdata/fengbufang.xcuserdatad/xcschemes/xcschememanagement.plist ================================================ SchemeUserState AppStoreDemo.xcscheme_^#shared#^_ orderHint 0 ================================================ FILE: README.md ================================================ # appstore-clone an awesome clone of iOS App Store.[中文简介](https://github.com/DragonTnT/appstore-clone/blob/master/%E4%B8%AD%E6%96%87%E7%AE%80%E4%BB%8B.md) ## Screenshots ![](https://github.com/DragonTnT/Resource/blob/master/AppStore/cover_appstore.png) ## Description App Store is always my favorite app in iphone. As a iOS developer, i decide to make a clone for it. It took me a few days to do this work, and i have not finished it yet. In the future, i will add more contents on it. If you are interested in the project, you can also submit your content. ## GIF | ![](https://github.com/DragonTnT/Resource/blob/master/AppStore/showcase1.gif) | ![](https://github.com/DragonTnT/Resource/blob/master/AppStore/showcase2.gif) | ![](https://github.com/DragonTnT/Resource/blob/master/AppStore/showcase3.gif)| ![](https://github.com/DragonTnT/Resource/blob/master/AppStore/showcase4.gif) |:-|:-|:-|:-| ## Features - No other link libraries, so just download the project and run the app - No network, only load resource locally - The project has a clear structure, you can find the code you want easily ## Finals If you like this project, you can give me a **star**. Thanks~ ================================================ FILE: 中文简介.md ================================================ # appstore-clone 本项目是对iOS中的软件App Store的模仿 ## 介绍 App Store应该是大家在iphone里最常用的软件之一。作为一名iOS开发者,我一直有模仿App Store的想法。我花了一些业余时间来做这个demo,目前还有一些内容没有完成。剩下的部分,我会慢慢补齐。 如果你对这个项目有兴趣的话,欢迎提交你的代码,让我们一起来完成它~ ## 特点 - 没有任何第三方的库,下载下来直接运行就OK了。真机运行的话,改下证书就好了。 - 没有任何的网络请求,所以的资源都是加载自本地。(也确实没法拿到苹果的数据😓) - 项目结构很清晰,你可以轻易找到如何实现的代码。 ## 最后 如果你觉得项目还不错的话,可以给我一个**Star**。谢谢。