├── Sample ├── Assets.xcassets │ ├── Contents.json │ ├── umi.imageset │ │ ├── umi.jpg │ │ └── Contents.json │ ├── yuhi.imageset │ │ ├── yuhi.jpg │ │ └── Contents.json │ ├── noman.imageset │ │ ├── noman.png │ │ └── Contents.json │ ├── yama1.imageset │ │ ├── yama1.jpg │ │ └── Contents.json │ ├── yama2.imageset │ │ ├── yama2.jpg │ │ └── Contents.json │ ├── AccentColor.colorset │ │ └── Contents.json │ ├── notification.imageset │ │ ├── iconfinder_bell-notification-notify-reminder-ring_3643784.png │ │ └── Contents.json │ ├── timeline.imageset │ │ ├── iconfinder_newspaper_news_daily_business_media_article-_15_7218177.png │ │ └── Contents.json │ ├── iconfinder_00-ELASTOFONT-STORE-READY_user-circle_2703062.imageset │ │ ├── iconfinder_00-ELASTOFONT-STORE-READY_user-circle_2703062.png │ │ └── Contents.json │ └── AppIcon.appiconset │ │ └── Contents.json ├── Utility │ ├── Storyboard.swift │ └── NSObject+NibLoading.swift ├── ViewController │ ├── ViewController.swift │ ├── CustomTopContentPagerViewController.swift │ ├── TimelineViewController.swift │ ├── Base.lproj │ │ └── Main.storyboard │ ├── NotificationViewController.swift │ ├── MyPostViewController.swift │ ├── TimelineViewController.storyboard │ ├── NotificationViewController.storyboard │ └── MyPostViewController.storyboard ├── Cell │ ├── MyPostCollectionCell.swift │ ├── NotificationTableCell.swift │ ├── TimelineTableCell.swift │ ├── MyPostCollectionCell.xib │ ├── NotificationTableCell.xib │ └── TimelineTableCell.xib ├── View │ ├── TopView.swift │ └── TopView.xib ├── Entity │ └── MockData.swift ├── AppDelegate.swift ├── Base.lproj │ └── LaunchScreen.storyboard ├── Info.plist └── SceneDelegate.swift ├── TopContentPager.xcodeproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── project.pbxproj ├── TopContentPager ├── Cell │ ├── ContentTopCell.swift │ └── ContentBodyCell.swift ├── Utilitie │ ├── NSObject+Name.swift │ └── UITableView+Register.swift ├── View │ ├── PagerItemView.swift │ ├── PagerOptions.swift │ ├── DefaultPagerItemView.swift │ └── PagerItemsView.swift ├── TopContentPager.h ├── ContentTableBody.swift ├── Info.plist ├── TopContentView.swift ├── ContentTableViewController.swift └── TopContentPagerViewController.swift ├── TopContentPager.podspec ├── SampleTests ├── Info.plist └── SampleTests.swift ├── SampleUITests ├── Info.plist └── SampleUITests.swift ├── TopContentPagerTests ├── Info.plist └── TopContentPagerTests.swift ├── LICENSE ├── .gitignore └── README.md /Sample/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Sample/Assets.xcassets/umi.imageset/umi.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsukiss/TopContentPager/HEAD/Sample/Assets.xcassets/umi.imageset/umi.jpg -------------------------------------------------------------------------------- /Sample/Assets.xcassets/yuhi.imageset/yuhi.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsukiss/TopContentPager/HEAD/Sample/Assets.xcassets/yuhi.imageset/yuhi.jpg -------------------------------------------------------------------------------- /Sample/Assets.xcassets/noman.imageset/noman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsukiss/TopContentPager/HEAD/Sample/Assets.xcassets/noman.imageset/noman.png -------------------------------------------------------------------------------- /Sample/Assets.xcassets/yama1.imageset/yama1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsukiss/TopContentPager/HEAD/Sample/Assets.xcassets/yama1.imageset/yama1.jpg -------------------------------------------------------------------------------- /Sample/Assets.xcassets/yama2.imageset/yama2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsukiss/TopContentPager/HEAD/Sample/Assets.xcassets/yama2.imageset/yama2.jpg -------------------------------------------------------------------------------- /TopContentPager.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Sample/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Sample/Assets.xcassets/umi.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "umi.jpg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Sample/Assets.xcassets/noman.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "noman.png", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Sample/Assets.xcassets/yama1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "yama1.jpg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Sample/Assets.xcassets/yama2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "yama2.jpg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Sample/Assets.xcassets/yuhi.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "yuhi.jpg", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Sample/Assets.xcassets/notification.imageset/iconfinder_bell-notification-notify-reminder-ring_3643784.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsukiss/TopContentPager/HEAD/Sample/Assets.xcassets/notification.imageset/iconfinder_bell-notification-notify-reminder-ring_3643784.png -------------------------------------------------------------------------------- /Sample/Assets.xcassets/timeline.imageset/iconfinder_newspaper_news_daily_business_media_article-_15_7218177.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsukiss/TopContentPager/HEAD/Sample/Assets.xcassets/timeline.imageset/iconfinder_newspaper_news_daily_business_media_article-_15_7218177.png -------------------------------------------------------------------------------- /Sample/Assets.xcassets/notification.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "iconfinder_bell-notification-notify-reminder-ring_3643784.png", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Sample/Assets.xcassets/timeline.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "iconfinder_newspaper_news_daily_business_media_article-_15_7218177.png", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /TopContentPager.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Sample/Assets.xcassets/iconfinder_00-ELASTOFONT-STORE-READY_user-circle_2703062.imageset/iconfinder_00-ELASTOFONT-STORE-READY_user-circle_2703062.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsukiss/TopContentPager/HEAD/Sample/Assets.xcassets/iconfinder_00-ELASTOFONT-STORE-READY_user-circle_2703062.imageset/iconfinder_00-ELASTOFONT-STORE-READY_user-circle_2703062.png -------------------------------------------------------------------------------- /Sample/Assets.xcassets/iconfinder_00-ELASTOFONT-STORE-READY_user-circle_2703062.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "iconfinder_00-ELASTOFONT-STORE-READY_user-circle_2703062.png", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /TopContentPager/Cell/ContentTopCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentTopCell.swift 3 | // TopContentPager 4 | // 5 | // Created by 田中厳貴 on 2021/01/08. 6 | // 7 | 8 | import UIKit 9 | 10 | final class ContentTopCell: UITableViewCell { 11 | override func awakeFromNib() { 12 | super.awakeFromNib() 13 | selectionStyle = .none 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /TopContentPager/Utilitie/NSObject+Name.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+Name.swift 3 | // TopContentPager 4 | // 5 | // Created by 田中厳貴 on 2021/01/13. 6 | // 7 | 8 | import Foundation 9 | 10 | extension NSObject { 11 | class var className: String { 12 | String(describing: self) 13 | } 14 | 15 | var className: String { 16 | String(describing: type(of: self)) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /TopContentPager/View/PagerItemView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PagerItemView.swift 3 | // TopContentPager 4 | // 5 | // Created by 田中厳貴 on 2021/01/08. 6 | // 7 | 8 | import UIKit 9 | 10 | public protocol PagerItemView: UIView { 11 | var activeColor: UIColor { get set } 12 | var deactiveColor: UIColor { get set } 13 | var activeBackgroundColor: UIColor { get set } 14 | var isSelected: Bool { get set } 15 | 16 | func badge(isHidden: Bool) 17 | } 18 | -------------------------------------------------------------------------------- /Sample/Utility/Storyboard.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Storyboard.swift 3 | // Sample 4 | // 5 | // Created by 田中厳貴 on 2021/01/13. 6 | // 7 | 8 | import UIKit 9 | 10 | final class Storyboard { 11 | 12 | public static func instantiate(_ type: T.Type) -> T { 13 | let storyboardName = String(describing: type) 14 | let sb = UIStoryboard(name: storyboardName, bundle: Bundle(for: type)) 15 | let vc = sb.instantiateInitialViewController() as! T 16 | return vc 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /TopContentPager/TopContentPager.h: -------------------------------------------------------------------------------- 1 | // 2 | // TopContentPager.h 3 | // TopContentPager 4 | // 5 | // Created by 田中厳貴 on 2021/01/08. 6 | // 7 | 8 | #import 9 | 10 | //! Project version number for TopContentPager. 11 | FOUNDATION_EXPORT double TopContentPagerVersionNumber; 12 | 13 | //! Project version string for TopContentPager. 14 | FOUNDATION_EXPORT const unsigned char TopContentPagerVersionString[]; 15 | 16 | // In this header, you should import all the public headers of your framework using statements like #import 17 | 18 | 19 | -------------------------------------------------------------------------------- /TopContentPager.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |spec| 2 | spec.name = "TopContentPager" 3 | spec.version = "1.1.1" 4 | spec.summary = "Pager Like Instagram Mypage View" 5 | spec.homepage = "https://github.com/itsukiss/TopContentPager" 6 | spec.license = { :type => 'MIT', :file => 'LICENSE' } 7 | spec.author = "itsukiss" 8 | spec.platform = :ios, "11.0" 9 | spec.swift_version = "5.0" 10 | spec.source = { :git => "https://github.com/itsukiss/TopContentPager.git", :tag => "#{spec.version}" } 11 | spec.source_files = "TopContentPager/**/*.swift" 12 | end 13 | -------------------------------------------------------------------------------- /TopContentPager/ContentTableBody.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentTableBody.swift 3 | // TopContentPager 4 | // 5 | // Created by 田中厳貴 on 2021/01/08. 6 | // 7 | 8 | import UIKit 9 | 10 | public protocol ContentTableBody: UIViewController { 11 | var pagerItem: PagerItem { get } 12 | var scrollView: UIScrollView! { get } 13 | @discardableResult func refresh(sender: UIRefreshControl, contentViewController: ContentTableViewController) -> Bool 14 | } 15 | 16 | public extension ContentTableBody { 17 | func refresh(sender: UIRefreshControl, contentViewController: ContentTableViewController) -> Bool { 18 | return false 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Sample/ViewController/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // Sample 4 | // 5 | // Created by 田中厳貴 on 2021/01/08. 6 | // 7 | 8 | import UIKit 9 | import TopContentPager 10 | 11 | class ViewController: UIViewController { 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | } 16 | 17 | override func viewDidAppear(_ animated: Bool) { 18 | super.viewDidAppear(animated) 19 | let topContentVC = CustomTopContentPagerViewController() 20 | topContentVC.modalPresentationStyle = .fullScreen 21 | present(topContentVC, animated: true, completion: nil) 22 | } 23 | } 24 | 25 | -------------------------------------------------------------------------------- /Sample/Cell/MyPostCollectionCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MyPostCollectionCell.swift 3 | // Sample 4 | // 5 | // Created by 田中厳貴 on 2021/01/27. 6 | // 7 | 8 | import UIKit 9 | 10 | class MyPostCollectionCell: UICollectionViewCell { 11 | 12 | @IBOutlet weak var postImageView: UIImageView! { 13 | didSet { 14 | postImageView.contentMode = .scaleAspectFill 15 | } 16 | } 17 | override func awakeFromNib() { 18 | super.awakeFromNib() 19 | backgroundColor = #colorLiteral(red: 0.8039215803, green: 0.8039215803, blue: 0.8039215803, alpha: 1) 20 | } 21 | 22 | func prepare(image: UIImage) { 23 | postImageView.image = image 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Sample/Cell/NotificationTableCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NotificationTableCell.swift 3 | // Sample 4 | // 5 | // Created by 田中厳貴 on 2021/01/27. 6 | // 7 | 8 | import UIKit 9 | 10 | final class NotificationTableCell: UITableViewCell { 11 | 12 | @IBOutlet private weak var contentImageView: UIImageView! { 13 | didSet { 14 | contentImageView.layer.cornerRadius = contentImageView.frame.height / 2 15 | contentImageView.clipsToBounds = true 16 | } 17 | } 18 | @IBOutlet private weak var notificationLabel: UILabel! 19 | 20 | func prepare(text: String) { 21 | contentImageView.image = UIImage(named: "noman") 22 | notificationLabel.text = text 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /SampleTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /SampleUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /TopContentPagerTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /TopContentPager/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | 22 | 23 | -------------------------------------------------------------------------------- /SampleTests/SampleTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SampleTests.swift 3 | // SampleTests 4 | // 5 | // Created by 田中厳貴 on 2021/01/08. 6 | // 7 | 8 | import XCTest 9 | @testable import Sample 10 | 11 | class SampleTests: XCTestCase { 12 | 13 | override func setUpWithError() throws { 14 | // Put setup code here. This method is called before the invocation of each test method in the class. 15 | } 16 | 17 | override func tearDownWithError() throws { 18 | // Put teardown code here. This method is called after the invocation of each test method in the class. 19 | } 20 | 21 | func testExample() throws { 22 | // This is an example of a functional test case. 23 | // Use XCTAssert and related functions to verify your tests produce the correct results. 24 | } 25 | 26 | func testPerformanceExample() throws { 27 | // This is an example of a performance test case. 28 | self.measure { 29 | // Put the code you want to measure the time of here. 30 | } 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /TopContentPagerTests/TopContentPagerTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TopContentPagerTests.swift 3 | // TopContentPagerTests 4 | // 5 | // Created by 田中厳貴 on 2021/01/08. 6 | // 7 | 8 | import XCTest 9 | @testable import TopContentPager 10 | 11 | class TopContentPagerTests: XCTestCase { 12 | 13 | override func setUpWithError() throws { 14 | // Put setup code here. This method is called before the invocation of each test method in the class. 15 | } 16 | 17 | override func tearDownWithError() throws { 18 | // Put teardown code here. This method is called after the invocation of each test method in the class. 19 | } 20 | 21 | func testExample() throws { 22 | // This is an example of a functional test case. 23 | // Use XCTAssert and related functions to verify your tests produce the correct results. 24 | } 25 | 26 | func testPerformanceExample() throws { 27 | // This is an example of a performance test case. 28 | self.measure { 29 | // Put the code you want to measure the time of here. 30 | } 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Itsuki Tanaka 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /TopContentPager/Cell/ContentBodyCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentBodyCell.swift 3 | // TopContentPager 4 | // 5 | // Created by 田中厳貴 on 2021/01/08. 6 | // 7 | 8 | import UIKit 9 | 10 | final class ContentBodyCell: UITableViewCell { 11 | 12 | private weak var bodyViewController: UIViewController? 13 | 14 | override func awakeFromNib() { 15 | super.awakeFromNib() 16 | selectionStyle = .none 17 | } 18 | 19 | func prepare(viewController: UIViewController) { 20 | guard bodyViewController == nil else { return } 21 | bodyViewController = viewController 22 | self.addSubview(viewController.view) 23 | viewController.view.translatesAutoresizingMaskIntoConstraints = false 24 | NSLayoutConstraint.activate([ 25 | viewController.view.topAnchor.constraint(equalTo: self.topAnchor), 26 | viewController.view.bottomAnchor.constraint(equalTo: self.bottomAnchor), 27 | viewController.view.leadingAnchor.constraint(equalTo: self.leadingAnchor), 28 | viewController.view.trailingAnchor.constraint(equalTo: self.trailingAnchor), 29 | ]) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Sample/Cell/TimelineTableCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TimelineTableCell.swift 3 | // Sample 4 | // 5 | // Created by 田中厳貴 on 2021/01/27. 6 | // 7 | 8 | import UIKit 9 | 10 | class TimelineTableCell: UITableViewCell { 11 | 12 | @IBOutlet weak var profileImageView: UIImageView! { 13 | didSet { 14 | profileImageView.layer.cornerRadius = profileImageView.frame.height / 2 15 | profileImageView.clipsToBounds = true 16 | } 17 | } 18 | @IBOutlet weak var userNameLabel: UILabel! 19 | 20 | @IBOutlet weak var postImageView: UIImageView! { 21 | didSet { 22 | postImageView.contentMode = .scaleAspectFill 23 | } 24 | } 25 | @IBOutlet weak var postNoteLabel: UILabel! 26 | 27 | override func awakeFromNib() { 28 | super.awakeFromNib() 29 | 30 | } 31 | 32 | func prepare(image: UIImage) { 33 | profileImageView.image = UIImage(named: "noman") 34 | userNameLabel.text = "Elyane Bent Caillot" 35 | postImageView.image = image 36 | postNoteLabel.text = "This is my favorite place.\nI went there in last summer.\n#summer #sea #favorite" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcodeのビルド生成物 2 | # ビルドすると自動生成されるので無視 3 | build/ 4 | DerivedData 5 | 6 | # Xcodeのプライベート設定 7 | # (ウィンドウサイズ、ブックマーク、ブレイクポイントなど) 8 | # 開発者ごとに違うものなので無視 9 | *.pbxuser 10 | *.mode1v3 11 | *.mode2v3 12 | *.perspectivev3 13 | 14 | # ! を最初につけると無視しない設定になる 15 | # 上記4つのデフォルト設定ファイルは無視しない 16 | !default.pbxuser 17 | !default.mode1v3 18 | !default.mode2v3 19 | !default.perspectivev3 20 | 21 | # VCSのメタデータが含まれ、コミットしてしまうとmergeが困難になるなどの問題が発生する 22 | # VCS = Version Controll System 23 | *.xccheckout 24 | 25 | # 同じ名前のクラスを定義しようとした時などに手動でdeprecated設定にすると生成されるファイル 26 | # ユーザごとに異なるので無視 27 | *.moved-aside 28 | 29 | # Headerファイルのマッピング設定 30 | # ユーザごとに異なるので無視 31 | *.hmap 32 | 33 | # iOSのアプリケーションファイル 34 | # ビルドすれば作成されるので無視 35 | *.ipa 36 | 37 | # Xcodeで何かするたびに編集されるファイル 38 | *.xcuserstate 39 | 40 | 41 | # ビルドのカスタムscheme(ロケーション等) 42 | # ユーザごとに異なるので無視 43 | xcuserdata 44 | 45 | # CocoaPodで外部ライブラリがインストールされるフォルダ 46 | # $ pod install すると自動生成されるので無視 47 | Pods/* 48 | 49 | # スワップファイル 50 | # アプリケーションのクラッシュに備えて、 vimエディタでの編集開始時に作成され、編集後に削除される編集情報の記録ファイル 51 | *.swp 52 | 53 | # gitkeepは空のフォルダが必要な場合に作成するファイル 54 | !.gitkeep 55 | 56 | # Macで、アイコンの位置や表示設定などの 57 | # フォルダ表示設定に関するメタデータを記録するための隠しファイル 58 | # プロジェクトには不要なので無視 59 | .DS_Store 60 | -------------------------------------------------------------------------------- /Sample/View/TopView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TopView.swift 3 | // Sample 4 | // 5 | // Created by 田中厳貴 on 2021/01/13. 6 | // 7 | 8 | import UIKit 9 | import TopContentPager 10 | 11 | class TopView: TopContentView { 12 | 13 | @IBOutlet weak var profileImageView: UIImageView! { 14 | didSet { 15 | profileImageView.layer.cornerRadius = profileImageView.frame.height / 2 16 | profileImageView.clipsToBounds = true 17 | } 18 | } 19 | 20 | @IBOutlet weak var userNameLabel: UILabel! 21 | @IBOutlet weak var descriptionLabel: UILabel! { 22 | didSet { 23 | descriptionLabel.numberOfLines = 0 24 | descriptionLabel.lineBreakMode = .byClipping 25 | } 26 | } 27 | 28 | override func awakeFromNib() { 29 | super.awakeFromNib() 30 | updateTab(options: .init(indicatorHeight: 1)) 31 | prepare() 32 | } 33 | 34 | func prepare() { 35 | profileImageView.image = UIImage(named: "noman") 36 | userNameLabel.text = "Elyane Bent Caillot" 37 | descriptionLabel.text = "Universed inside you.\nFounder & Designer & Creative Director.\nFrom France." 38 | updateLayout() 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Sample/ViewController/CustomTopContentPagerViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CustomTopContentPagerViewController.swift 3 | // Sample 4 | // 5 | // Created by 田中厳貴 on 2021/01/15. 6 | // 7 | 8 | import UIKit 9 | import TopContentPager 10 | 11 | final class CustomTopContentPagerViewController: TopContentPagerViewController { 12 | private let topView = TopView.instantiate() 13 | private let myPostVC = MyPostViewController.create() 14 | private let timelineVC = TimelineViewController.create() 15 | private let notificationVC = NotificationViewController.create() 16 | 17 | override func setupWillLoadDataSource() { 18 | super.setupWillLoadDataSource() 19 | dataSource = self 20 | } 21 | 22 | override func viewDidLoad() { 23 | super.viewDidLoad() 24 | } 25 | } 26 | 27 | extension CustomTopContentPagerViewController: TopContentPagerDataSource { 28 | func topContentPagerViewControllerTopContentView(_ viewController: TopContentPagerViewController) -> TopContentView { 29 | topView 30 | } 31 | 32 | func topContentPagerViewControllerViewControllers(_ viewController: TopContentPagerViewController) -> [ContentTableBody] { 33 | [myPostVC, timelineVC, notificationVC] 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Sample/Entity/MockData.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MockData.swift 3 | // Sample 4 | // 5 | // Created by 田中厳貴 on 2021/01/27. 6 | // 7 | import UIKit 8 | 9 | enum MockData { 10 | static let postList: [UIImage] = [ 11 | UIImage(named: "umi")!, 12 | UIImage(named: "yama1")!, 13 | UIImage(named: "yuhi")!, 14 | UIImage(named: "yama2")!, 15 | UIImage(named: "umi")!, 16 | UIImage(named: "yama1")!, 17 | UIImage(named: "yuhi")!, 18 | UIImage(named: "yama2")!, 19 | UIImage(named: "umi")!, 20 | UIImage(named: "yama1")!, 21 | UIImage(named: "yuhi")!, 22 | UIImage(named: "yama2")!, 23 | UIImage(named: "umi")!, 24 | UIImage(named: "yama1")!, 25 | UIImage(named: "yuhi")!, 26 | UIImage(named: "yama2")!, 27 | UIImage(named: "umi")!, 28 | UIImage(named: "yama1")!, 29 | UIImage(named: "yuhi")!, 30 | UIImage(named: "yama2")!, 31 | UIImage(named: "umi")!, 32 | UIImage(named: "yama1")!, 33 | UIImage(named: "yuhi")!, 34 | UIImage(named: "yama2")!, 35 | ] 36 | 37 | static let notiList: [String] = [ 38 | "があなたをフォローしました。", 39 | "があなたの投稿にいいね!しました。", 40 | "からメッセージが届いています。\nすぐに返信しましょう!", 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /TopContentPager/Utilitie/UITableView+Register.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UITableView+Register.swift 3 | // TopContentPager 4 | // 5 | // Created by 田中厳貴 on 2021/01/13. 6 | // 7 | 8 | import UIKit 9 | 10 | extension UITableView { 11 | 12 | func registerNib(classType type: T.Type) { 13 | let nib = UINib(nibName: type.className, bundle: Bundle(for: type)) 14 | register(nib, forCellReuseIdentifier: type.className) 15 | } 16 | 17 | func registerClass(classType type: T.Type) { 18 | register(T.self, forCellReuseIdentifier: type.className) 19 | } 20 | 21 | func registerNib(classType type: T.Type) { 22 | let nib = UINib(nibName: type.className, bundle: Bundle(for: type)) 23 | register(nib, forHeaderFooterViewReuseIdentifier: type.className) 24 | } 25 | 26 | func registerClass(classType type: T.Type) { 27 | register(T.self, forHeaderFooterViewReuseIdentifier: type.className) 28 | } 29 | 30 | func dequeueReusableCell(classType type: T.Type, for indexPath: IndexPath) -> T { 31 | dequeueReusableCell(withIdentifier: type.className, for: indexPath) as! T 32 | } 33 | 34 | func dequeueReusableHeaderFooterView(classType type: T.Type) -> T { 35 | dequeueReusableHeaderFooterView(withIdentifier: type.className) as! T 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Sample/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Sample 4 | // 5 | // Created by 田中厳貴 on 2021/01/08. 6 | // 7 | 8 | import UIKit 9 | 10 | @main 11 | class AppDelegate: UIResponder, UIApplicationDelegate { 12 | 13 | 14 | 15 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 16 | // Override point for customization after application launch. 17 | return true 18 | } 19 | 20 | // MARK: UISceneSession Lifecycle 21 | 22 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { 23 | // Called when a new scene session is being created. 24 | // Use this method to select a configuration to create the new scene with. 25 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) 26 | } 27 | 28 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { 29 | // Called when the user discards a scene session. 30 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. 31 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return. 32 | } 33 | 34 | 35 | } 36 | 37 | -------------------------------------------------------------------------------- /SampleUITests/SampleUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SampleUITests.swift 3 | // SampleUITests 4 | // 5 | // Created by 田中厳貴 on 2021/01/08. 6 | // 7 | 8 | import XCTest 9 | 10 | class SampleUITests: XCTestCase { 11 | 12 | override func setUpWithError() throws { 13 | // Put setup code here. This method is called before the invocation of each test method in the class. 14 | 15 | // In UI tests it is usually best to stop immediately when a failure occurs. 16 | continueAfterFailure = false 17 | 18 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 19 | } 20 | 21 | override func tearDownWithError() throws { 22 | // Put teardown code here. This method is called after the invocation of each test method in the class. 23 | } 24 | 25 | func testExample() throws { 26 | // UI tests must launch the application that they test. 27 | let app = XCUIApplication() 28 | app.launch() 29 | 30 | // Use recording to get started writing UI tests. 31 | // Use XCTAssert and related functions to verify your tests produce the correct results. 32 | } 33 | 34 | func testLaunchPerformance() throws { 35 | if #available(macOS 10.15, iOS 13.0, tvOS 13.0, *) { 36 | // This measures how long it takes to launch your application. 37 | measure(metrics: [XCTApplicationLaunchMetric()]) { 38 | XCUIApplication().launch() 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Sample/ViewController/TimelineViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TimelineViewController.swift 3 | // Sample 4 | // 5 | // Created by 田中厳貴 on 2021/01/13. 6 | // 7 | 8 | import UIKit 9 | import TopContentPager 10 | 11 | final class TimelineViewController: UIViewController, ContentTableBody { 12 | var pagerItem: PagerItem = .image(.init(image: UIImage(named: "timeline"))) 13 | 14 | var scrollView: UIScrollView! 15 | @IBOutlet weak var tableView: UITableView! { 16 | didSet { 17 | scrollView = tableView 18 | } 19 | } 20 | 21 | static func create() -> Self { 22 | Storyboard.instantiate(self) 23 | } 24 | 25 | override func viewDidLoad() { 26 | super.viewDidLoad() 27 | tableView.dataSource = self 28 | tableView.separatorStyle = .none 29 | tableView.register(UINib(nibName: "TimelineTableCell", bundle: nil), forCellReuseIdentifier: "TimelineTableCell") 30 | } 31 | 32 | override func viewDidAppear(_ animated: Bool) { 33 | super.viewDidAppear(animated) 34 | tableView.reloadData() 35 | } 36 | } 37 | 38 | extension TimelineViewController: UITableViewDataSource { 39 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 40 | MockData.postList.count 41 | } 42 | 43 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 44 | let cell = tableView.dequeueReusableCell(withIdentifier: "TimelineTableCell", for: indexPath) as! TimelineTableCell 45 | cell.prepare(image: MockData.postList[indexPath.row]) 46 | return cell 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Sample/ViewController/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Sample/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Sample/ViewController/NotificationViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NotificationViewController.swift 3 | // Sample 4 | // 5 | // Created by 田中厳貴 on 2021/01/13. 6 | // 7 | 8 | import UIKit 9 | import TopContentPager 10 | 11 | final class NotificationViewController: UIViewController, ContentTableBody { 12 | var pagerItem: PagerItem = .textAndImage(text: .init(title: "お知らせ"), image: .init(image: UIImage(named: "notification"))) 13 | 14 | 15 | private var mockData: [String] = [] 16 | var scrollView: UIScrollView! 17 | @IBOutlet weak var tableView: UITableView! { 18 | didSet { 19 | scrollView = tableView 20 | } 21 | } 22 | 23 | static func create() -> Self { 24 | Storyboard.instantiate(self) 25 | } 26 | 27 | override func viewDidLoad() { 28 | super.viewDidLoad() 29 | tableView.dataSource = self 30 | tableView.separatorStyle = .none 31 | tableView.register(UINib(nibName: "NotificationTableCell", bundle: nil), forCellReuseIdentifier: "NotificationTableCell") 32 | 33 | for _ in 0...20 { 34 | let user = "user\(Int.random(in: 100000 ... 999999))" 35 | let noti = MockData.notiList.randomElement() ?? "" 36 | mockData.append("\(user)\(noti)") 37 | } 38 | } 39 | 40 | override func viewDidAppear(_ animated: Bool) { 41 | super.viewDidAppear(animated) 42 | tableView.reloadData() 43 | } 44 | } 45 | 46 | extension NotificationViewController: UITableViewDataSource { 47 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 48 | mockData.count 49 | } 50 | 51 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 52 | let cell = tableView.dequeueReusableCell(withIdentifier: "NotificationTableCell", for: indexPath) as! NotificationTableCell 53 | cell.prepare(text: mockData[indexPath.row]) 54 | return cell 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Sample/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | } 93 | ], 94 | "info" : { 95 | "author" : "xcode", 96 | "version" : 1 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /Sample/Utility/NSObject+NibLoading.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+NibLoading.swift 3 | // Sample 4 | // 5 | // Created by 田中厳貴 on 2021/01/13. 6 | // 7 | 8 | import UIKit 9 | 10 | extension NSObject { 11 | 12 | /// nibファイルから自身のインスタンスを取得。 13 | /// 引数に誤りがある(例:存在しないファイル名を指定する)と実行時エラーになるので注意。 14 | /// 15 | /// - Parameters: 16 | /// - nameOrNil: nilの場合は自身のクラス名(例えばUIViewなら"UIView"に相当)。非nilの場合は`UINib.init(nibName:,bundle:)`のと同じ。 17 | /// - bundleOrNil: `UINib.init(nibName:,bundle:)`のと同じ。 18 | /// - ownerOrNil: `UINib.instantiate(withOwner:,options:)`のと同じ。 19 | /// - optionsOrNil: `UINib.instantiate(withOwner:,options:)`のと同じ。 20 | /// - Returns: nibファイルから最初に見つかった自身のインスタンス。 21 | /// 22 | /// 例: 23 | /// ``` 24 | /// // MyView.nibからロード。 25 | /// let myView: MyView = MyView.instantiate() 26 | /// ``` 27 | /// - SeeAlso: UINib 28 | class func instantiate(nibName nameOrNil: String? = nil, bundle bundleOrNil: Bundle? = nil, withOwner ownerOrNil: Any? = nil, options optionsOrNil: [UINib.OptionsKey: Any]? = nil) -> Self { 29 | let nibName = (nameOrNil ?? "\(self)") 30 | return UINib.instantiate(type: self, nibName: nibName, bundle: bundleOrNil ?? Bundle(for: self), withOwner: ownerOrNil, options: optionsOrNil)! 31 | } 32 | } 33 | 34 | extension UINib { 35 | 36 | /// `instantiate()`返り値から指定Typeのインスタンスのみを返す。 37 | /// 38 | /// - Parameters: 39 | /// - type: 取得したいType。 40 | /// - name: `UINib.init(nibName:,bundle:)`のと同じ。 41 | /// - bundleOrNil: `UINib.init(nibName:,bundle:)`のと同じ。 42 | /// - ownerOrNil: `UINib.instantiate(withOwner:,options:)`のと同じ。 43 | /// - optionsOrNil: `UINib.instantiate(withOwner:,options:)`のと同じ。 44 | /// - Returns: 最初に見つかったインスタンス。無かった場合はnil。 45 | /// - SeeAlso: UINib 46 | class func instantiate(type: T.Type, nibName name: String, bundle bundleOrNil: Bundle?, withOwner ownerOrNil: Any?, options optionsOrNil: [UINib.OptionsKey: Any]? = nil) -> T? { 47 | 48 | for any in self.init(nibName: name, bundle: bundleOrNil).instantiate(withOwner: ownerOrNil, options: optionsOrNil) { 49 | if let obj = any as? T { 50 | return obj 51 | } 52 | } 53 | return nil 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Sample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UIApplicationSceneManifest 24 | 25 | UIApplicationSupportsMultipleScenes 26 | 27 | UISceneConfigurations 28 | 29 | UIWindowSceneSessionRoleApplication 30 | 31 | 32 | UISceneConfigurationName 33 | Default Configuration 34 | UISceneDelegateClassName 35 | $(PRODUCT_MODULE_NAME).SceneDelegate 36 | UISceneStoryboardFile 37 | Main 38 | 39 | 40 | 41 | 42 | UIApplicationSupportsIndirectInputEvents 43 | 44 | UILaunchStoryboardName 45 | LaunchScreen 46 | UIMainStoryboardFile 47 | Main 48 | UIRequiredDeviceCapabilities 49 | 50 | armv7 51 | 52 | UISupportedInterfaceOrientations 53 | 54 | UIInterfaceOrientationPortrait 55 | UIInterfaceOrientationLandscapeLeft 56 | UIInterfaceOrientationLandscapeRight 57 | 58 | UISupportedInterfaceOrientations~ipad 59 | 60 | UIInterfaceOrientationPortrait 61 | UIInterfaceOrientationPortraitUpsideDown 62 | UIInterfaceOrientationLandscapeLeft 63 | UIInterfaceOrientationLandscapeRight 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /Sample/ViewController/MyPostViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MyPostViewController.swift 3 | // Sample 4 | // 5 | // Created by 田中厳貴 on 2021/01/26. 6 | // 7 | 8 | import UIKit 9 | import TopContentPager 10 | 11 | final class MyPostViewController: UIViewController, ContentTableBody { 12 | enum Const { 13 | static let margin: CGFloat = 2 14 | static let itemSpace: CGFloat = 1 15 | } 16 | 17 | var pagerItem: PagerItem = .text(.init(title: "投稿")) 18 | var scrollView: UIScrollView! 19 | @IBOutlet weak var collectionView: UICollectionView! { 20 | didSet { 21 | scrollView = collectionView 22 | } 23 | } 24 | 25 | static func create() -> Self { 26 | Storyboard.instantiate(self) 27 | } 28 | 29 | override func viewDidLoad() { 30 | super.viewDidLoad() 31 | collectionView.dataSource = self 32 | setup() 33 | } 34 | 35 | private func setup() { 36 | collectionView.register(UINib(nibName: "MyPostCollectionCell", bundle: nil), forCellWithReuseIdentifier: "MyPostCollectionCell") 37 | let layout = UICollectionViewFlowLayout() 38 | let length = self.view.frame.width / 3 - ((Const.margin * 2 + Const.itemSpace * 2) / 2) 39 | layout.itemSize = CGSize(width: length, height: length) 40 | layout.sectionInset = UIEdgeInsets(top: Const.margin, left: Const.margin, bottom: Const.margin, right: Const.margin) 41 | layout.minimumInteritemSpacing = Const.itemSpace 42 | layout.minimumLineSpacing = Const.itemSpace * 2 43 | collectionView.collectionViewLayout = layout 44 | } 45 | 46 | override func viewDidAppear(_ animated: Bool) { 47 | super.viewDidAppear(animated) 48 | collectionView.reloadData() 49 | } 50 | } 51 | 52 | extension MyPostViewController: UICollectionViewDataSource { 53 | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 54 | MockData.postList.count 55 | } 56 | 57 | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 58 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MyPostCollectionCell", for: indexPath) as! MyPostCollectionCell 59 | cell.prepare(image: MockData.postList[indexPath.row]) 60 | return cell 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Sample/SceneDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.swift 3 | // Sample 4 | // 5 | // Created by 田中厳貴 on 2021/01/08. 6 | // 7 | 8 | import UIKit 9 | 10 | class SceneDelegate: UIResponder, UIWindowSceneDelegate { 11 | 12 | var window: UIWindow? 13 | 14 | 15 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { 16 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. 17 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. 18 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). 19 | guard let _ = (scene as? UIWindowScene) else { return } 20 | } 21 | 22 | func sceneDidDisconnect(_ scene: UIScene) { 23 | // Called as the scene is being released by the system. 24 | // This occurs shortly after the scene enters the background, or when its session is discarded. 25 | // Release any resources associated with this scene that can be re-created the next time the scene connects. 26 | // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead). 27 | } 28 | 29 | func sceneDidBecomeActive(_ scene: UIScene) { 30 | // Called when the scene has moved from an inactive state to an active state. 31 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. 32 | } 33 | 34 | func sceneWillResignActive(_ scene: UIScene) { 35 | // Called when the scene will move from an active state to an inactive state. 36 | // This may occur due to temporary interruptions (ex. an incoming phone call). 37 | } 38 | 39 | func sceneWillEnterForeground(_ scene: UIScene) { 40 | // Called as the scene transitions from the background to the foreground. 41 | // Use this method to undo the changes made on entering the background. 42 | } 43 | 44 | func sceneDidEnterBackground(_ scene: UIScene) { 45 | // Called as the scene transitions from the foreground to the background. 46 | // Use this method to save data, release shared resources, and store enough scene-specific state information 47 | // to restore the scene back to its current state. 48 | } 49 | 50 | 51 | } 52 | 53 | -------------------------------------------------------------------------------- /TopContentPager/TopContentView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TopContentView.swift 3 | // TopContentPager 4 | // 5 | // Created by 田中厳貴 on 2021/01/26. 6 | // 7 | 8 | import UIKit 9 | 10 | protocol TopContentViewDelegate: AnyObject { 11 | func needsReload() 12 | } 13 | 14 | public protocol TopContentViewDataSource: AnyObject { 15 | func topContentViewHeight(_ view: TopContentView) -> CGFloat? 16 | } 17 | 18 | open class TopContentView: UIView { 19 | 20 | open var isHideTabView: Bool { 21 | false 22 | } 23 | 24 | public weak var dataSource: TopContentViewDataSource? 25 | public var tabView: PagerItemsView? 26 | 27 | weak var delegate: TopContentViewDelegate? 28 | var tabViewHeight: CGFloat { 29 | tabView?.options.itemHeight ?? 0 30 | } 31 | var estimateHeight: CGFloat { 32 | if let height = dataSource?.topContentViewHeight(self) { 33 | return height 34 | } else { 35 | return self.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize, withHorizontalFittingPriority: .fittingSizeLevel, verticalFittingPriority: .defaultLow).height 36 | } 37 | } 38 | 39 | private var tabViewHeightConstraint: NSLayoutConstraint? 40 | 41 | open override func awakeFromNib() { 42 | super.awakeFromNib() 43 | if !isHideTabView { 44 | tabView = .init() 45 | setupTab() 46 | } 47 | } 48 | 49 | public func updateTab(options: UpdatePagerOptions) { 50 | tabView?.update(pagerOptions: options) 51 | tabViewHeightConstraint?.constant = tabViewHeight 52 | updateLayout() 53 | } 54 | 55 | public func updateLayout() { 56 | UIView.animate(withDuration: 0) { 57 | self.removeFromSuperview() 58 | self.layoutIfNeeded() 59 | self.delegate?.needsReload() 60 | } 61 | } 62 | 63 | private func setupTab() { 64 | guard let tabView = tabView else { return } 65 | tabView.update(pagerOptions: .init()) 66 | tabView.autoresizingMask = .flexibleWidth 67 | tabView.translatesAutoresizingMaskIntoConstraints = false 68 | self.addSubview(tabView) 69 | tabViewHeightConstraint = tabView.heightAnchor.constraint(equalToConstant: tabViewHeight) 70 | NSLayoutConstraint.activate([ 71 | tabView.leadingAnchor.constraint(equalTo: leadingAnchor), 72 | tabView.trailingAnchor.constraint(equalTo: trailingAnchor), 73 | tabView.bottomAnchor.constraint(equalTo: bottomAnchor), 74 | tabViewHeightConstraint! 75 | ]) 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Sample/Cell/MyPostCollectionCell.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /TopContentPager/View/PagerOptions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PagerOptions.swift 3 | // TopContentPager 4 | // 5 | // Created by 田中厳貴 on 2021/01/08. 6 | // 7 | 8 | import UIKit 9 | 10 | final public class PagerOptions { 11 | public var itemHeight: CGFloat = 44 12 | public var indicatorHeight: CGFloat = 2 13 | public var indicatorColor: UIColor = UIColor.black 14 | public var activeBackgroundColor: UIColor = UIColor.clear 15 | public var backgroundColor: UIColor = UIColor.white 16 | public var lineColor: UIColor = UIColor.white 17 | public var deactiveLabelColor: UIColor = UIColor.gray 18 | public var activeLabelColor: UIColor = UIColor.black 19 | public var indicatorSideSpace: CGFloat = 0 20 | 21 | init() { 22 | 23 | } 24 | 25 | init(itemHeight: CGFloat = 44, 26 | indicatorHeight: CGFloat = 2, 27 | indicatorColor: UIColor = UIColor.black, 28 | activeBackgroundColor: UIColor = UIColor.clear, 29 | backgroundColor: UIColor = UIColor.white, 30 | lineColor: UIColor = UIColor.white, 31 | deactiveLabelColor: UIColor = UIColor.gray, 32 | activeLabelColor: UIColor = UIColor.black, 33 | indicatorSideSpace: CGFloat = 0) { 34 | self.itemHeight = itemHeight 35 | self.indicatorHeight = indicatorHeight 36 | self.indicatorColor = indicatorColor 37 | self.activeBackgroundColor = activeBackgroundColor 38 | self.backgroundColor = backgroundColor 39 | self.lineColor = lineColor 40 | self.deactiveLabelColor = deactiveLabelColor 41 | self.activeLabelColor = activeLabelColor 42 | self.indicatorSideSpace = indicatorSideSpace 43 | } 44 | } 45 | 46 | final public class UpdatePagerOptions { 47 | public var itemHeight: CGFloat? 48 | public var indicatorHeight: CGFloat? 49 | public var indicatorColor: UIColor? 50 | public var activeBackgroundColor: UIColor? 51 | public var backgroundColor: UIColor? 52 | public var lineColor: UIColor? 53 | public var deactiveLabelColor: UIColor? 54 | public var activeLabelColor: UIColor? 55 | public var indicatorSideSpace: CGFloat? 56 | 57 | public init() { } 58 | 59 | public init(itemHeight: CGFloat? = nil, 60 | indicatorHeight: CGFloat? = nil, 61 | indicatorColor: UIColor? = nil, 62 | activeBackgroundColor: UIColor? = nil, 63 | backgroundColor: UIColor? = nil, 64 | lineColor: UIColor? = nil, 65 | deactiveLabelColor: UIColor? = nil, 66 | activeLabelColor: UIColor? = nil, 67 | indicatorSideSpace: CGFloat? = nil) { 68 | self.itemHeight = itemHeight 69 | self.indicatorHeight = indicatorHeight 70 | self.indicatorColor = indicatorColor 71 | self.activeBackgroundColor = activeBackgroundColor 72 | self.backgroundColor = backgroundColor 73 | self.lineColor = lineColor 74 | self.deactiveLabelColor = deactiveLabelColor 75 | self.activeLabelColor = activeLabelColor 76 | self.indicatorSideSpace = indicatorSideSpace 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Sample/ViewController/TimelineViewController.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /Sample/ViewController/NotificationViewController.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /Sample/ViewController/MyPostViewController.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /Sample/Cell/NotificationTableCell.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /TopContentPager/View/DefaultPagerItemView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DefaultPagerItemView.swift 3 | // TopContentPager 4 | // 5 | // Created by 田中厳貴 on 2021/02/10. 6 | // 7 | 8 | import UIKit 9 | 10 | final public class DefaultPagerItemView: UIView, PagerItemView { 11 | private var backView = UIView() 12 | private var titleLabel = UILabel() 13 | private var badgeView = UIView() 14 | private var stackView = UIStackView() 15 | private var imageView = UIImageView() 16 | 17 | public var activeColor: UIColor = UIColor.black 18 | public var deactiveColor: UIColor = UIColor.gray 19 | public var activeBackgroundColor: UIColor = UIColor.clear 20 | 21 | private var imageViewHeight: NSLayoutConstraint? 22 | private var imageViewWidth: NSLayoutConstraint? 23 | 24 | public override init(frame: CGRect) { 25 | super.init(frame: frame) 26 | setupView() 27 | } 28 | 29 | required init?(coder: NSCoder) { 30 | fatalError("init(coder:) has not been implemented") 31 | } 32 | 33 | private func setupView() { 34 | backView.translatesAutoresizingMaskIntoConstraints = false 35 | addSubview(backView) 36 | NSLayoutConstraint.activate([ 37 | backView.topAnchor.constraint(equalTo: topAnchor), 38 | backView.bottomAnchor.constraint(equalTo: bottomAnchor), 39 | backView.trailingAnchor.constraint(equalTo: trailingAnchor), 40 | backView.leadingAnchor.constraint(equalTo: leadingAnchor) 41 | ]) 42 | 43 | stackView.translatesAutoresizingMaskIntoConstraints = false 44 | stackView.axis = .horizontal 45 | stackView.alignment = .center 46 | stackView.distribution = .fill 47 | stackView.spacing = 4 48 | addSubview(stackView) 49 | let stackViewTrailing = stackView.trailingAnchor.constraint(equalTo: trailingAnchor) 50 | let stackViewLeading = stackView.leadingAnchor.constraint(equalTo: leadingAnchor) 51 | 52 | stackViewTrailing.priority = .defaultLow 53 | stackViewLeading.priority = .defaultLow 54 | 55 | NSLayoutConstraint.activate([ 56 | stackView.topAnchor.constraint(equalTo: topAnchor), 57 | stackView.bottomAnchor.constraint(equalTo: bottomAnchor), 58 | stackView.trailingAnchor.constraint(lessThanOrEqualTo: trailingAnchor), 59 | stackView.leadingAnchor.constraint(greaterThanOrEqualTo: leadingAnchor), 60 | stackView.centerXAnchor.constraint(equalTo: centerXAnchor), 61 | stackViewTrailing, 62 | stackViewLeading 63 | ]) 64 | 65 | titleLabel.translatesAutoresizingMaskIntoConstraints = false 66 | titleLabel.setContentCompressionResistancePriority(.required, for: .horizontal) 67 | imageView.translatesAutoresizingMaskIntoConstraints = false 68 | imageViewHeight = imageView.heightAnchor.constraint(equalToConstant: 24) 69 | imageViewWidth = imageView.widthAnchor.constraint(equalToConstant: 24) 70 | 71 | NSLayoutConstraint.activate([ 72 | imageViewHeight!, 73 | imageViewWidth! 74 | ]) 75 | stackView.addArrangedSubview(imageView) 76 | stackView.addArrangedSubview(titleLabel) 77 | 78 | badgeView.translatesAutoresizingMaskIntoConstraints = false 79 | addSubview(badgeView) 80 | NSLayoutConstraint.activate([ 81 | badgeView.widthAnchor.constraint(equalToConstant: 10), 82 | badgeView.heightAnchor.constraint(equalToConstant: 10), 83 | badgeView.leadingAnchor.constraint(equalTo: titleLabel.trailingAnchor, constant: 3), 84 | badgeView.bottomAnchor.constraint(equalTo: titleLabel.topAnchor, constant: 3) 85 | ]) 86 | } 87 | 88 | 89 | private lazy var setupLayout: Void = { 90 | titleLabel.font = UIFont.boldSystemFont(ofSize: 13) 91 | titleLabel.textColor = UIColor.black 92 | badgeView.isHidden = true 93 | badgeView.layer.cornerRadius = badgeView.frame.height / 2 94 | badgeView.clipsToBounds = true 95 | badgeView.backgroundColor = UIColor.red 96 | }() 97 | 98 | public override func layoutSubviews() { 99 | super.layoutSubviews() 100 | _ = setupLayout 101 | } 102 | 103 | public var isSelected: Bool = false { 104 | didSet { 105 | if isSelected { 106 | titleLabel.textColor = activeColor 107 | imageView.tintColor = activeColor 108 | backView.backgroundColor = activeBackgroundColor 109 | } else { 110 | titleLabel.textColor = deactiveColor 111 | imageView.tintColor = deactiveColor 112 | backView.backgroundColor = .clear 113 | } 114 | } 115 | } 116 | 117 | public func configure(with res: PagerItem) { 118 | switch res { 119 | case .text(let textItem): 120 | self.titleLabel.isHidden = false 121 | self.imageView.isHidden = true 122 | self.titleLabel.text = textItem.title 123 | self.titleLabel.font = textItem.font 124 | case .image(let imageItem): 125 | self.titleLabel.isHidden = true 126 | self.imageView.isHidden = false 127 | self.imageView.image = imageItem.image?.withRenderingMode(.alwaysTemplate) 128 | self.imageViewHeight?.constant = imageItem.size.height 129 | self.imageViewWidth?.constant = imageItem.size.width 130 | self.imageView.layer.cornerRadius = imageItem.cornerRadius 131 | self.imageView.clipsToBounds = true 132 | case .textAndImage(let textItem, let imageItem): 133 | self.titleLabel.isHidden = false 134 | self.imageView.isHidden = false 135 | self.titleLabel.text = textItem.title 136 | self.titleLabel.font = textItem.font 137 | self.imageView.image = imageItem.image?.withRenderingMode(.alwaysTemplate) 138 | self.imageViewHeight?.constant = imageItem.size.height 139 | self.imageViewWidth?.constant = imageItem.size.width 140 | self.imageView.layer.cornerRadius = imageItem.cornerRadius 141 | self.imageView.clipsToBounds = true 142 | case .custom(_): 143 | break 144 | } 145 | } 146 | 147 | public func badge(isHidden: Bool) { 148 | self.badgeView.isHidden = isHidden 149 | } 150 | } 151 | 152 | -------------------------------------------------------------------------------- /TopContentPager/ContentTableViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WishDetailInformationViewController.swift 3 | // TopContentPager 4 | // 5 | // Created by 田中厳貴 on 2021/01/08. 6 | // 7 | 8 | import UIKit 9 | 10 | public protocol ContentTableViewDelegate: class { 11 | func didEndDragging(viewController: ContentTableViewController, willDecelerate decelerate: Bool) 12 | func didEndDecelerationg(viewController: ContentTableViewController) 13 | func didScroll(viewController: ContentTableViewController) 14 | } 15 | 16 | public final class ContentTableViewController: UIViewController { 17 | 18 | public var tableView = ContentInnerTableView() 19 | 20 | private var observation: NSKeyValueObservation? 21 | private var refreshControl = UIRefreshControl() 22 | weak var delegate: ContentTableViewDelegate? 23 | public private(set) var viewController: ContentTableBody! 24 | lazy var setupContentHeight: Void = { 25 | observation = nil 26 | observation = viewController.scrollView.observe(\.contentSize, options: [.new, .old, .prior]) { [weak self] ( _, contentSize) in 27 | if let newValue = contentSize.newValue { 28 | self?.bodyContentHeight = newValue.height 29 | } 30 | self?.tableView.reloadData() 31 | } 32 | }() 33 | public private(set) var topView: TopContentView! 34 | private var bodyContentHeight: CGFloat = 0 35 | 36 | public struct Input { 37 | let topView: TopContentView 38 | let viewController: ContentTableBody 39 | } 40 | 41 | public static func create(with input: Input) -> Self { 42 | let vc = Self.init() 43 | vc.viewController = input.viewController 44 | vc.topView = input.topView 45 | return vc 46 | } 47 | 48 | public override func viewDidLoad() { 49 | super.viewDidLoad() 50 | setupView() 51 | setup() 52 | } 53 | 54 | private func setupView() { 55 | tableView.translatesAutoresizingMaskIntoConstraints = false 56 | view.addSubview(tableView) 57 | NSLayoutConstraint.activate([ 58 | tableView.topAnchor.constraint(equalTo: view.topAnchor), 59 | tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor), 60 | tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor), 61 | tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor) 62 | ]) 63 | } 64 | 65 | private func setup() { 66 | tableView.registerClass(classType: ContentTopCell.self) 67 | tableView.registerClass(classType: ContentBodyCell.self) 68 | tableView.separatorColor = .clear 69 | tableView.dataSource = self 70 | tableView.delegate = self 71 | tableView.contentInsetAdjustmentBehavior = .never 72 | tableView.refreshControl = viewController.refresh(sender: refreshControl, contentViewController: self) ? refreshControl : nil 73 | refreshControl.addTarget(self, action: #selector(refresh(sender:)), for: .valueChanged) 74 | 75 | addChild(viewController) 76 | viewController.didMove(toParent: self) 77 | } 78 | 79 | @objc func refresh(sender: UIRefreshControl) { 80 | viewController.refresh(sender: sender, contentViewController: self) 81 | } 82 | } 83 | 84 | extension ContentTableViewController: UIScrollViewDelegate{ 85 | public func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { 86 | delegate?.didEndDragging(viewController: self, willDecelerate: decelerate) 87 | } 88 | 89 | public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { 90 | delegate?.didEndDecelerationg(viewController: self) 91 | } 92 | 93 | public func scrollViewDidScroll(_ scrollView: UIScrollView) { 94 | delegate?.didScroll(viewController: self) 95 | } 96 | } 97 | 98 | extension ContentTableViewController: UITableViewDataSource { 99 | public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 100 | 2 101 | } 102 | 103 | public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 104 | if indexPath.row == 0 { 105 | return tableView.dequeueReusableCell(classType: ContentTopCell.self, for: indexPath) 106 | } else { 107 | let cell = tableView.dequeueReusableCell(classType: ContentBodyCell.self, for: indexPath) 108 | if let bodyVC = viewController { 109 | cell.prepare(viewController: bodyVC) 110 | } 111 | return cell 112 | } 113 | } 114 | 115 | 116 | } 117 | 118 | extension ContentTableViewController: UITableViewDelegate { 119 | public func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { 120 | if cell is ContentBodyCell { 121 | _ = setupContentHeight 122 | } 123 | } 124 | 125 | public func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { 126 | if indexPath.row == 0 { 127 | return topView.estimateHeight 128 | } else { 129 | return bodyContentHeight 130 | } 131 | } 132 | public func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat { 133 | if indexPath.row == 0 { 134 | return topView.estimateHeight 135 | } else { 136 | return bodyContentHeight 137 | } 138 | } 139 | } 140 | 141 | public class ContentInnerTableView: UITableView { 142 | public override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { 143 | func findScrollView(targetView: UIView?) -> UIView? { 144 | guard let targetView = targetView else { return nil } 145 | if let _ = targetView as? UIScrollView { 146 | return targetView 147 | } else { 148 | return findScrollView(targetView: targetView.superview) 149 | } 150 | } 151 | if let scrollView = findScrollView(targetView: superview) as? UIScrollView { 152 | if let index = indexPathForRow(at: point), let _ = cellForRow(at: index) as? ContentTopCell { 153 | scrollView.isScrollEnabled = false 154 | } else { 155 | scrollView.isScrollEnabled = true 156 | } 157 | } 158 | return super.hitTest(point, with: event) 159 | } 160 | } 161 | 162 | 163 | -------------------------------------------------------------------------------- /Sample/Cell/TimelineTableCell.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 |

5 | 6 | License 7 | 8 | 9 | 10 | Platform 11 | 12 | 13 | 14 | Version 15 | 16 |

17 | 18 | # Overview 19 | 20 | 21 | ## What is `TopContentPager` ? 22 | `TopContentPager` is framework you can make page like Instagram's MyPage simply. 23 | 24 | Specifically, This is UIPageViewController with common scrollable headerView and PageSection fixed at the top in all pages. 25 | 26 | A big feature of TopContentPager is that there is a common header that can be scrolled to the top. 27 | 28 | It is very difficult to implement such an implementation using ordinary UIPageViewController and UITableView. 29 | Because the top header also needs to be scrolled along with the UITableView. 30 | 31 | Also, because the header at the top is common, if you scroll to the top on any page, you need to scroll to the top on all other pages. 32 | There are many other complicated parts regarding scrolling, but I will omit them here. 33 | See the `Logic` section for more details. 34 | 35 | `TopContentPager` makes it easy to create such pages without having to worry about these difficult issues. 36 |









37 | 38 | 39 | # Usage 40 | 1. ***Make Base Class*** 41 | 42 | First, you can make base class extended `TopContentPagerViewController` and add `TopContentPagerDataSource` to that. 43 | 44 | Then, you must add `setupWillLoadDataSource()` function and bind dataSource in the function. 45 | ```swift 46 | class CustomTopContentPagerViewController: TopContentPagerViewController { 47 | 48 | override func setupWillLoadDataSource() { 49 | super.setupWillLoadDataSource() 50 | dataSource = self 51 | } 52 | 53 | override func viewDidLoad() { 54 | super.viewDidLoad() 55 | } 56 | } 57 | 58 | extension CustomTopContentPagerViewController: TopContentPagerDataSource { 59 | func topContentPagerViewControllerTopContentView(_ viewController: TopContentPagerViewController) -> TopContentView { 60 | // return common HeaderView in all pages. 61 | TopView() 62 | } 63 | 64 | func topContentPagerViewControllerViewControllers(_ viewController: TopContentPagerViewController) -> [ContentTableBody] { 65 | // return ViewControllers for each page. 66 | [Page1ViewController(), Page2ViewController(), Page3ViewController()] 67 | } 68 | } 69 | ``` 70 | 2. ***Make Content Page*** 71 | 72 | Second, please add `TopContentTableBody` to every ViewController to use them as page. 73 | 74 | You can set pagerItem. (`PagerItem` has types `.text`, `.image`, `.textAndImage`, `.custom`) 75 | 76 | So, need to set tableView(or collectionView) to scrollView. 77 | ```swift 78 | class Page1ViewController: UIViewController, ContentTableBody { 79 | 80 | var pagerItem: PagerItem = .text(.init(title: "ページ1")) 81 | var scrollView: UIScrollView! 82 | @IBOutlet weak var tableView: UITableView! { 83 | didSet { 84 | scrollView = tableView 85 | } 86 | } 87 | } 88 | ``` 89 | 3. ***Make TopView*** 90 | 91 | Finally, make TopView extended `TopContentView`. 92 | 93 | ```swift 94 | class TopView: TopContentView { 95 | // write your custom code 96 | } 97 | ``` 98 | 99 | That's all. you can use `TopContentViewController`. 100 | If you want to customize, you read properties section and sample code. 101 | 102 | # Logic 103 | ## View Hierarchy 104 | ``` 105 | TopContentViewController.View 106 | ├ ScrollView 107 | | ├ ScrollContentView 108 | | └ ContentTableViewController.View 109 | | └ ContentInnerTableView 110 | | ├ ContentTopCell 111 | | | └ (TopContentView) *When you scroll vertically. 112 | | └ ContentBodyCell 113 | | └ YourPageViewController.View extended ContentTableBody 114 | └ EscapeView 115 | └ (TopContentView) *When you scroll horizontally. 116 | ``` 117 |

118 | 119 |

120 | 121 | ## Fix Scrollable Header View Logic 122 | See the gif image below for the logic of scrolling. 123 | 124 | Sorry, `UIView.TopContentProtocol` in the image is old. 125 | Now, you can use `TopContentView`. 126 | 127 |

128 | 129 |

130 | 131 | # Sample 132 | you can get Sample Code. 133 | It's in the `Sample` folder in this repository. 134 | 135 | # Properties and functions 136 | ## TopContentPagerViewController 137 | | property | access | description | 138 | |:-:|:-:|:-:| 139 | | selectedIndex | get, set | current page index | 140 | | dataSource | get, set | dataSource for topview and pageViewControllers | 141 | | delegate | get, set | see `TopContentPagerDelegate`. it is able to make TopMargin on TopView. | 142 | | viewControllers | get | innner ViewControllers with TableView | 143 | | selectedViewController | get | current page ViewController | 144 | | tabHeight | get | PagerItemsView's Height | 145 | | headerHeight | get | TopView's Height | 146 | | topMargin | get | TopMarginHeight by delegate | 147 | 148 | ## TopContentView 149 | | property | access | description | 150 | |:-:|:-:|:-:| 151 | | isHideTabView | get, override | flag to hide pagerItemsView | 152 | | tabView | get, set | PagerItemsView | 153 | | dataSource | get, set | see `TopContentViewDataSource`. you can set TopViewHeight if you don't want to use autolayout. | 154 | | tabViewHeight | get | PagerItemsView's Height | 155 | | estimateHeight | get | TopView's Height | 156 | 157 | | function | description | 158 | |:-:|:-:| 159 | | updateTab(options: UpdatePagerOptions) | customize PagerItemsView. see `UpdatePagerOptions` properties in code. | 160 | | updateLayout() | if you update TopView autolayout, you have to call this method. | 161 | 162 | ## PagerItem 163 | | case | description | 164 | |:-:|:-:| 165 | | text | you can set page title text. (text and font) | 166 | | image | you can set page title image. (image, size, and cornerRaius) | 167 | | textAndImage | you can set page title text on the right side of the image | 168 | | custom | you can set custom view for page title. you have to make custom view with `PageItemView` protocol. | 169 | 170 | 171 | 172 | # Installation 173 | ## CocoaPods 174 | TopContentPager is available through [CocoaPods](https://cocoapods.org). To install 175 | it, simply add the following line to your Podfile: 176 | 177 | ```ruby 178 | pod "TopContentPager" 179 | ``` 180 | 181 | # Requirements 182 | 183 | - Swift 5 or greater 184 | - iOS 11.0 or greater 185 | -------------------------------------------------------------------------------- /TopContentPager/View/PagerItemsView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PagerItemsView.swift 3 | // TopContentPager 4 | // 5 | // Created by 田中厳貴 on 2021/01/08. 6 | // 7 | 8 | import UIKit 9 | 10 | public enum PagerItem { 11 | case text(TextItem) 12 | case image(ImageItem) 13 | case textAndImage(text: TextItem, image: ImageItem) 14 | case custom(PagerItemView) 15 | 16 | public struct TextItem { 17 | let title: String 18 | let font: UIFont 19 | 20 | public init(title: String, font: UIFont = UIFont.boldSystemFont(ofSize: 13)) { 21 | self.title = title 22 | self.font = font 23 | } 24 | } 25 | 26 | public struct ImageItem { 27 | let image: UIImage? 28 | let size: CGSize 29 | let cornerRadius: CGFloat 30 | 31 | public init(image: UIImage?, size: CGSize = CGSize(width: 24, height: 24), cornerRadius: CGFloat = 0) { 32 | self.image = image 33 | self.size = size 34 | self.cornerRadius = cornerRadius 35 | } 36 | } 37 | 38 | 39 | } 40 | 41 | 42 | 43 | final public class PagerItemsView: UIView { 44 | 45 | private var indicatorHeight: CGFloat = 2.0 46 | private var indicatorMinWidth: CGFloat = 0 47 | private var itemViewHeight: CGFloat = 20.0 48 | private var lineHeight: CGFloat = 2.0 49 | private var indicatorSideSpace: CGFloat = 0 50 | 51 | public var items: [PagerItem] = [] 52 | public var itemViews: [PagerItemView] = [] 53 | public private(set) var options: PagerOptions = .init() 54 | 55 | private var indicator: UIView! 56 | private var frontView: UIView! 57 | private var lineView: UIView! 58 | private var maskLayer: CALayer! 59 | 60 | private var indicatorWidth: CGFloat { 61 | self.frame.size.width / max(CGFloat(self.items.count), 1) 62 | } 63 | 64 | override init(frame: CGRect) { 65 | super.init(frame: frame) 66 | self.prepare() 67 | } 68 | 69 | required public init?(coder aDecoder: NSCoder) { 70 | fatalError("init(coder:) has not been implemented") 71 | } 72 | 73 | public func update(pagerOptions: UpdatePagerOptions) { 74 | options.itemHeight = pagerOptions.itemHeight ?? options.itemHeight 75 | options.indicatorHeight = pagerOptions.indicatorHeight ?? options.indicatorHeight 76 | options.indicatorColor = pagerOptions.indicatorColor ?? options.indicatorColor 77 | options.activeBackgroundColor = pagerOptions.activeBackgroundColor ?? options.activeBackgroundColor 78 | options.backgroundColor = pagerOptions.backgroundColor ?? options.backgroundColor 79 | options.lineColor = pagerOptions.lineColor ?? options.lineColor 80 | options.deactiveLabelColor = pagerOptions.deactiveLabelColor ?? options.deactiveLabelColor 81 | options.activeLabelColor = pagerOptions.activeLabelColor ?? options.activeLabelColor 82 | options.indicatorSideSpace = pagerOptions.indicatorSideSpace ?? options.indicatorSideSpace 83 | updateView() 84 | } 85 | 86 | func updateView() { 87 | self.frame.size.height = options.itemHeight 88 | self.itemViewHeight = options.itemHeight 89 | self.indicatorHeight = options.indicatorHeight 90 | self.lineHeight = options.indicatorHeight 91 | self.backgroundColor = options.backgroundColor 92 | self.indicatorSideSpace = options.indicatorSideSpace 93 | self.lineView.backgroundColor = options.lineColor 94 | self.indicator.backgroundColor = options.indicatorColor 95 | itemViews.forEach { itemView in 96 | itemView.activeColor = options.activeLabelColor 97 | itemView.deactiveColor = options.deactiveLabelColor 98 | itemView.activeBackgroundColor = options.activeBackgroundColor 99 | } 100 | } 101 | 102 | func addItem(item: PagerItem) { 103 | self.items.append(item) 104 | switch item { 105 | case .text, .image, .textAndImage: 106 | let itemView = DefaultPagerItemView(frame: CGRect(x: 0, y: 0, width: self.indicatorMinWidth, height: self.bounds.height)) 107 | itemView.configure(with: item) 108 | self.itemViews.append(itemView) 109 | self.addSubview(itemView) 110 | case .custom(let itemView): 111 | itemView.frame = CGRect(x: 0, y: 0, width: self.indicatorMinWidth, height: self.bounds.height) 112 | self.itemViews.append(itemView) 113 | self.addSubview(itemView) 114 | } 115 | 116 | self.setNeedsLayout() 117 | updateView() 118 | } 119 | 120 | func adjustIndicator() { 121 | self.indicator.frame.size.width = self.indicatorWidth - (self.indicatorSideSpace * 2) 122 | } 123 | 124 | func adjustSelected(page: Int) { 125 | self.itemViews.forEach { 126 | $0.isSelected = false 127 | } 128 | self.itemViews[safe: page]?.isSelected = true 129 | UIView.animate(withDuration: 0.1, delay: 0, options: .beginFromCurrentState, animations: { 130 | self.indicator.frame.origin.x = (CGFloat(page) * self.indicatorWidth) + self.indicatorSideSpace 131 | }, completion: nil) 132 | } 133 | 134 | public func updatebadge(index: Int, isHidden: Bool) { 135 | self.itemViews[safe: index]?.badge(isHidden: isHidden) 136 | } 137 | 138 | override public func layoutSubviews() { 139 | super.layoutSubviews() 140 | 141 | for (i, view) in self.itemViews.enumerated() { 142 | let itemMaxWidth: CGFloat = self.frame.size.width / max(CGFloat(self.items.count), 1) 143 | view.frame = CGRect(x: itemMaxWidth * CGFloat(i), y: 0, width: self.indicatorWidth, height: itemViewHeight - lineHeight) 144 | } 145 | 146 | self.frontView.frame = self.bounds 147 | 148 | self.indicator.frame = CGRect( 149 | x: 0, y: self.frame.size.height - self.indicatorHeight, 150 | width: self.indicatorWidth, height: self.indicatorHeight 151 | ) 152 | self.lineView.frame = CGRect(x: 0, y: self.frame.size.height - lineHeight, width: self.frame.width, height: lineHeight) 153 | self.maskLayer.frame = CGRect(x: 0, y: self.frame.size.height - lineHeight, width: self.frame.width, height: lineHeight) 154 | self.adjustIndicator() 155 | } 156 | } 157 | 158 | private extension PagerItemsView { 159 | 160 | func prepare() { 161 | self.frontView = UIView(frame: self.bounds) 162 | self.addSubview(self.frontView) 163 | 164 | self.lineView = UIView(frame: CGRect(x: 0, y: self.frame.size.height - lineHeight, width: self.frame.width, height: lineHeight)) 165 | self.addSubview(self.lineView) 166 | 167 | self.indicator = UIView(frame: 168 | CGRect( 169 | x: 0, y: self.frame.size.height - self.indicatorHeight, 170 | width: self.indicatorWidth, height: self.indicatorHeight 171 | ) 172 | ) 173 | self.addSubview(self.indicator) 174 | 175 | self.maskLayer = UIView(frame: CGRect(x: 0, y: self.frame.size.height - lineHeight, width: self.frame.width, height: lineHeight)).layer 176 | self.frontView.layer.mask = self.maskLayer 177 | } 178 | } 179 | 180 | extension Array { 181 | public subscript(safe index: Int) -> Element? { 182 | guard index >= 0, index < endIndex else { return nil } 183 | return self[index] 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /TopContentPager/TopContentPagerViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TopContentPagerViewController.swift 3 | // TopContentPager 4 | // 5 | // Created by 田中厳貴 on 2021/01/08. 6 | // 7 | 8 | import UIKit 9 | 10 | public protocol TopContentPagerDataSource: AnyObject { 11 | func topContentPagerViewControllerTopContentView(_ viewController: TopContentPagerViewController) -> TopContentView 12 | func topContentPagerViewControllerViewControllers(_ viewController: TopContentPagerViewController) -> [ContentTableBody] 13 | } 14 | 15 | @objc public protocol TopContentPagerDelegate: AnyObject { 16 | @objc optional func topContentPagerViewControllerTabTopMargin(_ viewController: TopContentPagerViewController) -> CGFloat 17 | } 18 | 19 | class EscapeView: UIView { } 20 | 21 | open class TopContentPagerViewController: UIViewController, UIGestureRecognizerDelegate { 22 | 23 | public weak var dataSource: TopContentPagerDataSource? 24 | public weak var delegate: TopContentPagerDelegate? 25 | public var selectedViewController: ContentTableViewController { 26 | viewControllers[selectedIndex] 27 | } 28 | public var selectedIndex: Int = 0 { 29 | didSet { 30 | contentOffsetX = scrollView.contentOffset.x 31 | addContentViewToEscapeView() 32 | topView.tabView?.adjustSelected(page: selectedIndex) 33 | UIView.animate(withDuration: 0.25, animations: { 34 | self.scrollView.setContentOffset(CGPoint(x: self.scrollView.bounds.size.width * CGFloat(self.selectedIndex), y: 0), animated: false) 35 | }) { _ in 36 | self.contentOffsetX = self.scrollView.contentOffset.x 37 | if let constant = self.escapeViewTopConstraint?.constant, -(self.topView.frame.height - self.tabHeight) < constant { 38 | self.addContentViewToCell() 39 | } 40 | } 41 | } 42 | } 43 | 44 | public private(set) var viewControllers: [ContentTableViewController] = [] 45 | public private(set) var tabHeight: CGFloat! 46 | public private(set) var headerHeight: CGFloat! 47 | public private(set) var safeAreaBar = UIView() 48 | public var topMargin: CGFloat { 49 | delegate?.topContentPagerViewControllerTabTopMargin?(self) ?? 0 50 | } 51 | 52 | private var topView: TopContentView! 53 | private let scrollView = UIScrollView() 54 | private let scrollContainerView = UIView() 55 | private let escapeView = EscapeView() 56 | private var escapeViewTopConstraint: NSLayoutConstraint? 57 | private var escapeViewHeightConstraint: NSLayoutConstraint? 58 | private var containerViews: [UIView] = [] 59 | private var tabBarTitles: [PagerItem] = [] 60 | private var contentOffsetX: CGFloat = 0 61 | private lazy var setupLayout: Void = { 62 | tabBarTitles.forEach { topView.tabView?.addItem(item: $0) } 63 | selectedIndex = 0 64 | topView.tabView?.adjustSelected(page: selectedIndex) 65 | topView.tabView?.update(pagerOptions: .init()) 66 | topView.isUserInteractionEnabled = true 67 | let tabBarTapRecognizer = UITapGestureRecognizer(target: self, action: #selector(tapTab(_:))) 68 | tabBarTapRecognizer.delegate = self 69 | topView.tabView?.addGestureRecognizer(tabBarTapRecognizer) 70 | }() 71 | 72 | open override func viewDidLoad() { 73 | super.viewDidLoad() 74 | view.backgroundColor = .white 75 | setupWillLoadDataSource() 76 | guard let dataSource = dataSource, !dataSource.topContentPagerViewControllerViewControllers(self).isEmpty else { 77 | return 78 | } 79 | loadDataSource(dataSource: dataSource) 80 | setup() 81 | viewControllers.forEach { vc in 82 | vc.delegate = self 83 | } 84 | } 85 | 86 | open override func viewDidLayoutSubviews() { 87 | super.viewWillLayoutSubviews() 88 | guard let dataSource = dataSource, !dataSource.topContentPagerViewControllerViewControllers(self).isEmpty else { 89 | return 90 | } 91 | _ = setupLayout 92 | } 93 | 94 | open func setupWillLoadDataSource() { } 95 | 96 | public func updateHeader() { 97 | escapeViewHeightConstraint?.constant = topView.estimateHeight 98 | escapeView.layoutIfNeeded() 99 | addContentViewToEscapeView() 100 | viewControllers.forEach { $0.tableView.reloadData() } 101 | tabHeight = topView.tabViewHeight + topMargin 102 | headerHeight = topView.estimateHeight 103 | topView.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: headerHeight) 104 | addContentViewToCell() 105 | escapeViewHeightConstraint?.constant = topView.estimateHeight 106 | escapeView.layoutIfNeeded() 107 | } 108 | 109 | private func loadDataSource(dataSource: TopContentPagerDataSource) { 110 | topView = dataSource.topContentPagerViewControllerTopContentView(self) 111 | topView.delegate = self 112 | topView.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: topView.estimateHeight) 113 | tabHeight = topView.tabViewHeight + topMargin 114 | headerHeight = topView.estimateHeight 115 | let bodyViewControllers = dataSource.topContentPagerViewControllerViewControllers(self) 116 | tabBarTitles = bodyViewControllers.map { $0.pagerItem } 117 | bodyViewControllers.forEach { item in 118 | let contentVC = ContentTableViewController.create(with: .init(topView: topView, viewController: item)) 119 | viewControllers.append(contentVC) 120 | } 121 | } 122 | 123 | private func setup() { 124 | scrollView.delegate = self 125 | scrollView.isPagingEnabled = true 126 | scrollView.scrollsToTop = false 127 | scrollView.isDirectionalLockEnabled = true 128 | scrollView.showsHorizontalScrollIndicator = false 129 | scrollView.showsVerticalScrollIndicator = false 130 | scrollView.translatesAutoresizingMaskIntoConstraints = false 131 | scrollView.alwaysBounceVertical = false 132 | scrollView.isScrollEnabled = false 133 | view.addSubview(scrollView) 134 | NSLayoutConstraint.activate([ 135 | scrollView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), 136 | scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor), 137 | scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor), 138 | scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor) 139 | ]) 140 | 141 | scrollContainerView.translatesAutoresizingMaskIntoConstraints = false 142 | scrollView.addSubview(scrollContainerView) 143 | NSLayoutConstraint.activate([ 144 | scrollContainerView.topAnchor.constraint(equalTo: scrollView.topAnchor), 145 | scrollContainerView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor), 146 | scrollContainerView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor), 147 | scrollContainerView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor), 148 | scrollContainerView.heightAnchor.constraint(equalTo: scrollView.heightAnchor), 149 | scrollContainerView.widthAnchor.constraint(equalTo: scrollView.widthAnchor, multiplier: CGFloat(viewControllers.count)) 150 | ]) 151 | 152 | viewControllers.enumerated().forEach { index, vc in 153 | vc.view.translatesAutoresizingMaskIntoConstraints = false 154 | scrollContainerView.addSubview(vc.view) 155 | var constraints: [NSLayoutConstraint] = [] 156 | switch index { 157 | case 0: 158 | constraints.append(vc.view.leadingAnchor.constraint(equalTo: scrollContainerView.leadingAnchor)) 159 | case viewControllers.count - 1: 160 | guard let previousVC = viewControllers[safe: index - 1] else { return } 161 | constraints.append(contentsOf: [ 162 | vc.view.trailingAnchor.constraint(equalTo: scrollContainerView.trailingAnchor), 163 | vc.view.leadingAnchor.constraint(equalTo: previousVC.view.trailingAnchor) 164 | ]) 165 | default: 166 | guard let previousVC = viewControllers[safe: index - 1] else { return } 167 | constraints.append(vc.view.leadingAnchor.constraint(equalTo: previousVC.view.trailingAnchor)) 168 | } 169 | constraints.append(contentsOf: [ 170 | vc.view.topAnchor.constraint(equalTo: scrollContainerView.topAnchor), 171 | vc.view.bottomAnchor.constraint(equalTo: scrollContainerView.bottomAnchor), 172 | vc.view.widthAnchor.constraint(equalToConstant: view.frame.width) 173 | ]) 174 | addChild(vc) 175 | vc.didMove(toParent: self) 176 | NSLayoutConstraint.activate(constraints) 177 | } 178 | 179 | escapeView.translatesAutoresizingMaskIntoConstraints = false 180 | escapeView.isUserInteractionEnabled = false 181 | escapeView.backgroundColor = .clear 182 | view.addSubview(escapeView) 183 | escapeViewTopConstraint = escapeView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor) 184 | escapeViewHeightConstraint = escapeView.heightAnchor.constraint(equalToConstant: headerHeight) 185 | NSLayoutConstraint.activate([ 186 | escapeViewTopConstraint!, 187 | escapeView.trailingAnchor.constraint(equalTo: view.trailingAnchor), 188 | escapeView.leadingAnchor.constraint(equalTo: view.leadingAnchor), 189 | escapeViewHeightConstraint! 190 | ]) 191 | 192 | safeAreaBar.translatesAutoresizingMaskIntoConstraints = false 193 | safeAreaBar.isUserInteractionEnabled = false 194 | safeAreaBar.backgroundColor = .white 195 | safeAreaBar.clipsToBounds = true 196 | view.addSubview(safeAreaBar) 197 | 198 | NSLayoutConstraint.activate([ 199 | safeAreaBar.topAnchor.constraint(equalTo: view.topAnchor), 200 | safeAreaBar.trailingAnchor.constraint(equalTo: view.trailingAnchor), 201 | safeAreaBar.leadingAnchor.constraint(equalTo: view.leadingAnchor), 202 | safeAreaBar.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor) 203 | ]) 204 | } 205 | 206 | @objc private func tapTab(_ sender: UITapGestureRecognizer) { 207 | guard let tabView = topView.tabView else { return } 208 | let position = sender.location(in: self.topView.tabView) 209 | if position.y < tabView.frame.size.height - tabView.options.itemHeight { 210 | return 211 | } 212 | 213 | let index = Int(floor(position.x / (tabView.frame.size.width / max(CGFloat(tabView.items.count), 1)))) 214 | self.selectedIndex = index 215 | } 216 | 217 | private func addContentViewToCell() { 218 | if topView.superview != escapeView { return } 219 | escapeView.isUserInteractionEnabled = false 220 | 221 | let cells = selectedViewController.tableView.visibleCells.filter { $0.isKind(of: ContentTopCell.self) } 222 | if let cell = cells.first as? ContentTopCell { 223 | cell.addSubview(topView) 224 | } 225 | } 226 | 227 | private func addContentViewToEscapeView() { 228 | if topView.superview == escapeView { return } 229 | escapeView.isUserInteractionEnabled = true 230 | escapeView.addSubview(topView) 231 | 232 | let topConstant = max(0, min(topView.frame.height - tabHeight, selectedViewController.tableView.contentOffset.y)) 233 | escapeViewTopConstraint?.constant = -topConstant 234 | escapeView.layoutIfNeeded() 235 | } 236 | 237 | private func tableViewsScroll() { 238 | let viewControllers = self.viewControllers.filter({ !($0 == selectedViewController) }) 239 | let contentHeight = topView.frame.height - tabHeight 240 | viewControllers.forEach { vc in 241 | var contentOffset: CGPoint 242 | if selectedViewController.tableView.contentOffset.y >= contentHeight { 243 | contentOffset = vc.tableView.contentOffset.y >= contentHeight ? vc.tableView.contentOffset : CGPoint(x: 0, y: contentHeight) 244 | } else { 245 | contentOffset = selectedViewController.tableView.contentOffset 246 | } 247 | vc.tableView.setContentOffset(contentOffset, animated: false) 248 | } 249 | } 250 | } 251 | 252 | extension TopContentPagerViewController: UIScrollViewDelegate { 253 | public func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { 254 | contentOffsetX = scrollView.contentOffset.x 255 | if let constant = escapeViewTopConstraint?.constant, !decelerate && -(topView.frame.height - tabHeight) < constant { 256 | addContentViewToCell() 257 | } 258 | } 259 | 260 | public func scrollViewDidScroll(_ scrollView: UIScrollView) { 261 | if scrollView.contentOffset.x != contentOffsetX { 262 | scrollView.contentOffset.y = 0 263 | } 264 | } 265 | 266 | public func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { 267 | contentOffsetX = scrollView.contentOffset.x 268 | addContentViewToEscapeView() 269 | } 270 | 271 | public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { 272 | let index = Int(scrollView.contentOffset.x / scrollView.bounds.size.width) 273 | selectedIndex = index 274 | } 275 | } 276 | 277 | extension TopContentPagerViewController: ContentTableViewDelegate { 278 | 279 | public func didEndDragging(viewController: ContentTableViewController, willDecelerate decelerate: Bool) { 280 | guard !decelerate, viewController == self.selectedViewController else { return } 281 | self.tableViewsScroll() 282 | } 283 | 284 | public func didEndDecelerationg(viewController: ContentTableViewController) { 285 | guard viewController == self.selectedViewController else { return } 286 | self.tableViewsScroll() 287 | } 288 | 289 | public func didScroll(viewController: ContentTableViewController) { 290 | guard viewController == self.selectedViewController else { return } 291 | if selectedViewController.tableView.contentOffset.y > self.topView.frame.height - self.tabHeight { 292 | self.addContentViewToEscapeView() 293 | } else { 294 | self.addContentViewToCell() 295 | } 296 | } 297 | } 298 | 299 | extension TopContentPagerViewController: TopContentViewDelegate { 300 | func needsReload() { 301 | updateHeader() 302 | } 303 | } 304 | -------------------------------------------------------------------------------- /Sample/View/TopView.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 30 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 47 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 99 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | -------------------------------------------------------------------------------- /TopContentPager.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 8435A4BF25BFFA1C00EEDA77 /* TopContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8435A4BE25BFFA1C00EEDA77 /* TopContentView.swift */; }; 11 | 8435A54225C061E400EEDA77 /* MyPostViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8435A54125C061E400EEDA77 /* MyPostViewController.swift */; }; 12 | 8435A54925C061F600EEDA77 /* MyPostViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8435A54825C061F600EEDA77 /* MyPostViewController.storyboard */; }; 13 | 8435A55025C0664E00EEDA77 /* MyPostCollectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8435A54F25C0664E00EEDA77 /* MyPostCollectionCell.swift */; }; 14 | 8435A55725C0665A00EEDA77 /* MyPostCollectionCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 8435A55625C0665A00EEDA77 /* MyPostCollectionCell.xib */; }; 15 | 843FD85625AE8EBF00AD615F /* NSObject+Name.swift in Sources */ = {isa = PBXBuildFile; fileRef = 843FD85525AE8EBF00AD615F /* NSObject+Name.swift */; }; 16 | 843FD85D25AE8EEF00AD615F /* UITableView+Register.swift in Sources */ = {isa = PBXBuildFile; fileRef = 843FD85C25AE8EEF00AD615F /* UITableView+Register.swift */; }; 17 | 843FD88525AEA08200AD615F /* TimelineViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 843FD88425AEA08200AD615F /* TimelineViewController.swift */; }; 18 | 843FD88C25AEA08E00AD615F /* NotificationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 843FD88B25AEA08E00AD615F /* NotificationViewController.swift */; }; 19 | 843FD89325AEA09A00AD615F /* TimelineViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 843FD89225AEA09A00AD615F /* TimelineViewController.storyboard */; }; 20 | 843FD8A625AEA0B600AD615F /* NotificationViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 843FD8A525AEA0B600AD615F /* NotificationViewController.storyboard */; }; 21 | 843FD8AD25AEA21E00AD615F /* TopView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 843FD8AC25AEA21E00AD615F /* TopView.swift */; }; 22 | 843FD8C225AEAC4C00AD615F /* Storyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 843FD8C125AEAC4C00AD615F /* Storyboard.swift */; }; 23 | 843FD8CE25AEC11A00AD615F /* NSObject+NibLoading.swift in Sources */ = {isa = PBXBuildFile; fileRef = 843FD8CD25AEC11A00AD615F /* NSObject+NibLoading.swift */; }; 24 | 843FDA0625B0969100AD615F /* CustomTopContentPagerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 843FDA0525B0969100AD615F /* CustomTopContentPagerViewController.swift */; }; 25 | 84BB333525A85EEF0056A35F /* TopContentPager.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84BB332B25A85EEF0056A35F /* TopContentPager.framework */; }; 26 | 84BB333A25A85EEF0056A35F /* TopContentPagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84BB333925A85EEF0056A35F /* TopContentPagerTests.swift */; }; 27 | 84BB333C25A85EEF0056A35F /* TopContentPager.h in Headers */ = {isa = PBXBuildFile; fileRef = 84BB332E25A85EEF0056A35F /* TopContentPager.h */; settings = {ATTRIBUTES = (Public, ); }; }; 28 | 84BB338E25A85FBC0056A35F /* ContentBodyCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84BB337D25A85FBC0056A35F /* ContentBodyCell.swift */; }; 29 | 84BB339025A85FBC0056A35F /* ContentTopCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84BB337F25A85FBC0056A35F /* ContentTopCell.swift */; }; 30 | 84BB339525A85FBC0056A35F /* ContentTableBody.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84BB338525A85FBC0056A35F /* ContentTableBody.swift */; }; 31 | 84BB339725A85FBC0056A35F /* ContentTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84BB338725A85FBC0056A35F /* ContentTableViewController.swift */; }; 32 | 84BB339925A85FBC0056A35F /* TopContentPagerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84BB338925A85FBC0056A35F /* TopContentPagerViewController.swift */; }; 33 | 84BB33A525A8655A0056A35F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84BB33A425A8655A0056A35F /* AppDelegate.swift */; }; 34 | 84BB33A725A8655A0056A35F /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84BB33A625A8655A0056A35F /* SceneDelegate.swift */; }; 35 | 84BB33A925A8655A0056A35F /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84BB33A825A8655A0056A35F /* ViewController.swift */; }; 36 | 84BB33AC25A8655A0056A35F /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 84BB33AA25A8655A0056A35F /* Main.storyboard */; }; 37 | 84BB33AE25A8655B0056A35F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 84BB33AD25A8655B0056A35F /* Assets.xcassets */; }; 38 | 84BB33B125A8655B0056A35F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 84BB33AF25A8655B0056A35F /* LaunchScreen.storyboard */; }; 39 | 84BB33BC25A8655B0056A35F /* SampleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84BB33BB25A8655B0056A35F /* SampleTests.swift */; }; 40 | 84BB33C725A8655B0056A35F /* SampleUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84BB33C625A8655B0056A35F /* SampleUITests.swift */; }; 41 | 84BB33D925A86B450056A35F /* PagerItemsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84BB33D825A86B450056A35F /* PagerItemsView.swift */; }; 42 | 84BB33E525A86B870056A35F /* PagerOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84BB33E425A86B870056A35F /* PagerOptions.swift */; }; 43 | 84BB33F125A872ED0056A35F /* PagerItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84BB33F025A872ED0056A35F /* PagerItemView.swift */; }; 44 | 84C8D31A25C15B0B005CDC11 /* MockData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84C8D31925C15B0B005CDC11 /* MockData.swift */; }; 45 | 84D77DD725C0762C0035CA1D /* TopView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 84D77DD625C0762C0035CA1D /* TopView.xib */; }; 46 | 84D77DED25C088210035CA1D /* TimelineTableCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84D77DEC25C088210035CA1D /* TimelineTableCell.swift */; }; 47 | 84D77DF425C0882D0035CA1D /* TimelineTableCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 84D77DF325C0882D0035CA1D /* TimelineTableCell.xib */; }; 48 | 84D77DFB25C149840035CA1D /* NotificationTableCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84D77DFA25C149840035CA1D /* NotificationTableCell.swift */; }; 49 | 84D77E0225C149910035CA1D /* NotificationTableCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 84D77E0125C149910035CA1D /* NotificationTableCell.xib */; }; 50 | 84DC4E9925D3A5C500BA8ACB /* DefaultPagerItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84DC4E9825D3A5C500BA8ACB /* DefaultPagerItemView.swift */; }; 51 | /* End PBXBuildFile section */ 52 | 53 | /* Begin PBXContainerItemProxy section */ 54 | 84BB333625A85EEF0056A35F /* PBXContainerItemProxy */ = { 55 | isa = PBXContainerItemProxy; 56 | containerPortal = 84BB332225A85EEF0056A35F /* Project object */; 57 | proxyType = 1; 58 | remoteGlobalIDString = 84BB332A25A85EEF0056A35F; 59 | remoteInfo = TopContentPager; 60 | }; 61 | 84BB33B825A8655B0056A35F /* PBXContainerItemProxy */ = { 62 | isa = PBXContainerItemProxy; 63 | containerPortal = 84BB332225A85EEF0056A35F /* Project object */; 64 | proxyType = 1; 65 | remoteGlobalIDString = 84BB33A125A8655A0056A35F; 66 | remoteInfo = Sample; 67 | }; 68 | 84BB33C325A8655B0056A35F /* PBXContainerItemProxy */ = { 69 | isa = PBXContainerItemProxy; 70 | containerPortal = 84BB332225A85EEF0056A35F /* Project object */; 71 | proxyType = 1; 72 | remoteGlobalIDString = 84BB33A125A8655A0056A35F; 73 | remoteInfo = Sample; 74 | }; 75 | /* End PBXContainerItemProxy section */ 76 | 77 | /* Begin PBXFileReference section */ 78 | 8435A4BE25BFFA1C00EEDA77 /* TopContentView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TopContentView.swift; sourceTree = ""; }; 79 | 8435A54125C061E400EEDA77 /* MyPostViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPostViewController.swift; sourceTree = ""; }; 80 | 8435A54825C061F600EEDA77 /* MyPostViewController.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = MyPostViewController.storyboard; sourceTree = ""; }; 81 | 8435A54F25C0664E00EEDA77 /* MyPostCollectionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPostCollectionCell.swift; sourceTree = ""; }; 82 | 8435A55625C0665A00EEDA77 /* MyPostCollectionCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MyPostCollectionCell.xib; sourceTree = ""; }; 83 | 843FD85525AE8EBF00AD615F /* NSObject+Name.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSObject+Name.swift"; sourceTree = ""; }; 84 | 843FD85C25AE8EEF00AD615F /* UITableView+Register.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITableView+Register.swift"; sourceTree = ""; }; 85 | 843FD88425AEA08200AD615F /* TimelineViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineViewController.swift; sourceTree = ""; }; 86 | 843FD88B25AEA08E00AD615F /* NotificationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationViewController.swift; sourceTree = ""; }; 87 | 843FD89225AEA09A00AD615F /* TimelineViewController.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = TimelineViewController.storyboard; sourceTree = ""; }; 88 | 843FD8A525AEA0B600AD615F /* NotificationViewController.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = NotificationViewController.storyboard; sourceTree = ""; }; 89 | 843FD8AC25AEA21E00AD615F /* TopView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TopView.swift; sourceTree = ""; }; 90 | 843FD8C125AEAC4C00AD615F /* Storyboard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Storyboard.swift; sourceTree = ""; }; 91 | 843FD8CD25AEC11A00AD615F /* NSObject+NibLoading.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSObject+NibLoading.swift"; sourceTree = ""; }; 92 | 843FDA0525B0969100AD615F /* CustomTopContentPagerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomTopContentPagerViewController.swift; sourceTree = ""; }; 93 | 84BB332B25A85EEF0056A35F /* TopContentPager.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = TopContentPager.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 94 | 84BB332E25A85EEF0056A35F /* TopContentPager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TopContentPager.h; sourceTree = ""; }; 95 | 84BB332F25A85EEF0056A35F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 96 | 84BB333425A85EEF0056A35F /* TopContentPagerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TopContentPagerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 97 | 84BB333925A85EEF0056A35F /* TopContentPagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TopContentPagerTests.swift; sourceTree = ""; }; 98 | 84BB333B25A85EEF0056A35F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 99 | 84BB337D25A85FBC0056A35F /* ContentBodyCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentBodyCell.swift; sourceTree = ""; }; 100 | 84BB337F25A85FBC0056A35F /* ContentTopCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentTopCell.swift; sourceTree = ""; }; 101 | 84BB338525A85FBC0056A35F /* ContentTableBody.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentTableBody.swift; sourceTree = ""; }; 102 | 84BB338725A85FBC0056A35F /* ContentTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentTableViewController.swift; sourceTree = ""; }; 103 | 84BB338925A85FBC0056A35F /* TopContentPagerViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TopContentPagerViewController.swift; sourceTree = ""; }; 104 | 84BB33A225A8655A0056A35F /* Sample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Sample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 105 | 84BB33A425A8655A0056A35F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 106 | 84BB33A625A8655A0056A35F /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; 107 | 84BB33A825A8655A0056A35F /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 108 | 84BB33AB25A8655A0056A35F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 109 | 84BB33AD25A8655B0056A35F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 110 | 84BB33B025A8655B0056A35F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 111 | 84BB33B225A8655B0056A35F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 112 | 84BB33B725A8655B0056A35F /* SampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 113 | 84BB33BB25A8655B0056A35F /* SampleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SampleTests.swift; sourceTree = ""; }; 114 | 84BB33BD25A8655B0056A35F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 115 | 84BB33C225A8655B0056A35F /* SampleUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SampleUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 116 | 84BB33C625A8655B0056A35F /* SampleUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SampleUITests.swift; sourceTree = ""; }; 117 | 84BB33C825A8655B0056A35F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 118 | 84BB33D825A86B450056A35F /* PagerItemsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PagerItemsView.swift; sourceTree = ""; }; 119 | 84BB33E425A86B870056A35F /* PagerOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PagerOptions.swift; sourceTree = ""; }; 120 | 84BB33F025A872ED0056A35F /* PagerItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PagerItemView.swift; sourceTree = ""; }; 121 | 84C8D31925C15B0B005CDC11 /* MockData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockData.swift; sourceTree = ""; }; 122 | 84D77DD625C0762C0035CA1D /* TopView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = TopView.xib; sourceTree = ""; }; 123 | 84D77DEC25C088210035CA1D /* TimelineTableCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineTableCell.swift; sourceTree = ""; }; 124 | 84D77DF325C0882D0035CA1D /* TimelineTableCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = TimelineTableCell.xib; sourceTree = ""; }; 125 | 84D77DFA25C149840035CA1D /* NotificationTableCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationTableCell.swift; sourceTree = ""; }; 126 | 84D77E0125C149910035CA1D /* NotificationTableCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = NotificationTableCell.xib; sourceTree = ""; }; 127 | 84DC4E9825D3A5C500BA8ACB /* DefaultPagerItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultPagerItemView.swift; sourceTree = ""; }; 128 | /* End PBXFileReference section */ 129 | 130 | /* Begin PBXFrameworksBuildPhase section */ 131 | 84BB332825A85EEF0056A35F /* Frameworks */ = { 132 | isa = PBXFrameworksBuildPhase; 133 | buildActionMask = 2147483647; 134 | files = ( 135 | ); 136 | runOnlyForDeploymentPostprocessing = 0; 137 | }; 138 | 84BB333125A85EEF0056A35F /* Frameworks */ = { 139 | isa = PBXFrameworksBuildPhase; 140 | buildActionMask = 2147483647; 141 | files = ( 142 | 84BB333525A85EEF0056A35F /* TopContentPager.framework in Frameworks */, 143 | ); 144 | runOnlyForDeploymentPostprocessing = 0; 145 | }; 146 | 84BB339F25A8655A0056A35F /* Frameworks */ = { 147 | isa = PBXFrameworksBuildPhase; 148 | buildActionMask = 2147483647; 149 | files = ( 150 | ); 151 | runOnlyForDeploymentPostprocessing = 0; 152 | }; 153 | 84BB33B425A8655B0056A35F /* Frameworks */ = { 154 | isa = PBXFrameworksBuildPhase; 155 | buildActionMask = 2147483647; 156 | files = ( 157 | ); 158 | runOnlyForDeploymentPostprocessing = 0; 159 | }; 160 | 84BB33BF25A8655B0056A35F /* Frameworks */ = { 161 | isa = PBXFrameworksBuildPhase; 162 | buildActionMask = 2147483647; 163 | files = ( 164 | ); 165 | runOnlyForDeploymentPostprocessing = 0; 166 | }; 167 | /* End PBXFrameworksBuildPhase section */ 168 | 169 | /* Begin PBXGroup section */ 170 | 843FD87925AE9F6300AD615F /* Utilitie */ = { 171 | isa = PBXGroup; 172 | children = ( 173 | 843FD85525AE8EBF00AD615F /* NSObject+Name.swift */, 174 | 843FD85C25AE8EEF00AD615F /* UITableView+Register.swift */, 175 | ); 176 | path = Utilitie; 177 | sourceTree = ""; 178 | }; 179 | 84BB332125A85EEF0056A35F = { 180 | isa = PBXGroup; 181 | children = ( 182 | 84BB332D25A85EEF0056A35F /* TopContentPager */, 183 | 84BB333825A85EEF0056A35F /* TopContentPagerTests */, 184 | 84BB33A325A8655A0056A35F /* Sample */, 185 | 84BB33BA25A8655B0056A35F /* SampleTests */, 186 | 84BB33C525A8655B0056A35F /* SampleUITests */, 187 | 84BB332C25A85EEF0056A35F /* Products */, 188 | ); 189 | sourceTree = ""; 190 | }; 191 | 84BB332C25A85EEF0056A35F /* Products */ = { 192 | isa = PBXGroup; 193 | children = ( 194 | 84BB332B25A85EEF0056A35F /* TopContentPager.framework */, 195 | 84BB333425A85EEF0056A35F /* TopContentPagerTests.xctest */, 196 | 84BB33A225A8655A0056A35F /* Sample.app */, 197 | 84BB33B725A8655B0056A35F /* SampleTests.xctest */, 198 | 84BB33C225A8655B0056A35F /* SampleUITests.xctest */, 199 | ); 200 | name = Products; 201 | sourceTree = ""; 202 | }; 203 | 84BB332D25A85EEF0056A35F /* TopContentPager */ = { 204 | isa = PBXGroup; 205 | children = ( 206 | 843FD87925AE9F6300AD615F /* Utilitie */, 207 | 84BB33D725A86B360056A35F /* View */, 208 | 84BB337B25A85FBC0056A35F /* Cell */, 209 | 84BB338525A85FBC0056A35F /* ContentTableBody.swift */, 210 | 84BB338725A85FBC0056A35F /* ContentTableViewController.swift */, 211 | 8435A4BE25BFFA1C00EEDA77 /* TopContentView.swift */, 212 | 84BB338925A85FBC0056A35F /* TopContentPagerViewController.swift */, 213 | 84BB332E25A85EEF0056A35F /* TopContentPager.h */, 214 | 84BB332F25A85EEF0056A35F /* Info.plist */, 215 | ); 216 | path = TopContentPager; 217 | sourceTree = ""; 218 | }; 219 | 84BB333825A85EEF0056A35F /* TopContentPagerTests */ = { 220 | isa = PBXGroup; 221 | children = ( 222 | 84BB333925A85EEF0056A35F /* TopContentPagerTests.swift */, 223 | 84BB333B25A85EEF0056A35F /* Info.plist */, 224 | ); 225 | path = TopContentPagerTests; 226 | sourceTree = ""; 227 | }; 228 | 84BB337B25A85FBC0056A35F /* Cell */ = { 229 | isa = PBXGroup; 230 | children = ( 231 | 84BB337D25A85FBC0056A35F /* ContentBodyCell.swift */, 232 | 84BB337F25A85FBC0056A35F /* ContentTopCell.swift */, 233 | ); 234 | path = Cell; 235 | sourceTree = ""; 236 | }; 237 | 84BB33A325A8655A0056A35F /* Sample */ = { 238 | isa = PBXGroup; 239 | children = ( 240 | 84BB33A425A8655A0056A35F /* AppDelegate.swift */, 241 | 84BB33A625A8655A0056A35F /* SceneDelegate.swift */, 242 | 84C8D36C25C4FE2C005CDC11 /* ViewController */, 243 | 84C8D37225C4FE68005CDC11 /* Cell */, 244 | 84C8D37825C4FE8C005CDC11 /* View */, 245 | 84C8D38425C4FEBC005CDC11 /* Entity */, 246 | 84C8D37E25C4FEA3005CDC11 /* Utility */, 247 | 84BB33AD25A8655B0056A35F /* Assets.xcassets */, 248 | 84BB33AF25A8655B0056A35F /* LaunchScreen.storyboard */, 249 | 84BB33B225A8655B0056A35F /* Info.plist */, 250 | ); 251 | path = Sample; 252 | sourceTree = ""; 253 | }; 254 | 84BB33BA25A8655B0056A35F /* SampleTests */ = { 255 | isa = PBXGroup; 256 | children = ( 257 | 84BB33BB25A8655B0056A35F /* SampleTests.swift */, 258 | 84BB33BD25A8655B0056A35F /* Info.plist */, 259 | ); 260 | path = SampleTests; 261 | sourceTree = ""; 262 | }; 263 | 84BB33C525A8655B0056A35F /* SampleUITests */ = { 264 | isa = PBXGroup; 265 | children = ( 266 | 84BB33C625A8655B0056A35F /* SampleUITests.swift */, 267 | 84BB33C825A8655B0056A35F /* Info.plist */, 268 | ); 269 | path = SampleUITests; 270 | sourceTree = ""; 271 | }; 272 | 84BB33D725A86B360056A35F /* View */ = { 273 | isa = PBXGroup; 274 | children = ( 275 | 84BB33D825A86B450056A35F /* PagerItemsView.swift */, 276 | 84BB33F025A872ED0056A35F /* PagerItemView.swift */, 277 | 84DC4E9825D3A5C500BA8ACB /* DefaultPagerItemView.swift */, 278 | 84BB33E425A86B870056A35F /* PagerOptions.swift */, 279 | ); 280 | path = View; 281 | sourceTree = ""; 282 | }; 283 | 84C8D36C25C4FE2C005CDC11 /* ViewController */ = { 284 | isa = PBXGroup; 285 | children = ( 286 | 84BB33A825A8655A0056A35F /* ViewController.swift */, 287 | 84BB33AA25A8655A0056A35F /* Main.storyboard */, 288 | 843FDA0525B0969100AD615F /* CustomTopContentPagerViewController.swift */, 289 | 8435A54125C061E400EEDA77 /* MyPostViewController.swift */, 290 | 8435A54825C061F600EEDA77 /* MyPostViewController.storyboard */, 291 | 843FD88425AEA08200AD615F /* TimelineViewController.swift */, 292 | 843FD89225AEA09A00AD615F /* TimelineViewController.storyboard */, 293 | 843FD88B25AEA08E00AD615F /* NotificationViewController.swift */, 294 | 843FD8A525AEA0B600AD615F /* NotificationViewController.storyboard */, 295 | ); 296 | path = ViewController; 297 | sourceTree = ""; 298 | }; 299 | 84C8D37225C4FE68005CDC11 /* Cell */ = { 300 | isa = PBXGroup; 301 | children = ( 302 | 84D77DEC25C088210035CA1D /* TimelineTableCell.swift */, 303 | 84D77DF325C0882D0035CA1D /* TimelineTableCell.xib */, 304 | 84D77DFA25C149840035CA1D /* NotificationTableCell.swift */, 305 | 84D77E0125C149910035CA1D /* NotificationTableCell.xib */, 306 | 8435A54F25C0664E00EEDA77 /* MyPostCollectionCell.swift */, 307 | 8435A55625C0665A00EEDA77 /* MyPostCollectionCell.xib */, 308 | ); 309 | path = Cell; 310 | sourceTree = ""; 311 | }; 312 | 84C8D37825C4FE8C005CDC11 /* View */ = { 313 | isa = PBXGroup; 314 | children = ( 315 | 843FD8AC25AEA21E00AD615F /* TopView.swift */, 316 | 84D77DD625C0762C0035CA1D /* TopView.xib */, 317 | ); 318 | path = View; 319 | sourceTree = ""; 320 | }; 321 | 84C8D37E25C4FEA3005CDC11 /* Utility */ = { 322 | isa = PBXGroup; 323 | children = ( 324 | 843FD8C125AEAC4C00AD615F /* Storyboard.swift */, 325 | 843FD8CD25AEC11A00AD615F /* NSObject+NibLoading.swift */, 326 | ); 327 | path = Utility; 328 | sourceTree = ""; 329 | }; 330 | 84C8D38425C4FEBC005CDC11 /* Entity */ = { 331 | isa = PBXGroup; 332 | children = ( 333 | 84C8D31925C15B0B005CDC11 /* MockData.swift */, 334 | ); 335 | path = Entity; 336 | sourceTree = ""; 337 | }; 338 | /* End PBXGroup section */ 339 | 340 | /* Begin PBXHeadersBuildPhase section */ 341 | 84BB332625A85EEF0056A35F /* Headers */ = { 342 | isa = PBXHeadersBuildPhase; 343 | buildActionMask = 2147483647; 344 | files = ( 345 | 84BB333C25A85EEF0056A35F /* TopContentPager.h in Headers */, 346 | ); 347 | runOnlyForDeploymentPostprocessing = 0; 348 | }; 349 | /* End PBXHeadersBuildPhase section */ 350 | 351 | /* Begin PBXNativeTarget section */ 352 | 84BB332A25A85EEF0056A35F /* TopContentPager */ = { 353 | isa = PBXNativeTarget; 354 | buildConfigurationList = 84BB333F25A85EEF0056A35F /* Build configuration list for PBXNativeTarget "TopContentPager" */; 355 | buildPhases = ( 356 | 84BB332625A85EEF0056A35F /* Headers */, 357 | 84BB332725A85EEF0056A35F /* Sources */, 358 | 84BB332825A85EEF0056A35F /* Frameworks */, 359 | 84BB332925A85EEF0056A35F /* Resources */, 360 | ); 361 | buildRules = ( 362 | ); 363 | dependencies = ( 364 | ); 365 | name = TopContentPager; 366 | productName = TopContentPager; 367 | productReference = 84BB332B25A85EEF0056A35F /* TopContentPager.framework */; 368 | productType = "com.apple.product-type.framework"; 369 | }; 370 | 84BB333325A85EEF0056A35F /* TopContentPagerTests */ = { 371 | isa = PBXNativeTarget; 372 | buildConfigurationList = 84BB334225A85EEF0056A35F /* Build configuration list for PBXNativeTarget "TopContentPagerTests" */; 373 | buildPhases = ( 374 | 84BB333025A85EEF0056A35F /* Sources */, 375 | 84BB333125A85EEF0056A35F /* Frameworks */, 376 | 84BB333225A85EEF0056A35F /* Resources */, 377 | ); 378 | buildRules = ( 379 | ); 380 | dependencies = ( 381 | 84BB333725A85EEF0056A35F /* PBXTargetDependency */, 382 | ); 383 | name = TopContentPagerTests; 384 | productName = TopContentPagerTests; 385 | productReference = 84BB333425A85EEF0056A35F /* TopContentPagerTests.xctest */; 386 | productType = "com.apple.product-type.bundle.unit-test"; 387 | }; 388 | 84BB33A125A8655A0056A35F /* Sample */ = { 389 | isa = PBXNativeTarget; 390 | buildConfigurationList = 84BB33C925A8655B0056A35F /* Build configuration list for PBXNativeTarget "Sample" */; 391 | buildPhases = ( 392 | 84BB339E25A8655A0056A35F /* Sources */, 393 | 84BB339F25A8655A0056A35F /* Frameworks */, 394 | 84BB33A025A8655A0056A35F /* Resources */, 395 | ); 396 | buildRules = ( 397 | ); 398 | dependencies = ( 399 | ); 400 | name = Sample; 401 | productName = Sample; 402 | productReference = 84BB33A225A8655A0056A35F /* Sample.app */; 403 | productType = "com.apple.product-type.application"; 404 | }; 405 | 84BB33B625A8655B0056A35F /* SampleTests */ = { 406 | isa = PBXNativeTarget; 407 | buildConfigurationList = 84BB33CC25A8655B0056A35F /* Build configuration list for PBXNativeTarget "SampleTests" */; 408 | buildPhases = ( 409 | 84BB33B325A8655B0056A35F /* Sources */, 410 | 84BB33B425A8655B0056A35F /* Frameworks */, 411 | 84BB33B525A8655B0056A35F /* Resources */, 412 | ); 413 | buildRules = ( 414 | ); 415 | dependencies = ( 416 | 84BB33B925A8655B0056A35F /* PBXTargetDependency */, 417 | ); 418 | name = SampleTests; 419 | productName = SampleTests; 420 | productReference = 84BB33B725A8655B0056A35F /* SampleTests.xctest */; 421 | productType = "com.apple.product-type.bundle.unit-test"; 422 | }; 423 | 84BB33C125A8655B0056A35F /* SampleUITests */ = { 424 | isa = PBXNativeTarget; 425 | buildConfigurationList = 84BB33CF25A8655B0056A35F /* Build configuration list for PBXNativeTarget "SampleUITests" */; 426 | buildPhases = ( 427 | 84BB33BE25A8655B0056A35F /* Sources */, 428 | 84BB33BF25A8655B0056A35F /* Frameworks */, 429 | 84BB33C025A8655B0056A35F /* Resources */, 430 | ); 431 | buildRules = ( 432 | ); 433 | dependencies = ( 434 | 84BB33C425A8655B0056A35F /* PBXTargetDependency */, 435 | ); 436 | name = SampleUITests; 437 | productName = SampleUITests; 438 | productReference = 84BB33C225A8655B0056A35F /* SampleUITests.xctest */; 439 | productType = "com.apple.product-type.bundle.ui-testing"; 440 | }; 441 | /* End PBXNativeTarget section */ 442 | 443 | /* Begin PBXProject section */ 444 | 84BB332225A85EEF0056A35F /* Project object */ = { 445 | isa = PBXProject; 446 | attributes = { 447 | LastSwiftUpdateCheck = 1230; 448 | LastUpgradeCheck = 1230; 449 | TargetAttributes = { 450 | 84BB332A25A85EEF0056A35F = { 451 | CreatedOnToolsVersion = 12.3; 452 | LastSwiftMigration = 1230; 453 | }; 454 | 84BB333325A85EEF0056A35F = { 455 | CreatedOnToolsVersion = 12.3; 456 | }; 457 | 84BB33A125A8655A0056A35F = { 458 | CreatedOnToolsVersion = 12.3; 459 | }; 460 | 84BB33B625A8655B0056A35F = { 461 | CreatedOnToolsVersion = 12.3; 462 | TestTargetID = 84BB33A125A8655A0056A35F; 463 | }; 464 | 84BB33C125A8655B0056A35F = { 465 | CreatedOnToolsVersion = 12.3; 466 | TestTargetID = 84BB33A125A8655A0056A35F; 467 | }; 468 | }; 469 | }; 470 | buildConfigurationList = 84BB332525A85EEF0056A35F /* Build configuration list for PBXProject "TopContentPager" */; 471 | compatibilityVersion = "Xcode 9.3"; 472 | developmentRegion = en; 473 | hasScannedForEncodings = 0; 474 | knownRegions = ( 475 | en, 476 | Base, 477 | ); 478 | mainGroup = 84BB332125A85EEF0056A35F; 479 | productRefGroup = 84BB332C25A85EEF0056A35F /* Products */; 480 | projectDirPath = ""; 481 | projectRoot = ""; 482 | targets = ( 483 | 84BB332A25A85EEF0056A35F /* TopContentPager */, 484 | 84BB333325A85EEF0056A35F /* TopContentPagerTests */, 485 | 84BB33A125A8655A0056A35F /* Sample */, 486 | 84BB33B625A8655B0056A35F /* SampleTests */, 487 | 84BB33C125A8655B0056A35F /* SampleUITests */, 488 | ); 489 | }; 490 | /* End PBXProject section */ 491 | 492 | /* Begin PBXResourcesBuildPhase section */ 493 | 84BB332925A85EEF0056A35F /* Resources */ = { 494 | isa = PBXResourcesBuildPhase; 495 | buildActionMask = 2147483647; 496 | files = ( 497 | ); 498 | runOnlyForDeploymentPostprocessing = 0; 499 | }; 500 | 84BB333225A85EEF0056A35F /* Resources */ = { 501 | isa = PBXResourcesBuildPhase; 502 | buildActionMask = 2147483647; 503 | files = ( 504 | ); 505 | runOnlyForDeploymentPostprocessing = 0; 506 | }; 507 | 84BB33A025A8655A0056A35F /* Resources */ = { 508 | isa = PBXResourcesBuildPhase; 509 | buildActionMask = 2147483647; 510 | files = ( 511 | 84BB33B125A8655B0056A35F /* LaunchScreen.storyboard in Resources */, 512 | 84BB33AE25A8655B0056A35F /* Assets.xcassets in Resources */, 513 | 84D77DD725C0762C0035CA1D /* TopView.xib in Resources */, 514 | 843FD89325AEA09A00AD615F /* TimelineViewController.storyboard in Resources */, 515 | 8435A55725C0665A00EEDA77 /* MyPostCollectionCell.xib in Resources */, 516 | 84D77E0225C149910035CA1D /* NotificationTableCell.xib in Resources */, 517 | 8435A54925C061F600EEDA77 /* MyPostViewController.storyboard in Resources */, 518 | 843FD8A625AEA0B600AD615F /* NotificationViewController.storyboard in Resources */, 519 | 84D77DF425C0882D0035CA1D /* TimelineTableCell.xib in Resources */, 520 | 84BB33AC25A8655A0056A35F /* Main.storyboard in Resources */, 521 | ); 522 | runOnlyForDeploymentPostprocessing = 0; 523 | }; 524 | 84BB33B525A8655B0056A35F /* Resources */ = { 525 | isa = PBXResourcesBuildPhase; 526 | buildActionMask = 2147483647; 527 | files = ( 528 | ); 529 | runOnlyForDeploymentPostprocessing = 0; 530 | }; 531 | 84BB33C025A8655B0056A35F /* Resources */ = { 532 | isa = PBXResourcesBuildPhase; 533 | buildActionMask = 2147483647; 534 | files = ( 535 | ); 536 | runOnlyForDeploymentPostprocessing = 0; 537 | }; 538 | /* End PBXResourcesBuildPhase section */ 539 | 540 | /* Begin PBXSourcesBuildPhase section */ 541 | 84BB332725A85EEF0056A35F /* Sources */ = { 542 | isa = PBXSourcesBuildPhase; 543 | buildActionMask = 2147483647; 544 | files = ( 545 | 8435A4BF25BFFA1C00EEDA77 /* TopContentView.swift in Sources */, 546 | 84BB339525A85FBC0056A35F /* ContentTableBody.swift in Sources */, 547 | 843FD85625AE8EBF00AD615F /* NSObject+Name.swift in Sources */, 548 | 84BB339725A85FBC0056A35F /* ContentTableViewController.swift in Sources */, 549 | 84BB33F125A872ED0056A35F /* PagerItemView.swift in Sources */, 550 | 843FD85D25AE8EEF00AD615F /* UITableView+Register.swift in Sources */, 551 | 84BB338E25A85FBC0056A35F /* ContentBodyCell.swift in Sources */, 552 | 84DC4E9925D3A5C500BA8ACB /* DefaultPagerItemView.swift in Sources */, 553 | 84BB33E525A86B870056A35F /* PagerOptions.swift in Sources */, 554 | 84BB33D925A86B450056A35F /* PagerItemsView.swift in Sources */, 555 | 84BB339925A85FBC0056A35F /* TopContentPagerViewController.swift in Sources */, 556 | 84BB339025A85FBC0056A35F /* ContentTopCell.swift in Sources */, 557 | ); 558 | runOnlyForDeploymentPostprocessing = 0; 559 | }; 560 | 84BB333025A85EEF0056A35F /* Sources */ = { 561 | isa = PBXSourcesBuildPhase; 562 | buildActionMask = 2147483647; 563 | files = ( 564 | 84BB333A25A85EEF0056A35F /* TopContentPagerTests.swift in Sources */, 565 | ); 566 | runOnlyForDeploymentPostprocessing = 0; 567 | }; 568 | 84BB339E25A8655A0056A35F /* Sources */ = { 569 | isa = PBXSourcesBuildPhase; 570 | buildActionMask = 2147483647; 571 | files = ( 572 | 843FD88C25AEA08E00AD615F /* NotificationViewController.swift in Sources */, 573 | 8435A55025C0664E00EEDA77 /* MyPostCollectionCell.swift in Sources */, 574 | 84BB33A925A8655A0056A35F /* ViewController.swift in Sources */, 575 | 843FD88525AEA08200AD615F /* TimelineViewController.swift in Sources */, 576 | 8435A54225C061E400EEDA77 /* MyPostViewController.swift in Sources */, 577 | 84C8D31A25C15B0B005CDC11 /* MockData.swift in Sources */, 578 | 843FD8C225AEAC4C00AD615F /* Storyboard.swift in Sources */, 579 | 84BB33A525A8655A0056A35F /* AppDelegate.swift in Sources */, 580 | 843FD8AD25AEA21E00AD615F /* TopView.swift in Sources */, 581 | 843FDA0625B0969100AD615F /* CustomTopContentPagerViewController.swift in Sources */, 582 | 84D77DED25C088210035CA1D /* TimelineTableCell.swift in Sources */, 583 | 84D77DFB25C149840035CA1D /* NotificationTableCell.swift in Sources */, 584 | 843FD8CE25AEC11A00AD615F /* NSObject+NibLoading.swift in Sources */, 585 | 84BB33A725A8655A0056A35F /* SceneDelegate.swift in Sources */, 586 | ); 587 | runOnlyForDeploymentPostprocessing = 0; 588 | }; 589 | 84BB33B325A8655B0056A35F /* Sources */ = { 590 | isa = PBXSourcesBuildPhase; 591 | buildActionMask = 2147483647; 592 | files = ( 593 | 84BB33BC25A8655B0056A35F /* SampleTests.swift in Sources */, 594 | ); 595 | runOnlyForDeploymentPostprocessing = 0; 596 | }; 597 | 84BB33BE25A8655B0056A35F /* Sources */ = { 598 | isa = PBXSourcesBuildPhase; 599 | buildActionMask = 2147483647; 600 | files = ( 601 | 84BB33C725A8655B0056A35F /* SampleUITests.swift in Sources */, 602 | ); 603 | runOnlyForDeploymentPostprocessing = 0; 604 | }; 605 | /* End PBXSourcesBuildPhase section */ 606 | 607 | /* Begin PBXTargetDependency section */ 608 | 84BB333725A85EEF0056A35F /* PBXTargetDependency */ = { 609 | isa = PBXTargetDependency; 610 | target = 84BB332A25A85EEF0056A35F /* TopContentPager */; 611 | targetProxy = 84BB333625A85EEF0056A35F /* PBXContainerItemProxy */; 612 | }; 613 | 84BB33B925A8655B0056A35F /* PBXTargetDependency */ = { 614 | isa = PBXTargetDependency; 615 | target = 84BB33A125A8655A0056A35F /* Sample */; 616 | targetProxy = 84BB33B825A8655B0056A35F /* PBXContainerItemProxy */; 617 | }; 618 | 84BB33C425A8655B0056A35F /* PBXTargetDependency */ = { 619 | isa = PBXTargetDependency; 620 | target = 84BB33A125A8655A0056A35F /* Sample */; 621 | targetProxy = 84BB33C325A8655B0056A35F /* PBXContainerItemProxy */; 622 | }; 623 | /* End PBXTargetDependency section */ 624 | 625 | /* Begin PBXVariantGroup section */ 626 | 84BB33AA25A8655A0056A35F /* Main.storyboard */ = { 627 | isa = PBXVariantGroup; 628 | children = ( 629 | 84BB33AB25A8655A0056A35F /* Base */, 630 | ); 631 | name = Main.storyboard; 632 | sourceTree = ""; 633 | }; 634 | 84BB33AF25A8655B0056A35F /* LaunchScreen.storyboard */ = { 635 | isa = PBXVariantGroup; 636 | children = ( 637 | 84BB33B025A8655B0056A35F /* Base */, 638 | ); 639 | name = LaunchScreen.storyboard; 640 | sourceTree = ""; 641 | }; 642 | /* End PBXVariantGroup section */ 643 | 644 | /* Begin XCBuildConfiguration section */ 645 | 84BB333D25A85EEF0056A35F /* Debug */ = { 646 | isa = XCBuildConfiguration; 647 | buildSettings = { 648 | ALWAYS_SEARCH_USER_PATHS = NO; 649 | CLANG_ANALYZER_NONNULL = YES; 650 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 651 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 652 | CLANG_CXX_LIBRARY = "libc++"; 653 | CLANG_ENABLE_MODULES = YES; 654 | CLANG_ENABLE_OBJC_ARC = YES; 655 | CLANG_ENABLE_OBJC_WEAK = YES; 656 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 657 | CLANG_WARN_BOOL_CONVERSION = YES; 658 | CLANG_WARN_COMMA = YES; 659 | CLANG_WARN_CONSTANT_CONVERSION = YES; 660 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 661 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 662 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 663 | CLANG_WARN_EMPTY_BODY = YES; 664 | CLANG_WARN_ENUM_CONVERSION = YES; 665 | CLANG_WARN_INFINITE_RECURSION = YES; 666 | CLANG_WARN_INT_CONVERSION = YES; 667 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 668 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 669 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 670 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 671 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 672 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 673 | CLANG_WARN_STRICT_PROTOTYPES = YES; 674 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 675 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 676 | CLANG_WARN_UNREACHABLE_CODE = YES; 677 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 678 | COPY_PHASE_STRIP = NO; 679 | CURRENT_PROJECT_VERSION = 1; 680 | DEBUG_INFORMATION_FORMAT = dwarf; 681 | ENABLE_STRICT_OBJC_MSGSEND = YES; 682 | ENABLE_TESTABILITY = YES; 683 | GCC_C_LANGUAGE_STANDARD = gnu11; 684 | GCC_DYNAMIC_NO_PIC = NO; 685 | GCC_NO_COMMON_BLOCKS = YES; 686 | GCC_OPTIMIZATION_LEVEL = 0; 687 | GCC_PREPROCESSOR_DEFINITIONS = ( 688 | "DEBUG=1", 689 | "$(inherited)", 690 | ); 691 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 692 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 693 | GCC_WARN_UNDECLARED_SELECTOR = YES; 694 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 695 | GCC_WARN_UNUSED_FUNCTION = YES; 696 | GCC_WARN_UNUSED_VARIABLE = YES; 697 | IPHONEOS_DEPLOYMENT_TARGET = 14.3; 698 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 699 | MTL_FAST_MATH = YES; 700 | ONLY_ACTIVE_ARCH = YES; 701 | SDKROOT = iphoneos; 702 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 703 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 704 | VERSIONING_SYSTEM = "apple-generic"; 705 | VERSION_INFO_PREFIX = ""; 706 | }; 707 | name = Debug; 708 | }; 709 | 84BB333E25A85EEF0056A35F /* Release */ = { 710 | isa = XCBuildConfiguration; 711 | buildSettings = { 712 | ALWAYS_SEARCH_USER_PATHS = NO; 713 | CLANG_ANALYZER_NONNULL = YES; 714 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 715 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 716 | CLANG_CXX_LIBRARY = "libc++"; 717 | CLANG_ENABLE_MODULES = YES; 718 | CLANG_ENABLE_OBJC_ARC = YES; 719 | CLANG_ENABLE_OBJC_WEAK = YES; 720 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 721 | CLANG_WARN_BOOL_CONVERSION = YES; 722 | CLANG_WARN_COMMA = YES; 723 | CLANG_WARN_CONSTANT_CONVERSION = YES; 724 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 725 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 726 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 727 | CLANG_WARN_EMPTY_BODY = YES; 728 | CLANG_WARN_ENUM_CONVERSION = YES; 729 | CLANG_WARN_INFINITE_RECURSION = YES; 730 | CLANG_WARN_INT_CONVERSION = YES; 731 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 732 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 733 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 734 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 735 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 736 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 737 | CLANG_WARN_STRICT_PROTOTYPES = YES; 738 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 739 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 740 | CLANG_WARN_UNREACHABLE_CODE = YES; 741 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 742 | COPY_PHASE_STRIP = NO; 743 | CURRENT_PROJECT_VERSION = 1; 744 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 745 | ENABLE_NS_ASSERTIONS = NO; 746 | ENABLE_STRICT_OBJC_MSGSEND = YES; 747 | GCC_C_LANGUAGE_STANDARD = gnu11; 748 | GCC_NO_COMMON_BLOCKS = YES; 749 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 750 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 751 | GCC_WARN_UNDECLARED_SELECTOR = YES; 752 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 753 | GCC_WARN_UNUSED_FUNCTION = YES; 754 | GCC_WARN_UNUSED_VARIABLE = YES; 755 | IPHONEOS_DEPLOYMENT_TARGET = 14.3; 756 | MTL_ENABLE_DEBUG_INFO = NO; 757 | MTL_FAST_MATH = YES; 758 | SDKROOT = iphoneos; 759 | SWIFT_COMPILATION_MODE = wholemodule; 760 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 761 | VALIDATE_PRODUCT = YES; 762 | VERSIONING_SYSTEM = "apple-generic"; 763 | VERSION_INFO_PREFIX = ""; 764 | }; 765 | name = Release; 766 | }; 767 | 84BB334025A85EEF0056A35F /* Debug */ = { 768 | isa = XCBuildConfiguration; 769 | buildSettings = { 770 | CLANG_ENABLE_MODULES = YES; 771 | CODE_SIGN_STYLE = Automatic; 772 | DEFINES_MODULE = YES; 773 | DEVELOPMENT_TEAM = MH79AD8W2K; 774 | DYLIB_COMPATIBILITY_VERSION = 1; 775 | DYLIB_CURRENT_VERSION = 1; 776 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 777 | INFOPLIST_FILE = TopContentPager/Info.plist; 778 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 779 | LD_RUNPATH_SEARCH_PATHS = ( 780 | "$(inherited)", 781 | "@executable_path/Frameworks", 782 | "@loader_path/Frameworks", 783 | ); 784 | PRODUCT_BUNDLE_IDENTIFIER = "tanaka-itsuki.TopContentPager"; 785 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 786 | SKIP_INSTALL = YES; 787 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 788 | SWIFT_VERSION = 5.0; 789 | TARGETED_DEVICE_FAMILY = "1,2"; 790 | }; 791 | name = Debug; 792 | }; 793 | 84BB334125A85EEF0056A35F /* Release */ = { 794 | isa = XCBuildConfiguration; 795 | buildSettings = { 796 | CLANG_ENABLE_MODULES = YES; 797 | CODE_SIGN_STYLE = Automatic; 798 | DEFINES_MODULE = YES; 799 | DEVELOPMENT_TEAM = MH79AD8W2K; 800 | DYLIB_COMPATIBILITY_VERSION = 1; 801 | DYLIB_CURRENT_VERSION = 1; 802 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 803 | INFOPLIST_FILE = TopContentPager/Info.plist; 804 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 805 | LD_RUNPATH_SEARCH_PATHS = ( 806 | "$(inherited)", 807 | "@executable_path/Frameworks", 808 | "@loader_path/Frameworks", 809 | ); 810 | PRODUCT_BUNDLE_IDENTIFIER = "tanaka-itsuki.TopContentPager"; 811 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 812 | SKIP_INSTALL = YES; 813 | SWIFT_VERSION = 5.0; 814 | TARGETED_DEVICE_FAMILY = "1,2"; 815 | }; 816 | name = Release; 817 | }; 818 | 84BB334325A85EEF0056A35F /* Debug */ = { 819 | isa = XCBuildConfiguration; 820 | buildSettings = { 821 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 822 | CODE_SIGN_STYLE = Automatic; 823 | DEVELOPMENT_TEAM = MH79AD8W2K; 824 | INFOPLIST_FILE = TopContentPagerTests/Info.plist; 825 | LD_RUNPATH_SEARCH_PATHS = ( 826 | "$(inherited)", 827 | "@executable_path/Frameworks", 828 | "@loader_path/Frameworks", 829 | ); 830 | PRODUCT_BUNDLE_IDENTIFIER = "tanaka-itsuki.TopContentPagerTests"; 831 | PRODUCT_NAME = "$(TARGET_NAME)"; 832 | SWIFT_VERSION = 5.0; 833 | TARGETED_DEVICE_FAMILY = "1,2"; 834 | }; 835 | name = Debug; 836 | }; 837 | 84BB334425A85EEF0056A35F /* Release */ = { 838 | isa = XCBuildConfiguration; 839 | buildSettings = { 840 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 841 | CODE_SIGN_STYLE = Automatic; 842 | DEVELOPMENT_TEAM = MH79AD8W2K; 843 | INFOPLIST_FILE = TopContentPagerTests/Info.plist; 844 | LD_RUNPATH_SEARCH_PATHS = ( 845 | "$(inherited)", 846 | "@executable_path/Frameworks", 847 | "@loader_path/Frameworks", 848 | ); 849 | PRODUCT_BUNDLE_IDENTIFIER = "tanaka-itsuki.TopContentPagerTests"; 850 | PRODUCT_NAME = "$(TARGET_NAME)"; 851 | SWIFT_VERSION = 5.0; 852 | TARGETED_DEVICE_FAMILY = "1,2"; 853 | }; 854 | name = Release; 855 | }; 856 | 84BB33CA25A8655B0056A35F /* Debug */ = { 857 | isa = XCBuildConfiguration; 858 | buildSettings = { 859 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 860 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 861 | CODE_SIGN_STYLE = Automatic; 862 | DEVELOPMENT_TEAM = MH79AD8W2K; 863 | INFOPLIST_FILE = Sample/Info.plist; 864 | LD_RUNPATH_SEARCH_PATHS = ( 865 | "$(inherited)", 866 | "@executable_path/Frameworks", 867 | ); 868 | PRODUCT_BUNDLE_IDENTIFIER = "tanaka-itsuki.Sample"; 869 | PRODUCT_NAME = "$(TARGET_NAME)"; 870 | SWIFT_VERSION = 5.0; 871 | TARGETED_DEVICE_FAMILY = "1,2"; 872 | }; 873 | name = Debug; 874 | }; 875 | 84BB33CB25A8655B0056A35F /* Release */ = { 876 | isa = XCBuildConfiguration; 877 | buildSettings = { 878 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 879 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 880 | CODE_SIGN_STYLE = Automatic; 881 | DEVELOPMENT_TEAM = MH79AD8W2K; 882 | INFOPLIST_FILE = Sample/Info.plist; 883 | LD_RUNPATH_SEARCH_PATHS = ( 884 | "$(inherited)", 885 | "@executable_path/Frameworks", 886 | ); 887 | PRODUCT_BUNDLE_IDENTIFIER = "tanaka-itsuki.Sample"; 888 | PRODUCT_NAME = "$(TARGET_NAME)"; 889 | SWIFT_VERSION = 5.0; 890 | TARGETED_DEVICE_FAMILY = "1,2"; 891 | }; 892 | name = Release; 893 | }; 894 | 84BB33CD25A8655B0056A35F /* Debug */ = { 895 | isa = XCBuildConfiguration; 896 | buildSettings = { 897 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 898 | BUNDLE_LOADER = "$(TEST_HOST)"; 899 | CODE_SIGN_STYLE = Automatic; 900 | DEVELOPMENT_TEAM = MH79AD8W2K; 901 | INFOPLIST_FILE = SampleTests/Info.plist; 902 | IPHONEOS_DEPLOYMENT_TARGET = 14.3; 903 | LD_RUNPATH_SEARCH_PATHS = ( 904 | "$(inherited)", 905 | "@executable_path/Frameworks", 906 | "@loader_path/Frameworks", 907 | ); 908 | PRODUCT_BUNDLE_IDENTIFIER = "tanaka-itsuki.SampleTests"; 909 | PRODUCT_NAME = "$(TARGET_NAME)"; 910 | SWIFT_VERSION = 5.0; 911 | TARGETED_DEVICE_FAMILY = "1,2"; 912 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Sample.app/Sample"; 913 | }; 914 | name = Debug; 915 | }; 916 | 84BB33CE25A8655B0056A35F /* Release */ = { 917 | isa = XCBuildConfiguration; 918 | buildSettings = { 919 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 920 | BUNDLE_LOADER = "$(TEST_HOST)"; 921 | CODE_SIGN_STYLE = Automatic; 922 | DEVELOPMENT_TEAM = MH79AD8W2K; 923 | INFOPLIST_FILE = SampleTests/Info.plist; 924 | IPHONEOS_DEPLOYMENT_TARGET = 14.3; 925 | LD_RUNPATH_SEARCH_PATHS = ( 926 | "$(inherited)", 927 | "@executable_path/Frameworks", 928 | "@loader_path/Frameworks", 929 | ); 930 | PRODUCT_BUNDLE_IDENTIFIER = "tanaka-itsuki.SampleTests"; 931 | PRODUCT_NAME = "$(TARGET_NAME)"; 932 | SWIFT_VERSION = 5.0; 933 | TARGETED_DEVICE_FAMILY = "1,2"; 934 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Sample.app/Sample"; 935 | }; 936 | name = Release; 937 | }; 938 | 84BB33D025A8655B0056A35F /* Debug */ = { 939 | isa = XCBuildConfiguration; 940 | buildSettings = { 941 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 942 | CODE_SIGN_STYLE = Automatic; 943 | DEVELOPMENT_TEAM = MH79AD8W2K; 944 | INFOPLIST_FILE = SampleUITests/Info.plist; 945 | LD_RUNPATH_SEARCH_PATHS = ( 946 | "$(inherited)", 947 | "@executable_path/Frameworks", 948 | "@loader_path/Frameworks", 949 | ); 950 | PRODUCT_BUNDLE_IDENTIFIER = "tanaka-itsuki.SampleUITests"; 951 | PRODUCT_NAME = "$(TARGET_NAME)"; 952 | SWIFT_VERSION = 5.0; 953 | TARGETED_DEVICE_FAMILY = "1,2"; 954 | TEST_TARGET_NAME = Sample; 955 | }; 956 | name = Debug; 957 | }; 958 | 84BB33D125A8655B0056A35F /* Release */ = { 959 | isa = XCBuildConfiguration; 960 | buildSettings = { 961 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 962 | CODE_SIGN_STYLE = Automatic; 963 | DEVELOPMENT_TEAM = MH79AD8W2K; 964 | INFOPLIST_FILE = SampleUITests/Info.plist; 965 | LD_RUNPATH_SEARCH_PATHS = ( 966 | "$(inherited)", 967 | "@executable_path/Frameworks", 968 | "@loader_path/Frameworks", 969 | ); 970 | PRODUCT_BUNDLE_IDENTIFIER = "tanaka-itsuki.SampleUITests"; 971 | PRODUCT_NAME = "$(TARGET_NAME)"; 972 | SWIFT_VERSION = 5.0; 973 | TARGETED_DEVICE_FAMILY = "1,2"; 974 | TEST_TARGET_NAME = Sample; 975 | }; 976 | name = Release; 977 | }; 978 | /* End XCBuildConfiguration section */ 979 | 980 | /* Begin XCConfigurationList section */ 981 | 84BB332525A85EEF0056A35F /* Build configuration list for PBXProject "TopContentPager" */ = { 982 | isa = XCConfigurationList; 983 | buildConfigurations = ( 984 | 84BB333D25A85EEF0056A35F /* Debug */, 985 | 84BB333E25A85EEF0056A35F /* Release */, 986 | ); 987 | defaultConfigurationIsVisible = 0; 988 | defaultConfigurationName = Release; 989 | }; 990 | 84BB333F25A85EEF0056A35F /* Build configuration list for PBXNativeTarget "TopContentPager" */ = { 991 | isa = XCConfigurationList; 992 | buildConfigurations = ( 993 | 84BB334025A85EEF0056A35F /* Debug */, 994 | 84BB334125A85EEF0056A35F /* Release */, 995 | ); 996 | defaultConfigurationIsVisible = 0; 997 | defaultConfigurationName = Release; 998 | }; 999 | 84BB334225A85EEF0056A35F /* Build configuration list for PBXNativeTarget "TopContentPagerTests" */ = { 1000 | isa = XCConfigurationList; 1001 | buildConfigurations = ( 1002 | 84BB334325A85EEF0056A35F /* Debug */, 1003 | 84BB334425A85EEF0056A35F /* Release */, 1004 | ); 1005 | defaultConfigurationIsVisible = 0; 1006 | defaultConfigurationName = Release; 1007 | }; 1008 | 84BB33C925A8655B0056A35F /* Build configuration list for PBXNativeTarget "Sample" */ = { 1009 | isa = XCConfigurationList; 1010 | buildConfigurations = ( 1011 | 84BB33CA25A8655B0056A35F /* Debug */, 1012 | 84BB33CB25A8655B0056A35F /* Release */, 1013 | ); 1014 | defaultConfigurationIsVisible = 0; 1015 | defaultConfigurationName = Release; 1016 | }; 1017 | 84BB33CC25A8655B0056A35F /* Build configuration list for PBXNativeTarget "SampleTests" */ = { 1018 | isa = XCConfigurationList; 1019 | buildConfigurations = ( 1020 | 84BB33CD25A8655B0056A35F /* Debug */, 1021 | 84BB33CE25A8655B0056A35F /* Release */, 1022 | ); 1023 | defaultConfigurationIsVisible = 0; 1024 | defaultConfigurationName = Release; 1025 | }; 1026 | 84BB33CF25A8655B0056A35F /* Build configuration list for PBXNativeTarget "SampleUITests" */ = { 1027 | isa = XCConfigurationList; 1028 | buildConfigurations = ( 1029 | 84BB33D025A8655B0056A35F /* Debug */, 1030 | 84BB33D125A8655B0056A35F /* Release */, 1031 | ); 1032 | defaultConfigurationIsVisible = 0; 1033 | defaultConfigurationName = Release; 1034 | }; 1035 | /* End XCConfigurationList section */ 1036 | }; 1037 | rootObject = 84BB332225A85EEF0056A35F /* Project object */; 1038 | } 1039 | --------------------------------------------------------------------------------