Full Code of DragonTnT/appstore-clone for AI

master 7166ceba69b6 cached
102 files
261.2 KB
73.7k tokens
1 requests
Download .txt
Showing preview only (293K chars total). Download the full file or copy to clipboard to get everything.
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
================================================
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
    <dependencies>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
    </dependencies>
    <scenes>
        <!--View Controller-->
        <scene sceneID="EHf-IW-A2E">
            <objects>
                <viewController id="01J-lp-oVM" sceneMemberID="viewController">
                    <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                        <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                        <viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
                    </view>
                </viewController>
                <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
            </objects>
            <point key="canvasLocation" x="53" y="375"/>
        </scene>
    </scenes>
</document>


================================================
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<T>(_ 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<T>(_ cellType: T.Type) where T: UITableViewCell {
        let identifier = "\(cellType)"
        register(cellType, forCellReuseIdentifier: identifier)
    }
    // 从缓存池池出队已经存在的 cell
    func ut_dequeueReusable<T: UITableViewCell>(_ cell: T.Type, for indexPath: IndexPath) -> T {
        let cell = dequeueReusableCell(withIdentifier: "\(T.self)", for: indexPath) as! T
        return cell
    }
    
    // 注册有nib的headerFooterView
    func ut_registerNibHeaderFooterView<T>(_ 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<T>(_ viewType: T.Type) where T: UITableViewHeaderFooterView {
        let identifier = "\(viewType)"
        register(viewType, forHeaderFooterViewReuseIdentifier: identifier)
    }
    
    // 从缓存池里取出已注册的headerFooterView
    func ut_dequeueReusableHeaderFooterView<T: UITableViewHeaderFooterView>(_ view: T.Type) -> T {
        let headerFooterView = dequeueReusableHeaderFooterView(withIdentifier: "\(T.self)") as! T
        return headerFooterView
    }
}

extension UICollectionView {
    // 注册有nib的cell
    func ut_registerNibCell<T>(_ 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<T>(_ cellType: T.Type) where T: UICollectionViewCell {
        let identifier = "\(cellType)"
        register(cellType, forCellWithReuseIdentifier: identifier)
    }
    // 从缓存池池出队已经存在的 cell
    func ut_dequeueReusable<T: UICollectionViewCell>(_ 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
================================================
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
    <device id="retina6_1" orientation="portrait">
        <adaptation id="fullscreen"/>
    </device>
    <dependencies>
        <deployment identifier="iOS"/>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
    </dependencies>
    <objects>
        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
        <tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" rowHeight="141" id="KGk-i7-Jjw" customClass="DetailNewFeaturesCell" customModule="AppStoreTodayDemo" customModuleProvider="target">
            <rect key="frame" x="0.0" y="0.0" width="311" height="141"/>
            <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
            <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
                <rect key="frame" x="0.0" y="0.0" width="311" height="140.5"/>
                <autoresizingMask key="autoresizingMask"/>
                <subviews>
                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="What's News" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="WYL-Ra-05C">
                        <rect key="frame" x="20" y="14" width="129" height="22"/>
                        <constraints>
                            <constraint firstAttribute="height" constant="22" id="2A0-JI-vso"/>
                        </constraints>
                        <fontDescription key="fontDescription" type="system" weight="medium" pointSize="22"/>
                        <nil key="textColor"/>
                        <nil key="highlightedColor"/>
                    </label>
                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Version 1.0.5" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ivW-Y5-4Qt">
                        <rect key="frame" x="20" y="51" width="87" height="15"/>
                        <constraints>
                            <constraint firstAttribute="height" constant="15" id="gFX-W4-du9"/>
                        </constraints>
                        <fontDescription key="fontDescription" type="system" pointSize="15"/>
                        <color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                        <nil key="highlightedColor"/>
                    </label>
                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Version History" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="c6v-Mp-5MT">
                        <rect key="frame" x="174" y="14.5" width="117" height="21"/>
                        <fontDescription key="fontDescription" type="system" pointSize="17"/>
                        <color key="textColor" red="0.0" green="0.47843137250000001" blue="1" alpha="1" colorSpace="calibratedRGB"/>
                        <nil key="highlightedColor"/>
                    </label>
                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="two days ago" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="0YL-Ny-Dnu">
                        <rect key="frame" x="200" y="49.5" width="91" height="18"/>
                        <fontDescription key="fontDescription" type="system" pointSize="15"/>
                        <color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                        <nil key="highlightedColor"/>
                    </label>
                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Here are more styles for playing. You can chose more than 2 characters for yourself. Just have a try!" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="7GW-j5-owT">
                        <rect key="frame" x="20" y="80" width="271" height="40.5"/>
                        <fontDescription key="fontDescription" type="system" pointSize="15"/>
                        <nil key="textColor"/>
                        <nil key="highlightedColor"/>
                    </label>
                </subviews>
                <constraints>
                    <constraint firstAttribute="trailing" secondItem="0YL-Ny-Dnu" secondAttribute="trailing" constant="20" id="57D-GZ-Kl6"/>
                    <constraint firstItem="c6v-Mp-5MT" firstAttribute="centerY" secondItem="WYL-Ra-05C" secondAttribute="centerY" id="61Y-lb-r1s"/>
                    <constraint firstItem="7GW-j5-owT" firstAttribute="top" secondItem="ivW-Y5-4Qt" secondAttribute="bottom" constant="14" id="Imd-C3-ZnS"/>
                    <constraint firstItem="WYL-Ra-05C" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="20" id="Jxu-6Y-2oA"/>
                    <constraint firstItem="7GW-j5-owT" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="20" id="PAZ-JE-PXn"/>
                    <constraint firstItem="WYL-Ra-05C" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" constant="14" id="Qci-kQ-hmb"/>
                    <constraint firstAttribute="trailing" secondItem="7GW-j5-owT" secondAttribute="trailing" constant="20" id="Uld-nK-B0x"/>
                    <constraint firstItem="0YL-Ny-Dnu" firstAttribute="centerY" secondItem="ivW-Y5-4Qt" secondAttribute="centerY" id="XNq-iK-C6P"/>
                    <constraint firstItem="ivW-Y5-4Qt" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="20" id="dVj-S6-tbb"/>
                    <constraint firstAttribute="trailing" secondItem="c6v-Mp-5MT" secondAttribute="trailing" constant="20" id="jEw-fb-ryV"/>
                    <constraint firstAttribute="bottom" secondItem="7GW-j5-owT" secondAttribute="bottom" constant="20" id="mFv-Cb-RYD"/>
                    <constraint firstItem="ivW-Y5-4Qt" firstAttribute="top" secondItem="WYL-Ra-05C" secondAttribute="bottom" constant="15" id="v00-I7-hCn"/>
                </constraints>
            </tableViewCellContentView>
            <viewLayoutGuide key="safeArea" id="njF-e1-oar"/>
            <point key="canvasLocation" x="-254" y="79"/>
        </tableViewCell>
    </objects>
</document>


================================================
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
================================================
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
    <device id="retina6_1" orientation="portrait">
        <adaptation id="fullscreen"/>
    </device>
    <dependencies>
        <deployment identifier="iOS"/>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
    </dependencies>
    <objects>
        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
        <tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" rowHeight="226" id="KGk-i7-Jjw" customClass="DetailTopInfoCell" customModule="AppStoreTodayDemo" customModuleProvider="target">
            <rect key="frame" x="0.0" y="0.0" width="352" height="226"/>
            <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
            <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
                <rect key="frame" x="0.0" y="0.0" width="352" height="225.5"/>
                <autoresizingMask key="autoresizingMask"/>
                <subviews>
                    <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="logo_game" translatesAutoresizingMaskIntoConstraints="NO" id="tiI-sU-Z6o">
                        <rect key="frame" x="20" y="20" width="118" height="118"/>
                        <constraints>
                            <constraint firstAttribute="width" constant="118" id="sNK-yB-9Bc"/>
                            <constraint firstAttribute="height" constant="118" id="uH5-sj-TUo"/>
                        </constraints>
                        <userDefinedRuntimeAttributes>
                            <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
                                <real key="value" value="26"/>
                            </userDefinedRuntimeAttribute>
                        </userDefinedRuntimeAttributes>
                    </imageView>
                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Onmyoji " textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="d15-nB-2Mo">
                        <rect key="frame" x="158" y="30" width="93" height="25"/>
                        <constraints>
                            <constraint firstAttribute="height" constant="25" id="N5f-Md-Sbm"/>
                        </constraints>
                        <fontDescription key="fontDescription" name="PingFangSC-Medium" family="PingFang SC" pointSize="22"/>
                        <nil key="textColor"/>
                        <nil key="highlightedColor"/>
                    </label>
                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="The best game" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="G2w-No-SZn">
                        <rect key="frame" x="158" y="61" width="105" height="18"/>
                        <constraints>
                            <constraint firstAttribute="height" constant="18" id="AOh-ES-23M"/>
                        </constraints>
                        <fontDescription key="fontDescription" name="PingFangSC-Regular" family="PingFang SC" pointSize="15"/>
                        <color key="textColor" red="0.60392156862745094" green="0.60271537303924561" blue="0.60271537303924561" alpha="1" colorSpace="calibratedRGB"/>
                        <nil key="highlightedColor"/>
                    </label>
                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="BEu-g1-DnR">
                        <rect key="frame" x="158" y="108" width="30" height="30"/>
                        <constraints>
                            <constraint firstAttribute="height" constant="30" id="4Wk-WY-NHU"/>
                            <constraint firstAttribute="width" constant="30" id="Ojo-Su-b9f"/>
                        </constraints>
                        <state key="normal" image="detail_download"/>
                    </button>
                    <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="FHb-ar-fAV">
                        <rect key="frame" x="20" y="173" width="90" height="15"/>
                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                        <constraints>
                            <constraint firstAttribute="height" constant="15" id="3kI-7L-0C2"/>
                            <constraint firstAttribute="width" constant="90" id="52I-tz-OfJ"/>
                        </constraints>
                    </view>
                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="4.0, 250rateings" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="F85-Ay-7P0">
                        <rect key="frame" x="20" y="198" width="95.5" height="14"/>
                        <constraints>
                            <constraint firstAttribute="height" constant="14" id="meS-bI-B2v"/>
                        </constraints>
                        <fontDescription key="fontDescription" type="system" weight="medium" pointSize="12"/>
                        <color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                        <nil key="highlightedColor"/>
                    </label>
                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="9+" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Svi-qZ-ImG">
                        <rect key="frame" x="306.5" y="166" width="28" height="22"/>
                        <constraints>
                            <constraint firstAttribute="height" constant="22" id="6F0-T3-qKz"/>
                        </constraints>
                        <fontDescription key="fontDescription" type="system" weight="medium" pointSize="22"/>
                        <color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                        <nil key="highlightedColor"/>
                    </label>
                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Age" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="fPj-UT-IDK">
                        <rect key="frame" x="309" y="198" width="23" height="14"/>
                        <constraints>
                            <constraint firstAttribute="height" constant="14" id="AsL-9R-LdB"/>
                        </constraints>
                        <fontDescription key="fontDescription" type="system" weight="medium" pointSize="12"/>
                        <color key="textColor" white="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                        <nil key="highlightedColor"/>
                    </label>
                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="strategy" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="6Bz-mf-KEg">
                        <rect key="frame" x="215.5" y="198" width="48.5" height="14"/>
                        <constraints>
                            <constraint firstAttribute="height" constant="14" id="rGS-XF-rkm"/>
                        </constraints>
                        <fontDescription key="fontDescription" type="system" weight="medium" pointSize="12"/>
                        <color key="textColor" white="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                        <nil key="highlightedColor"/>
                    </label>
                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="#31" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="dOx-33-HFw">
                        <rect key="frame" x="221" y="165.5" width="38" height="22"/>
                        <constraints>
                            <constraint firstAttribute="height" constant="22" id="CAC-bT-V3S"/>
                        </constraints>
                        <fontDescription key="fontDescription" type="system" weight="medium" pointSize="22"/>
                        <color key="textColor" white="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                        <nil key="highlightedColor"/>
                    </label>
                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="UGM-oL-VZP">
                        <rect key="frame" x="302" y="108" width="30" height="30"/>
                        <constraints>
                            <constraint firstAttribute="width" constant="30" id="Ke9-FR-rjR"/>
                            <constraint firstAttribute="height" constant="30" id="SYW-hY-5G7"/>
                        </constraints>
                        <state key="normal" image="detail_more"/>
                    </button>
                </subviews>
                <constraints>
                    <constraint firstItem="fPj-UT-IDK" firstAttribute="centerY" secondItem="F85-Ay-7P0" secondAttribute="centerY" id="1hm-iv-oSq"/>
                    <constraint firstItem="d15-nB-2Mo" firstAttribute="leading" secondItem="tiI-sU-Z6o" secondAttribute="trailing" constant="20" id="3Gy-1R-wMX"/>
                    <constraint firstItem="6Bz-mf-KEg" firstAttribute="centerX" secondItem="dOx-33-HFw" secondAttribute="centerX" id="8Yq-yi-8Qf"/>
                    <constraint firstItem="6Bz-mf-KEg" firstAttribute="centerY" secondItem="F85-Ay-7P0" secondAttribute="centerY" id="CiH-2C-9n5"/>
                    <constraint firstItem="BEu-g1-DnR" firstAttribute="leading" secondItem="tiI-sU-Z6o" secondAttribute="trailing" constant="20" id="DFI-0o-8D9"/>
                    <constraint firstItem="UGM-oL-VZP" firstAttribute="centerY" secondItem="BEu-g1-DnR" secondAttribute="centerY" id="KFz-oV-X14"/>
                    <constraint firstItem="tiI-sU-Z6o" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="20" id="NL2-2w-P2g"/>
                    <constraint firstItem="G2w-No-SZn" firstAttribute="leading" secondItem="tiI-sU-Z6o" secondAttribute="trailing" constant="20" id="Sum-cp-nHl"/>
                    <constraint firstItem="FHb-ar-fAV" firstAttribute="top" secondItem="tiI-sU-Z6o" secondAttribute="bottom" constant="35" id="TIn-bc-cK7"/>
                    <constraint firstItem="fPj-UT-IDK" firstAttribute="top" secondItem="Svi-qZ-ImG" secondAttribute="bottom" constant="10" id="Z5t-sB-Mms"/>
                    <constraint firstAttribute="trailing" secondItem="UGM-oL-VZP" secondAttribute="trailing" constant="20" id="bUf-oy-TTg"/>
                    <constraint firstAttribute="trailing" secondItem="fPj-UT-IDK" secondAttribute="trailing" constant="20" id="bnt-5w-p6N"/>
                    <constraint firstItem="BEu-g1-DnR" firstAttribute="bottom" secondItem="tiI-sU-Z6o" secondAttribute="bottom" id="cIf-jj-QLj"/>
                    <constraint firstItem="FHb-ar-fAV" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="20" id="eaO-Jm-qR9"/>
                    <constraint firstItem="G2w-No-SZn" firstAttribute="top" secondItem="d15-nB-2Mo" secondAttribute="bottom" constant="6" id="gUB-jJ-4pw"/>
                    <constraint firstItem="Svi-qZ-ImG" firstAttribute="centerX" secondItem="fPj-UT-IDK" secondAttribute="centerX" id="iRi-GX-mLB"/>
                    <constraint firstItem="fPj-UT-IDK" firstAttribute="leading" secondItem="6Bz-mf-KEg" secondAttribute="trailing" constant="45" id="ijW-JX-A80"/>
                    <constraint firstItem="tiI-sU-Z6o" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" constant="20" id="rTB-35-R9r"/>
                    <constraint firstItem="F85-Ay-7P0" firstAttribute="top" secondItem="FHb-ar-fAV" secondAttribute="bottom" constant="10" id="s7Q-ox-TxJ"/>
                    <constraint firstItem="F85-Ay-7P0" firstAttribute="leading" secondItem="FHb-ar-fAV" secondAttribute="leading" id="uAC-vI-p2v"/>
                    <constraint firstItem="d15-nB-2Mo" firstAttribute="top" secondItem="tiI-sU-Z6o" secondAttribute="top" constant="10" id="vve-h4-9wJ"/>
                    <constraint firstItem="6Bz-mf-KEg" firstAttribute="top" secondItem="dOx-33-HFw" secondAttribute="bottom" constant="10.5" id="y6m-oN-bsI"/>
                </constraints>
            </tableViewCellContentView>
            <viewLayoutGuide key="safeArea" id="njF-e1-oar"/>
            <connections>
                <outlet property="starBackView" destination="FHb-ar-fAV" id="B2I-BD-jDX"/>
            </connections>
            <point key="canvasLocation" x="95.652173913043484" y="12.723214285714285"/>
        </tableViewCell>
    </objects>
    <resources>
        <image name="detail_download" width="30" height="30"/>
        <image name="detail_more" width="30" height="30"/>
        <image name="logo_game" width="800" height="800"/>
    </resources>
</document>


================================================
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
================================================
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
    <device id="retina4_7" orientation="portrait">
        <adaptation id="fullscreen"/>
    </device>
    <dependencies>
        <deployment identifier="iOS"/>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
    </dependencies>
    <objects>
        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
        <view contentMode="scaleToFill" id="iN0-l3-epB" customClass="DownloadBottomView" customModule="AppStoreDemo" customModuleProvider="target">
            <rect key="frame" x="0.0" y="0.0" width="340" height="287"/>
            <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
            <subviews>
                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="App Store" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="87E-8y-Hmq">
                    <rect key="frame" x="16" y="11" width="81" height="24"/>
                    <fontDescription key="fontDescription" name="PingFangSC-Medium" family="PingFang SC" pointSize="17"/>
                    <nil key="textColor"/>
                    <nil key="highlightedColor"/>
                </label>
                <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="DN7-AT-k3m">
                    <rect key="frame" x="272" y="7" width="52" height="32"/>
                    <fontDescription key="fontDescription" type="system" weight="medium" pointSize="16"/>
                    <state key="normal" title="Cancel"/>
                    <connections>
                        <action selector="cancelAction:" destination="iN0-l3-epB" eventType="touchUpInside" id="vW2-jR-ifX"/>
                    </connections>
                </button>
                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Kxr-1J-Ugh">
                    <rect key="frame" x="0.0" y="45" width="340" height="0.5"/>
                    <color key="backgroundColor" red="0.55686274509803924" green="0.55686274509803924" blue="0.5725490196078431" alpha="1" colorSpace="calibratedRGB"/>
                    <constraints>
                        <constraint firstAttribute="height" constant="0.69999999999999996" id="t9G-5S-avS"/>
                    </constraints>
                </view>
                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="yAp-G7-ISi">
                    <rect key="frame" x="16" y="120.5" width="324" height="0.5"/>
                    <color key="backgroundColor" red="0.55686274509803924" green="0.55686274509803924" blue="0.5725490196078431" alpha="1" colorSpace="calibratedRGB"/>
                    <constraints>
                        <constraint firstAttribute="height" constant="0.29999999999999999" id="5pu-1N-dxz"/>
                    </constraints>
                </view>
                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="gm0-EC-Lnz">
                    <rect key="frame" x="16" y="166" width="324" height="0.5"/>
                    <color key="backgroundColor" red="0.5568627451" green="0.5568627451" blue="0.57254901960000004" alpha="1" colorSpace="calibratedRGB"/>
                    <constraints>
                        <constraint firstAttribute="height" constant="0.29999999999999999" id="MC7-a8-4we"/>
                    </constraints>
                </view>
                <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="RXj-2n-vDf">
                    <rect key="frame" x="55" y="63.5" width="40" height="40"/>
                    <constraints>
                        <constraint firstAttribute="width" constant="40" id="FRC-nN-BHi"/>
                        <constraint firstAttribute="height" constant="40" id="Vvt-rT-lHA"/>
                    </constraints>
                </imageView>
                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="iUb-N3-zJQ">
                    <rect key="frame" x="111" y="58.5" width="39.5" height="17"/>
                    <constraints>
                        <constraint firstAttribute="height" constant="17" id="8S8-JH-31x"/>
                    </constraints>
                    <fontDescription key="fontDescription" type="system" pointSize="16"/>
                    <nil key="textColor"/>
                    <nil key="highlightedColor"/>
                </label>
                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="f6P-zA-4Z6">
                    <rect key="frame" x="111" y="75.5" width="39.5" height="17"/>
                    <constraints>
                        <constraint firstAttribute="height" constant="17" id="Heu-z5-cbp"/>
                    </constraints>
                    <fontDescription key="fontDescription" type="system" pointSize="16"/>
                    <color key="textColor" red="0.5568627451" green="0.5568627451" blue="0.57254901960000004" alpha="1" colorSpace="calibratedRGB"/>
                    <nil key="highlightedColor"/>
                </label>
                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="OFFERS IN-APP PURCHASES" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="oaH-fj-5IL">
                    <rect key="frame" x="111" y="92.5" width="214" height="17"/>
                    <constraints>
                        <constraint firstAttribute="height" constant="17" id="R8B-GB-78L"/>
                    </constraints>
                    <fontDescription key="fontDescription" type="system" pointSize="16"/>
                    <color key="textColor" red="0.5568627451" green="0.5568627451" blue="0.57254901960000004" alpha="1" colorSpace="calibratedRGB"/>
                    <nil key="highlightedColor"/>
                </label>
                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="ACCOUNT" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="VyH-tv-SSg">
                    <rect key="frame" x="31" y="135" width="64" height="17"/>
                    <constraints>
                        <constraint firstAttribute="height" constant="17" id="fXZ-yf-qBo"/>
                    </constraints>
                    <fontDescription key="fontDescription" type="system" pointSize="13"/>
                    <color key="textColor" red="0.5568627451" green="0.5568627451" blue="0.57254901960000004" alpha="1" colorSpace="calibratedRGB"/>
                    <nil key="highlightedColor"/>
                </label>
                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="hello@qq.com" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="JCZ-Gk-LId">
                    <rect key="frame" x="111" y="135" width="103.5" height="17"/>
                    <constraints>
                        <constraint firstAttribute="height" constant="17" id="qFg-3Q-y0h"/>
                    </constraints>
                    <fontDescription key="fontDescription" type="system" pointSize="16"/>
                    <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
                    <nil key="highlightedColor"/>
                </label>
                <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="demo_icon" translatesAutoresizingMaskIntoConstraints="NO" id="T3T-T0-BjE">
                    <rect key="frame" x="148" y="186.5" width="44" height="44"/>
                    <constraints>
                        <constraint firstAttribute="width" constant="44" id="Ke6-vY-hfR"/>
                        <constraint firstAttribute="height" constant="44" id="dAW-Ao-aCP"/>
                    </constraints>
                </imageView>
                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Confirm with Side Button" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Kg6-VW-sWQ">
                    <rect key="frame" x="74" y="238.5" width="192" height="21"/>
                    <fontDescription key="fontDescription" type="system" pointSize="17"/>
                    <nil key="textColor"/>
                    <nil key="highlightedColor"/>
                </label>
            </subviews>
            <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
            <constraints>
                <constraint firstItem="Kg6-VW-sWQ" firstAttribute="centerX" secondItem="T3T-T0-BjE" secondAttribute="centerX" id="1mu-uI-WBs"/>
                <constraint firstItem="f6P-zA-4Z6" firstAttribute="leading" secondItem="iUb-N3-zJQ" secondAttribute="leading" id="4fO-Vb-c1b"/>
                <constraint firstItem="oaH-fj-5IL" firstAttribute="top" secondItem="f6P-zA-4Z6" secondAttribute="bottom" id="5pf-rY-nN2"/>
                <constraint firstItem="VyH-tv-SSg" firstAttribute="top" secondItem="yAp-G7-ISi" secondAttribute="bottom" constant="14" id="8XY-LN-bWU"/>
                <constraint firstAttribute="trailing" secondItem="gm0-EC-Lnz" secondAttribute="trailing" id="9AL-vZ-n2U"/>
                <constraint firstItem="vUN-kp-3ea" firstAttribute="trailing" secondItem="yAp-G7-ISi" secondAttribute="trailing" id="AXA-qv-kkz"/>
                <constraint firstItem="87E-8y-Hmq" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" constant="11" id="BIU-fB-tRK"/>
                <constraint firstItem="RXj-2n-vDf" firstAttribute="leading" secondItem="vUN-kp-3ea" secondAttribute="leading" constant="55" id="BzP-1P-eLN"/>
                <constraint firstItem="yAp-G7-ISi" firstAttribute="top" secondItem="Kxr-1J-Ugh" secondAttribute="bottom" constant="75" id="EyY-qW-r0w"/>
                <constraint firstItem="vUN-kp-3ea" firstAttribute="trailing" secondItem="Kxr-1J-Ugh" secondAttribute="trailing" id="Eyp-kE-nzO"/>
                <constraint firstItem="Kxr-1J-Ugh" firstAttribute="leading" secondItem="vUN-kp-3ea" secondAttribute="leading" id="JTL-nt-Eik"/>
                <constraint firstItem="DN7-AT-k3m" firstAttribute="centerY" secondItem="87E-8y-Hmq" secondAttribute="centerY" id="LiF-o2-czQ"/>
                <constraint firstItem="VyH-tv-SSg" firstAttribute="trailing" secondItem="RXj-2n-vDf" secondAttribute="trailing" id="XHL-p4-hMl"/>
                <constraint firstItem="87E-8y-Hmq" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="16" id="cbe-D7-ySr"/>
                <constraint firstItem="T3T-T0-BjE" firstAttribute="top" secondItem="gm0-EC-Lnz" secondAttribute="bottom" constant="20" id="gOe-4k-Psx"/>
                <constraint firstItem="T3T-T0-BjE" firstAttribute="centerX" secondItem="iN0-l3-epB" secondAttribute="centerX" id="gsk-we-k6U"/>
                <constraint firstItem="f6P-zA-4Z6" firstAttribute="top" secondItem="iUb-N3-zJQ" secondAttribute="bottom" id="gzv-x0-4O2"/>
                <constraint firstItem="JCZ-Gk-LId" firstAttribute="leading" secondItem="oaH-fj-5IL" secondAttribute="leading" id="iB1-6h-Q9f"/>
                <constraint firstItem="Kg6-VW-sWQ" firstAttribute="top" secondItem="T3T-T0-BjE" secondAttribute="bottom" constant="8" id="j93-sE-NGH"/>
                <constraint firstItem="gm0-EC-Lnz" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="16" id="jW2-Kn-Iq5"/>
                <constraint firstItem="oaH-fj-5IL" firstAttribute="leading" secondItem="f6P-zA-4Z6" secondAttribute="leading" id="jkK-KZ-eo4"/>
                <constraint firstItem="RXj-2n-vDf" firstAttribute="top" secondItem="Kxr-1J-Ugh" secondAttribute="bottom" constant="18" id="lbR-Bw-3fd"/>
                <constraint firstItem="JCZ-Gk-LId" firstAttribute="centerY" secondItem="VyH-tv-SSg" secondAttribute="centerY" id="lnM-Vy-ktN"/>
                <constraint firstItem="gm0-EC-Lnz" firstAttribute="top" secondItem="yAp-G7-ISi" secondAttribute="bottom" constant="45" id="mp2-UH-eyk"/>
                <constraint firstAttribute="trailing" secondItem="DN7-AT-k3m" secondAttribute="trailing" constant="16" id="tw5-dM-7Z4"/>
                <constraint firstItem="yAp-G7-ISi" firstAttribute="leading" secondItem="vUN-kp-3ea" secondAttribute="leading" constant="16" id="uHW-W7-BEd"/>
                <constraint firstItem="iUb-N3-zJQ" firstAttribute="top" secondItem="Kxr-1J-Ugh" secondAttribute="bottom" constant="13" id="vtD-3q-L3b"/>
                <constraint firstItem="iUb-N3-zJQ" firstAttribute="leading" secondItem="RXj-2n-vDf" secondAttribute="trailing" constant="16" id="y4w-Ig-EE4"/>
                <constraint firstItem="Kxr-1J-Ugh" firstAttribute="top" secondItem="vUN-kp-3ea" secondAttribute="top" constant="45" id="zHC-4Z-Gu7"/>
            </constraints>
            <nil key="simulatedTopBarMetrics"/>
            <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
            <viewLayoutGuide key="safeArea" id="vUN-kp-3ea"/>
            <connections>
                <outlet property="descLabel" destination="f6P-zA-4Z6" id="Uko-Rh-Hx9"/>
                <outlet property="iconImageView" destination="RXj-2n-vDf" id="kYd-UV-4wW"/>
                <outlet property="nameLabel" destination="iUb-N3-zJQ" id="kzc-aj-SmS"/>
            </connections>
            <point key="canvasLocation" x="12.800000000000001" y="31.934032983508249"/>
        </view>
    </objects>
    <resources>
        <image name="demo_icon" width="490" height="490"/>
    </resources>
</document>


================================================
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
================================================
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
    <device id="retina4_7" orientation="portrait">
        <adaptation id="fullscreen"/>
    </device>
    <dependencies>
        <deployment identifier="iOS"/>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
    </dependencies>
    <objects>
        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
        <view contentMode="scaleToFill" id="iN0-l3-epB" customClass="DownloadClickView" customModule="AppStoreDemo" customModuleProvider="target">
            <rect key="frame" x="0.0" y="0.0" width="140" height="115"/>
            <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
            <subviews>
                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Fb4-yR-I1B">
                    <rect key="frame" x="128" y="7.5" width="12" height="100"/>
                    <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                    <constraints>
                        <constraint firstAttribute="height" constant="100" id="13A-aO-9xA"/>
                        <constraint firstAttribute="width" constant="12" id="bEK-Vj-e9T"/>
                    </constraints>
                    <userDefinedRuntimeAttributes>
                        <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
                            <real key="value" value="3"/>
                        </userDefinedRuntimeAttribute>
                    </userDefinedRuntimeAttributes>
                </view>
                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Double Click to install" textAlignment="right" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="KaE-LV-hOw">
                    <rect key="frame" x="10" y="31" width="113" height="53.5"/>
                    <constraints>
                        <constraint firstAttribute="width" constant="113" id="ABf-HV-B4c"/>
                    </constraints>
                    <fontDescription key="fontDescription" name="PingFangSC-Regular" family="PingFang SC" pointSize="19"/>
                    <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                    <nil key="highlightedColor"/>
                </label>
            </subviews>
            <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
            <constraints>
                <constraint firstItem="Fb4-yR-I1B" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="centerY" id="fD9-Mk-kIt"/>
                <constraint firstItem="vUN-kp-3ea" firstAttribute="trailing" secondItem="Fb4-yR-I1B" secondAttribute="trailing" id="fct-cv-ij0"/>
                <constraint firstItem="Fb4-yR-I1B" firstAttribute="leading" secondItem="KaE-LV-hOw" secondAttribute="trailing" constant="5" id="hFq-4s-9bA"/>
                <constraint firstItem="KaE-LV-hOw" firstAttribute="centerY" secondItem="Fb4-yR-I1B" secondAttribute="centerY" id="hLR-4n-6z6"/>
            </constraints>
            <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
            <viewLayoutGuide key="safeArea" id="vUN-kp-3ea"/>
            <point key="canvasLocation" x="14.4" y="-127.28635682158921"/>
        </view>
    </objects>
</document>


================================================
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
================================================
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
    <device id="retina6_1" orientation="portrait">
        <adaptation id="fullscreen"/>
    </device>
    <dependencies>
        <deployment identifier="iOS"/>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
    </dependencies>
    <objects>
        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
        <collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" id="gTV-IL-0wX" customClass="RecommandCollectionViewCell" customModule="AppStoreTodayDemo" customModuleProvider="target">
            <rect key="frame" x="0.0" y="0.0" width="269" height="312"/>
            <autoresizingMask key="autoresizingMask"/>
            <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
                <rect key="frame" x="0.0" y="0.0" width="269" height="312"/>
                <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
                <subviews>
                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Ksh-3O-JUB">
                        <rect key="frame" x="0.0" y="12" width="264" height="14"/>
                        <constraints>
                            <constraint firstAttribute="height" constant="14" id="4XY-mK-22K"/>
                        </constraints>
                        <fontDescription key="fontDescription" type="system" weight="medium" pointSize="11"/>
                        <color key="textColor" red="0.039215686274509803" green="0.37254901960784315" blue="0.99607843137254903" alpha="1" colorSpace="calibratedRGB"/>
                        <nil key="highlightedColor"/>
                    </label>
                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="FOr-7j-iyZ">
                        <rect key="frame" x="0.0" y="31" width="264" height="27"/>
                        <constraints>
                            <constraint firstAttribute="height" constant="27" id="n6e-19-2uq"/>
                        </constraints>
                        <fontDescription key="fontDescription" type="system" pointSize="22"/>
                        <nil key="textColor"/>
                        <nil key="highlightedColor"/>
                    </label>
                    <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="4pJ-qq-aqw" userLabel="lineView">
                        <rect key="frame" x="0.0" y="1" width="269" height="1"/>
                        <color key="backgroundColor" red="0.8784313725490196" green="0.8784313725490196" blue="0.8784313725490196" alpha="1" colorSpace="calibratedRGB"/>
                        <constraints>
                            <constraint firstAttribute="height" constant="0.80000000000000004" id="fj9-Ef-W9Y"/>
                        </constraints>
                    </view>
                    <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="Ihz-tS-UsA">
                        <rect key="frame" x="0.0" y="101" width="269" height="179"/>
                        <userDefinedRuntimeAttributes>
                            <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
                                <real key="value" value="5"/>
                            </userDefinedRuntimeAttribute>
                        </userDefinedRuntimeAttributes>
                    </imageView>
                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="qhw-hX-BZR">
                        <rect key="frame" x="0.0" y="63" width="264" height="24"/>
                        <constraints>
                            <constraint firstAttribute="height" constant="24" id="rKO-B0-0eC"/>
                        </constraints>
                        <fontDescription key="fontDescription" type="system" pointSize="20"/>
                        <color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                        <nil key="highlightedColor"/>
                    </label>
                </subviews>
            </view>
            <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
            <constraints>
                <constraint firstItem="FOr-7j-iyZ" firstAttribute="leading" secondItem="gTV-IL-0wX" secondAttribute="leading" id="1Y4-vd-dz4"/>
                <constraint firstItem="4pJ-qq-aqw" firstAttribute="leading" secondItem="gTV-IL-0wX" secondAttribute="leading" id="3Fa-af-gJH"/>
                <constraint firstAttribute="bottom" secondItem="Ihz-tS-UsA" secondAttribute="bottom" constant="32" id="Bxp-Tm-a5r"/>
                <constraint firstAttribute="trailing" secondItem="FOr-7j-iyZ" secondAttribute="trailing" constant="5" id="Gc0-fT-9Ru"/>
                <constraint firstItem="Ihz-tS-UsA" firstAttribute="top" secondItem="qhw-hX-BZR" secondAttribute="bottom" constant="14" id="JWg-A9-dRk"/>
                <constraint firstItem="Ksh-3O-JUB" firstAttribute="top" secondItem="4pJ-qq-aqw" secondAttribute="bottom" constant="10" id="O6P-vZ-Ocb"/>
                <constraint firstAttribute="trailing" secondItem="Ihz-tS-UsA" secondAttribute="trailing" id="YHp-Bp-4ca"/>
                <constraint firstItem="FOr-7j-iyZ" firstAttribute="top" secondItem="Ksh-3O-JUB" secondAttribute="bottom" constant="5" id="eSP-eV-t3d"/>
                <constraint firstAttribute="trailing" secondItem="qhw-hX-BZR" secondAttribute="trailing" constant="5" id="ih9-ZX-L1O"/>
                <constraint firstItem="Ksh-3O-JUB" firstAttribute="leading" secondItem="gTV-IL-0wX" secondAttribute="leading" id="oGY-mn-e6K"/>
                <constraint firstItem="qhw-hX-BZR" firstAttribute="leading" secondItem="gTV-IL-0wX" secondAttribute="leading" id="onC-st-p5f"/>
                <constraint firstItem="4pJ-qq-aqw" firstAttribute="top" secondItem="gTV-IL-0wX" secondAttribute="top" constant="1" id="rhi-Gx-9h1"/>
                <constraint firstAttribute="trailing" secondItem="Ksh-3O-JUB" secondAttribute="trailing" constant="5" id="skl-Hg-3BK"/>
                <constraint firstItem="qhw-hX-BZR" firstAttribute="top" secondItem="FOr-7j-iyZ" secondAttribute="bottom" constant="5" id="smI-Wd-jyA"/>
                <constraint firstAttribute="trailing" secondItem="4pJ-qq-aqw" secondAttribute="trailing" id="tdh-Mf-2QG"/>
                <constraint firstItem="Ihz-tS-UsA" firstAttribute="leading" secondItem="gTV-IL-0wX" secondAttribute="leading" id="yIh-WX-jLS"/>
            </constraints>
            <viewLayoutGuide key="safeArea" id="ZTg-uK-7eu"/>
            <size key="customSize" width="269" height="312"/>
            <connections>
                <outlet property="coverImageView" destination="Ihz-tS-UsA" id="eBd-Va-pFq"/>
                <outlet property="descLabel" destination="qhw-hX-BZR" id="xJb-nK-dtz"/>
                <outlet property="featureLabel" destination="Ksh-3O-JUB" id="8oH-Te-0jx"/>
                <outlet property="nameLabel" destination="FOr-7j-iyZ" id="VAX-qu-7T4"/>
            </connections>
            <point key="canvasLocation" x="100.72463768115942" y="-205.58035714285714"/>
        </collectionViewCell>
    </objects>
</document>


================================================
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
================================================
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
    <device id="retina6_1" orientation="portrait">
        <adaptation id="fullscreen"/>
    </device>
    <dependencies>
        <deployment identifier="iOS"/>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
    </dependencies>
    <objects>
        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
        <collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" id="gTV-IL-0wX" customClass="GameTopicCollectionViewCell" customModule="AppStoreDemo" customModuleProvider="target">
            <rect key="frame" x="0.0" y="0.0" width="322" height="87"/>
            <autoresizingMask key="autoresizingMask"/>
            <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
                <rect key="frame" x="0.0" y="0.0" width="322" height="87"/>
                <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
                <subviews>
                    <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="EZb-bM-8Tl">
                        <rect key="frame" x="0.0" y="8" width="62" height="62"/>
                        <constraints>
                            <constraint firstAttribute="width" constant="62" id="Auj-Bl-al7"/>
                            <constraint firstAttribute="height" constant="62" id="fUm-d0-x2g"/>
                        </constraints>
                    </imageView>
                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="OZk-FT-FLd">
                        <rect key="frame" x="70" y="18" width="42" height="21"/>
                        <fontDescription key="fontDescription" type="system" pointSize="17"/>
                        <nil key="textColor"/>
                        <nil key="highlightedColor"/>
                    </label>
                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Kua-jD-qdJ">
                        <rect key="frame" x="70" y="44" width="33" height="16"/>
                        <fontDescription key="fontDescription" type="system" pointSize="13"/>
                        <color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                        <nil key="highlightedColor"/>
                    </label>
                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="hXZ-EQ-7Aq">
                        <rect key="frame" x="249" y="28.5" width="73" height="30"/>
                        <color key="backgroundColor" red="0.94117647059999998" green="0.94117647059999998" blue="0.96862745100000003" alpha="1" colorSpace="calibratedRGB"/>
                        <constraints>
                            <constraint firstAttribute="width" constant="73" id="TeE-4c-m8T"/>
                            <constraint firstAttribute="height" constant="30" id="q7e-kw-wlV"/>
                        </constraints>
                        <fontDescription key="fontDescription" type="system" weight="medium" pointSize="15"/>
                        <state key="normal" title="Get"/>
                        <userDefinedRuntimeAttributes>
                            <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
                                <real key="value" value="15"/>
                            </userDefinedRuntimeAttribute>
                        </userDefinedRuntimeAttributes>
                        <connections>
                            <action selector="getAction:" destination="gTV-IL-0wX" eventType="touchUpInside" id="qki-Wa-ztp"/>
                        </connections>
                    </button>
                    <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="0hw-LK-ltQ">
                        <rect key="frame" x="70" y="86" width="252" height="1"/>
                        <color key="backgroundColor" red="0.8784313725490196" green="0.8784313725490196" blue="0.8784313725490196" alpha="1" colorSpace="calibratedRGB"/>
                        <constraints>
                            <constraint firstAttribute="height" constant="0.80000000000000004" id="fBJ-Wf-tCA"/>
                        </constraints>
                    </view>
                </subviews>
            </view>
            <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
            <constraints>
                <constraint firstItem="Kua-jD-qdJ" firstAttribute="leading" secondItem="EZb-bM-8Tl" secondAttribute="trailing" constant="8" id="1fP-XW-KK8"/>
                <constraint firstItem="OZk-FT-FLd" firstAttribute="leading" secondItem="EZb-bM-8Tl" secondAttribute="trailing" constant="8" id="Aq5-3u-nud"/>
                <constraint firstItem="0hw-LK-ltQ" firstAttribute="leading" secondItem="Kua-jD-qdJ" secondAttribute="leading" id="DXU-bD-tus"/>
                <constraint firstItem="EZb-bM-8Tl" firstAttribute="top" secondItem="gTV-IL-0wX" secondAttribute="top" constant="8" id="Gfe-B6-xje"/>
                <constraint firstItem="EZb-bM-8Tl" firstAttribute="leading" secondItem="gTV-IL-0wX" secondAttribute="leading" id="T3H-lW-2vV"/>
                <constraint firstItem="Kua-jD-qdJ" firstAttribute="top" secondItem="OZk-FT-FLd" secondAttribute="bottom" constant="5" id="W9I-x6-Shc"/>
                <constraint firstAttribute="bottom" secondItem="0hw-LK-ltQ" secondAttribute="bottom" id="aGQ-fl-JXo"/>
                <constraint firstItem="hXZ-EQ-7Aq" firstAttribute="centerY" secondItem="gTV-IL-0wX" secondAttribute="centerY" id="dOk-bg-CQ1"/>
                <constraint firstAttribute="trailing" secondItem="hXZ-EQ-7Aq" secondAttribute="trailing" id="f9Q-r1-Sk4"/>
                <constraint firstItem="OZk-FT-FLd" firstAttribute="top" secondItem="EZb-bM-8Tl" secondAttribute="top" constant="10" id="mh0-MW-V4J"/>
                <constraint firstItem="ZTg-uK-7eu" firstAttribute="trailing" secondItem="0hw-LK-ltQ" secondAttribute="trailing" id="w09-oG-70H"/>
            </constraints>
            <viewLayoutGuide key="safeArea" id="ZTg-uK-7eu"/>
            <size key="customSize" width="322" height="87"/>
            <connections>
                <outlet property="descLabel" destination="Kua-jD-qdJ" id="7nY-rM-5mQ"/>
                <outlet property="iconImageView" destination="EZb-bM-8Tl" id="juN-9O-5EN"/>
                <outlet property="lineView" destination="0hw-LK-ltQ" id="SoG-rm-mez"/>
                <outlet property="nameLabel" destination="OZk-FT-FLd" id="g87-eP-RwD"/>
            </connections>
            <point key="canvasLocation" x="131.8840579710145" y="62.611607142857139"/>
        </collectionViewCell>
    </objects>
</document>


================================================
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
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>CFBundleDevelopmentRegion</key>
	<string>$(DEVELOPMENT_LANGUAGE)</string>
	<key>CFBundleExecutable</key>
	<string>$(EXECUTABLE_NAME)</string>
	<key>CFBundleIdentifier</key>
	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
	<key>CFBundleInfoDictionaryVersion</key>
	<string>6.0</string>
	<key>CFBundleName</key>
	<string>$(PRODUCT_NAME)</string>
	<key>CFBundlePackageType</key>
	<string>APPL</string>
	<key>CFBundleShortVersionString</key>
	<string>1.0</string>
	<key>CFBundleVersion</key>
	<string>1</string>
	<key>LSRequiresIPhoneOS</key>
	<true/>
	<key>UILaunchStoryboardName</key>
	<string>LaunchScreen</string>
	<key>UIMainStoryboardFile</key>
	<string>Main</string>
	<key>UIRequiredDeviceCapabilities</key>
	<array>
		<string>armv7</string>
	</array>
	<key>UISupportedInterfaceOrientations</key>
	<array>
		<string>UIInterfaceOrientationPortrait</string>
		<string>UIInterfaceOrientationLandscapeLeft</string>
		<string>UIInterfaceOrientationLandscapeRight</string>
	</array>
	<key>UISupportedInterfaceOrientations~ipad</key>
	<array>
		<string>UIInterfaceOrientationPortrait</string>
		<string>UIInterfaceOrientationPortraitUpsideDown</string>
		<string>UIInterfaceOrientationLandscapeLeft</string>
		<string>UIInterfaceOrientationLandscapeRight</string>
	</array>
	<key>UIUserInterfaceStyle</key>
	<string>Light</string>
</dict>
</plist>


================================================
FILE: AppStoreDemo/Main.storyboard
================================================
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="15400" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="7gp-xX-vDA">
    <device id="retina6_1" orientation="portrait" appearance="light"/>
    <dependencies>
        <deployment identifier="iOS"/>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15404"/>
        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
    </dependencies>
    <scenes>
        <!--Tab Bar Controller-->
        <scene sceneID="YMh-JZ-4oj">
            <objects>
                <tabBarController automaticallyAdjustsScrollViewInsets="NO" id="7gp-xX-vDA" sceneMemberID="viewController">
                    <toolbarItems/>
                    <tabBar key="tabBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="VyS-0d-PkQ">
                        <rect key="frame" x="0.0" y="0.0" width="1000" height="1000"/>
                        <autoresizingMask key="autoresizingMask"/>
                        <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                    </tabBar>
                    <connections>
                        <segue destination="Aqn-rC-J8Q" kind="relationship" relationship="viewControllers" id="nto-yd-aVS"/>
                        <segue destination="0bk-jh-Gf1" kind="relationship" relationship="viewControllers" id="ZWJ-Ff-wnV"/>
                        <segue destination="sfx-ou-gVP" kind="relationship" relationship="viewControllers" id="FEh-q6-so7"/>
                        <segue destination="gLP-mw-QVg" kind="relationship" relationship="viewControllers" id="dm8-sU-NtG"/>
                    </connections>
                </tabBarController>
                <placeholder placeholderIdentifier="IBFirstResponder" id="Zpc-zI-abD" userLabel="First Responder" sceneMemberID="firstResponder"/>
            </objects>
            <point key="canvasLocation" x="-458" y="-306"/>
        </scene>
        <!--Search-->
        <scene sceneID="Owz-qb-slY">
            <objects>
                <tableViewController id="Qxn-VB-fUK" customClass="SearchTableViewController" customModule="AppStoreDemo" customModuleProvider="target" sceneMemberID="viewController">
                    <tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" id="QEW-sE-ETH">
                        <rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                        <view key="tableFooterView" contentMode="scaleToFill" id="4ai-DU-AEW">
                            <rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
                            <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
                            <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                        </view>
                        <connections>
                            <outlet property="dataSource" destination="Qxn-VB-fUK" id="r8T-64-Iwg"/>
                            <outlet property="delegate" destination="Qxn-VB-fUK" id="fwu-HP-vV6"/>
                        </connections>
                    </tableView>
                    <navigationItem key="navigationItem" title="Search" largeTitleDisplayMode="always" id="pP4-E8-Y0F"/>
                </tableViewController>
                <placeholder placeholderIdentifier="IBFirstResponder" id="Wp5-D3-Lwg" userLabel="First Responder" sceneMemberID="firstResponder"/>
            </objects>
            <point key="canvasLocation" x="2061" y="806"/>
        </scene>
        <!--Updates-->
        <scene sceneID="5or-tl-5GI">
            <objects>
                <tableViewController id="S1q-f5-Z8X" customClass="UpdateTableViewController" customModule="AppStoreDemo" customModuleProvider="target" sceneMemberID="viewController">
                    <tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="none" allowsSelection="NO" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" id="Zgl-og-TgX">
                        <rect key="frame" x="0.0" y="0.0" width="414" height="756"/>
                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                        <inset key="separatorInset" minX="20" minY="0.0" maxX="0.0" maxY="0.0"/>
                        <connections>
                            <outlet property="dataSource" destination="S1q-f5-Z8X" id="dmv-eC-fVg"/>
                            <outlet property="delegate" destination="S1q-f5-Z8X" id="zq0-ox-mKA"/>
                        </connections>
                    </tableView>
                    <navigationItem key="navigationItem" title="Updates" largeTitleDisplayMode="always" id="Q6X-mc-MFL"/>
                </tableViewController>
                <placeholder placeholderIdentifier="IBFirstResponder" id="gmK-MI-F1S" userLabel="First Responder" sceneMemberID="firstResponder"/>
            </objects>
            <point key="canvasLocation" x="880" y="1026"/>
        </scene>
        <!--Today-->
        <scene sceneID="ja9-Bk-QuO">
            <objects>
                <tableViewController id="Aqn-rC-J8Q" customClass="TodayViewController" customModule="AppStoreDemo" customModuleProvider="target" sceneMemberID="viewController">
                    <tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" id="zun-x5-pzu">
                        <rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                        <connections>
                            <outlet property="dataSource" destination="Aqn-rC-J8Q" id="of2-9S-K8j"/>
                            <outlet property="delegate" destination="Aqn-rC-J8Q" id="Dmr-kZ-Fbf"/>
                        </connections>
                    </tableView>
                    <tabBarItem key="tabBarItem" title="Today" image="tabbar_today" id="wO6-Bm-6Qm"/>
                    <navigationItem key="navigationItem" id="DTm-bH-qfa"/>
                </tableViewController>
                <placeholder placeholderIdentifier="IBFirstResponder" id="ReK-BJ-5vP" userLabel="First Responder" sceneMemberID="firstResponder"/>
            </objects>
            <point key="canvasLocation" x="1209" y="-1187"/>
        </scene>
        <!--Search-->
        <scene sceneID="tOJ-8S-p6p">
            <objects>
                <navigationController automaticallyAdjustsScrollViewInsets="NO" id="gLP-mw-QVg" sceneMemberID="viewController">
                    <tabBarItem key="tabBarItem" title="Search" image="tabbar_search" id="M1r-rh-aBa"/>
                    <toolbarItems/>
                    <navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" largeTitles="YES" id="lmM-4Y-70L">
                        <rect key="frame" x="0.0" y="44" width="414" height="96"/>
                        <autoresizingMask key="autoresizingMask"/>
                        <color key="barTintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                    </navigationBar>
                    <nil name="viewControllers"/>
                    <connections>
                        <segue destination="Qxn-VB-fUK" kind="relationship" relationship="rootViewController" id="KjB-XS-G6z"/>
                    </connections>
                </navigationController>
                <placeholder placeholderIdentifier="IBFirstResponder" id="eGc-GV-yKt" userLabel="First Responder" sceneMemberID="firstResponder"/>
            </objects>
            <point key="canvasLocation" x="2061" y="66"/>
        </scene>
        <!--Updates-->
        <scene sceneID="k4R-VK-LO4">
            <objects>
                <navigationController automaticallyAdjustsScrollViewInsets="NO" id="sfx-ou-gVP" sceneMemberID="viewController">
                    <tabBarItem key="tabBarItem" title="Updates" image="tabbar_updates" id="ztJ-x8-NSN"/>
                    <toolbarItems/>
                    <navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" translucent="NO" largeTitles="YES" id="d1t-VT-7Y5">
                        <rect key="frame" x="0.0" y="44" width="414" height="96"/>
                        <autoresizingMask key="autoresizingMask"/>
                        <color key="barTintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                    </navigationBar>
                    <nil name="viewControllers"/>
                    <connections>
                        <segue destination="S1q-f5-Z8X" kind="relationship" relationship="rootViewController" id="r9c-06-fFn"/>
                    </connections>
                </navigationController>
                <placeholder placeholderIdentifier="IBFirstResponder" id="hY8-Hj-V9m" userLabel="First Responder" sceneMemberID="firstResponder"/>
            </objects>
            <point key="canvasLocation" x="881" y="291"/>
        </scene>
        <!--Games-->
        <scene sceneID="cjX-zC-40O">
            <objects>
                <navigationController automaticallyAdjustsScrollViewInsets="NO" id="0bk-jh-Gf1" sceneMemberID="viewController">
                    <tabBarItem key="tabBarItem" title="Games" image="tabbar_games" id="f89-e2-CjX"/>
                    <toolbarItems/>
                    <navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" translucent="NO" largeTitles="YES" id="txg-gB-TGy">
                        <rect key="frame" x="0.0" y="44" width="414" height="96"/>
                        <autoresizingMask key="autoresizingMask"/>
                        <color key="barTintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                    </navigationBar>
                    <nil name="viewControllers"/>
                    <connections>
                        <segue destination="wxJ-JV-VGz" kind="relationship" relationship="rootViewController" id="4ge-Hg-8WZ"/>
                    </connections>
                </navigationController>
                <placeholder placeholderIdentifier="IBFirstResponder" id="efs-GP-7qb" userLabel="First Responder" sceneMemberID="firstResponder"/>
            </objects>
            <point key="canvasLocation" x="2060.8695652173915" y="-938.16964285714278"/>
        </scene>
        <!--Game-->
        <scene sceneID="U3a-Wp-BNs">
            <objects>
                <tableViewController id="wxJ-JV-VGz" customClass="GameTableViewController" customModule="AppStoreDemo" customModuleProvider="target" sceneMemberID="viewController">
                    <tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="none" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" id="KJw-Rc-EV2">
                        <rect key="frame" x="0.0" y="0.0" width="414" height="756"/>
                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                        <connections>
                            <outlet property="dataSource" destination="wxJ-JV-VGz" id="hIa-3P-DDK"/>
                            <outlet property="delegate" destination="wxJ-JV-VGz" id="PaO-Wf-Xdj"/>
                        </connections>
                    </tableView>
                    <navigationItem key="navigationItem" title="Game" largeTitleDisplayMode="always" id="d9w-dm-4CR"/>
                </tableViewController>
                <placeholder placeholderIdentifier="IBFirstResponder" id="aNN-aQ-LD5" userLabel="First Responder" sceneMemberID="firstResponder"/>
            </objects>
            <point key="canvasLocation" x="2929" y="-938"/>
        </scene>
    </scenes>
    <resources>
        <image name="tabbar_games" width="20" height="22"/>
        <image name="tabbar_search" width="22" height="22"/>
        <image name="tabbar_today" width="18" height="22"/>
        <image name="tabbar_updates" width="18" height="22"/>
    </resources>
</document>


================================================
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
================================================
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
    <device id="retina6_1" orientation="portrait">
        <adaptation id="fullscreen"/>
    </device>
    <dependencies>
        <deployment identifier="iOS"/>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
    </dependencies>
    <objects>
        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
        <tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" rowHeight="171" id="KGk-i7-Jjw" customClass="UpdateTableViewCell" customModule="AppStoreDemo" customModuleProvider="target">
            <rect key="frame" x="0.0" y="0.0" width="460" height="168"/>
            <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
            <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
                <rect key="frame" x="0.0" y="0.0" width="460" height="167.5"/>
                <autoresizingMask key="autoresizingMask"/>
                <subviews>
                    <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="CBp-XE-tH2" userLabel="topLine">
                        <rect key="frame" x="20" y="1" width="420" height="0.5"/>
                        <color key="backgroundColor" red="0.8784313725490196" green="0.8784313725490196" blue="0.8784313725490196" alpha="1" colorSpace="calibratedRGB"/>
                        <constraints>
                            <constraint firstAttribute="height" constant="0.5" id="if4-Fh-NQt"/>
                        </constraints>
                    </view>
                    <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="logo_weibo" translatesAutoresizingMaskIntoConstraints="NO" id="TqU-LX-Ops">
                        <rect key="frame" x="20" y="17.5" width="62" height="62"/>
                        <constraints>
                            <constraint firstAttribute="width" constant="62" id="LbP-RU-7nB"/>
                            <constraint firstAttribute="height" constant="62" id="tbN-0b-D21"/>
                        </constraints>
                    </imageView>
                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="CKm-YC-TFA">
                        <rect key="frame" x="94" y="30.5" width="262" height="21"/>
                        <fontDescription key="fontDescription" type="system" pointSize="17"/>
                        <nil key="textColor"/>
                        <nil key="highlightedColor"/>
                    </label>
                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="hrX-rc-0n3">
                        <rect key="frame" x="94" y="54.5" width="33" height="16"/>
                        <fontDescription key="fontDescription" type="system" pointSize="13"/>
                        <color key="textColor" red="0.56862745098039214" green="0.56862745098039214" blue="0.58039215686274503" alpha="1" colorSpace="calibratedRGB"/>
                        <nil key="highlightedColor"/>
                    </label>
                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="bKo-X1-PHr">
                        <rect key="frame" x="366" y="33.5" width="74" height="30"/>
                        <color key="backgroundColor" red="0.94117647058823528" green="0.94117647058823528" blue="0.96862745098039216" alpha="1" colorSpace="calibratedRGB"/>
                        <constraints>
                            <constraint firstAttribute="width" constant="74" id="GWP-J9-IBB"/>
                            <constraint firstAttribute="height" constant="30" id="GhT-m1-HQB"/>
                        </constraints>
                        <fontDescription key="fontDescription" name="Helvetica-Bold" family="Helvetica" pointSize="14"/>
                        <state key="normal" title="UPDATE"/>
                        <userDefinedRuntimeAttributes>
                            <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
                                <real key="value" value="15"/>
                            </userDefinedRuntimeAttribute>
                        </userDefinedRuntimeAttributes>
                    </button>
                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="ContentLabel" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="jbI-7A-QXF" userLabel="contentLabel">
                        <rect key="frame" x="20" y="102.5" width="382" height="32"/>
                        <fontDescription key="fontDescription" type="system" pointSize="15"/>
                        <nil key="textColor"/>
                        <nil key="highlightedColor"/>
                    </label>
                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Pending" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="URh-dw-PlV">
                        <rect key="frame" x="20" y="14.5" width="77.5" height="24"/>
                        <fontDescription key="fontDescription" type="boldSystem" pointSize="20"/>
                        <nil key="textColor"/>
                        <nil key="highlightedColor"/>
                    </label>
                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="wV8-ap-qcL">
                        <rect key="frame" x="356" y="9.5" width="84" height="34"/>
                        <fontDescription key="fontDescription" type="system" pointSize="18"/>
                        <state key="normal" title="Update All"/>
                        <connections>
                            <action selector="updateAllBtnAction:" destination="KGk-i7-Jjw" eventType="touchUpInside" id="syz-AN-flT"/>
                        </connections>
                    </button>
                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="psI-hH-YSr">
                        <rect key="frame" x="20" y="128.5" width="40" height="20"/>
                        <fontDescription key="fontDescription" type="system" pointSize="16"/>
                        <color key="textColor" red="0.55686274509803924" green="0.55686274509803924" blue="0.5725490196078431" alpha="1" colorSpace="calibratedRGB"/>
                        <nil key="highlightedColor"/>
                    </label>
                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="uHz-PH-qrY">
                        <rect key="frame" x="404" y="103.5" width="36" height="30"/>
                        <constraints>
                            <constraint firstAttribute="width" constant="36" id="VvL-68-2C4"/>
                            <constraint firstAttribute="height" constant="30" id="t1j-5p-qYx"/>
                        </constraints>
                        <fontDescription key="fontDescription" type="system" pointSize="15"/>
                        <state key="normal" title="more"/>
                        <connections>
                            <action selector="showMoreBtnAction:" destination="KGk-i7-Jjw" eventType="touchUpInside" id="sMp-zR-WoG"/>
                        </connections>
                    </button>
                </subviews>
                <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                <constraints>
                    <constraint firstItem="uHz-PH-qrY" firstAttribute="leading" secondItem="jbI-7A-QXF" secondAttribute="trailing" constant="2" id="1PO-qm-MMz"/>
                    <constraint firstAttribute="trailing" secondItem="wV8-ap-qcL" secondAttribute="trailing" constant="20" id="1rT-bS-hqn"/>
                    <constraint firstItem="wV8-ap-qcL" firstAttribute="centerY" secondItem="URh-dw-PlV" secondAttribute="centerY" id="2ZZ-n3-jcW"/>
                    <constraint firstItem="URh-dw-PlV" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="20" id="7Ro-Su-ilD"/>
                    <constraint firstItem="CKm-YC-TFA" firstAttribute="leading" secondItem="TqU-LX-Ops" secondAttribute="trailing" constant="12" id="AF2-ao-2by"/>
                    <constraint firstAttribute="bottom" secondItem="psI-hH-YSr" secondAttribute="bottom" constant="19" id="AKu-nb-rza"/>
                    <constraint firstItem="CKm-YC-TFA" firstAttribute="top" secondItem="TqU-LX-Ops" secondAttribute="top" constant="13" id="BnW-bk-nGq"/>
                    <constraint firstItem="jbI-7A-QXF" firstAttribute="top" secondItem="TqU-LX-Ops" secondAttribute="bottom" constant="23" id="EV6-Ii-AM5"/>
                    <constraint firstItem="CBp-XE-tH2" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="20" id="HQJ-md-mYn"/>
                    <constraint firstItem="jbI-7A-QXF" firstAttribute="leading" secondItem="TqU-LX-Ops" secondAttribute="leading" id="L8C-DI-Jrc"/>
                    <constraint firstAttribute="trailing" secondItem="bKo-X1-PHr" secondAttribute="trailing" constant="20" id="XdE-at-J52"/>
                    <constraint firstItem="uHz-PH-qrY" firstAttribute="centerY" secondItem="jbI-7A-QXF" secondAttribute="centerY" id="XhY-Rf-LzJ"/>
                    <constraint firstItem="psI-hH-
Download .txt
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
Condensed preview — 102 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (294K chars).
[
  {
    "path": "AppStoreDemo/AppDelegate.swift",
    "chars": 475,
    "preview": "//\n//  AppDelegate.swift\n//  AppStoreDemo\n//\n//  Created by Allen long on 2019/7/31.\n//  Copyright © 2019 Utimes. All ri"
  },
  {
    "path": "AppStoreDemo/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "chars": 1668,
    "preview": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"iphone\",\n      \"size\" : \"20x20\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\""
  },
  {
    "path": "AppStoreDemo/Assets.xcassets/Contents.json",
    "chars": 62,
    "preview": "{\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "AppStoreDemo/Assets.xcassets/app_logo/Contents.json",
    "chars": 62,
    "preview": "{\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "AppStoreDemo/Assets.xcassets/app_logo/logo_broadcast.imageset/Contents.json",
    "chars": 312,
    "preview": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"logo_broadcast.jpeg\",\n      \"scale\" : \"1x\"\n    }"
  },
  {
    "path": "AppStoreDemo/Assets.xcassets/app_logo/logo_car.imageset/Contents.json",
    "chars": 305,
    "preview": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"logo_car.jpg\",\n      \"scale\" : \"1x\"\n    },\n    {"
  },
  {
    "path": "AppStoreDemo/Assets.xcassets/app_logo/logo_game.imageset/Contents.json",
    "chars": 306,
    "preview": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"logo_game.jpg\",\n      \"scale\" : \"1x\"\n    },\n    "
  },
  {
    "path": "AppStoreDemo/Assets.xcassets/app_logo/logo_jump.imageset/Contents.json",
    "chars": 306,
    "preview": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"logo_jump.jpg\",\n      \"scale\" : \"1x\"\n    },\n    "
  },
  {
    "path": "AppStoreDemo/Assets.xcassets/app_logo/logo_smile.imageset/Contents.json",
    "chars": 307,
    "preview": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"logo_smile.jpg\",\n      \"scale\" : \"1x\"\n    },\n   "
  },
  {
    "path": "AppStoreDemo/Assets.xcassets/app_logo/logo_weibo.imageset/Contents.json",
    "chars": 308,
    "preview": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"logo_weibo.jpeg\",\n      \"scale\" : \"1x\"\n    },\n  "
  },
  {
    "path": "AppStoreDemo/Assets.xcassets/common/Contents.json",
    "chars": 62,
    "preview": "{\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "AppStoreDemo/Assets.xcassets/common/circle_download.imageset/Contents.json",
    "chars": 360,
    "preview": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n     "
  },
  {
    "path": "AppStoreDemo/Assets.xcassets/common/close_button.imageset/Contents.json",
    "chars": 354,
    "preview": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n     "
  },
  {
    "path": "AppStoreDemo/Assets.xcassets/common/demo_icon.imageset/Contents.json",
    "chars": 306,
    "preview": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"demo_icon.png\",\n      \"scale\" : \"1x\"\n    },\n    "
  },
  {
    "path": "AppStoreDemo/Assets.xcassets/common/detail_download.imageset/Contents.json",
    "chars": 360,
    "preview": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n     "
  },
  {
    "path": "AppStoreDemo/Assets.xcassets/common/detail_more.imageset/Contents.json",
    "chars": 352,
    "preview": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n     "
  },
  {
    "path": "AppStoreDemo/Assets.xcassets/common/navigation_back.imageset/Contents.json",
    "chars": 312,
    "preview": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n     "
  },
  {
    "path": "AppStoreDemo/Assets.xcassets/cover/Contents.json",
    "chars": 62,
    "preview": "{\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "AppStoreDemo/Assets.xcassets/cover/cover_1.imageset/Contents.json",
    "chars": 305,
    "preview": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"cover_1.jpeg\",\n      \"scale\" : \"1x\"\n    },\n    {"
  },
  {
    "path": "AppStoreDemo/Assets.xcassets/cover/cover_2.imageset/Contents.json",
    "chars": 305,
    "preview": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"cover_2.jpeg\",\n      \"scale\" : \"1x\"\n    },\n    {"
  },
  {
    "path": "AppStoreDemo/Assets.xcassets/cover/cover_3.imageset/Contents.json",
    "chars": 305,
    "preview": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"cover_3.jpeg\",\n      \"scale\" : \"1x\"\n    },\n    {"
  },
  {
    "path": "AppStoreDemo/Assets.xcassets/cover/cover_4.imageset/Contents.json",
    "chars": 305,
    "preview": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"cover_4.jpeg\",\n      \"scale\" : \"1x\"\n    },\n    {"
  },
  {
    "path": "AppStoreDemo/Assets.xcassets/cover/cover_5.imageset/Contents.json",
    "chars": 305,
    "preview": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"cover_5.jpeg\",\n      \"scale\" : \"1x\"\n    },\n    {"
  },
  {
    "path": "AppStoreDemo/Assets.xcassets/cover_detail/Contents.json",
    "chars": 62,
    "preview": "{\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "AppStoreDemo/Assets.xcassets/cover_detail/cover_detail.imageset/Contents.json",
    "chars": 309,
    "preview": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"cover_detail.jpg\",\n      \"scale\" : \"1x\"\n    },\n "
  },
  {
    "path": "AppStoreDemo/Assets.xcassets/cover_detail/cover_detail1.imageset/Contents.json",
    "chars": 310,
    "preview": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"cover_detail1.jpg\",\n      \"scale\" : \"1x\"\n    },\n"
  },
  {
    "path": "AppStoreDemo/Assets.xcassets/cover_detail/cover_detail2.imageset/Contents.json",
    "chars": 310,
    "preview": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"cover_detail2.jpg\",\n      \"scale\" : \"1x\"\n    },\n"
  },
  {
    "path": "AppStoreDemo/Assets.xcassets/tabBar/Contents.json",
    "chars": 62,
    "preview": "{\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "AppStoreDemo/Assets.xcassets/tabBar/tabbar_apps.imageset/Contents.json",
    "chars": 352,
    "preview": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n     "
  },
  {
    "path": "AppStoreDemo/Assets.xcassets/tabBar/tabbar_games.imageset/Contents.json",
    "chars": 354,
    "preview": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n     "
  },
  {
    "path": "AppStoreDemo/Assets.xcassets/tabBar/tabbar_search.imageset/Contents.json",
    "chars": 356,
    "preview": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n     "
  },
  {
    "path": "AppStoreDemo/Assets.xcassets/tabBar/tabbar_today.imageset/Contents.json",
    "chars": 354,
    "preview": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n     "
  },
  {
    "path": "AppStoreDemo/Assets.xcassets/tabBar/tabbar_updates.imageset/Contents.json",
    "chars": 358,
    "preview": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n     "
  },
  {
    "path": "AppStoreDemo/Base.lproj/LaunchScreen.storyboard",
    "chars": 1658,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard"
  },
  {
    "path": "AppStoreDemo/Common/CommonCollectionFlowLayout.swift",
    "chars": 1836,
    "preview": "//\n//  GameCollectionFlowLayout.swift\n//  AppStoreDemo\n//\n//  Created by Allen long on 2019/8/6.\n//  Copyright © 2019 Ut"
  },
  {
    "path": "AppStoreDemo/Common/CommonSectionHeaderView.swift",
    "chars": 1461,
    "preview": "//\n//  CommonSectionHeaderView.swift\n//  AppStoreDemo\n//\n//  Created by Allen long on 2019/8/9.\n//  Copyright © 2019 Uti"
  },
  {
    "path": "AppStoreDemo/Extension/String+Extension.swift",
    "chars": 625,
    "preview": "//\n//  String+Extension.swift\n//  AppStoreDemo\n//\n//  Created by Allen long on 2019/8/2.\n//  Copyright © 2019 Utimes. Al"
  },
  {
    "path": "AppStoreDemo/Extension/UIColor+Extension.swift",
    "chars": 344,
    "preview": "//\n//  UIColor+Extension.swift\n//  AppStoreDemo\n//\n//  Created by Allen long on 2019/8/5.\n//  Copyright © 2019 Utimes. A"
  },
  {
    "path": "AppStoreDemo/Extension/UITableView+Extension.swift",
    "chars": 2509,
    "preview": "//\n//  UITableView+Extension.swift\n//  AppStoreDemo\n//\n//  Created by Allen long on 2019/8/5.\n//  Copyright © 2019 Utime"
  },
  {
    "path": "AppStoreDemo/Extension/UIView+Extension.swift",
    "chars": 1640,
    "preview": "//\n//  UIView+Extension.swift\n//  AppStoreDemo\n//\n//  Created by Allen long on 2019/8/2.\n//  Copyright © 2019 Utimes. Al"
  },
  {
    "path": "AppStoreDemo/Extension/UIViewController+Extension.swift",
    "chars": 4593,
    "preview": "//\n//  UIViewController+Extension.swift\n//  AppStoreDemo\n//\n//  Created by Erwin on 2019/8/4.\n//  Copyright © 2019 Utime"
  },
  {
    "path": "AppStoreDemo/Game/Controller/DetailViewController.swift",
    "chars": 6093,
    "preview": "//\n//  DetailViewController.swift\n//  AppStoreDemo\n//\n//  Created by Allen long on 2019/8/7.\n//  Copyright © 2019 Utimes"
  },
  {
    "path": "AppStoreDemo/Game/Controller/DownloadPresentationController.swift",
    "chars": 1821,
    "preview": "//\n//  DownloadPresentationController.swift\n//  AppStoreDemo\n//\n//  Created by Allen long on 2019/9/5.\n//  Copyright © 2"
  },
  {
    "path": "AppStoreDemo/Game/Controller/DownloadViewController.swift",
    "chars": 3039,
    "preview": "//\n//  DownloadViewController.swift\n//  AppStoreDemo\n//\n//  Created by Allen long on 2019/9/5.\n//  Copyright © 2019 Utim"
  },
  {
    "path": "AppStoreDemo/Game/Controller/GameTableViewController.swift",
    "chars": 2992,
    "preview": "//\n//  GameTableViewController.swift\n//  AppStoreDemo\n//\n//  Created by Allen long on 2019/8/6.\n//  Copyright © 2019 Uti"
  },
  {
    "path": "AppStoreDemo/Game/Model/DownloadTransitioning.swift",
    "chars": 3469,
    "preview": "//\n//  DownloadTransitioning.swift\n//  AppStoreDemo\n//\n//  Created by Allen long on 2019/9/5.\n//  Copyright © 2019 Utime"
  },
  {
    "path": "AppStoreDemo/Game/Model/GameRecommandModel.swift",
    "chars": 291,
    "preview": "//\n//  GameRecommandModel.swift\n//  AppStoreDemo\n//\n//  Created by Allen long on 2019/8/6.\n//  Copyright © 2019 Utimes. "
  },
  {
    "path": "AppStoreDemo/Game/Model/GameTopicModel.swift",
    "chars": 262,
    "preview": "//\n//  GameTopicModel.swift\n//  AppStoreDemo\n//\n//  Created by Allen long on 2019/8/6.\n//  Copyright © 2019 Utimes. All "
  },
  {
    "path": "AppStoreDemo/Game/View/Detail/DetailInfomationTableViewCell.swift",
    "chars": 1913,
    "preview": "//\n//  DetailInfomationTableViewCell.swift\n//  AppStoreDemo\n//\n//  Created by Allen long on 2019/8/9.\n//  Copyright © 20"
  },
  {
    "path": "AppStoreDemo/Game/View/Detail/DetailInformationCell.swift",
    "chars": 2237,
    "preview": "//\n//  DetailInformationCell.swift\n//  AppStoreDemo\n//\n//  Created by Allen long on 2019/8/9.\n//  Copyright © 2019 Utime"
  },
  {
    "path": "AppStoreDemo/Game/View/Detail/DetailNavigationView.swift",
    "chars": 1155,
    "preview": "//\n//  DetailNavigationView.swift\n//  AppStoreDemo\n//\n//  Created by Allen long on 2019/8/7.\n//  Copyright © 2019 Utimes"
  },
  {
    "path": "AppStoreDemo/Game/View/Detail/DetailNewFeaturesCell.swift",
    "chars": 501,
    "preview": "//\n//  DetailNewFeaturesCell.swift\n//  AppStoreDemo\n//\n//  Created by Allen long on 2019/8/8.\n//  Copyright © 2019 Utime"
  },
  {
    "path": "AppStoreDemo/Game/View/Detail/DetailNewFeaturesCell.xib",
    "chars": 7833,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.XIB\" version=\"3.0\" toolsVe"
  },
  {
    "path": "AppStoreDemo/Game/View/Detail/DetailPreviewCell.swift",
    "chars": 2306,
    "preview": "//\n//  DetailPreviewCell.swift\n//  AppStoreDemo\n//\n//  Created by Allen long on 2019/8/8.\n//  Copyright © 2019 Utimes. A"
  },
  {
    "path": "AppStoreDemo/Game/View/Detail/DetailPreviewCollectionView.swift",
    "chars": 774,
    "preview": "//\n//  DetailPreviewCollectionView.swift\n//  AppStoreDemo\n//\n//  Created by Allen long on 2019/8/8.\n//  Copyright © 2019"
  },
  {
    "path": "AppStoreDemo/Game/View/Detail/DetailPreviewCollectionViewCell.swift",
    "chars": 750,
    "preview": "//\n//  DetailPreviewCollectionViewCell.swift\n//  AppStoreDemo\n//\n//  Created by Allen long on 2019/8/8.\n//  Copyright © "
  },
  {
    "path": "AppStoreDemo/Game/View/Detail/DetailTopInfoCell.swift",
    "chars": 561,
    "preview": "//\n//  DetailTopInfoCell.swift\n//  AppStoreDemo\n//\n//  Created by Allen long on 2019/8/8.\n//  Copyright © 2019 Utimes. A"
  },
  {
    "path": "AppStoreDemo/Game/View/Detail/DetailTopInfoCell.xib",
    "chars": 15266,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.XIB\" version=\"3.0\" toolsVe"
  },
  {
    "path": "AppStoreDemo/Game/View/Download/DownloadBottomView.swift",
    "chars": 884,
    "preview": "//\n//  DownloadBottomView.swift\n//  AppStoreDemo\n//\n//  Created by Allen long on 2019/9/5.\n//  Copyright © 2019 Utimes. "
  },
  {
    "path": "AppStoreDemo/Game/View/Download/DownloadBottomView.xib",
    "chars": 15708,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.XIB\" version=\"3.0\" toolsVe"
  },
  {
    "path": "AppStoreDemo/Game/View/Download/DownloadClickView.swift",
    "chars": 1497,
    "preview": "//\n//  DownloadClickView.swift\n//  AppStoreDemo\n//\n//  Created by Allen long on 2019/9/5.\n//  Copyright © 2019 Utimes. A"
  },
  {
    "path": "AppStoreDemo/Game/View/Download/DownloadClickView.xib",
    "chars": 4186,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.XIB\" version=\"3.0\" toolsVe"
  },
  {
    "path": "AppStoreDemo/Game/View/Link/GameLinkTableView.swift",
    "chars": 1621,
    "preview": "//\n//  GameLinkTableView.swift\n//  AppStoreDemo\n//\n//  Created by Allen long on 2019/8/7.\n//  Copyright © 2019 Utimes. A"
  },
  {
    "path": "AppStoreDemo/Game/View/Link/GameLinkTableViewCell.swift",
    "chars": 1131,
    "preview": "//\n//  GameLinkTableViewCell.swift\n//  AppStoreDemo\n//\n//  Created by Allen long on 2019/8/7.\n//  Copyright © 2019 Utime"
  },
  {
    "path": "AppStoreDemo/Game/View/Recommand/GameRecommandCollectionView.swift",
    "chars": 772,
    "preview": "//\n//  GameRecommandCollectionView.swift\n//  AppStoreDemo\n//\n//  Created by Allen long on 2019/8/6.\n//  Copyright © 2019"
  },
  {
    "path": "AppStoreDemo/Game/View/Recommand/GameRecommandTableViewCell.swift",
    "chars": 2547,
    "preview": "//\n//  GameRecommandTableViewCell.swift\n//  AppStoreDemo\n//\n//  Created by Allen long on 2019/8/6.\n//  Copyright © 2019 "
  },
  {
    "path": "AppStoreDemo/Game/View/Recommand/RecommandCollectionViewCell.swift",
    "chars": 793,
    "preview": "//\n//  RecommandCollectionViewCell.swift\n//  AppStoreDemo\n//\n//  Created by Allen long on 2019/8/6.\n//  Copyright © 2019"
  },
  {
    "path": "AppStoreDemo/Game/View/Recommand/RecommandCollectionViewCell.xib",
    "chars": 8782,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.XIB\" version=\"3.0\" toolsVe"
  },
  {
    "path": "AppStoreDemo/Game/View/Topic/GameTopicCollectionView.swift",
    "chars": 769,
    "preview": "//\n//  GameTopicCollectionView.swift\n//  AppStoreDemo\n//\n//  Created by Allen long on 2019/8/6.\n//  Copyright © 2019 Uti"
  },
  {
    "path": "AppStoreDemo/Game/View/Topic/GameTopicCollectionViewCell.swift",
    "chars": 970,
    "preview": "//\n//  GameTopicCollectionViewCell.swift\n//  AppStoreDemo\n//\n//  Created by Allen long on 2019/8/6.\n//  Copyright © 2019"
  },
  {
    "path": "AppStoreDemo/Game/View/Topic/GameTopicCollectionViewCell.xib",
    "chars": 8207,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.XIB\" version=\"3.0\" toolsVe"
  },
  {
    "path": "AppStoreDemo/Game/View/Topic/GameTopicTableViewCell.swift",
    "chars": 3406,
    "preview": "//\n//  GameTopicTableViewCell.swift\n//  AppStoreDemo\n//\n//  Created by Allen long on 2019/8/6.\n//  Copyright © 2019 Utim"
  },
  {
    "path": "AppStoreDemo/Info.plist",
    "chars": 1520,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "AppStoreDemo/Main.storyboard",
    "chars": 13691,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3"
  },
  {
    "path": "AppStoreDemo/Search/Controller/SearchTableViewController.swift",
    "chars": 2076,
    "preview": "//\n//  SearchTableViewController.swift\n//  AppStoreDemo\n//\n//  Created by Allen long on 2019/8/2.\n//  Copyright © 2019 U"
  },
  {
    "path": "AppStoreDemo/Search/View/SearchTableViewCell.swift",
    "chars": 1352,
    "preview": "//\n//  SearchTableViewCell.swift\n//  AppStoreDemo\n//\n//  Created by Allen long on 2019/8/2.\n//  Copyright © 2019 Utimes."
  },
  {
    "path": "AppStoreDemo/Today/Controller/CardDetailViewController.swift",
    "chars": 5764,
    "preview": "//\n//  CardDetailViewController.swift\n//  AppStoreDemo\n//\n//  Created by Allen long on 2019/7/31.\n//  Copyright © 2019 U"
  },
  {
    "path": "AppStoreDemo/Today/Controller/CardPresentationController.swift",
    "chars": 1625,
    "preview": "//\n//  TodayPresentationController.swift\n//  AppStoreDemo\n//\n//  Created by Allen long on 2019/8/2.\n//  Copyright © 2019"
  },
  {
    "path": "AppStoreDemo/Today/Controller/TodayViewController.swift",
    "chars": 3532,
    "preview": "//\n//  TodayViewController.swift\n//  AppStoreDemo\n//\n//  Created by Allen long on 2019/7/31.\n//  Copyright © 2019 Utimes"
  },
  {
    "path": "AppStoreDemo/Today/Model/TodayAnimationTransition.swift",
    "chars": 4025,
    "preview": "//\n//  TodayAnimationTransition.swift\n//  AppStoreDemo\n//\n//  Created by Allen long on 2019/7/31.\n//  Copyright © 2019 U"
  },
  {
    "path": "AppStoreDemo/Today/View/DetailScrollView.swift",
    "chars": 3170,
    "preview": "//\n//  DetailScrollView.swift\n//  AppStoreDemo\n//\n//  Created by Allen long on 2019/7/31.\n//  Copyright © 2019 Utimes. A"
  },
  {
    "path": "AppStoreDemo/Today/View/TodayTableHeaderView.swift",
    "chars": 1739,
    "preview": "//\n//  TodayTableHeaderView.swift\n//  AppStoreDemo\n//\n//  Created by Allen long on 2019/7/31.\n//  Copyright © 2019 Utime"
  },
  {
    "path": "AppStoreDemo/Today/View/TodayTableViewCell.swift",
    "chars": 1633,
    "preview": "//\n//  TodayTableViewCell.swift\n//  AppStoreDemo\n//\n//  Created by Allen long on 2019/7/31.\n//  Copyright © 2019 Utimes."
  },
  {
    "path": "AppStoreDemo/Update/Controller/UpdateTableViewController.swift",
    "chars": 3651,
    "preview": "//\n//  UpdateTableViewController.swift\n//  AppStoreDemo\n//\n//  Created by Erwin on 2019/8/4.\n//  Copyright © 2019 Utimes"
  },
  {
    "path": "AppStoreDemo/Update/Model/UpdateModel.swift",
    "chars": 357,
    "preview": "//\n//  UpdateModel.swift\n//  AppStoreDemo\n//\n//  Created by Allen long on 2019/8/5.\n//  Copyright © 2019 Utimes. All rig"
  },
  {
    "path": "AppStoreDemo/Update/View/UpdateTableViewCell.swift",
    "chars": 2131,
    "preview": "//\n//  UpdateTableViewCell.swift\n//  AppStoreDemo\n//\n//  Created by Allen long on 2019/8/5.\n//  Copyright © 2019 Utimes."
  },
  {
    "path": "AppStoreDemo/Update/View/UpdateTableViewCell.xib",
    "chars": 14682,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.XIB\" version=\"3.0\" toolsVe"
  },
  {
    "path": "AppStoreDemo/User/Controller/UserTableViewController.swift",
    "chars": 904,
    "preview": "//\n//  UserTableViewController.swift\n//  AppStoreDemo\n//\n//  Created by Allen long on 2019/8/5.\n//  Copyright © 2019 Uti"
  },
  {
    "path": "AppStoreDemo/User/View/User.storyboard",
    "chars": 25831,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3"
  },
  {
    "path": "AppStoreDemo/Utils/GlobalConstants.swift",
    "chars": 1794,
    "preview": "//\n//  GlobalConstants.swift\n//  AppStoreDemo\n//\n//  Created by Allen long on 2019/8/2.\n//  Copyright © 2019 Utimes. All"
  },
  {
    "path": "AppStoreDemo/Utils/GlobalFunctions.swift",
    "chars": 627,
    "preview": "//\n//  GlobalFunctions.swift\n//  AppStoreDemo\n//\n//  Created by Allen long on 2019/8/2.\n//  Copyright © 2019 Utimes. All"
  },
  {
    "path": "AppStoreDemo/Utils/StarView.swift",
    "chars": 1484,
    "preview": "//\n//  StarView.swift\n//  AppStoreDemo\n//\n//  Created by Allen long on 2019/8/8.\n//  Copyright © 2019 Utimes. All rights"
  },
  {
    "path": "AppStoreDemo.xcodeproj/project.pbxproj",
    "chars": 45591,
    "preview": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 50;\n\tobjects = {\n\n/* Begin PBXBuildFile section *"
  },
  {
    "path": "AppStoreDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
    "chars": 157,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:AppStoreDemo.xc"
  },
  {
    "path": "AppStoreDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "chars": 238,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "AppStoreDemo.xcodeproj/xcuserdata/fengbufang.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist",
    "chars": 140,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Bucket\n   uuid = \"30E1E68A-18E1-48F7-BC71-4D838AD72221\"\n   type = \"1\"\n   version"
  },
  {
    "path": "AppStoreDemo.xcodeproj/xcuserdata/fengbufang.xcuserdatad/xcschemes/xcschememanagement.plist",
    "chars": 347,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "README.md",
    "chars": 1188,
    "preview": "# appstore-clone\n\nan awesome clone of iOS App Store.[中文简介](https://github.com/DragonTnT/appstore-clone/blob/master/%E4%B"
  },
  {
    "path": "中文简介.md",
    "chars": 353,
    "preview": "# appstore-clone\n\n本项目是对iOS中的软件App Store的模仿\n\n\n\n## 介绍\n\nApp Store应该是大家在iphone里最常用的软件之一。作为一名iOS开发者,我一直有模仿App Store的想法。我花了一些业"
  }
]

// ... and 3 more files (download for full content)

About this extraction

This page contains the full source code of the DragonTnT/appstore-clone GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 102 files (261.2 KB), approximately 73.7k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!