├── .gitignore
├── Example
├── Gemfile
├── Gemfile.lock
├── MultiSelectionTable-Example.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ └── xcuserdata
│ │ └── nunogoncalves.xcuserdatad
│ │ ├── xcdebugger
│ │ └── Breakpoints_v2.xcbkptlist
│ │ └── xcschemes
│ │ ├── MultiSelectionTable-Example.xcscheme
│ │ ├── MultiSelectionTable.xcscheme
│ │ └── xcschememanagement.plist
├── MultiSelectionTable-Example.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
├── MultiSelectionTable-Example
│ ├── Albums
│ │ ├── Album+DictInit.swift
│ │ ├── Album.swift
│ │ ├── AlbumCell.swift
│ │ ├── AlbumCell.xib
│ │ ├── AlbunsViewController.swift
│ │ └── Band.swift
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ │ ├── AppIcon.appiconset
│ │ │ └── Contents.json
│ │ ├── Contents.json
│ │ ├── IronManMask.imageset
│ │ │ ├── Contents.json
│ │ │ └── IronManMask.png
│ │ ├── Music.imageset
│ │ │ ├── Contents.json
│ │ │ └── music-notes.png
│ │ ├── MusicNote.imageset
│ │ │ ├── Contents.json
│ │ │ └── MusicNote.png
│ │ ├── SupermanFlyLeft.imageset
│ │ │ ├── Contents.json
│ │ │ └── SuperManFlyLeft.png
│ │ └── SupermanFlyRight.imageset
│ │ │ ├── Contents.json
│ │ │ └── SuperManFlyRight.png
│ ├── Base.lproj
│ │ ├── LaunchScreen.storyboard
│ │ └── Main.storyboard
│ ├── BridgingHeader.h
│ ├── Heroes
│ │ ├── Hero+DictInit.swift
│ │ ├── Hero.swift
│ │ ├── HeroCell.swift
│ │ ├── HeroCell.xib
│ │ ├── HeroCellMover.swift
│ │ ├── HeroDetailsViewController.swift
│ │ ├── HeroesList.swift
│ │ ├── HeroesViewController.swift
│ │ └── SuperManAnimator.swift
│ ├── Info.plist
│ ├── Models
│ │ ├── NetworkError.swift
│ │ └── Result.swift
│ ├── UseCases
│ │ ├── Albums
│ │ │ └── Albums.Fetcher.swift
│ │ ├── Cache
│ │ │ └── ImageLoader.swift
│ │ ├── Heroes
│ │ │ └── Heroes.Fetcher.swift
│ │ └── Network
│ │ │ └── Network.Get.swift
│ ├── albums.json
│ └── covers
│ │ ├── 1000x1000.jpg
│ │ ├── Dark_Side_of_the_Moon.png
│ │ ├── Eurythmics_-_Touch.jpg
│ │ ├── PuddleOfMudd_-_ComeClean.jpg
│ │ ├── Thisisacting_albumcover.png
│ │ ├── YouveComeALongWayBaby2.jpg
│ │ ├── appetite-for-destruction.jpg
│ │ ├── badadchaseylane.jpg
│ │ ├── blackice.jpg
│ │ ├── californication.jpg
│ │ ├── funhouse.png
│ │ ├── ghoststoriesfull.jpg
│ │ ├── imnotdead.png
│ │ ├── itsmylife.jpg
│ │ ├── joyride.jpg
│ │ ├── nevermind.jpg
│ │ ├── queengreatest2.jpg
│ │ ├── rosenrot.jpg
│ │ ├── s&m.jpg
│ │ ├── switch.jpg
│ │ ├── threedollarbillyall.jpg
│ │ ├── thriller.jpg
│ │ ├── truthaboutlove.png
│ │ └── u2bestof.jpg
├── MultiSelectionTable.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
├── MultiSelectionTableTests
│ ├── Info.plist
│ └── MultiSelectionTableTests.swift
├── MultiSelectionTableUITests
│ ├── Info.plist
│ └── MultiSelectionTableUITests.swift
├── Podfile
├── Podfile.lock
└── Pods
│ ├── Local Podspecs
│ └── MultiSelectionTableView.podspec.json
│ ├── Manifest.lock
│ ├── Pods.xcodeproj
│ ├── project.pbxproj
│ └── xcuserdata
│ │ └── nunogoncalves.xcuserdatad
│ │ └── xcschemes
│ │ ├── MultiSelectionTableView.xcscheme
│ │ ├── Pods-MultiSelectionTable-Example.xcscheme
│ │ ├── Pods-MultiSelectionTable-ExampleTests.xcscheme
│ │ ├── Pods-MultiSelectionTable-ExampleUITests.xcscheme
│ │ └── xcschememanagement.plist
│ └── Target Support Files
│ ├── MultiSelectionTableView
│ ├── Info.plist
│ ├── MultiSelectionTableView-Info.plist
│ ├── MultiSelectionTableView-dummy.m
│ ├── MultiSelectionTableView-prefix.pch
│ ├── MultiSelectionTableView-umbrella.h
│ ├── MultiSelectionTableView.modulemap
│ └── MultiSelectionTableView.xcconfig
│ ├── Pods-MultiSelectionTable-Example
│ ├── Info.plist
│ ├── Pods-MultiSelectionTable-Example-Info.plist
│ ├── Pods-MultiSelectionTable-Example-acknowledgements.markdown
│ ├── Pods-MultiSelectionTable-Example-acknowledgements.plist
│ ├── Pods-MultiSelectionTable-Example-dummy.m
│ ├── Pods-MultiSelectionTable-Example-frameworks.sh
│ ├── Pods-MultiSelectionTable-Example-resources.sh
│ ├── Pods-MultiSelectionTable-Example-umbrella.h
│ ├── Pods-MultiSelectionTable-Example.debug.xcconfig
│ ├── Pods-MultiSelectionTable-Example.modulemap
│ └── Pods-MultiSelectionTable-Example.release.xcconfig
│ ├── Pods-MultiSelectionTable-ExampleTests
│ ├── Info.plist
│ ├── Pods-MultiSelectionTable-ExampleTests-Info.plist
│ ├── Pods-MultiSelectionTable-ExampleTests-acknowledgements.markdown
│ ├── Pods-MultiSelectionTable-ExampleTests-acknowledgements.plist
│ ├── Pods-MultiSelectionTable-ExampleTests-dummy.m
│ ├── Pods-MultiSelectionTable-ExampleTests-frameworks.sh
│ ├── Pods-MultiSelectionTable-ExampleTests-resources.sh
│ ├── Pods-MultiSelectionTable-ExampleTests-umbrella.h
│ ├── Pods-MultiSelectionTable-ExampleTests.debug.xcconfig
│ ├── Pods-MultiSelectionTable-ExampleTests.modulemap
│ └── Pods-MultiSelectionTable-ExampleTests.release.xcconfig
│ └── Pods-MultiSelectionTable-ExampleUITests
│ ├── Info.plist
│ ├── Pods-MultiSelectionTable-ExampleUITests-Info.plist
│ ├── Pods-MultiSelectionTable-ExampleUITests-acknowledgements.markdown
│ ├── Pods-MultiSelectionTable-ExampleUITests-acknowledgements.plist
│ ├── Pods-MultiSelectionTable-ExampleUITests-dummy.m
│ ├── Pods-MultiSelectionTable-ExampleUITests-frameworks.sh
│ ├── Pods-MultiSelectionTable-ExampleUITests-resources.sh
│ ├── Pods-MultiSelectionTable-ExampleUITests-umbrella.h
│ ├── Pods-MultiSelectionTable-ExampleUITests.debug.xcconfig
│ ├── Pods-MultiSelectionTable-ExampleUITests.modulemap
│ └── Pods-MultiSelectionTable-ExampleUITests.release.xcconfig
├── LICENSE.md
├── MultiSelectionTableView.podspec
├── README.md
├── Resources
├── MultiSelectionTableView1.gif
├── MultiselectionSupermanAnimation.gif
├── StyleBlack.png
├── StyleGreenBlue.png
├── StyleRed.png
└── StyleWhite.png
└── Source
├── Animators
├── Selection
│ ├── CellSelectionAnimator.swift
│ └── CellSelectionPulseAnimator.swift
└── Translation
│ ├── CellMover.swift
│ ├── CellReplacer.swift
│ └── CellTransitionAnimator.swift
├── DataSource.swift
├── ItemIndex.swift
├── MultiSelectionDataSource.swift
├── MultiSelectionDelegate.swift
├── MultiSelectionTableView.swift
├── TableViewHeader.swift
└── UIControlEvents.swift
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | *.xcuserstate
3 |
4 | Example/MultiSelectionTable.xcworkspace/xcuserdata/nunogoncalves.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
5 |
--------------------------------------------------------------------------------
/Example/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 |
3 | gem 'cocoapods', '~> 1.8.3'
4 |
--------------------------------------------------------------------------------
/Example/Gemfile.lock:
--------------------------------------------------------------------------------
1 | GEM
2 | remote: https://rubygems.org/
3 | specs:
4 | CFPropertyList (3.0.2)
5 | activesupport (4.2.11.1)
6 | i18n (~> 0.7)
7 | minitest (~> 5.1)
8 | thread_safe (~> 0.3, >= 0.3.4)
9 | tzinfo (~> 1.1)
10 | algoliasearch (1.27.1)
11 | httpclient (~> 2.8, >= 2.8.3)
12 | json (>= 1.5.1)
13 | atomos (0.1.3)
14 | claide (1.0.3)
15 | cocoapods (1.8.4)
16 | activesupport (>= 4.0.2, < 5)
17 | claide (>= 1.0.2, < 2.0)
18 | cocoapods-core (= 1.8.4)
19 | cocoapods-deintegrate (>= 1.0.3, < 2.0)
20 | cocoapods-downloader (>= 1.2.2, < 2.0)
21 | cocoapods-plugins (>= 1.0.0, < 2.0)
22 | cocoapods-search (>= 1.0.0, < 2.0)
23 | cocoapods-stats (>= 1.0.0, < 2.0)
24 | cocoapods-trunk (>= 1.4.0, < 2.0)
25 | cocoapods-try (>= 1.1.0, < 2.0)
26 | colored2 (~> 3.1)
27 | escape (~> 0.0.4)
28 | fourflusher (>= 2.3.0, < 3.0)
29 | gh_inspector (~> 1.0)
30 | molinillo (~> 0.6.6)
31 | nap (~> 1.0)
32 | ruby-macho (~> 1.4)
33 | xcodeproj (>= 1.11.1, < 2.0)
34 | cocoapods-core (1.8.4)
35 | activesupport (>= 4.0.2, < 6)
36 | algoliasearch (~> 1.0)
37 | concurrent-ruby (~> 1.1)
38 | fuzzy_match (~> 2.0.4)
39 | nap (~> 1.0)
40 | cocoapods-deintegrate (1.0.4)
41 | cocoapods-downloader (1.3.0)
42 | cocoapods-plugins (1.0.0)
43 | nap
44 | cocoapods-search (1.0.0)
45 | cocoapods-stats (1.1.0)
46 | cocoapods-trunk (1.4.1)
47 | nap (>= 0.8, < 2.0)
48 | netrc (~> 0.11)
49 | cocoapods-try (1.1.0)
50 | colored2 (3.1.2)
51 | concurrent-ruby (1.1.5)
52 | escape (0.0.4)
53 | fourflusher (2.3.1)
54 | fuzzy_match (2.0.4)
55 | gh_inspector (1.1.3)
56 | httpclient (2.8.3)
57 | i18n (0.9.5)
58 | concurrent-ruby (~> 1.0)
59 | json (2.2.0)
60 | minitest (5.13.0)
61 | molinillo (0.6.6)
62 | nanaimo (0.2.6)
63 | nap (1.1.0)
64 | netrc (0.11.0)
65 | ruby-macho (1.4.0)
66 | thread_safe (0.3.6)
67 | tzinfo (1.2.5)
68 | thread_safe (~> 0.1)
69 | xcodeproj (1.13.0)
70 | CFPropertyList (>= 2.3.3, < 4.0)
71 | atomos (~> 0.1.3)
72 | claide (>= 1.0.2, < 2.0)
73 | colored2 (~> 3.1)
74 | nanaimo (~> 0.2.6)
75 |
76 | PLATFORMS
77 | ruby
78 |
79 | DEPENDENCIES
80 | cocoapods (~> 1.8.3)
81 |
82 | BUNDLED WITH
83 | 2.0.2
84 |
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example.xcodeproj/xcuserdata/nunogoncalves.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example.xcodeproj/xcuserdata/nunogoncalves.xcuserdatad/xcschemes/MultiSelectionTable-Example.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
43 |
44 |
54 |
56 |
62 |
63 |
64 |
65 |
69 |
70 |
71 |
72 |
73 |
74 |
80 |
82 |
88 |
89 |
90 |
91 |
93 |
94 |
97 |
98 |
99 |
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example.xcodeproj/xcuserdata/nunogoncalves.xcuserdatad/xcschemes/MultiSelectionTable.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
43 |
49 |
50 |
51 |
52 |
53 |
59 |
60 |
61 |
62 |
63 |
64 |
74 |
76 |
82 |
83 |
84 |
85 |
89 |
90 |
91 |
92 |
93 |
94 |
100 |
102 |
108 |
109 |
110 |
111 |
113 |
114 |
117 |
118 |
119 |
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example.xcodeproj/xcuserdata/nunogoncalves.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | MultiSelectionTable-Example.xcscheme
8 |
9 | orderHint
10 | 6
11 |
12 | MultiSelectionTable.xcscheme
13 |
14 | isShown
15 |
16 | orderHint
17 | 0
18 |
19 |
20 | SuppressBuildableAutocreation
21 |
22 | 85C315111DECEFF30095AFA1
23 |
24 | primary
25 |
26 |
27 | 85C315281DECEFF30095AFA1
28 |
29 | primary
30 |
31 |
32 | 85C315331DECEFF30095AFA1
33 |
34 | primary
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example/Albums/Album+DictInit.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Album+DictInit.swift
3 | // MultiSelectionTable
4 | //
5 | // Created by Nuno Gonçalves on 10/12/16.
6 | // Copyright © 2016 Nuno Gonçalves. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension Album {
12 |
13 | init?(dictionary: [String : Any]) {
14 | guard let name = dictionary["name"] as? String,
15 | let year = dictionary["year"] as? Int,
16 | let coverImageName = dictionary["coverImageUrl"] as? String,
17 | let bandDic = dictionary["band"] as? [String: Any],
18 | let bandName = bandDic["name"] as? String
19 | else {
20 | return nil
21 | }
22 |
23 | self.band = Band(name: bandName)
24 | self.name = name
25 | self.coverImageName = coverImageName
26 | print(coverImageName)
27 | self.year = year
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example/Albums/Album.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Album.swift
3 | // MultiSelectionTable
4 | //
5 | // Created by Nuno Gonçalves on 29/11/16.
6 | // Copyright © 2016 Nuno Gonçalves. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | struct Album : Equatable {
12 |
13 | let band: Band
14 | let name: String
15 | let coverImageName: String
16 | let year: Int
17 |
18 | static func all(finished: @escaping ([Album]) -> ()) {
19 | Albums.Fetcher.fetch(got: finished)
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example/Albums/AlbumCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AlbumCell.swift
3 | // MultiSelectionTable
4 | //
5 | // Created by Nuno Gonçalves on 30/11/16.
6 | // Copyright © 2016 Nuno Gonçalves. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class AlbumCell: UITableViewCell {
12 |
13 | @IBOutlet weak var albumImageView: UIImageView!
14 | @IBOutlet weak var nameLabel: UILabel!
15 | @IBOutlet weak var subtitleLabel: UILabel!
16 | @IBOutlet weak var yearLabel: UILabel!
17 |
18 | @IBOutlet weak var bottomLineHeightConstraint: NSLayoutConstraint!
19 |
20 | override func awakeFromNib() {
21 | super.awakeFromNib()
22 |
23 | bottomLineHeightConstraint.constant = 0.5
24 | }
25 |
26 | override func prepareForReuse() {
27 | albumImageView.image = nil
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example/Albums/AlbumCell.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 |
41 |
47 |
48 |
49 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example/Albums/AlbunsViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // MultiSelectionTable
4 | //
5 | // Created by Nuno Gonçalves on 28/11/16.
6 | // Copyright © 2016 Nuno Gonçalves. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import MultiSelectionTableView
11 |
12 | class AlbunsViewController: UIViewController {
13 |
14 | @IBOutlet weak var multiSelectionTableContainer: UIStackView!
15 | @IBOutlet weak var searchTextField: UITextField!
16 |
17 | override var preferredStatusBarStyle: UIStatusBarStyle { return .lightContent }
18 |
19 | private var multiSelectionDataSource: MultiSelectionDataSource!
20 | @IBOutlet fileprivate weak var multiSelectionTableView: MultiSelectionTableView!
21 |
22 | fileprivate var filteredAlbuns: [Album] = [] {
23 | didSet {
24 | multiSelectionDataSource.allItems = filteredAlbuns
25 | }
26 | }
27 | fileprivate var allAlbums: [Album] = []
28 |
29 | @IBAction func clearSearchText() {
30 | searchTextField.text = nil
31 | searchTextField.resignFirstResponder()
32 | filteredAlbuns = allAlbums
33 | }
34 |
35 | @IBAction func textUpdated(_ sender: UITextField) {
36 | if let searchText = sender.text,
37 | searchText.count > 0 {
38 | filteredAlbuns = allAlbums.filter { $0.name.lowercased().contains(searchText.lowercased()) }
39 | } else {
40 | filteredAlbuns = allAlbums
41 | }
42 | }
43 |
44 | override func viewDidLoad() {
45 | super.viewDidLoad()
46 |
47 | multiSelectionDataSource = MultiSelectionDataSource(multiSelectionTableView: multiSelectionTableView)
48 | multiSelectionDataSource.delegate = self
49 | multiSelectionDataSource.register(nib: UINib(nibName: "AlbumCell", bundle: nil), for: "AlbumCell")
50 |
51 | Album.all { [weak self] albums in
52 | self?.allAlbums = albums
53 | self?.multiSelectionDataSource.allItems = albums
54 | }
55 |
56 | multiSelectionTableView.dataSource = multiSelectionDataSource
57 | multiSelectionTableView.allItemsContentInset = UIEdgeInsets(top: 110, left: 0, bottom: 0, right: 0)
58 | multiSelectionTableView.selectedItemsContentInset = UIEdgeInsets(top: 110, left: 0, bottom: 0, right: 0)
59 | multiSelectionTableView.addTarget(self, action: #selector(selectedItem(multiSelectionTableView:)), for: .itemSelected)
60 | multiSelectionTableView.addTarget(self, action: #selector(unselectedItem(multiSelectionTableView:)), for: .itemUnselected)
61 | }
62 |
63 | @objc private func selectedItem(multiSelectionTableView: MultiSelectionTableView) {
64 | print("selected item")
65 | }
66 |
67 | @objc private func unselectedItem(multiSelectionTableView: MultiSelectionTableView) {
68 | print("unselected item")
69 | }
70 | }
71 |
72 | extension AlbunsViewController : MultiSelectionTableDelegate {
73 |
74 | func paint(_ cell: UITableViewCell, at indexPath: IndexPath, in tableView: UITableView, with item: Any) {
75 | if let cell = cell as? AlbumCell,
76 | let album = item as? Album {
77 | cell.nameLabel.text = album.band.name
78 | cell.subtitleLabel.text = album.name
79 | cell.yearLabel.text = "\(album.year)"
80 |
81 | let image = UIImage(named: album.coverImageName)
82 | cell.albumImageView.image = image
83 | }
84 | }
85 | }
86 |
87 | extension AlbunsViewController : UITextFieldDelegate {
88 |
89 | func textFieldShouldReturn(_ textField: UITextField) -> Bool {
90 | textField.resignFirstResponder()
91 | return true
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example/Albums/Band.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Band.swift
3 | // MultiSelectionTable
4 | //
5 | // Created by Nuno Gonçalves on 29/11/16.
6 | // Copyright © 2016 Nuno Gonçalves. All rights reserved.
7 | //
8 |
9 | struct Band: Equatable {
10 |
11 | let name: String
12 |
13 | static func ==(left: Band, right: Band) -> Bool {
14 | return left.name == right.name
15 | }
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // MultiSelectionTable
4 | //
5 | // Created by Nuno Gonçalves on 28/11/16.
6 | // Copyright © 2016 Nuno Gonçalves. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import CoreData
11 |
12 | @UIApplicationMain
13 | class AppDelegate: UIResponder, UIApplicationDelegate {
14 |
15 | var window: UIWindow?
16 |
17 | func application(
18 | _ application: UIApplication,
19 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
20 | ) -> Bool {
21 | return true
22 | }
23 |
24 | func applicationWillResignActive(_ application: UIApplication) {}
25 | func applicationDidEnterBackground(_ application: UIApplication) {}
26 | func applicationWillEnterForeground(_ application: UIApplication) {}
27 | func applicationDidBecomeActive(_ application: UIApplication) {}
28 | func applicationWillTerminate(_ application: UIApplication) {}
29 | }
30 |
31 |
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "29x29",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "29x29",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "40x40",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "40x40",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "60x60",
36 | "scale" : "2x"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "size" : "60x60",
41 | "scale" : "3x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "20x20",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "20x20",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "29x29",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "29x29",
61 | "scale" : "2x"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "size" : "40x40",
66 | "scale" : "1x"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "size" : "40x40",
71 | "scale" : "2x"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "size" : "76x76",
76 | "scale" : "1x"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "size" : "76x76",
81 | "scale" : "2x"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "size" : "83.5x83.5",
86 | "scale" : "2x"
87 | }
88 | ],
89 | "info" : {
90 | "version" : 1,
91 | "author" : "xcode"
92 | }
93 | }
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example/Assets.xcassets/IronManMask.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "IronManMask.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example/Assets.xcassets/IronManMask.imageset/IronManMask.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nunogoncalves/iOS-MultiSelectionTable/f5ceb6a15e1eb5db71308384fddcb4e817021024/Example/MultiSelectionTable-Example/Assets.xcassets/IronManMask.imageset/IronManMask.png
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example/Assets.xcassets/Music.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "music-notes.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example/Assets.xcassets/Music.imageset/music-notes.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nunogoncalves/iOS-MultiSelectionTable/f5ceb6a15e1eb5db71308384fddcb4e817021024/Example/MultiSelectionTable-Example/Assets.xcassets/Music.imageset/music-notes.png
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example/Assets.xcassets/MusicNote.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "MusicNote.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example/Assets.xcassets/MusicNote.imageset/MusicNote.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nunogoncalves/iOS-MultiSelectionTable/f5ceb6a15e1eb5db71308384fddcb4e817021024/Example/MultiSelectionTable-Example/Assets.xcassets/MusicNote.imageset/MusicNote.png
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example/Assets.xcassets/SupermanFlyLeft.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "SuperManFlyLeft.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example/Assets.xcassets/SupermanFlyLeft.imageset/SuperManFlyLeft.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nunogoncalves/iOS-MultiSelectionTable/f5ceb6a15e1eb5db71308384fddcb4e817021024/Example/MultiSelectionTable-Example/Assets.xcassets/SupermanFlyLeft.imageset/SuperManFlyLeft.png
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example/Assets.xcassets/SupermanFlyRight.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "SuperManFlyRight.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example/Assets.xcassets/SupermanFlyRight.imageset/SuperManFlyRight.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nunogoncalves/iOS-MultiSelectionTable/f5ceb6a15e1eb5db71308384fddcb4e817021024/Example/MultiSelectionTable-Example/Assets.xcassets/SupermanFlyRight.imageset/SuperManFlyRight.png
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example/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 |
27 |
28 |
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example/BridgingHeader.h:
--------------------------------------------------------------------------------
1 | //
2 | // BridgingHeader.h
3 | // MultiSelectionTable
4 | //
5 | // Created by Nuno Gonçalves on 09/12/16.
6 | // Copyright © 2016 Nuno Gonçalves. All rights reserved.
7 | //
8 |
9 | #ifndef BridgingHeader_h
10 | #define BridgingHeader_h
11 |
12 |
13 | #endif /* BridgingHeader_h */
14 |
15 | #import
16 |
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example/Heroes/Hero+DictInit.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Hero+DictInit.swift
3 | // MultiSelectionTable
4 | //
5 | // Created by Nuno Gonçalves on 10/12/16.
6 | // Copyright © 2016 Nuno Gonçalves. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension Hero {
12 |
13 | init?(dictionary: [String : Any]) {
14 | guard let id = dictionary["id"] as? Int,
15 | let name = dictionary["name"] as? String,
16 | let thumb = dictionary["thumbnail"] as? [String : String],
17 | let thumbPath = thumb["path"],
18 | let thumbExtension = thumb["extension"],
19 | let url = URL(string: "\(thumbPath).\(thumbExtension)")
20 | else {
21 | return nil
22 | }
23 |
24 | self.id = id
25 | self.name = name
26 | self.imageURL = url
27 | self.description = dictionary["description"] as? String
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example/Heroes/Hero.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Hero.swift
3 | // MultiSelectionTable
4 | //
5 | // Created by Nuno Gonçalves on 09/12/16.
6 | // Copyright © 2016 Nuno Gonçalves. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | struct Hero : Equatable {
12 | let id: Int
13 | let name: String
14 | let imageURL: URL
15 | let description: String?
16 |
17 | static func all(named name: String? = nil, in page: Int, finished: @escaping (HeroesList) -> ()) {
18 | Heroes.Fetcher.fetch(named: name, in: page, got: finished)
19 | }
20 |
21 | static func ==(leftHero: Hero, rightHero: Hero) -> Bool {
22 | return leftHero.id == rightHero.id && leftHero.name == rightHero.name
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example/Heroes/HeroCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HeroCell.swift
3 | // MultiSelectionTable
4 | //
5 | // Created by Nuno Gonçalves on 09/12/16.
6 | // Copyright © 2016 Nuno Gonçalves. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class HeroCell: UITableViewCell {
12 |
13 | @IBOutlet weak var heroImageView: UIImageView!
14 | @IBOutlet weak var heroNameLabel: UILabel!
15 | @IBOutlet weak var bottomLine: UIView!
16 |
17 | @IBAction func infoTapped() {
18 | showInfo?()
19 | }
20 |
21 | var showInfo: (() -> ())?
22 |
23 | override func awakeFromNib() {
24 | super.awakeFromNib()
25 | }
26 |
27 | override func prepareForReuse() {
28 | heroImageView.image = nil
29 | bottomLine.isHidden = false
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example/Heroes/HeroCell.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 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example/Heroes/HeroCellMover.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HeroCellMover.swift
3 | // MultiSelectionTable
4 | //
5 | // Created by Nuno Gonçalves on 09/12/16.
6 | // Copyright © 2016 Nuno Gonçalves. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import MultiSelectionTableView
11 |
12 | class HeroCellMover : CellTransitionAnimator {
13 |
14 | func selectionTransition(in containerView: UIView,
15 | fromTableView: UITableView,
16 | fromIndexPath: IndexPath,
17 | toTableView: UITableView,
18 | toIndexPath: IndexPath) {
19 |
20 | toTableView.insertRows(at: [toIndexPath], with: .left)
21 | fromTableView.deleteRows(at: [fromIndexPath], with: .right)
22 | }
23 |
24 | func unselectionTransition(in containerView: UIView,
25 | fromTableView: UITableView,
26 | fromIndexPath: IndexPath,
27 | toTableView: UITableView,
28 | toIndexPath: IndexPath) {
29 |
30 | toTableView.insertRows(at: [toIndexPath], with: .right)
31 | fromTableView.deleteRows(at: [fromIndexPath], with: .left)
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example/Heroes/HeroDetailsViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HeroDetailsViewController.swift
3 | // MultiSelectionTable-Example
4 | //
5 | // Created by Nuno Gonçalves on 17/12/16.
6 | // Copyright © 2016 Nuno Gonçalves. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class HeroDetailsViewController : UIViewController {
12 |
13 | @IBOutlet weak var descriptionLabel: UILabel!
14 | @IBOutlet weak var heroImageView: UIImageView!
15 |
16 | @IBAction func closeTapped() {
17 | dismiss(animated: true, completion: nil)
18 | }
19 |
20 | var hero: Hero!
21 |
22 | override func viewDidLoad() {
23 | super.viewDidLoad()
24 | descriptionLabel.text = hero.description
25 |
26 | Cache.ImageLoader().image(with: hero.imageURL) { image in
27 | self.heroImageView.image = image
28 | }
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example/Heroes/HeroesList.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HeroesGroup.swift
3 | // MultiSelectionTable-Example
4 | //
5 | // Created by Nuno Gonçalves on 16/12/16.
6 | // Copyright © 2016 Nuno Gonçalves. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | struct HeroesList {
12 | static let emptyHeroesList = HeroesList(heroes: [], totalCount: 0, currentPage: 0, totalPages: 0)
13 |
14 | let heroes: [Hero]
15 |
16 | let totalCount: Int
17 | let currentPage: Int
18 | let totalPages: Int
19 |
20 | var hasMorePages: Bool {
21 | return currentPage < totalPages
22 | }
23 |
24 | var isFirstPage: Bool {
25 | return currentPage == 0
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example/Heroes/HeroesViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HeroesViewController.swift
3 | // MultiSelectionTable
4 | //
5 | // Created by Nuno Gonçalves on 08/12/16.
6 | // Copyright © 2016 Nuno Gonçalves. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import MultiSelectionTableView
11 |
12 | class HeroesViewController : UIViewController {
13 |
14 | @IBOutlet weak var multiSelectionTableContainer: UIStackView!
15 | @IBOutlet weak var searchTextField: UITextField!
16 |
17 | override var preferredStatusBarStyle: UIStatusBarStyle { return .lightContent }
18 |
19 | private var multiSelectionDataSource: MultiSelectionDataSource!
20 | @IBOutlet fileprivate weak var multiSelectionTableView: MultiSelectionTableView!
21 |
22 | fileprivate lazy var mainStoryboard: UIStoryboard = {
23 | return UIStoryboard(name: "Main", bundle: nil)
24 | }()
25 |
26 | @IBAction func clearSearchText() {
27 | searchTextField.text = nil
28 | searchTextField.resignFirstResponder()
29 | searchText = ""
30 | }
31 |
32 | fileprivate var searchText = "" {
33 | didSet {
34 | multiSelectionDataSource.allItems = []
35 | searchHeroes()
36 | }
37 | }
38 |
39 | fileprivate func searchHeroes(in page: Int = 0) {
40 | guard !isLoading else { return }
41 |
42 | if page == 0 {
43 | let loadingView = UIActivityIndicatorView()
44 | loadingView.transform = CGAffineTransform.init(scaleX: 2, y: 2)
45 | loadingView.startAnimating()
46 | multiSelectionTableView.stateView = loadingView
47 | }
48 | isLoading = true
49 | Hero.all(named: searchText, in: page) { [weak self] heroesList in
50 | self?.multiSelectionTableView.stateView = nil
51 | self?.heroesList = heroesList
52 | self?.isLoading = false
53 | }
54 | }
55 |
56 | fileprivate var heroesList: HeroesList = HeroesList.emptyHeroesList {
57 | didSet {
58 | if heroesList.isFirstPage {
59 | multiSelectionDataSource.allItems = heroesList.heroes
60 | } else {
61 | multiSelectionDataSource.allItems.append(contentsOf: heroesList.heroes)
62 | }
63 | if heroesList.heroes.count <= 1 {
64 | let label = UILabel()
65 | label.text = "No heroes"
66 | label.textColor = .white
67 | label.textAlignment = .center
68 | multiSelectionTableView.stateView = label
69 | }
70 | }
71 | }
72 |
73 | fileprivate let imageLoader = Cache.ImageLoader.shared
74 |
75 | fileprivate var isLoading = false
76 |
77 | override func viewDidLoad() {
78 | super.viewDidLoad()
79 |
80 | multiSelectionDataSource = MultiSelectionDataSource(multiSelectionTableView: multiSelectionTableView)
81 | multiSelectionDataSource.delegate = self
82 | multiSelectionDataSource.register(nib: UINib(nibName: "HeroCell", bundle: nil), for: "HeroCell")
83 |
84 | multiSelectionDataSource.allItems = heroesList.heroes
85 | searchHeroes()
86 |
87 | multiSelectionTableView.dataSource = multiSelectionDataSource
88 | multiSelectionTableView.allItemsContentInset = UIEdgeInsets(top: 105, left: 0, bottom: 0, right: 0)
89 | multiSelectionTableView.selectedItemsContentInset = UIEdgeInsets(top: 105, left: 0, bottom: 0, right: 0)
90 | multiSelectionTableView.supportsPagination = true
91 | multiSelectionTableView.paginationNotificationRowIndex = 20
92 | multiSelectionTableView.cellAnimator = CellSelectionPulseAnimator(pulseColor: .black)
93 | multiSelectionTableView.cellTransitioner = SuperManAnimator()
94 |
95 | multiSelectionTableView.addTarget(self, action: #selector(loadMoreHeroes(multiSelectionTableView:)), for: .scrollReachingEnd)
96 | }
97 |
98 | @objc private func loadMoreHeroes(multiSelectionTableView: MultiSelectionTableView) {
99 | if heroesList.hasMorePages {
100 | searchHeroes(in: heroesList.currentPage + 1)
101 | }
102 | }
103 |
104 | }
105 |
106 | extension HeroesViewController : MultiSelectionTableDelegate {
107 |
108 | func paint(
109 | _ cell: UITableViewCell,
110 | at indexPath: IndexPath,
111 | in tableView: UITableView, with item: Any
112 | ) {
113 | if let cell = cell as? HeroCell,
114 | let hero = item as? Hero {
115 | cell.heroNameLabel.text = hero.name
116 | if let image = imageLoader.cachedImage(with: hero.imageURL) {
117 | cell.heroImageView.image = image
118 | } else {
119 | imageLoader.image(with: hero.imageURL) { image in
120 | if let cell = tableView.cellForRow(at: indexPath) as? HeroCell {
121 | cell.heroImageView.image = image
122 | }
123 | }
124 | }
125 |
126 | cell.showInfo = { [weak self] in
127 | if let heroViewController = self?.mainStoryboard.instantiateViewController(withIdentifier: "HeroDetailsViewController") as? HeroDetailsViewController {
128 | heroViewController.hero = hero
129 | self?.show(heroViewController, sender: self)
130 | }
131 | }
132 | }
133 | }
134 |
135 | }
136 |
137 | extension HeroesViewController : UITextFieldDelegate {
138 |
139 | func textFieldShouldReturn(_ textField: UITextField) -> Bool {
140 | searchText = textField.text ?? ""
141 | textField.resignFirstResponder()
142 | return true
143 | }
144 | }
145 |
146 |
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example/Heroes/SuperManAnimator.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SuperManAnimator.swift
3 | // MultiSelectionTable
4 | //
5 | // Created by Nuno Gonçalves on 09/12/16.
6 | //
7 | //
8 |
9 | import MultiSelectionTableView
10 |
11 | public class SuperManAnimator : CellTransitionAnimator {
12 |
13 | public init() {}
14 |
15 | private let duration: TimeInterval = 0.5
16 |
17 | public func selectionTransition(in containerView: UIView,
18 | fromTableView: UITableView,
19 | fromIndexPath: IndexPath,
20 | toTableView: UITableView,
21 | toIndexPath: IndexPath) {
22 |
23 | toTableView.insertRows(at: [toIndexPath], with: .top)
24 |
25 | var _newCellAdded: UITableViewCell?
26 |
27 | if let cell = toTableView.cellForRow(at: toIndexPath) {
28 | _newCellAdded = cell
29 | } else if let cell = toTableView.visibleCells.last {
30 | _newCellAdded = cell
31 | }
32 |
33 | guard let newCellAdded = _newCellAdded else { return }
34 |
35 | newCellAdded.contentView.isHidden = true
36 | let newCellConvertedFrame = newCellAdded.convert(newCellAdded.contentView.frame, to: containerView)
37 |
38 | guard let cellToDelete = fromTableView.cellForRow(at: fromIndexPath) else { return }
39 | (cellToDelete as? HeroCell)?.bottomLine.isHidden = true
40 |
41 | if let movingCell = cellToDelete.contentView.snapshotView(afterScreenUpdates: true) {
42 | let viewToHide = UIView(frame: CGRect(x: 0, y: 0, width: movingCell.frame.width, height: movingCell.frame.height))
43 | movingCell.addSubview(viewToHide)
44 | let superman = UIImageView(image: #imageLiteral(resourceName: "SupermanFlyRight"))
45 | superman.frame = CGRect(x: -90, y: movingCell.frame.midY - 20, width: 100, height: 40)
46 | movingCell.addSubview(superman)
47 |
48 | cellToDelete.contentView.isHidden = true
49 | containerView.addSubview(movingCell)
50 | movingCell.frame = fromTableView.convert(cellToDelete.frame, to: containerView)
51 |
52 | fromTableView.deleteRows(at: [fromIndexPath], with: .top)
53 |
54 | UIView.animate(withDuration: duration, animations: {
55 | movingCell.frame = newCellConvertedFrame
56 | }, completion: { _ in
57 | newCellAdded.contentView.isHidden = false
58 | cellToDelete.contentView.isHidden = false
59 | UIView.animate(withDuration: self.duration,
60 | animations: {
61 | superman.frame = superman.frame.offsetBy(dx: movingCell.frame.width + superman.frame.width,
62 | dy: 0)
63 | },
64 | completion: { _ in
65 | movingCell.removeFromSuperview()
66 | })
67 | viewToHide.isHidden = true
68 | })
69 | }
70 |
71 | }
72 |
73 | public func unselectionTransition(in containerView: UIView,
74 | fromTableView: UITableView,
75 | fromIndexPath: IndexPath,
76 | toTableView: UITableView,
77 | toIndexPath: IndexPath) {
78 |
79 | let newIndexPath = toIndexPath
80 | let allItemsTable = toTableView
81 |
82 | let indexPath = fromIndexPath
83 | let selectedItemsTable = fromTableView
84 |
85 | allItemsTable.insertRows(at: [newIndexPath], with: .bottom)
86 |
87 | var _newCellAdded: UITableViewCell?
88 |
89 | if let cell = allItemsTable.cellForRow(at: newIndexPath) {
90 | _newCellAdded = cell
91 | } else if let cell = allItemsTable.visibleCells.last {
92 | _newCellAdded = cell
93 | }
94 |
95 | guard let newCellAdded = _newCellAdded else { return }
96 |
97 | newCellAdded.contentView.isHidden = true
98 | let newCellConvertedFrame = newCellAdded.convert(newCellAdded.contentView.frame, to: containerView)
99 |
100 | guard let cellToDelete = selectedItemsTable.cellForRow(at: indexPath) else { return }
101 |
102 | if let movingCell = cellToDelete.contentView.snapshotView(afterScreenUpdates: false) {
103 | let viewToHide = UIView(frame: CGRect(x: 0, y: 0, width: movingCell.frame.width, height: movingCell.frame.height))
104 | movingCell.addSubview(viewToHide)
105 | let superman = UIImageView(image: #imageLiteral(resourceName: "SupermanFlyLeft"))
106 | superman.frame = CGRect(x: movingCell.frame.width - 5, y: movingCell.frame.midY - 20, width: 100, height: 40)
107 | movingCell.addSubview(superman)
108 |
109 | cellToDelete.contentView.isHidden = true
110 | containerView.addSubview(movingCell)
111 | movingCell.frame = selectedItemsTable.convert(cellToDelete.frame, to: containerView)
112 |
113 | selectedItemsTable.deleteRows(at: [indexPath], with: .top)
114 |
115 | UIView.animate(withDuration: duration, animations: {
116 | movingCell.frame = newCellConvertedFrame
117 | }, completion: { _ in
118 | newCellAdded.contentView.isHidden = false
119 | UIView.animate(withDuration: self.duration,
120 | animations: {
121 | superman.frame = superman.frame.offsetBy(dx: -(movingCell.frame.width + superman.frame.width),
122 | dy: 0)
123 | },
124 | completion: { _ in
125 | movingCell.removeFromSuperview()
126 | })
127 | viewToHide.isHidden = true
128 | })
129 | }
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSApplicationCategoryType
22 |
23 | LSRequiresIPhoneOS
24 |
25 | NSAppTransportSecurity
26 |
27 | NSAllowsArbitraryLoads
28 |
29 |
30 | UILaunchStoryboardName
31 | LaunchScreen
32 | UIMainStoryboardFile
33 | Main
34 | UIRequiredDeviceCapabilities
35 |
36 | armv7
37 |
38 | UIStatusBarStyle
39 | UIStatusBarStyleLightContent
40 | UISupportedInterfaceOrientations
41 |
42 | UIInterfaceOrientationPortrait
43 | UIInterfaceOrientationLandscapeLeft
44 | UIInterfaceOrientationLandscapeRight
45 |
46 | UISupportedInterfaceOrientations~ipad
47 |
48 | UIInterfaceOrientationPortrait
49 | UIInterfaceOrientationPortraitUpsideDown
50 | UIInterfaceOrientationLandscapeLeft
51 | UIInterfaceOrientationLandscapeRight
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example/Models/NetworkError.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NetworkError.swift
3 | // MultiSelectionTable
4 | //
5 | // Created by Nuno Gonçalves on 11/12/16.
6 | // Copyright © 2016 Nuno Gonçalves. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | enum NetworkError : Error {
12 | case parse
13 | case data
14 | }
15 |
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example/Models/Result.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Result.swift
3 | // MultiSelectionTable
4 | //
5 | // Created by Nuno Gonçalves on 11/12/16.
6 | // Copyright © 2016 Nuno Gonçalves. All rights reserved.
7 | //
8 |
9 | enum Result {
10 | case success(T)
11 | case failure(NetworkError)
12 | }
13 |
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example/UseCases/Albums/Albums.Fetcher.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Albums.Fetcher.swift
3 | // MultiSelectionTable
4 | //
5 | // Created by Nuno Gonçalves on 10/12/16.
6 | // Copyright © 2016 Nuno Gonçalves. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | struct Albums {
12 |
13 | struct Fetcher {
14 |
15 | static func fetch(got: @escaping ([Album]) -> ()) {
16 |
17 | guard let path = Bundle.main.path(forResource: "albums", ofType: "json"),
18 | let data = try? Data(contentsOf: URL(fileURLWithPath: path)),
19 | let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any]
20 | else { return }
21 |
22 | if let dicAlbums = json["albums"] as? [[String : Any]] {
23 | let albums = dicAlbums.compactMap { Album(dictionary: $0) }
24 | got(albums)
25 | } else {
26 | //We should really propagate the Result the the caller
27 | got([])
28 | }
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example/UseCases/Cache/ImageLoader.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ImageLoader.swift
3 | // MultiSelectionTable-Example
4 | //
5 | // Created by Nuno Gonçalves on 12/12/16.
6 | //
7 | //
8 |
9 | import UIKit
10 |
11 | struct Cache {
12 |
13 | final class ImageLoader {
14 |
15 | static let shared = ImageLoader()
16 |
17 | fileprivate let cache: NSCache = {
18 | let cache = NSCache()
19 | cache.name = "ImageCache"
20 | return cache
21 | }()
22 |
23 | fileprivate let physicalCacheURL: URL = {
24 | var url = try! FileManager.default.url(
25 | for: .cachesDirectory,
26 | in: .userDomainMask,
27 | appropriateFor: nil,
28 | create: true
29 | )
30 | url = url.appendingPathComponent("ImageCache", isDirectory: true)
31 |
32 | try! FileManager.default.createDirectory(at: url, withIntermediateDirectories: true, attributes: nil)
33 |
34 | return url
35 | }()
36 |
37 | func cachedImage(with url: URL) -> UIImage? {
38 | let hash = "\(url.hashValue)" as NSString
39 |
40 | print(hash)
41 | // Check if image exists on volatile cache
42 | if let image = cache.object(forKey: hash) {
43 | return image
44 | }
45 |
46 | // Check if image exists on physical cache
47 | if let image = UIImage(contentsOfFile: pathOnPhysicalCacheForFile(with: hash)) {
48 | cache.setObject(image, forKey: hash)
49 | return image
50 | }
51 |
52 | return nil
53 | }
54 |
55 | func image(with url: URL, completionHandler: @escaping (UIImage) -> Void){
56 | let request = URLRequest(url: url)
57 | image(with: request, completionHandler: completionHandler)
58 | }
59 |
60 | private func image(with request: URLRequest, completionHandler: @escaping (UIImage) -> Void) {
61 | let hash = String(describing: request.url.hash) as NSString
62 |
63 | if let image = cache.object(forKey: hash) {
64 | completionHandler(image)
65 | return
66 | }
67 |
68 | if let image = UIImage(contentsOfFile: pathOnPhysicalCacheForFile(with: hash)) {
69 | cache.setObject(image, forKey: hash)
70 | completionHandler(image)
71 | return
72 | }
73 |
74 | // Download
75 | let task = URLSession.shared.downloadTask(with: request) { url, response, error in
76 | guard let tempUrl = url else {
77 | print("Error downloading \(request)")
78 | print("Error \(error)")
79 | print("Response \(response)")
80 | return
81 | }
82 | let finalUrl = self.URLOnPhysicalCacheForFile(with: hash)
83 |
84 | self.saveFile(in: finalUrl, with: hash, oldUrl: tempUrl, completionHandler: completionHandler)
85 |
86 | }
87 | task.resume()
88 | }
89 |
90 | private func saveFile(
91 | in url: URL,
92 | with hash: NSString,
93 | oldUrl: URL,
94 | completionHandler: @escaping (UIImage) -> Void
95 | ) {
96 | do {
97 | if FileManager.default.fileExists(atPath: url.path) {
98 | try FileManager.default.removeItem(at: url)
99 | }
100 |
101 | try FileManager.default.moveItem(at: oldUrl, to: url)
102 | if let image = UIImage(contentsOfFile: url.path) {
103 | self.cache.setObject(image, forKey: hash)
104 | OperationQueue.main.addOperation {
105 | completionHandler(image)
106 | }
107 | }
108 |
109 | } catch {
110 | print("Error saving image to final location: \(error)")
111 | }
112 | }
113 |
114 | private func URLOnPhysicalCacheForFile(with hash: NSString) -> URL {
115 | let URL = physicalCacheURL.appendingPathComponent(hash as String)
116 | return URL
117 | }
118 |
119 | private func pathOnPhysicalCacheForFile(with hash: NSString) -> String {
120 | return URLOnPhysicalCacheForFile(with: hash).path
121 | }
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example/UseCases/Heroes/Heroes.Fetcher.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Heroes.Fetcher.swift
3 | // MultiSelectionTable
4 | //
5 | // Created by Nuno Gonçalves on 09/12/16.
6 | // Copyright © 2016 Nuno Gonçalves. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | struct Heroes {
12 |
13 | struct Fetcher {
14 |
15 | static func fetch(named name: String? = nil, in page: Int = 0, got: @escaping (HeroesList) -> ()) {
16 | let url = buildUrl(with: page, and: name)
17 | Network.get(from: url) { result in
18 | switch result {
19 | case .success(let json):
20 | guard let dataDic = json["data"] as? [String : Any] else {
21 | return got(HeroesList(heroes: [],
22 | totalCount: 0,
23 | currentPage: 0,
24 | totalPages: 0))
25 | }
26 |
27 | let totalCount = dataDic["total"] as? Int ?? 0
28 | let offset = dataDic["offset"] as? Int ?? 0
29 | let limit = dataDic["limit"] as? Int ?? 0
30 |
31 | let totalPages = limit == 0 ? 0 : Int(ceil(Double(totalCount / limit)))
32 | let currentPage = totalCount == 0 ? 0 : Int(ceil(Double(totalPages) * Double(offset) / Double(totalCount)))
33 |
34 | let dicCharacters = dataDic["results"] as! [[String : Any]]
35 | let heroes = dicCharacters.flatMap { Hero(dictionary: $0) }
36 |
37 | let heroesList = HeroesList(heroes: heroes,
38 | totalCount: totalCount,
39 | currentPage: currentPage,
40 | totalPages: totalPages)
41 | got(heroesList)
42 | case .failure(_):
43 | //We should really propagate the Result the the caller
44 | return got(HeroesList(heroes: [],
45 | totalCount: 0,
46 | currentPage: 0,
47 | totalPages: 0))
48 | }
49 | }
50 | }
51 |
52 | static let ts = "1"
53 | static let pub = "ab781b828ce0d61d8d053bde7d8c3fde"
54 | static let priv = "f9490b9557c2e8e7c52b72a632898b63658dcf5b"
55 | static let charactersUrl = "http://gateway.marvel.com:80/v1/public/characters"
56 |
57 | static var hash : String {
58 | return "\(ts)\(priv)\(pub)".md5ed
59 | }
60 |
61 | fileprivate static func buildUrl(with page: Int, and name: String? = nil) -> URL {
62 | let limit = 25
63 | var urlStr = "\(charactersUrl)?limit=\(limit)&offset=\(page * limit)&apikey=\(pub)&ts=1&hash=\(hash)"
64 | if let name = name,
65 | !name.isEmpty {
66 | urlStr.append("&nameStartsWith=\(name)")
67 | urlStr = urlStr.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
68 | }
69 | return URL(string: urlStr)!
70 | }
71 | }
72 | }
73 |
74 | fileprivate extension String {
75 | var md5ed: String! {
76 | let str = self.cString(using: String.Encoding.utf8)
77 | let strLen = CC_LONG(self.lengthOfBytes(using: String.Encoding.utf8))
78 | let digestLen = Int(CC_MD5_DIGEST_LENGTH)
79 | let result = UnsafeMutablePointer.allocate(capacity: digestLen)
80 |
81 | CC_MD5(str!, strLen, result)
82 |
83 | let hash = NSMutableString()
84 | for i in 0..) -> ()) {
18 | DispatchQueue.global(qos: .userInteractive).async {
19 | let task = urlSession.dataTask(with: url, completionHandler: { data, response, error in
20 | if let data = data {
21 | do {
22 | let json = try dictionary(from: data)
23 | DispatchQueue.main.async {
24 | result(.success(json))
25 | }
26 | } catch {
27 | DispatchQueue.main.async {
28 | result(.failure(.parse))
29 | }
30 |
31 | }
32 | } else {
33 | DispatchQueue.main.async {
34 | result(.failure(.data))
35 | }
36 | }
37 | })
38 | task.resume()
39 | }
40 | }
41 |
42 | private static func dictionary(from data: Data) throws -> JSONDictionary {
43 | let dataJSON = try JSONSerialization.jsonObject(with: data, options: .mutableContainers)
44 | if let dataDictionary = dataJSON as? JSONDictionary {
45 | return dataDictionary
46 | }
47 | throw NetworkError.parse
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example/albums.json:
--------------------------------------------------------------------------------
1 | {
2 | "albums" : [{
3 | "name" : "Nevermind",
4 | "coverImageUrl" : "covers/nevermind.jpg",
5 | "year": 1991,
6 | "band" : {
7 | "name" : "Nirvana"
8 |
9 | }
10 | }, {
11 | "name" : "Californication",
12 | "coverImageUrl" : "covers/californication.jpg",
13 | "year": 1999,
14 | "band" : {
15 | "name" : "Red Hot Chili Peppers"
16 |
17 | }
18 | }, {
19 | "name" : "Hoorray for boobies",
20 | "coverImageUrl" : "covers/badadchaseylane.jpg",
21 | "year": 1999,
22 | "band" : {
23 | "name" : "BloodHound Gang"
24 |
25 | }
26 | }, {
27 | "name" : "Three Dollar Bill Yall",
28 | "coverImageUrl" : "covers/threedollarbillyall.jpg",
29 | "year": 1997,
30 | "band" : {
31 | "name" : "Limp Bizkit"
32 |
33 | }
34 | }, {
35 | "name" : "S&M",
36 | "coverImageUrl" : "covers/s&m.jpg",
37 | "year": 1999,
38 | "band" : {
39 | "name" : "Metallica"
40 |
41 | }
42 | }, {
43 | "name" : "Joyride",
44 | "coverImageUrl" : "covers/joyride.jpg",
45 | "year": 1991,
46 | "band" : {
47 | "name" : "Roxette"
48 |
49 | }
50 | }, {
51 | "name" : "Best of",
52 | "coverImageUrl" : "covers/u2bestof.jpg",
53 | "year": 1998,
54 | "band" : {
55 | "name" : "U2"
56 |
57 | }
58 | }, {
59 | "name" : "Greatest Hits 2",
60 | "coverImageUrl" : "covers/queengreatest2.jpg",
61 | "year": 1991,
62 | "band" : {
63 | "name" : "Queen"
64 |
65 | }
66 | }, {
67 | "name" : "I'm not dead",
68 | "coverImageUrl" : "covers/imnotdead.png",
69 | "year": 2006,
70 | "band" : {
71 | "name" : "Pink"
72 |
73 | }
74 | }, {
75 | "name" : "Appetite for Destruction",
76 | "coverImageUrl" : "covers/appetite-for-destruction.jpg",
77 | "year": 1987,
78 | "band" : {
79 | "name" : "Guns N Roses"
80 |
81 | }
82 | }, {
83 | "name" : "Black Ice",
84 | "coverImageUrl" : "covers/blackice.jpg",
85 | "year": 2008,
86 | "band" : {
87 | "name" : "AC/DC"
88 |
89 | }
90 | }, {
91 | "name" : "Dark Side of the Moon",
92 | "coverImageUrl" : "covers/Dark_Side_of_the_Moon.png",
93 | "year": 1973,
94 | "band" : {
95 | "name" : "Pink Floyd"
96 |
97 | }
98 | }, {
99 | "name" : "Touch",
100 | "coverImageUrl" : "covers/Eurythmics_-_Touch.jpg",
101 | "year": 1983,
102 | "band" : {
103 | "name" : "Eurytmics"
104 |
105 | }
106 | }, {
107 | "name" : "Ghosts of Stories",
108 | "coverImageUrl" : "covers/ghoststoriesfull.jpg",
109 | "year": 2014,
110 | "band" : {
111 | "name" : "Coldplay"
112 |
113 | }
114 | }, {
115 | "name" : "Funhouse",
116 | "coverImageUrl" : "covers/funhouse.png",
117 | "year": 2008,
118 | "band" : {
119 | "name" : "Pink"
120 |
121 | }
122 | }, {
123 | "name" : "It's My Life",
124 | "coverImageUrl" : "covers/itsmylife.jpg",
125 | "year": 2000,
126 | "band" : {
127 | "name" : "Bon Jovi"
128 |
129 | }
130 | }, {
131 | "name" : "Rosenrot",
132 | "coverImageUrl" : "covers/rosenrot.jpg",
133 | "year": 2005,
134 | "band" : {
135 | "name" : "Ramstein"
136 |
137 | }
138 | }, {
139 | "name" : "Thriller",
140 | "coverImageUrl" : "covers/thriller.jpg",
141 | "year": 1982,
142 | "band" : {
143 | "name" : "Michael Jackson"
144 |
145 | }
146 | }, {
147 | "name" : "Switch",
148 | "coverImageUrl" : "covers/switch.jpg",
149 | "year": 2005,
150 | "band" : {
151 | "name" : "INXS"
152 |
153 | }
154 | }, {
155 | "name" : "This Is Acting",
156 | "coverImageUrl" : "covers/Thisisacting_albumcover.png",
157 | "year": 2016,
158 | "band" : {
159 | "name" : "Sia"
160 |
161 | }
162 | }, {
163 | "name" : "Come Clean",
164 | "coverImageUrl" : "covers/PuddleOfMudd_-_ComeClean.jpg",
165 | "year": 2001,
166 | "band" : {
167 | "name" : "Puddle of Mudd"
168 |
169 | }
170 | }, {
171 | "name" : "Proud Like a God",
172 | "coverImageUrl" : "covers/1000x1000.jpg",
173 | "year": 1997,
174 | "band" : {
175 | "name" : "Guano Apes"
176 |
177 | }
178 | }, {
179 | "name" : "You've Come a Long Way Baby",
180 | "coverImageUrl" : "covers/YouveComeALongWayBaby2.jpg",
181 | "year": 1998,
182 | "band" : {
183 | "name" : "Fat Boy Slim"
184 |
185 | }
186 | }
187 | ]
188 | }
189 |
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example/covers/1000x1000.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nunogoncalves/iOS-MultiSelectionTable/f5ceb6a15e1eb5db71308384fddcb4e817021024/Example/MultiSelectionTable-Example/covers/1000x1000.jpg
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example/covers/Dark_Side_of_the_Moon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nunogoncalves/iOS-MultiSelectionTable/f5ceb6a15e1eb5db71308384fddcb4e817021024/Example/MultiSelectionTable-Example/covers/Dark_Side_of_the_Moon.png
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example/covers/Eurythmics_-_Touch.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nunogoncalves/iOS-MultiSelectionTable/f5ceb6a15e1eb5db71308384fddcb4e817021024/Example/MultiSelectionTable-Example/covers/Eurythmics_-_Touch.jpg
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example/covers/PuddleOfMudd_-_ComeClean.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nunogoncalves/iOS-MultiSelectionTable/f5ceb6a15e1eb5db71308384fddcb4e817021024/Example/MultiSelectionTable-Example/covers/PuddleOfMudd_-_ComeClean.jpg
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example/covers/Thisisacting_albumcover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nunogoncalves/iOS-MultiSelectionTable/f5ceb6a15e1eb5db71308384fddcb4e817021024/Example/MultiSelectionTable-Example/covers/Thisisacting_albumcover.png
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example/covers/YouveComeALongWayBaby2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nunogoncalves/iOS-MultiSelectionTable/f5ceb6a15e1eb5db71308384fddcb4e817021024/Example/MultiSelectionTable-Example/covers/YouveComeALongWayBaby2.jpg
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example/covers/appetite-for-destruction.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nunogoncalves/iOS-MultiSelectionTable/f5ceb6a15e1eb5db71308384fddcb4e817021024/Example/MultiSelectionTable-Example/covers/appetite-for-destruction.jpg
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example/covers/badadchaseylane.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nunogoncalves/iOS-MultiSelectionTable/f5ceb6a15e1eb5db71308384fddcb4e817021024/Example/MultiSelectionTable-Example/covers/badadchaseylane.jpg
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example/covers/blackice.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nunogoncalves/iOS-MultiSelectionTable/f5ceb6a15e1eb5db71308384fddcb4e817021024/Example/MultiSelectionTable-Example/covers/blackice.jpg
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example/covers/californication.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nunogoncalves/iOS-MultiSelectionTable/f5ceb6a15e1eb5db71308384fddcb4e817021024/Example/MultiSelectionTable-Example/covers/californication.jpg
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example/covers/funhouse.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nunogoncalves/iOS-MultiSelectionTable/f5ceb6a15e1eb5db71308384fddcb4e817021024/Example/MultiSelectionTable-Example/covers/funhouse.png
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example/covers/ghoststoriesfull.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nunogoncalves/iOS-MultiSelectionTable/f5ceb6a15e1eb5db71308384fddcb4e817021024/Example/MultiSelectionTable-Example/covers/ghoststoriesfull.jpg
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example/covers/imnotdead.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nunogoncalves/iOS-MultiSelectionTable/f5ceb6a15e1eb5db71308384fddcb4e817021024/Example/MultiSelectionTable-Example/covers/imnotdead.png
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example/covers/itsmylife.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nunogoncalves/iOS-MultiSelectionTable/f5ceb6a15e1eb5db71308384fddcb4e817021024/Example/MultiSelectionTable-Example/covers/itsmylife.jpg
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example/covers/joyride.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nunogoncalves/iOS-MultiSelectionTable/f5ceb6a15e1eb5db71308384fddcb4e817021024/Example/MultiSelectionTable-Example/covers/joyride.jpg
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example/covers/nevermind.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nunogoncalves/iOS-MultiSelectionTable/f5ceb6a15e1eb5db71308384fddcb4e817021024/Example/MultiSelectionTable-Example/covers/nevermind.jpg
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example/covers/queengreatest2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nunogoncalves/iOS-MultiSelectionTable/f5ceb6a15e1eb5db71308384fddcb4e817021024/Example/MultiSelectionTable-Example/covers/queengreatest2.jpg
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example/covers/rosenrot.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nunogoncalves/iOS-MultiSelectionTable/f5ceb6a15e1eb5db71308384fddcb4e817021024/Example/MultiSelectionTable-Example/covers/rosenrot.jpg
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example/covers/s&m.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nunogoncalves/iOS-MultiSelectionTable/f5ceb6a15e1eb5db71308384fddcb4e817021024/Example/MultiSelectionTable-Example/covers/s&m.jpg
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example/covers/switch.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nunogoncalves/iOS-MultiSelectionTable/f5ceb6a15e1eb5db71308384fddcb4e817021024/Example/MultiSelectionTable-Example/covers/switch.jpg
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example/covers/threedollarbillyall.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nunogoncalves/iOS-MultiSelectionTable/f5ceb6a15e1eb5db71308384fddcb4e817021024/Example/MultiSelectionTable-Example/covers/threedollarbillyall.jpg
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example/covers/thriller.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nunogoncalves/iOS-MultiSelectionTable/f5ceb6a15e1eb5db71308384fddcb4e817021024/Example/MultiSelectionTable-Example/covers/thriller.jpg
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example/covers/truthaboutlove.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nunogoncalves/iOS-MultiSelectionTable/f5ceb6a15e1eb5db71308384fddcb4e817021024/Example/MultiSelectionTable-Example/covers/truthaboutlove.png
--------------------------------------------------------------------------------
/Example/MultiSelectionTable-Example/covers/u2bestof.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nunogoncalves/iOS-MultiSelectionTable/f5ceb6a15e1eb5db71308384fddcb4e817021024/Example/MultiSelectionTable-Example/covers/u2bestof.jpg
--------------------------------------------------------------------------------
/Example/MultiSelectionTable.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Example/MultiSelectionTable.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Example/MultiSelectionTableTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Example/MultiSelectionTableTests/MultiSelectionTableTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MultiSelectionTableTests.swift
3 | // MultiSelectionTableTests
4 | //
5 | // Created by Nuno Gonçalves on 28/11/16.
6 | // Copyright © 2016 Nuno Gonçalves. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import MultiSelectionTable
11 |
12 | class MultiSelectionTableTests: XCTestCase {
13 |
14 | var multiSelectionDataSource: MultiSelectionDataSource!
15 | let multiSelectionTableView = MultiSelectionTableView()
16 |
17 | override func setUp() {
18 | super.setUp()
19 |
20 | multiSelectionDataSource = MultiSelectionDataSource(multiSelectionTableView: multiSelectionTableView)
21 | multiSelectionTableView.dataSource = multiSelectionDataSource
22 |
23 | multiSelectionDataSource.allItems = [1, 2, 3]
24 |
25 | let view = MultiSelectionTableView()
26 | view.dataSource = multiSelectionDataSource
27 |
28 | }
29 |
30 | override func tearDown() {
31 | // Put teardown code here. This method is called after the invocation of each test method in the class.
32 | super.tearDown()
33 | }
34 |
35 | func testAllItemsCount() {
36 | XCTAssert(multiSelectionDataSource.allItemsCount == 3, "All items should be three")
37 | }
38 |
39 | func testInitialSelectedItemsCount() {
40 | XCTAssert(multiSelectionDataSource.selectedItemsCount == 0, "Selected Items should be zero at first")
41 | }
42 |
43 | func testSelectedItemsCountAfterOneSelection() {
44 | multiSelectionDataSource.selectedItem(at: 0)
45 | let selectedItems = multiSelectionDataSource.selectedItems
46 | XCTAssert(selectedItems.count == 1, "Selected Items should be one after selecting one item")
47 | XCTAssert(selectedItems.first == 1, "First item should be 1")
48 | }
49 |
50 | func testAllItemsCountAfterOneSelection() {
51 | multiSelectionDataSource.selectedItem(at: 0)
52 |
53 | let allItems = multiSelectionDataSource.allItems
54 | XCTAssert(allItems.count == 3, "All items should remain the same")
55 | }
56 |
57 | func testPerformanceExample() {
58 | // This is an example of a performance test case.
59 | self.measure {
60 | // Put the code you want to measure the time of here.
61 | }
62 | }
63 |
64 | }
65 |
--------------------------------------------------------------------------------
/Example/MultiSelectionTableUITests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Example/MultiSelectionTableUITests/MultiSelectionTableUITests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MultiSelectionTableUITests.swift
3 | // MultiSelectionTableUITests
4 | //
5 | // Created by Nuno Gonçalves on 28/11/16.
6 | // Copyright © 2016 Nuno Gonçalves. All rights reserved.
7 | //
8 |
9 | import XCTest
10 |
11 | class MultiSelectionTableUITests: XCTestCase {
12 |
13 | override func setUp() {
14 | super.setUp()
15 |
16 | // Put setup code here. This method is called before the invocation of each test method in the class.
17 |
18 | // In UI tests it is usually best to stop immediately when a failure occurs.
19 | continueAfterFailure = false
20 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method.
21 | XCUIApplication().launch()
22 |
23 | // 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.
24 | }
25 |
26 | override func tearDown() {
27 | // Put teardown code here. This method is called after the invocation of each test method in the class.
28 | super.tearDown()
29 | }
30 |
31 | func testExample() {
32 | // Use recording to get started writing UI tests.
33 | // Use XCTAssert and related functions to verify your tests produce the correct results.
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/Example/Podfile:
--------------------------------------------------------------------------------
1 | # Uncomment the next line to define a global platform for your project
2 | # platform :ios, '9.0'
3 |
4 | target 'MultiSelectionTable-Example' do
5 | # Comment the next line if you're not using Swift and don't want to use dynamic frameworks
6 | use_frameworks!
7 | pod 'MultiSelectionTableView', :path => '../'
8 |
9 | # Pods for MultiSelectionTable
10 |
11 | target 'MultiSelectionTable-ExampleTests' do
12 | inherit! :search_paths
13 | # Pods for testing
14 | end
15 |
16 | target 'MultiSelectionTable-ExampleUITests' do
17 | inherit! :search_paths
18 | # Pods for testing
19 | end
20 |
21 | end
22 |
--------------------------------------------------------------------------------
/Example/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - MultiSelectionTableView (0.1.0)
3 |
4 | DEPENDENCIES:
5 | - MultiSelectionTableView (from `../`)
6 |
7 | EXTERNAL SOURCES:
8 | MultiSelectionTableView:
9 | :path: "../"
10 |
11 | SPEC CHECKSUMS:
12 | MultiSelectionTableView: 7db06fbd1de16cfdd9fcf9783bc0c55cc2e85cf4
13 |
14 | PODFILE CHECKSUM: c1f5b0b3d32d622b8f70229b856cfc9b42548a01
15 |
16 | COCOAPODS: 1.8.4
17 |
--------------------------------------------------------------------------------
/Example/Pods/Local Podspecs/MultiSelectionTableView.podspec.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "MultiSelectionTableView",
3 | "version": "0.1.0",
4 | "summary": "Beautiful multi-selection table for iOS written in Swift 3",
5 | "description": "MultiSelectionTableView is a custom view that allows displaying a list of items and select as many as one wants. One sees two lists, the first lists all items the user might select, and the second lists the selected items. The table allows pagination and search, and the selected items will not leave context making it easy for the user to interact with your view.",
6 | "homepage": "https://github.com/nunogoncalves/iOS-MultiSelectionTable",
7 | "license": {
8 | "type": "MIT",
9 | "file": "LICENSE.md"
10 | },
11 | "authors": {
12 | "Nuno Gonçalves": ""
13 | },
14 | "source": {
15 | "git": "https://github.com/nunogoncalves/MultiSelectionTable.git",
16 | "tag": "0.1.0"
17 | },
18 | "social_media_url": "https://twitter.com/goncalvescmnuno",
19 | "platforms": {
20 | "ios": "9.0"
21 | },
22 | "source_files": "Source/**/*"
23 | }
24 |
--------------------------------------------------------------------------------
/Example/Pods/Manifest.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - MultiSelectionTableView (0.1.0)
3 |
4 | DEPENDENCIES:
5 | - MultiSelectionTableView (from `../`)
6 |
7 | EXTERNAL SOURCES:
8 | MultiSelectionTableView:
9 | :path: "../"
10 |
11 | SPEC CHECKSUMS:
12 | MultiSelectionTableView: 7db06fbd1de16cfdd9fcf9783bc0c55cc2e85cf4
13 |
14 | PODFILE CHECKSUM: c1f5b0b3d32d622b8f70229b856cfc9b42548a01
15 |
16 | COCOAPODS: 1.8.4
17 |
--------------------------------------------------------------------------------
/Example/Pods/Pods.xcodeproj/xcuserdata/nunogoncalves.xcuserdatad/xcschemes/MultiSelectionTableView.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
43 |
44 |
45 |
46 |
52 |
53 |
55 |
56 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/Example/Pods/Pods.xcodeproj/xcuserdata/nunogoncalves.xcuserdatad/xcschemes/Pods-MultiSelectionTable-Example.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
43 |
44 |
50 |
51 |
53 |
54 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/Example/Pods/Pods.xcodeproj/xcuserdata/nunogoncalves.xcuserdatad/xcschemes/Pods-MultiSelectionTable-ExampleTests.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
43 |
44 |
50 |
51 |
53 |
54 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/Example/Pods/Pods.xcodeproj/xcuserdata/nunogoncalves.xcuserdatad/xcschemes/Pods-MultiSelectionTable-ExampleUITests.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
43 |
44 |
50 |
51 |
53 |
54 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/Example/Pods/Pods.xcodeproj/xcuserdata/nunogoncalves.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | MultiSelectionTableView.xcscheme
8 |
9 | isShown
10 |
11 |
12 | Pods-MultiSelectionTable-Example.xcscheme
13 |
14 | isShown
15 |
16 |
17 | Pods-MultiSelectionTable-ExampleTests.xcscheme
18 |
19 | isShown
20 |
21 |
22 | Pods-MultiSelectionTable-ExampleUITests.xcscheme
23 |
24 | isShown
25 |
26 |
27 |
28 | SuppressBuildableAutocreation
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/MultiSelectionTableView/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | ${EXECUTABLE_NAME}
9 | CFBundleIdentifier
10 | ${PRODUCT_BUNDLE_IDENTIFIER}
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | ${PRODUCT_NAME}
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 0.1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | ${CURRENT_PROJECT_VERSION}
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/MultiSelectionTableView/MultiSelectionTableView-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | ${EXECUTABLE_NAME}
9 | CFBundleIdentifier
10 | ${PRODUCT_BUNDLE_IDENTIFIER}
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | ${PRODUCT_NAME}
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 0.1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | ${CURRENT_PROJECT_VERSION}
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/MultiSelectionTableView/MultiSelectionTableView-dummy.m:
--------------------------------------------------------------------------------
1 | #import
2 | @interface PodsDummy_MultiSelectionTableView : NSObject
3 | @end
4 | @implementation PodsDummy_MultiSelectionTableView
5 | @end
6 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/MultiSelectionTableView/MultiSelectionTableView-prefix.pch:
--------------------------------------------------------------------------------
1 | #ifdef __OBJC__
2 | #import
3 | #else
4 | #ifndef FOUNDATION_EXPORT
5 | #if defined(__cplusplus)
6 | #define FOUNDATION_EXPORT extern "C"
7 | #else
8 | #define FOUNDATION_EXPORT extern
9 | #endif
10 | #endif
11 | #endif
12 |
13 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/MultiSelectionTableView/MultiSelectionTableView-umbrella.h:
--------------------------------------------------------------------------------
1 | #ifdef __OBJC__
2 | #import
3 | #else
4 | #ifndef FOUNDATION_EXPORT
5 | #if defined(__cplusplus)
6 | #define FOUNDATION_EXPORT extern "C"
7 | #else
8 | #define FOUNDATION_EXPORT extern
9 | #endif
10 | #endif
11 | #endif
12 |
13 |
14 | FOUNDATION_EXPORT double MultiSelectionTableViewVersionNumber;
15 | FOUNDATION_EXPORT const unsigned char MultiSelectionTableViewVersionString[];
16 |
17 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/MultiSelectionTableView/MultiSelectionTableView.modulemap:
--------------------------------------------------------------------------------
1 | framework module MultiSelectionTableView {
2 | umbrella header "MultiSelectionTableView-umbrella.h"
3 |
4 | export *
5 | module * { export * }
6 | }
7 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/MultiSelectionTableView/MultiSelectionTableView.xcconfig:
--------------------------------------------------------------------------------
1 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/MultiSelectionTableView
2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
3 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
4 | PODS_BUILD_DIR = ${BUILD_DIR}
5 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
6 | PODS_ROOT = ${SRCROOT}
7 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/../..
8 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
9 | SKIP_INSTALL = YES
10 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
11 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-MultiSelectionTable-Example/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | ${EXECUTABLE_NAME}
9 | CFBundleIdentifier
10 | ${PRODUCT_BUNDLE_IDENTIFIER}
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | ${PRODUCT_NAME}
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | ${CURRENT_PROJECT_VERSION}
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-MultiSelectionTable-Example/Pods-MultiSelectionTable-Example-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | ${EXECUTABLE_NAME}
9 | CFBundleIdentifier
10 | ${PRODUCT_BUNDLE_IDENTIFIER}
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | ${PRODUCT_NAME}
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | ${CURRENT_PROJECT_VERSION}
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-MultiSelectionTable-Example/Pods-MultiSelectionTable-Example-acknowledgements.markdown:
--------------------------------------------------------------------------------
1 | # Acknowledgements
2 | This application makes use of the following third party libraries:
3 |
4 | ## MultiSelectionTableView
5 |
6 | Licensed under the **MIT** license
7 |
8 | > Copyright (c) 2016 Nuno Gonçalves
9 | >
10 | > Permission is hereby granted, free of charge, to any person obtaining
11 | > a copy of this software and associated documentation files (the
12 | > "Software"), to deal in the Software without restriction, including
13 | > without limitation the rights to use, copy, modify, merge, publish,
14 | > distribute, sublicense, and/or sell copies of the Software, and to
15 | > permit persons to whom the Software is furnished to do so, subject to
16 | > the following conditions:
17 | >
18 | > The above copyright notice and this permission notice shall be
19 | > included in all copies or substantial portions of the Software.
20 | >
21 | > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 | > EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 | > MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
24 | > IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
25 | > CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
26 | > TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
27 | > SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 |
29 | Generated by CocoaPods - https://cocoapods.org
30 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-MultiSelectionTable-Example/Pods-MultiSelectionTable-Example-acknowledgements.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreferenceSpecifiers
6 |
7 |
8 | FooterText
9 | This application makes use of the following third party libraries:
10 | Title
11 | Acknowledgements
12 | Type
13 | PSGroupSpecifier
14 |
15 |
16 | FooterText
17 | Licensed under the **MIT** license
18 |
19 | > Copyright (c) 2016 Nuno Gonçalves
20 | >
21 | > Permission is hereby granted, free of charge, to any person obtaining
22 | > a copy of this software and associated documentation files (the
23 | > "Software"), to deal in the Software without restriction, including
24 | > without limitation the rights to use, copy, modify, merge, publish,
25 | > distribute, sublicense, and/or sell copies of the Software, and to
26 | > permit persons to whom the Software is furnished to do so, subject to
27 | > the following conditions:
28 | >
29 | > The above copyright notice and this permission notice shall be
30 | > included in all copies or substantial portions of the Software.
31 | >
32 | > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
33 | > EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
34 | > MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
35 | > IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
36 | > CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
37 | > TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
38 | > SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
39 |
40 | License
41 | MIT
42 | Title
43 | MultiSelectionTableView
44 | Type
45 | PSGroupSpecifier
46 |
47 |
48 | FooterText
49 | Generated by CocoaPods - https://cocoapods.org
50 | Title
51 |
52 | Type
53 | PSGroupSpecifier
54 |
55 |
56 | StringsTable
57 | Acknowledgements
58 | Title
59 | Acknowledgements
60 |
61 |
62 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-MultiSelectionTable-Example/Pods-MultiSelectionTable-Example-dummy.m:
--------------------------------------------------------------------------------
1 | #import
2 | @interface PodsDummy_Pods_MultiSelectionTable_Example : NSObject
3 | @end
4 | @implementation PodsDummy_Pods_MultiSelectionTable_Example
5 | @end
6 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-MultiSelectionTable-Example/Pods-MultiSelectionTable-Example-frameworks.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | set -e
3 | set -u
4 | set -o pipefail
5 |
6 | function on_error {
7 | echo "$(realpath -mq "${0}"):$1: error: Unexpected failure"
8 | }
9 | trap 'on_error $LINENO' ERR
10 |
11 | if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then
12 | # If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy
13 | # frameworks to, so exit 0 (signalling the script phase was successful).
14 | exit 0
15 | fi
16 |
17 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
18 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
19 |
20 | COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}"
21 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}"
22 |
23 | # Used as a return value for each invocation of `strip_invalid_archs` function.
24 | STRIP_BINARY_RETVAL=0
25 |
26 | # This protects against multiple targets copying the same framework dependency at the same time. The solution
27 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html
28 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????")
29 |
30 | # Copies and strips a vendored framework
31 | install_framework()
32 | {
33 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then
34 | local source="${BUILT_PRODUCTS_DIR}/$1"
35 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then
36 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")"
37 | elif [ -r "$1" ]; then
38 | local source="$1"
39 | fi
40 |
41 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
42 |
43 | if [ -L "${source}" ]; then
44 | echo "Symlinked..."
45 | source="$(readlink "${source}")"
46 | fi
47 |
48 | # Use filter instead of exclude so missing patterns don't throw errors.
49 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\""
50 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}"
51 |
52 | local basename
53 | basename="$(basename -s .framework "$1")"
54 | binary="${destination}/${basename}.framework/${basename}"
55 |
56 | if ! [ -r "$binary" ]; then
57 | binary="${destination}/${basename}"
58 | elif [ -L "${binary}" ]; then
59 | echo "Destination binary is symlinked..."
60 | dirname="$(dirname "${binary}")"
61 | binary="${dirname}/$(readlink "${binary}")"
62 | fi
63 |
64 | # Strip invalid architectures so "fat" simulator / device frameworks work on device
65 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then
66 | strip_invalid_archs "$binary"
67 | fi
68 |
69 | # Resign the code if required by the build settings to avoid unstable apps
70 | code_sign_if_enabled "${destination}/$(basename "$1")"
71 |
72 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7.
73 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then
74 | local swift_runtime_libs
75 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u)
76 | for lib in $swift_runtime_libs; do
77 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\""
78 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}"
79 | code_sign_if_enabled "${destination}/${lib}"
80 | done
81 | fi
82 | }
83 |
84 | # Copies and strips a vendored dSYM
85 | install_dsym() {
86 | local source="$1"
87 | if [ -r "$source" ]; then
88 | # Copy the dSYM into a the targets temp dir.
89 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\""
90 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}"
91 |
92 | local basename
93 | basename="$(basename -s .framework.dSYM "$source")"
94 | binary="${DERIVED_FILES_DIR}/${basename}.framework.dSYM/Contents/Resources/DWARF/${basename}"
95 |
96 | # Strip invalid architectures so "fat" simulator / device frameworks work on device
97 | if [[ "$(file "$binary")" == *"Mach-O "*"dSYM companion"* ]]; then
98 | strip_invalid_archs "$binary"
99 | fi
100 |
101 | if [[ $STRIP_BINARY_RETVAL == 1 ]]; then
102 | # Move the stripped file into its final destination.
103 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\""
104 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.framework.dSYM" "${DWARF_DSYM_FOLDER_PATH}"
105 | else
106 | # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing.
107 | touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.framework.dSYM"
108 | fi
109 | fi
110 | }
111 |
112 | # Copies the bcsymbolmap files of a vendored framework
113 | install_bcsymbolmap() {
114 | local bcsymbolmap_path="$1"
115 | local destination="${BUILT_PRODUCTS_DIR}"
116 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}""
117 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}"
118 | }
119 |
120 | # Signs a framework with the provided identity
121 | code_sign_if_enabled() {
122 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY:-}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then
123 | # Use the current code_sign_identity
124 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}"
125 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'"
126 |
127 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then
128 | code_sign_cmd="$code_sign_cmd &"
129 | fi
130 | echo "$code_sign_cmd"
131 | eval "$code_sign_cmd"
132 | fi
133 | }
134 |
135 | # Strip invalid architectures
136 | strip_invalid_archs() {
137 | binary="$1"
138 | # Get architectures for current target binary
139 | binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)"
140 | # Intersect them with the architectures we are building for
141 | intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)"
142 | # If there are no archs supported by this binary then warn the user
143 | if [[ -z "$intersected_archs" ]]; then
144 | echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)."
145 | STRIP_BINARY_RETVAL=0
146 | return
147 | fi
148 | stripped=""
149 | for arch in $binary_archs; do
150 | if ! [[ "${ARCHS}" == *"$arch"* ]]; then
151 | # Strip non-valid architectures in-place
152 | lipo -remove "$arch" -output "$binary" "$binary"
153 | stripped="$stripped $arch"
154 | fi
155 | done
156 | if [[ "$stripped" ]]; then
157 | echo "Stripped $binary of architectures:$stripped"
158 | fi
159 | STRIP_BINARY_RETVAL=1
160 | }
161 |
162 |
163 | if [[ "$CONFIGURATION" == "Debug" ]]; then
164 | install_framework "${BUILT_PRODUCTS_DIR}/MultiSelectionTableView/MultiSelectionTableView.framework"
165 | fi
166 | if [[ "$CONFIGURATION" == "Release" ]]; then
167 | install_framework "${BUILT_PRODUCTS_DIR}/MultiSelectionTableView/MultiSelectionTableView.framework"
168 | fi
169 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then
170 | wait
171 | fi
172 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-MultiSelectionTable-Example/Pods-MultiSelectionTable-Example-resources.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | set -e
3 |
4 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
5 |
6 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt
7 | > "$RESOURCES_TO_COPY"
8 |
9 | XCASSET_FILES=()
10 |
11 | case "${TARGETED_DEVICE_FAMILY}" in
12 | 1,2)
13 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone"
14 | ;;
15 | 1)
16 | TARGET_DEVICE_ARGS="--target-device iphone"
17 | ;;
18 | 2)
19 | TARGET_DEVICE_ARGS="--target-device ipad"
20 | ;;
21 | 3)
22 | TARGET_DEVICE_ARGS="--target-device tv"
23 | ;;
24 | *)
25 | TARGET_DEVICE_ARGS="--target-device mac"
26 | ;;
27 | esac
28 |
29 | install_resource()
30 | {
31 | if [[ "$1" = /* ]] ; then
32 | RESOURCE_PATH="$1"
33 | else
34 | RESOURCE_PATH="${PODS_ROOT}/$1"
35 | fi
36 | if [[ ! -e "$RESOURCE_PATH" ]] ; then
37 | cat << EOM
38 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script.
39 | EOM
40 | exit 1
41 | fi
42 | case $RESOURCE_PATH in
43 | *.storyboard)
44 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}"
45 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS}
46 | ;;
47 | *.xib)
48 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}"
49 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS}
50 | ;;
51 | *.framework)
52 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
53 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
54 | echo "rsync -av $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
55 | rsync -av "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
56 | ;;
57 | *.xcdatamodel)
58 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\""
59 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom"
60 | ;;
61 | *.xcdatamodeld)
62 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\""
63 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd"
64 | ;;
65 | *.xcmappingmodel)
66 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\""
67 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm"
68 | ;;
69 | *.xcassets)
70 | ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH"
71 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE")
72 | ;;
73 | *)
74 | echo "$RESOURCE_PATH"
75 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY"
76 | ;;
77 | esac
78 | }
79 |
80 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
81 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
82 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then
83 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
84 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
85 | fi
86 | rm -f "$RESOURCES_TO_COPY"
87 |
88 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ]
89 | then
90 | # Find all other xcassets (this unfortunately includes those of path pods and other targets).
91 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d)
92 | while read line; do
93 | if [[ $line != "${PODS_ROOT}*" ]]; then
94 | XCASSET_FILES+=("$line")
95 | fi
96 | done <<<"$OTHER_XCASSETS"
97 |
98 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
99 | fi
100 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-MultiSelectionTable-Example/Pods-MultiSelectionTable-Example-umbrella.h:
--------------------------------------------------------------------------------
1 | #ifdef __OBJC__
2 | #import
3 | #else
4 | #ifndef FOUNDATION_EXPORT
5 | #if defined(__cplusplus)
6 | #define FOUNDATION_EXPORT extern "C"
7 | #else
8 | #define FOUNDATION_EXPORT extern
9 | #endif
10 | #endif
11 | #endif
12 |
13 |
14 | FOUNDATION_EXPORT double Pods_MultiSelectionTable_ExampleVersionNumber;
15 | FOUNDATION_EXPORT const unsigned char Pods_MultiSelectionTable_ExampleVersionString[];
16 |
17 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-MultiSelectionTable-Example/Pods-MultiSelectionTable-Example.debug.xcconfig:
--------------------------------------------------------------------------------
1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/MultiSelectionTableView"
3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
4 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/MultiSelectionTableView/MultiSelectionTableView.framework/Headers"
5 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
6 | OTHER_LDFLAGS = $(inherited) -framework "MultiSelectionTableView"
7 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
8 | PODS_BUILD_DIR = ${BUILD_DIR}
9 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
10 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
11 | PODS_ROOT = ${SRCROOT}/Pods
12 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
13 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-MultiSelectionTable-Example/Pods-MultiSelectionTable-Example.modulemap:
--------------------------------------------------------------------------------
1 | framework module Pods_MultiSelectionTable_Example {
2 | umbrella header "Pods-MultiSelectionTable-Example-umbrella.h"
3 |
4 | export *
5 | module * { export * }
6 | }
7 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-MultiSelectionTable-Example/Pods-MultiSelectionTable-Example.release.xcconfig:
--------------------------------------------------------------------------------
1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/MultiSelectionTableView"
3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
4 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/MultiSelectionTableView/MultiSelectionTableView.framework/Headers"
5 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
6 | OTHER_LDFLAGS = $(inherited) -framework "MultiSelectionTableView"
7 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
8 | PODS_BUILD_DIR = ${BUILD_DIR}
9 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
10 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
11 | PODS_ROOT = ${SRCROOT}/Pods
12 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
13 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-MultiSelectionTable-ExampleTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | ${EXECUTABLE_NAME}
9 | CFBundleIdentifier
10 | ${PRODUCT_BUNDLE_IDENTIFIER}
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | ${PRODUCT_NAME}
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | ${CURRENT_PROJECT_VERSION}
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-MultiSelectionTable-ExampleTests/Pods-MultiSelectionTable-ExampleTests-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | ${EXECUTABLE_NAME}
9 | CFBundleIdentifier
10 | ${PRODUCT_BUNDLE_IDENTIFIER}
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | ${PRODUCT_NAME}
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | ${CURRENT_PROJECT_VERSION}
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-MultiSelectionTable-ExampleTests/Pods-MultiSelectionTable-ExampleTests-acknowledgements.markdown:
--------------------------------------------------------------------------------
1 | # Acknowledgements
2 | This application makes use of the following third party libraries:
3 | Generated by CocoaPods - https://cocoapods.org
4 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-MultiSelectionTable-ExampleTests/Pods-MultiSelectionTable-ExampleTests-acknowledgements.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreferenceSpecifiers
6 |
7 |
8 | FooterText
9 | This application makes use of the following third party libraries:
10 | Title
11 | Acknowledgements
12 | Type
13 | PSGroupSpecifier
14 |
15 |
16 | FooterText
17 | Generated by CocoaPods - https://cocoapods.org
18 | Title
19 |
20 | Type
21 | PSGroupSpecifier
22 |
23 |
24 | StringsTable
25 | Acknowledgements
26 | Title
27 | Acknowledgements
28 |
29 |
30 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-MultiSelectionTable-ExampleTests/Pods-MultiSelectionTable-ExampleTests-dummy.m:
--------------------------------------------------------------------------------
1 | #import
2 | @interface PodsDummy_Pods_MultiSelectionTable_ExampleTests : NSObject
3 | @end
4 | @implementation PodsDummy_Pods_MultiSelectionTable_ExampleTests
5 | @end
6 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-MultiSelectionTable-ExampleTests/Pods-MultiSelectionTable-ExampleTests-frameworks.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | set -e
3 |
4 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
5 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
6 |
7 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}"
8 |
9 | install_framework()
10 | {
11 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then
12 | local source="${BUILT_PRODUCTS_DIR}/$1"
13 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then
14 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")"
15 | elif [ -r "$1" ]; then
16 | local source="$1"
17 | fi
18 |
19 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
20 |
21 | if [ -L "${source}" ]; then
22 | echo "Symlinked..."
23 | source="$(readlink "${source}")"
24 | fi
25 |
26 | # use filter instead of exclude so missing patterns dont' throw errors
27 | echo "rsync -av --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\""
28 | rsync -av --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}"
29 |
30 | local basename
31 | basename="$(basename -s .framework "$1")"
32 | binary="${destination}/${basename}.framework/${basename}"
33 | if ! [ -r "$binary" ]; then
34 | binary="${destination}/${basename}"
35 | fi
36 |
37 | # Strip invalid architectures so "fat" simulator / device frameworks work on device
38 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then
39 | strip_invalid_archs "$binary"
40 | fi
41 |
42 | # Resign the code if required by the build settings to avoid unstable apps
43 | code_sign_if_enabled "${destination}/$(basename "$1")"
44 |
45 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7.
46 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then
47 | local swift_runtime_libs
48 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]})
49 | for lib in $swift_runtime_libs; do
50 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\""
51 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}"
52 | code_sign_if_enabled "${destination}/${lib}"
53 | done
54 | fi
55 | }
56 |
57 | # Signs a framework with the provided identity
58 | code_sign_if_enabled() {
59 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then
60 | # Use the current code_sign_identitiy
61 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}"
62 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements "$1""
63 |
64 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then
65 | code_sign_cmd="$code_sign_cmd &"
66 | fi
67 | echo "$code_sign_cmd"
68 | eval "$code_sign_cmd"
69 | fi
70 | }
71 |
72 | # Strip invalid architectures
73 | strip_invalid_archs() {
74 | binary="$1"
75 | # Get architectures for current file
76 | archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)"
77 | stripped=""
78 | for arch in $archs; do
79 | if ! [[ "${VALID_ARCHS}" == *"$arch"* ]]; then
80 | # Strip non-valid architectures in-place
81 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1
82 | stripped="$stripped $arch"
83 | fi
84 | done
85 | if [[ "$stripped" ]]; then
86 | echo "Stripped $binary of architectures:$stripped"
87 | fi
88 | }
89 |
90 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then
91 | wait
92 | fi
93 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-MultiSelectionTable-ExampleTests/Pods-MultiSelectionTable-ExampleTests-resources.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | set -e
3 |
4 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
5 |
6 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt
7 | > "$RESOURCES_TO_COPY"
8 |
9 | XCASSET_FILES=()
10 |
11 | case "${TARGETED_DEVICE_FAMILY}" in
12 | 1,2)
13 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone"
14 | ;;
15 | 1)
16 | TARGET_DEVICE_ARGS="--target-device iphone"
17 | ;;
18 | 2)
19 | TARGET_DEVICE_ARGS="--target-device ipad"
20 | ;;
21 | 3)
22 | TARGET_DEVICE_ARGS="--target-device tv"
23 | ;;
24 | *)
25 | TARGET_DEVICE_ARGS="--target-device mac"
26 | ;;
27 | esac
28 |
29 | install_resource()
30 | {
31 | if [[ "$1" = /* ]] ; then
32 | RESOURCE_PATH="$1"
33 | else
34 | RESOURCE_PATH="${PODS_ROOT}/$1"
35 | fi
36 | if [[ ! -e "$RESOURCE_PATH" ]] ; then
37 | cat << EOM
38 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script.
39 | EOM
40 | exit 1
41 | fi
42 | case $RESOURCE_PATH in
43 | *.storyboard)
44 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}"
45 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS}
46 | ;;
47 | *.xib)
48 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}"
49 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS}
50 | ;;
51 | *.framework)
52 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
53 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
54 | echo "rsync -av $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
55 | rsync -av "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
56 | ;;
57 | *.xcdatamodel)
58 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\""
59 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom"
60 | ;;
61 | *.xcdatamodeld)
62 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\""
63 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd"
64 | ;;
65 | *.xcmappingmodel)
66 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\""
67 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm"
68 | ;;
69 | *.xcassets)
70 | ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH"
71 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE")
72 | ;;
73 | *)
74 | echo "$RESOURCE_PATH"
75 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY"
76 | ;;
77 | esac
78 | }
79 |
80 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
81 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
82 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then
83 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
84 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
85 | fi
86 | rm -f "$RESOURCES_TO_COPY"
87 |
88 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ]
89 | then
90 | # Find all other xcassets (this unfortunately includes those of path pods and other targets).
91 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d)
92 | while read line; do
93 | if [[ $line != "${PODS_ROOT}*" ]]; then
94 | XCASSET_FILES+=("$line")
95 | fi
96 | done <<<"$OTHER_XCASSETS"
97 |
98 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
99 | fi
100 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-MultiSelectionTable-ExampleTests/Pods-MultiSelectionTable-ExampleTests-umbrella.h:
--------------------------------------------------------------------------------
1 | #ifdef __OBJC__
2 | #import
3 | #else
4 | #ifndef FOUNDATION_EXPORT
5 | #if defined(__cplusplus)
6 | #define FOUNDATION_EXPORT extern "C"
7 | #else
8 | #define FOUNDATION_EXPORT extern
9 | #endif
10 | #endif
11 | #endif
12 |
13 |
14 | FOUNDATION_EXPORT double Pods_MultiSelectionTable_ExampleTestsVersionNumber;
15 | FOUNDATION_EXPORT const unsigned char Pods_MultiSelectionTable_ExampleTestsVersionString[];
16 |
17 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-MultiSelectionTable-ExampleTests/Pods-MultiSelectionTable-ExampleTests.debug.xcconfig:
--------------------------------------------------------------------------------
1 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/MultiSelectionTableView"
2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
3 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/MultiSelectionTableView/MultiSelectionTableView.framework/Headers"
4 | OTHER_LDFLAGS = $(inherited) -framework "MultiSelectionTableView"
5 | PODS_BUILD_DIR = ${BUILD_DIR}
6 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
7 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
8 | PODS_ROOT = ${SRCROOT}/Pods
9 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
10 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-MultiSelectionTable-ExampleTests/Pods-MultiSelectionTable-ExampleTests.modulemap:
--------------------------------------------------------------------------------
1 | framework module Pods_MultiSelectionTable_ExampleTests {
2 | umbrella header "Pods-MultiSelectionTable-ExampleTests-umbrella.h"
3 |
4 | export *
5 | module * { export * }
6 | }
7 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-MultiSelectionTable-ExampleTests/Pods-MultiSelectionTable-ExampleTests.release.xcconfig:
--------------------------------------------------------------------------------
1 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/MultiSelectionTableView"
2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
3 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/MultiSelectionTableView/MultiSelectionTableView.framework/Headers"
4 | OTHER_LDFLAGS = $(inherited) -framework "MultiSelectionTableView"
5 | PODS_BUILD_DIR = ${BUILD_DIR}
6 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
7 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
8 | PODS_ROOT = ${SRCROOT}/Pods
9 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
10 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-MultiSelectionTable-ExampleUITests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | ${EXECUTABLE_NAME}
9 | CFBundleIdentifier
10 | ${PRODUCT_BUNDLE_IDENTIFIER}
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | ${PRODUCT_NAME}
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | ${CURRENT_PROJECT_VERSION}
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-MultiSelectionTable-ExampleUITests/Pods-MultiSelectionTable-ExampleUITests-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | ${EXECUTABLE_NAME}
9 | CFBundleIdentifier
10 | ${PRODUCT_BUNDLE_IDENTIFIER}
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | ${PRODUCT_NAME}
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | ${CURRENT_PROJECT_VERSION}
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-MultiSelectionTable-ExampleUITests/Pods-MultiSelectionTable-ExampleUITests-acknowledgements.markdown:
--------------------------------------------------------------------------------
1 | # Acknowledgements
2 | This application makes use of the following third party libraries:
3 | Generated by CocoaPods - https://cocoapods.org
4 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-MultiSelectionTable-ExampleUITests/Pods-MultiSelectionTable-ExampleUITests-acknowledgements.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreferenceSpecifiers
6 |
7 |
8 | FooterText
9 | This application makes use of the following third party libraries:
10 | Title
11 | Acknowledgements
12 | Type
13 | PSGroupSpecifier
14 |
15 |
16 | FooterText
17 | Generated by CocoaPods - https://cocoapods.org
18 | Title
19 |
20 | Type
21 | PSGroupSpecifier
22 |
23 |
24 | StringsTable
25 | Acknowledgements
26 | Title
27 | Acknowledgements
28 |
29 |
30 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-MultiSelectionTable-ExampleUITests/Pods-MultiSelectionTable-ExampleUITests-dummy.m:
--------------------------------------------------------------------------------
1 | #import
2 | @interface PodsDummy_Pods_MultiSelectionTable_ExampleUITests : NSObject
3 | @end
4 | @implementation PodsDummy_Pods_MultiSelectionTable_ExampleUITests
5 | @end
6 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-MultiSelectionTable-ExampleUITests/Pods-MultiSelectionTable-ExampleUITests-frameworks.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | set -e
3 |
4 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
5 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
6 |
7 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}"
8 |
9 | install_framework()
10 | {
11 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then
12 | local source="${BUILT_PRODUCTS_DIR}/$1"
13 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then
14 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")"
15 | elif [ -r "$1" ]; then
16 | local source="$1"
17 | fi
18 |
19 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
20 |
21 | if [ -L "${source}" ]; then
22 | echo "Symlinked..."
23 | source="$(readlink "${source}")"
24 | fi
25 |
26 | # use filter instead of exclude so missing patterns dont' throw errors
27 | echo "rsync -av --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\""
28 | rsync -av --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}"
29 |
30 | local basename
31 | basename="$(basename -s .framework "$1")"
32 | binary="${destination}/${basename}.framework/${basename}"
33 | if ! [ -r "$binary" ]; then
34 | binary="${destination}/${basename}"
35 | fi
36 |
37 | # Strip invalid architectures so "fat" simulator / device frameworks work on device
38 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then
39 | strip_invalid_archs "$binary"
40 | fi
41 |
42 | # Resign the code if required by the build settings to avoid unstable apps
43 | code_sign_if_enabled "${destination}/$(basename "$1")"
44 |
45 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7.
46 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then
47 | local swift_runtime_libs
48 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]})
49 | for lib in $swift_runtime_libs; do
50 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\""
51 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}"
52 | code_sign_if_enabled "${destination}/${lib}"
53 | done
54 | fi
55 | }
56 |
57 | # Signs a framework with the provided identity
58 | code_sign_if_enabled() {
59 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then
60 | # Use the current code_sign_identitiy
61 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}"
62 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements "$1""
63 |
64 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then
65 | code_sign_cmd="$code_sign_cmd &"
66 | fi
67 | echo "$code_sign_cmd"
68 | eval "$code_sign_cmd"
69 | fi
70 | }
71 |
72 | # Strip invalid architectures
73 | strip_invalid_archs() {
74 | binary="$1"
75 | # Get architectures for current file
76 | archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)"
77 | stripped=""
78 | for arch in $archs; do
79 | if ! [[ "${VALID_ARCHS}" == *"$arch"* ]]; then
80 | # Strip non-valid architectures in-place
81 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1
82 | stripped="$stripped $arch"
83 | fi
84 | done
85 | if [[ "$stripped" ]]; then
86 | echo "Stripped $binary of architectures:$stripped"
87 | fi
88 | }
89 |
90 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then
91 | wait
92 | fi
93 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-MultiSelectionTable-ExampleUITests/Pods-MultiSelectionTable-ExampleUITests-resources.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | set -e
3 |
4 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
5 |
6 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt
7 | > "$RESOURCES_TO_COPY"
8 |
9 | XCASSET_FILES=()
10 |
11 | case "${TARGETED_DEVICE_FAMILY}" in
12 | 1,2)
13 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone"
14 | ;;
15 | 1)
16 | TARGET_DEVICE_ARGS="--target-device iphone"
17 | ;;
18 | 2)
19 | TARGET_DEVICE_ARGS="--target-device ipad"
20 | ;;
21 | 3)
22 | TARGET_DEVICE_ARGS="--target-device tv"
23 | ;;
24 | *)
25 | TARGET_DEVICE_ARGS="--target-device mac"
26 | ;;
27 | esac
28 |
29 | install_resource()
30 | {
31 | if [[ "$1" = /* ]] ; then
32 | RESOURCE_PATH="$1"
33 | else
34 | RESOURCE_PATH="${PODS_ROOT}/$1"
35 | fi
36 | if [[ ! -e "$RESOURCE_PATH" ]] ; then
37 | cat << EOM
38 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script.
39 | EOM
40 | exit 1
41 | fi
42 | case $RESOURCE_PATH in
43 | *.storyboard)
44 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}"
45 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS}
46 | ;;
47 | *.xib)
48 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}"
49 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS}
50 | ;;
51 | *.framework)
52 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
53 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
54 | echo "rsync -av $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
55 | rsync -av "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
56 | ;;
57 | *.xcdatamodel)
58 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\""
59 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom"
60 | ;;
61 | *.xcdatamodeld)
62 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\""
63 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd"
64 | ;;
65 | *.xcmappingmodel)
66 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\""
67 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm"
68 | ;;
69 | *.xcassets)
70 | ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH"
71 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE")
72 | ;;
73 | *)
74 | echo "$RESOURCE_PATH"
75 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY"
76 | ;;
77 | esac
78 | }
79 |
80 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
81 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
82 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then
83 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
84 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
85 | fi
86 | rm -f "$RESOURCES_TO_COPY"
87 |
88 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ]
89 | then
90 | # Find all other xcassets (this unfortunately includes those of path pods and other targets).
91 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d)
92 | while read line; do
93 | if [[ $line != "${PODS_ROOT}*" ]]; then
94 | XCASSET_FILES+=("$line")
95 | fi
96 | done <<<"$OTHER_XCASSETS"
97 |
98 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
99 | fi
100 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-MultiSelectionTable-ExampleUITests/Pods-MultiSelectionTable-ExampleUITests-umbrella.h:
--------------------------------------------------------------------------------
1 | #ifdef __OBJC__
2 | #import
3 | #else
4 | #ifndef FOUNDATION_EXPORT
5 | #if defined(__cplusplus)
6 | #define FOUNDATION_EXPORT extern "C"
7 | #else
8 | #define FOUNDATION_EXPORT extern
9 | #endif
10 | #endif
11 | #endif
12 |
13 |
14 | FOUNDATION_EXPORT double Pods_MultiSelectionTable_ExampleUITestsVersionNumber;
15 | FOUNDATION_EXPORT const unsigned char Pods_MultiSelectionTable_ExampleUITestsVersionString[];
16 |
17 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-MultiSelectionTable-ExampleUITests/Pods-MultiSelectionTable-ExampleUITests.debug.xcconfig:
--------------------------------------------------------------------------------
1 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/MultiSelectionTableView"
2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
3 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/MultiSelectionTableView/MultiSelectionTableView.framework/Headers"
4 | OTHER_LDFLAGS = $(inherited) -framework "MultiSelectionTableView"
5 | PODS_BUILD_DIR = ${BUILD_DIR}
6 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
7 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
8 | PODS_ROOT = ${SRCROOT}/Pods
9 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
10 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-MultiSelectionTable-ExampleUITests/Pods-MultiSelectionTable-ExampleUITests.modulemap:
--------------------------------------------------------------------------------
1 | framework module Pods_MultiSelectionTable_ExampleUITests {
2 | umbrella header "Pods-MultiSelectionTable-ExampleUITests-umbrella.h"
3 |
4 | export *
5 | module * { export * }
6 | }
7 |
--------------------------------------------------------------------------------
/Example/Pods/Target Support Files/Pods-MultiSelectionTable-ExampleUITests/Pods-MultiSelectionTable-ExampleUITests.release.xcconfig:
--------------------------------------------------------------------------------
1 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/MultiSelectionTableView"
2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
3 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/MultiSelectionTableView/MultiSelectionTableView.framework/Headers"
4 | OTHER_LDFLAGS = $(inherited) -framework "MultiSelectionTableView"
5 | PODS_BUILD_DIR = ${BUILD_DIR}
6 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
7 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
8 | PODS_ROOT = ${SRCROOT}/Pods
9 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
10 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Licensed under the **MIT** license
2 |
3 | > Copyright (c) 2016 Nuno Gonçalves
4 | >
5 | > Permission is hereby granted, free of charge, to any person obtaining
6 | > a copy of this software and associated documentation files (the
7 | > "Software"), to deal in the Software without restriction, including
8 | > without limitation the rights to use, copy, modify, merge, publish,
9 | > distribute, sublicense, and/or sell copies of the Software, and to
10 | > permit persons to whom the Software is furnished to do so, subject to
11 | > the following conditions:
12 | >
13 | > The above copyright notice and this permission notice shall be
14 | > included in all copies or substantial portions of the Software.
15 | >
16 | > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | > EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | > MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 | > IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20 | > CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21 | > TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22 | > SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 |
--------------------------------------------------------------------------------
/MultiSelectionTableView.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 | s.name = 'MultiSelectionTableView'
3 | s.version = '0.1.0'
4 | s.summary = 'Beautiful multi-selection table for iOS written in Swift 3'
5 | s.description = <<-DESC
6 | MultiSelectionTableView is a custom view that allows displaying a list of items and select as many as one wants. One sees two lists, the first lists all items the user might select, and the second lists the selected items. The table allows pagination and search, and the selected items will not leave context making it easy for the user to interact with your view.
7 | DESC
8 |
9 | s.homepage = 'https://github.com/nunogoncalves/iOS-MultiSelectionTable'
10 | s.license = { :type => 'MIT', :file => 'LICENSE.md' }
11 | s.author = { 'Nuno Gonçalves' => '' }
12 | s.source = { :git => 'https://github.com/nunogoncalves/MultiSelectionTable.git', :tag => s.version.to_s }
13 | s.social_media_url = 'https://twitter.com/goncalvescmnuno'
14 |
15 | s.ios.deployment_target = '9.0'
16 |
17 | s.source_files = 'Source/**/*'
18 |
19 | # s.resource_bundles = {
20 | # 'MultiSelectionTable' => ['MultiSelectionTable/Assets/*.png']
21 | # }
22 |
23 | end
24 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # iOS-MultiSelectionTable
2 | Beautifull way of having a multi-selection table on iOS
3 |
4 | [](https://developer.apple.com/swift/)
5 | [](https://developer.apple.com/swift/)
6 | [](https://developer.apple.com/swift/)
7 | [](https://opensource.org/licenses/MIT)
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | Based on [this](https://dribbble.com/shots/2904577-Multi-Selection-Experiment) dribbble by [Vitaly Rubtsov](https://dribbble.com/Vitwai)
17 |
18 | ## How it works:
19 | ```MultiSelectionTable``` underneath is composed of a view and a data source, much like the ```UITableView```'s ```UITableViewDataSource/Delegate```. They both know each other and communicate between themselves.
20 | The view is is composed by two configurable ```UITableView``` and a line seperating them. The DataSource keeps the data the ```UITableView```s display.
21 |
22 | ## Considerations:
23 | (before Usage, pay attention to the following considerations)
24 | - In order to achieve a nice effect when transitioning, cells on the right (selected cells) must be equal to the cells on the left (all items cells).
25 | - The item object you are displaying, must conform with the ```Equatable``` protocol so the control can know where to move the items when unselecting items.
26 | -You can also paginate and use search on your items list. The table keeps a reference to the selected items.
27 | - The Marvel developers API has a 3000 requests limit per day. If this is reached and you can't try the Marvel example, you need to create a developers account to get credentials. Then replace them in ```Heroes.Fetcher.swift``` file
28 |
29 | ## Usage:
30 |
31 | ### Most basic usage:
32 |
33 | Considering you are using MultiSelectionTableView in ViewController:
34 |
35 | ```swift
36 |
37 | var multiSelectionDataSource: MultiSelectionDataSource! //MyItems must be Equatable
38 | var multiSelectionTableView: MultiSelectionTableView!
39 |
40 | var allItems: [MyItem] = [] //MyItem must be Equatable
41 |
42 | override func viewDidLoad() {
43 | super.viewDidLoad()
44 |
45 | multiSelectionTableView = MultiSelectionTableView()
46 | view.addSubview(multiSelectionTableView)
47 |
48 | multiSelectionDataSource = MultiSelectionDataSource(multiSelectionTableView: multiSelectionTableView)
49 | multiSelectionDataSource.delegate = self
50 | let cellReuseIdentifier = "MyCell"
51 | multiSelectionDataSource.register(nib: UINib(nibName: "MyCustomCellNibName", bundle: nil), for: cellReuseIdentifier)
52 |
53 | multiSelectionDataSource.allItems = allItems
54 |
55 | multiSelectionTableView.dataSource = multiSelectionDataSource
56 | }
57 |
58 | extension ViewController : MultiSelectionTableDelegate {
59 |
60 | func paint(_ cell: UITableViewCell, for indexPath: IndexPath, with item: Any) {
61 | if let cell = cell as? MyCustomCell,
62 | let myItem = item as? MyItem {
63 | //configureCellWithMyItem
64 | }
65 | }
66 |
67 | }
68 | ```
69 |
70 | ### Costumization
71 | #### Colors style
72 | ```swift
73 | multiSelectionTableView.controlBackgroundColor = .black
74 | multiSelectionTableView.allItemsTableBackgroundColor = .black
75 | multiSelectionTableView.selectedItemsTableBackgroundColor = .black
76 | ```
77 | #### Horizontal movement width:
78 | Depending on your cell, you might want to set the horizontal width the line moves. This value is based on the center X anchor.
79 | ```swift
80 | multiSelectionTableView.seperatorWidthOffset = 100 //will move 100 point on both directions from the center
81 | ```
82 | #### Animations
83 | There are two animation types. The selection and the transition. You can customize your animations for both types.
84 | The default selection animation is a pulse starting on the tap point on the cell.
85 | The default transition animation moves a snapshot view of the selected cell to the corresponding side (depending on selection or unselection events)
86 | ```swift
87 |
88 | multiSelectionTableView.cellAnimator = CellSelectionPulseAnimator(pulseColor: .black) // Must conform to CellSelectionAnimator
89 | multiSelectionTableView.cellTransitioner = CellFlyerAnimator() // Must conform to CellTransitionAnimator
90 |
91 | ```
92 | You can check out the animator examples.
93 |
94 | ### Pagination
95 | If you want MultiSelectionTableView to handle pagination you need to set:
96 | ```swift
97 | multiSelectionTableView.supportsPagination = true
98 | ```
99 | and you can add a target action to the control.
100 |
101 | ```swift
102 | multiSelectionTableView.addTarget(self, action: #selector(loadMoreData(sender:)), for: .scrollReachingEnd)
103 | ```
104 |
105 | Aditionally, you can have some control of when to get more data setting
106 | ```swift
107 | multiSelectionTableView.paginationNotificationRowIndex = 5
108 | ```
109 | this will call .scrollReachingEnd action 5 rows before reaching the end of the table, so you can pre fetch next page data.
110 |
111 | ### Empty State View
112 | It's common for results to come from the web, take some time loading, and/or be empty, and/or display an error. `MultiSelectionTable` has got you covered.
113 | If you want to display a custom empty view, just set the `stateView` with your view. For example a loading indicator:
114 |
115 | ```swift
116 | let loadingView = UIActivityIndicatorView(
117 | loadingView.transform = CGAffineTransform.init(scaleX: 2, y: 2)
118 | loadingView.startAnimating()
119 | multiSelectionTableView.stateView = loadingView
120 | ```
121 |
122 | ### Target Actions
123 | ```swift```
124 | ...
125 | multiSelectionTableView.addTarget(self, action: #selector(selectedItem(sender:)), for: .itemSelected)
126 | multiSelectionTableView.addTarget(self, action: #selector(unselectedItem(sender:)), for: .itemUnselected)
127 |
128 | //only called if supportsPagination is set to true
129 | multiSelectionTableView.addTarget(self, action: #selector(loadMoreData(sender:)), for: .scrollReachingEnd)
130 | ...
131 |
132 | @objc private func selectedItem(sender: MultiSelectionTableView) {
133 | print("selected item")
134 | }
135 |
136 | @objc private func unselectedItem(sender: MultiSelectionTableView) {
137 | print("unselected item")
138 | }
139 | ...
140 | ```
141 | ## Requirements
142 |
143 | - iOS 9.0+
144 | - Xcode 8.0+
145 |
146 | ## Installation
147 |
148 |
149 | Cocoapods
150 |
151 | MultiSelectionTable is available through [CocoaPods](http://cocoapods.org). To install
152 | it, simply add the following line to your Podfile:
153 |
154 | ```ruby
155 | platform :ios, '9.0'
156 | use_frameworks!
157 |
158 | pod 'MultiSelectionTable', git: 'https://github.com/nunogoncalves/iOS-MultiSelectionTable'
159 | ```
160 | (Currently **MultiSelectionTable** is still not yet published to Cocoapods, so for now you need to add ```swift git: 'https://github.com/nunogoncalves/iOS-MultiSelectionTable'```.
161 |
162 |
163 |
164 | Manually
165 | Copy the contents of [Source](https://github.com/nunogoncalves/iOS-MultiSelectionTable/tree/master/Source) folder into your project and you're ready to go.
166 |
167 |
168 | ## TODOs
169 | Missing features and/or bugs can be found in the [Issues](https://github.com/nunogoncalves/iOS-MultiSelectionTable/issues) section.
170 |
171 | ## Author
172 |
173 | Nuno Gonçalves
174 |
175 | |
|
176 | |:-------------:|:-------------:|
177 | | nunogoncalves | @goncalvescmnuno |
178 |
179 | ## Contribute
180 | Feel free to contribute to **MultiSelectionTable**.
181 | Check [Issues](https://github.com/nunogoncalves/iOS-MultiSelectionTable/issues) before asking something or adding some contribuition that's already being done.
182 |
183 | ## Licence
184 |
185 | **iOS-MultiSelectionTable** is available under the MIT license. See the [LICENSE](https://github.com/nunogoncalves/iOS-MultiSelectionTable/blob/master/LICENSE.md) file for more info.
186 |
187 | ## Final note
188 | If you use `MultiSelectionTable` in a production app, let me know. I'll be very flattered and pleased and sure want to be aware of it. :)
189 |
--------------------------------------------------------------------------------
/Resources/MultiSelectionTableView1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nunogoncalves/iOS-MultiSelectionTable/f5ceb6a15e1eb5db71308384fddcb4e817021024/Resources/MultiSelectionTableView1.gif
--------------------------------------------------------------------------------
/Resources/MultiselectionSupermanAnimation.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nunogoncalves/iOS-MultiSelectionTable/f5ceb6a15e1eb5db71308384fddcb4e817021024/Resources/MultiselectionSupermanAnimation.gif
--------------------------------------------------------------------------------
/Resources/StyleBlack.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nunogoncalves/iOS-MultiSelectionTable/f5ceb6a15e1eb5db71308384fddcb4e817021024/Resources/StyleBlack.png
--------------------------------------------------------------------------------
/Resources/StyleGreenBlue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nunogoncalves/iOS-MultiSelectionTable/f5ceb6a15e1eb5db71308384fddcb4e817021024/Resources/StyleGreenBlue.png
--------------------------------------------------------------------------------
/Resources/StyleRed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nunogoncalves/iOS-MultiSelectionTable/f5ceb6a15e1eb5db71308384fddcb4e817021024/Resources/StyleRed.png
--------------------------------------------------------------------------------
/Resources/StyleWhite.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nunogoncalves/iOS-MultiSelectionTable/f5ceb6a15e1eb5db71308384fddcb4e817021024/Resources/StyleWhite.png
--------------------------------------------------------------------------------
/Source/Animators/Selection/CellSelectionAnimator.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CellSelectionAnimator.swift
3 | // MultiSelectionTableView
4 | //
5 | // Created by Nuno Gonçalves on 07/12/16.
6 | //
7 | //
8 |
9 | import UIKit
10 |
11 | public protocol CellSelectionAnimator {
12 | func animate(_ cell: UITableViewCell,
13 | startingAt origin: CGPoint?,
14 | finish: (() -> ())?)
15 | }
16 |
--------------------------------------------------------------------------------
/Source/Animators/Selection/CellSelectionPulseAnimator.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CellSelectionPulseAnimator.swift
3 | // MultiSelectionTableView
4 | //
5 | // Created by Nuno Gonçalves on 07/12/16.
6 | //
7 | //
8 |
9 | import UIKit
10 |
11 | public class CellSelectionPulseAnimator : CellSelectionAnimator {
12 |
13 | private let pathLayer = CAShapeLayer()
14 | private let pathAnimation = CABasicAnimation(keyPath: "path")
15 | private let opacityAnimation = CABasicAnimation(keyPath: "opacity")
16 |
17 | private let pulseColor: UIColor
18 | private let initialPulseOpacityAlpha = 1.0
19 | private let finalPulseOpacityAlpha = 0.3
20 |
21 | private let animationDuration: TimeInterval = 0.3
22 |
23 | public init(pulseColor: UIColor) {
24 | self.pulseColor = pulseColor
25 | }
26 |
27 | public func animate(_ cell: UITableViewCell,
28 | startingAt origin: CGPoint?,
29 | finish: (() -> ())?) {
30 |
31 | let startingPoint = origin ?? cell.contentView.center
32 |
33 | let smallCircle = UIBezierPath(ovalIn: CGRect(x: startingPoint.x - 1,
34 | y: startingPoint.y - 1,
35 | width: 2,
36 | height: 2))
37 |
38 | let maxRadius = maxDistance(between: startingPoint, andCornersIn: cell.contentView.frame)
39 |
40 | let bigCircle = UIBezierPath(arcCenter: startingPoint,
41 | radius: maxRadius,
42 | startAngle: 0,
43 | endAngle: 2 * CGFloat.pi,
44 | clockwise: true)
45 |
46 | pathLayer.lineWidth = 0
47 | cell.contentView.layer.masksToBounds = true
48 | pathLayer.fillColor = pulseColor.cgColor
49 | cell.contentView.layer.addSublayer(pathLayer)
50 |
51 | CATransaction.begin()
52 |
53 | pathAnimation.fromValue = smallCircle.cgPath
54 | pathAnimation.toValue = bigCircle.cgPath
55 |
56 | CATransaction.setCompletionBlock {
57 | finish?()
58 | }
59 |
60 | opacityAnimation.fromValue = initialPulseOpacityAlpha
61 | opacityAnimation.toValue = finalPulseOpacityAlpha
62 |
63 | let animationGroup = CAAnimationGroup()
64 | animationGroup.duration = animationDuration
65 | animationGroup.animations = [pathAnimation, opacityAnimation]
66 |
67 | pathLayer.add(animationGroup, forKey: "animation")
68 |
69 | CATransaction.commit()
70 | }
71 |
72 | func maxDistance(between point: CGPoint, andCornersIn rect: CGRect) -> CGFloat {
73 | let px = point.x
74 | let py = point.y
75 |
76 | let corners = [
77 | CGPoint(x: rect.origin.x, y: rect.origin.y),
78 | CGPoint(x: rect.width, y: rect.origin.y),
79 | CGPoint(x: rect.origin.x, y: rect.height),
80 | CGPoint(x: rect.width, y: rect.height)
81 | ]
82 |
83 | var maxDistance: CGFloat = 0
84 |
85 | for corner in corners {
86 | let dx = abs(px - corner.x)
87 | let dy = abs(py - corner.y)
88 | let length = sqrt(dx * dx + dy * dy)
89 | maxDistance = max(length, maxDistance)
90 | }
91 | return maxDistance
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/Source/Animators/Translation/CellMover.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CellMover.swift
3 | // MultiSelectionTableView
4 | //
5 | // Created by Nuno Gonçalves on 09/12/16.
6 | //
7 | //
8 |
9 | import UIKit
10 |
11 | public class CellMover : CellTransitionAnimator {
12 |
13 | public func selectionTransition(in containerView: UIView,
14 | fromTableView: UITableView,
15 | fromIndexPath: IndexPath,
16 | toTableView: UITableView,
17 | toIndexPath: IndexPath) {
18 |
19 | toTableView.insertRows(at: [toIndexPath], with: .fade)
20 |
21 | animateTransition(defaultRemovingAnimation: .right,
22 | sourceTableView: fromTableView,
23 | sourceIndexPath: fromIndexPath,
24 | destinationTableView: toTableView,
25 | destinationIndexPath: toIndexPath,
26 | containerView: containerView)
27 | }
28 |
29 | public func unselectionTransition(in containerView: UIView,
30 | fromTableView: UITableView,
31 | fromIndexPath: IndexPath,
32 | toTableView: UITableView,
33 | toIndexPath: IndexPath) {
34 |
35 | toTableView.insertRows(at: [toIndexPath], with: .bottom)
36 |
37 | animateTransition(defaultRemovingAnimation: .left,
38 | sourceTableView: fromTableView,
39 | sourceIndexPath: fromIndexPath,
40 | destinationTableView: toTableView,
41 | destinationIndexPath: toIndexPath,
42 | containerView: containerView)
43 | }
44 |
45 | private func animateTransition(defaultRemovingAnimation: UITableView.RowAnimation,
46 | sourceTableView: UITableView,
47 | sourceIndexPath: IndexPath,
48 | destinationTableView: UITableView,
49 | destinationIndexPath: IndexPath,
50 | containerView: UIView) {
51 |
52 | guard let oldCell = sourceTableView.cellForRow(at: sourceIndexPath),
53 | let newCell = destinationTableView.cellForRow(at: destinationIndexPath),
54 | let movingCell = oldCell.contentView.snapshotView(afterScreenUpdates: false)
55 | else {
56 | sourceTableView.deleteRows(at: [sourceIndexPath], with: defaultRemovingAnimation)
57 | return
58 | }
59 |
60 | newCell.contentView.isHidden = true
61 | oldCell.contentView.isHidden = true
62 |
63 | containerView.addSubview(movingCell)
64 | movingCell.frame = sourceTableView.convert(oldCell.frame, to: containerView)
65 |
66 | sourceTableView.deleteRows(at: [sourceIndexPath], with: .fade)
67 |
68 | let newCellConvertedFrame = newCell.convert(newCell.contentView.frame, to: containerView)
69 |
70 | UIView.animate(withDuration: 0.4, animations: {
71 | movingCell.frame = newCellConvertedFrame
72 | }, completion: { _ in
73 | movingCell.removeFromSuperview()
74 | newCell.contentView.isHidden = false
75 | oldCell.contentView.isHidden = false // because of reusage.
76 | })
77 | }
78 |
79 | }
80 |
--------------------------------------------------------------------------------
/Source/Animators/Translation/CellReplacer.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CellReplacer.swift
3 | // MultiSelectionTableView
4 | //
5 | // Created by Nuno Gonçalves on 09/12/16.
6 | //
7 | //
8 |
9 | import UIKit
10 |
11 | public class CellReplacer : CellTransitionAnimator {
12 |
13 | public init() {}
14 |
15 | public func selectionTransition(in containerView: UIView,
16 | fromTableView: UITableView,
17 | fromIndexPath: IndexPath,
18 | toTableView: UITableView,
19 | toIndexPath: IndexPath) {
20 |
21 | toTableView.insertRows(at: [toIndexPath], with: .left)
22 | fromTableView.deleteRows(at: [fromIndexPath], with: .right)
23 | }
24 |
25 | public func unselectionTransition(in containerView: UIView,
26 | fromTableView: UITableView,
27 | fromIndexPath: IndexPath,
28 | toTableView: UITableView,
29 | toIndexPath: IndexPath) {
30 | toTableView.insertRows(at: [toIndexPath], with: .right)
31 | fromTableView.deleteRows(at: [fromIndexPath], with: .left)
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Source/Animators/Translation/CellTransitionAnimator.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CellTransitionAnimator.swift
3 | // MultiSelectionTableView
4 | //
5 | // Created by Nuno Gonçalves on 09/12/16.
6 | //
7 | //
8 |
9 | import UIKit
10 |
11 | public protocol CellTransitionAnimator {
12 | func selectionTransition(in containerView: UIView,
13 | fromTableView: UITableView,
14 | fromIndexPath: IndexPath,
15 | toTableView: UITableView,
16 | toIndexPath: IndexPath)
17 |
18 | func unselectionTransition(in containerView: UIView,
19 | fromTableView: UITableView,
20 | fromIndexPath: IndexPath,
21 | toTableView: UITableView,
22 | toIndexPath: IndexPath)
23 | }
24 |
--------------------------------------------------------------------------------
/Source/DataSource.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DataSource.swift
3 | // MultiSelectionTableView
4 | //
5 | // Created by Nuno Gonçalves on 04/12/16.
6 | //
7 |
8 | import UIKit
9 |
10 | public protocol DataSource : class {
11 |
12 | var allItemsCount: Int { get }
13 | var selectedItemsCount: Int { get }
14 | func cell(for indexPath: IndexPath, inAllItemsTable tableView: UITableView) -> UITableViewCell
15 | func cell(for indexPath: IndexPath, inSelectedItemsTable tableView: UITableView) -> UITableViewCell
16 |
17 | func unselectedItem(at index: Int)
18 | func selectedItem(at index: Int)
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/Source/ItemIndex.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ItemIndex.swift
3 | // MultiSelectionTableView
4 | //
5 | // Created by Nuno Gonçalves on 01/12/16.
6 | //
7 |
8 | internal struct ItemIndex : Equatable {
9 |
10 | let item: T
11 | var index: Int
12 |
13 | static func ==(lhs: ItemIndex, rhs: ItemIndex) -> Bool {
14 | return lhs.item == rhs.item
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Source/MultiSelectionDataSource.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MultiSelectionDataSource.swift
3 | // MultiSelectionTableView
4 | //
5 | // Created by Nuno Gonçalves on 04/12/16.
6 | //
7 |
8 | import UIKit
9 |
10 | final public class MultiSelectionDataSource : DataSource {
11 |
12 | fileprivate let multiSelectionTableView: MultiSelectionTableView!
13 |
14 | public weak var delegate: MultiSelectionTableDelegate?
15 |
16 | fileprivate var allItemsIndexes: [ItemIndex] = []
17 | public var allItems: [T] = [] {
18 | didSet {
19 | mapToItemIndexesAndUpdateOriginSelectedIndexesIfNecessary()
20 | multiSelectionTableView.reloadAllItemsTable()
21 | }
22 | }
23 |
24 | public var allItemsCount: Int {
25 | return allItemsIndexes.count
26 | }
27 |
28 | fileprivate var selectedItemsIndexes: [ItemIndex] = []
29 | public var selectedItems: [T] {
30 | return selectedItemsIndexes.map { $0.item }
31 | }
32 |
33 | public var selectedItemsCount: Int {
34 | return selectedItemsIndexes.count
35 | }
36 |
37 | private func mapToItemIndexesAndUpdateOriginSelectedIndexesIfNecessary() {
38 | let _selectedItems = selectedItemsIndexes.map{ $0.item }
39 | allItemsIndexes = allItems.enumerated().compactMap { index, item in
40 | if let indexOfItem = _selectedItems.firstIndex(of: item) {
41 | selectedItemsIndexes[indexOfItem].index = index
42 | return nil
43 | }
44 | return ItemIndex(item: item, index: index)
45 | }
46 | }
47 |
48 | public init(multiSelectionTableView: MultiSelectionTableView) {
49 | self.multiSelectionTableView = multiSelectionTableView
50 | }
51 |
52 | fileprivate var cellReuseId = "Cell"
53 | public func register(nib: UINib, for cellReuseIdentifier: String) {
54 | cellReuseId = cellReuseIdentifier
55 | multiSelectionTableView.register(nib: nib, for: cellReuseId)
56 | }
57 |
58 | public func register(anyClass: AnyClass?, for cellReuseIdentifier: String) {
59 | cellReuseId = cellReuseIdentifier
60 | multiSelectionTableView.register(anyClass: anyClass, for: cellReuseId)
61 | }
62 |
63 | public func selectedItem(at index: Int) {
64 | let item = allItemsIndexes.remove(at: index)
65 | selectedItemsIndexes.append(item)
66 |
67 | multiSelectionTableView.addToSelectedItemsTable(at: index)
68 | }
69 |
70 | public func unselectedItem(at index: Int) {
71 |
72 | let item = selectedItemsIndexes.remove(at: index)
73 |
74 | guard let indexToAdd = findIndexToPutBack(item, in: allItemsIndexes) else {
75 | multiSelectionTableView.removeFromSelected(at: index)
76 | return
77 | }
78 |
79 | allItemsIndexes.insert(item, at: indexToAdd)
80 |
81 | multiSelectionTableView.putBackInAllItemsTable(at: indexToAdd, selectedItemAt: index)
82 |
83 | if selectedItemsIndexes.isEmpty {
84 | multiSelectionTableView.displayAllItems()
85 | }
86 |
87 | }
88 |
89 | private func findIndexToPutBack(_ item: ItemIndex, in list: [ItemIndex]) -> Int? {
90 | guard allItems.contains(item.item) else { return nil }
91 |
92 | for (index, iteratedItemIndex) in list.enumerated() {
93 | if iteratedItemIndex.index >= item.index {
94 | return index
95 | }
96 | }
97 |
98 | return item.index
99 | }
100 |
101 | public func cell(for indexPath: IndexPath, inAllItemsTable tableView: UITableView) -> UITableViewCell {
102 | let cell = tableView.dequeueReusableCell(withIdentifier: cellReuseId, for: indexPath)
103 | let item = allItemsIndexes[indexPath.row]
104 | delegate?.paint(cell, at: indexPath, in: tableView, with: item.item)
105 |
106 | return cell
107 | }
108 |
109 | public func cell(for indexPath: IndexPath, inSelectedItemsTable tableView: UITableView) -> UITableViewCell {
110 | let cell = tableView.dequeueReusableCell(withIdentifier: cellReuseId, for: indexPath)
111 | let item = selectedItemsIndexes[indexPath.row]
112 | delegate?.paint(cell, at: indexPath, in: tableView, with: item.item)
113 | return cell
114 | }
115 |
116 | }
117 |
--------------------------------------------------------------------------------
/Source/MultiSelectionDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MultiSelectionTableDelegate.swift
3 | // MultiSelectionTableView
4 | //
5 | // Created by Nuno Gonçalves on 01/12/16.
6 | //
7 |
8 | import UIKit
9 |
10 | public protocol MultiSelectionTableDelegate : class {
11 |
12 | func paint(_ cell: UITableViewCell, at indexPath: IndexPath, in tableView: UITableView, with object: Any)
13 |
14 | }
15 |
16 |
--------------------------------------------------------------------------------
/Source/MultiSelectionTableView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MultiSelectionTableControl.swift
3 | // MultiSelectionTableView
4 | //
5 | // Created by Nuno Gonçalves on 29/11/16.
6 | //
7 |
8 | import UIKit
9 |
10 | @IBDesignable
11 | final public class MultiSelectionTableView : UIControl {
12 |
13 | public weak var dataSource: DataSource?
14 |
15 | fileprivate let allItemsTable = UITableView()
16 | fileprivate let seperator = UIView()
17 | fileprivate let selectedItemsTable = UITableView()
18 |
19 | fileprivate var seperatorCenterXConstraint: NSLayoutConstraint!
20 | fileprivate var allItemsTableLeadingConstraint: NSLayoutConstraint!
21 | fileprivate var selectedItemsTableTrailingConstraint: NSLayoutConstraint!
22 |
23 | fileprivate var isSelectingMode = false
24 | @IBInspectable var seperatorWidthOffset: CGFloat = 100
25 |
26 | public var cellAnimator: CellSelectionAnimator = CellSelectionPulseAnimator(pulseColor: .defaultCellPulseColor)
27 | public var cellTransitioner: CellTransitionAnimator = CellMover()
28 |
29 | fileprivate var stateViewContainer = UIView()
30 |
31 | override public func awakeFromNib() {
32 | super.awakeFromNib()
33 |
34 | allItemsTable.backgroundColor = allItemsTableBackgroundColor
35 | allItemsTable.separatorColor = .clear
36 |
37 | blackLine.backgroundColor = seperatorColor
38 |
39 | selectedItemsTable.backgroundColor = selectedItemsTableBackgroundColor
40 | selectedItemsTable.separatorColor = .clear
41 | }
42 |
43 | @IBInspectable
44 | public var controlBackgroundColor: UIColor = .black {
45 | didSet {
46 | backgroundColor = controlBackgroundColor
47 | }
48 | }
49 |
50 | @IBInspectable
51 | public var seperatorColor: UIColor = .black {
52 | didSet {
53 | blackLine.backgroundColor = seperatorColor
54 | }
55 | }
56 |
57 | @IBInspectable
58 | public var allItemsTableBackgroundColor: UIColor = .defaultTableBackground {
59 | didSet {
60 | allItemsTable.backgroundColor = allItemsTableBackgroundColor
61 | }
62 | }
63 |
64 | @IBInspectable
65 | public var selectedItemsTableBackgroundColor: UIColor = .defaultTableBackground {
66 | didSet {
67 | selectedItemsTable.backgroundColor = selectedItemsTableBackgroundColor
68 | }
69 | }
70 |
71 | public var allItemsContentInset: UIEdgeInsets = .zero {
72 | didSet {
73 | allItemsTable.contentInset = allItemsContentInset
74 | allItemsTable.scrollIndicatorInsets = UIEdgeInsets(top: allItemsContentInset.top,
75 | left: 0,
76 | bottom: 0,
77 | right: 0)
78 | }
79 | }
80 |
81 | public var selectedItemsContentInset: UIEdgeInsets = .zero {
82 | didSet {
83 | selectedItemsTable.contentInset = selectedItemsContentInset
84 | selectedItemsTable.scrollIndicatorInsets = UIEdgeInsets(top: selectedItemsContentInset.top,
85 | left: 0,
86 | bottom: 0,
87 | right: 0)
88 | }
89 | }
90 |
91 | @IBInspectable
92 | public var supportsPagination: Bool = false
93 |
94 | //If you want to provide pagination, and don't want the next page of data to be fetch on the
95 | //last item to be displayed, set this var to be the higher than the last item.
96 | //A value of 1 would mean the table would signal the caller that
97 | //it's displaying the second to last row and therefor send a .scrollReachingEnd action.
98 | @IBInspectable
99 | public var paginationNotificationRowIndex: Int = 0
100 |
101 | public var stateView: UIView? {
102 | didSet {
103 | stateViewContainer.subviews.forEach { $0.removeFromSuperview() }
104 | if let view = stateView {
105 | stateViewContainer.addSubview(view)
106 | view.pinToEdges(of: stateViewContainer, top: allItemsContentInset.top)
107 | view.translatesAutoresizingMaskIntoConstraints = false
108 | stateViewContainer.isHidden = false
109 | } else {
110 | stateViewContainer.isHidden = true
111 | }
112 | }
113 | }
114 |
115 | override init(frame: CGRect) {
116 | super.init(frame: frame)
117 | initialize()
118 | }
119 |
120 | required public init?(coder aDecoder: NSCoder) {
121 | super.init(coder: aDecoder)
122 | initialize()
123 | }
124 |
125 | private func initialize() {
126 | backgroundColor = controlBackgroundColor
127 |
128 | buildSeperator()
129 | buildAllItemsTable()
130 | buildSelectedItemsTable()
131 |
132 | buildStateViewContainer()
133 |
134 | displayAllItems()
135 | }
136 |
137 | fileprivate let blackLine = UIView()
138 | private func buildSeperator() {
139 | addSubview(blackLine)
140 | blackLine.backgroundColor = seperatorColor
141 |
142 | blackLine.topAnchor.constraint(equalTo: topAnchor).isActive = true
143 | blackLine.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
144 | seperatorCenterXConstraint = blackLine.centerXAnchor.constraint(equalTo: centerXAnchor)
145 | seperatorCenterXConstraint.isActive = true
146 |
147 | blackLine.widthAnchor.constraint(equalToConstant: 4).isActive = true
148 | blackLine.translatesAutoresizingMaskIntoConstraints = false
149 |
150 | let grayIndicator = UIView()
151 | blackLine.addSubview(grayIndicator)
152 | grayIndicator.layer.cornerRadius = 2
153 | grayIndicator.backgroundColor = .lightGray
154 |
155 | grayIndicator.centerXAnchor.constraint(equalTo: blackLine.centerXAnchor).isActive = true
156 | grayIndicator.centerYAnchor.constraint(equalTo: blackLine.centerYAnchor).isActive = true
157 | grayIndicator.widthAnchor.constraint(equalToConstant: 4).isActive = true
158 | grayIndicator.heightAnchor.constraint(equalToConstant: 50).isActive = true
159 | grayIndicator.translatesAutoresizingMaskIntoConstraints = false
160 |
161 | addSubview(seperator)
162 | seperator.backgroundColor = .clear
163 |
164 | seperator.topAnchor.constraint(equalTo: topAnchor).isActive = true
165 | seperator.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
166 | seperator.leadingAnchor.constraint(equalTo: blackLine.leadingAnchor, constant: -10).isActive = true
167 | seperator.trailingAnchor.constraint(equalTo: blackLine.trailingAnchor, constant: 10).isActive = true
168 | seperator.translatesAutoresizingMaskIntoConstraints = false
169 |
170 | let panGesture = UIPanGestureRecognizer(target: self, action: #selector(swipped(gesture:)))
171 | seperator.addGestureRecognizer(panGesture)
172 | }
173 |
174 | private func buildAllItemsTable() {
175 | addSubview(allItemsTable)
176 | configure(allItemsTable)
177 |
178 | allItemsTableLeadingConstraint = allItemsTable.leadingAnchor.constraint(equalTo: leadingAnchor)
179 | allItemsTableLeadingConstraint.isActive = true
180 | allItemsTable.topAnchor.constraint(equalTo: topAnchor).isActive = true
181 | allItemsTable.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
182 | allItemsTable.trailingAnchor.constraint(equalTo: seperator.leadingAnchor, constant: 5).isActive = true
183 |
184 | allItemsTable.translatesAutoresizingMaskIntoConstraints = false
185 | }
186 |
187 | private func buildSelectedItemsTable() {
188 | addSubview(selectedItemsTable)
189 | configure(selectedItemsTable)
190 |
191 | selectedItemsTable.tableHeaderView = TableViewHeader(title: "SELECTED")
192 |
193 | selectedItemsTable.leadingAnchor.constraint(equalTo: seperator.trailingAnchor, constant: -5).isActive = true
194 | selectedItemsTable.topAnchor.constraint(equalTo: topAnchor).isActive = true
195 | selectedItemsTable.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
196 | selectedItemsTableTrailingConstraint = selectedItemsTable.trailingAnchor.constraint(equalTo: trailingAnchor)
197 | selectedItemsTableTrailingConstraint.isActive = true
198 | selectedItemsTable.translatesAutoresizingMaskIntoConstraints = false
199 | }
200 |
201 | private func configure(_ tableView: UITableView) {
202 | tableView.tableFooterView = UIView()
203 |
204 | tableView.backgroundView = nil
205 | tableView.backgroundColor = self.allItemsTableBackgroundColor
206 | tableView.separatorColor = .clear
207 | tableView.keyboardDismissMode = .interactive
208 |
209 | tableView.delegate = self
210 | tableView.dataSource = self
211 |
212 | tableView.estimatedRowHeight = 100
213 | tableView.rowHeight = UITableView.automaticDimension
214 |
215 | let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(didTapTable(gestureRecognizer:)))
216 | tableView.addGestureRecognizer(tapGestureRecognizer)
217 |
218 | }
219 |
220 | private func buildStateViewContainer() {
221 | addSubview(stateViewContainer)
222 | stateViewContainer.isHidden = true
223 | stateViewContainer.pinToEdges(of: allItemsTable)
224 | stateViewContainer.translatesAutoresizingMaskIntoConstraints = false
225 | }
226 |
227 | //Used this method instead of `tableView(tableView:didSelectRowAt:)` because we need the
228 | //location on the cell where the cell was tapped. the delegate method won't give us that.
229 | @objc private func didTapTable(gestureRecognizer: UITapGestureRecognizer) {
230 | guard let tableView = gestureRecognizer.view as? UITableView else { return }
231 |
232 | let location = gestureRecognizer.location(in: tableView)
233 | guard let indexPath = tableView.indexPathForRow(at: location),
234 | let cell = tableView.cellForRow(at: indexPath)
235 | else {
236 | return
237 | }
238 |
239 | let origin = tableView.convert(location, to: cell.contentView)
240 |
241 | let actionAnimation: () -> ()
242 | if tableView == selectedItemsTable {
243 | actionAnimation = { [weak self] in
244 | self?.dataSource?.unselectedItem(at: indexPath.row)
245 | }
246 | sendActions(for: .itemUnselected)
247 | } else {
248 | actionAnimation = { [weak self] in
249 | self?.dataSource?.selectedItem(at: indexPath.row)
250 | }
251 | sendActions(for: .itemSelected)
252 | }
253 |
254 | cellAnimator.animate(cell, startingAt: origin) {
255 | actionAnimation()
256 | }
257 | }
258 |
259 | @objc private func swipped(gesture: UIPanGestureRecognizer) {
260 | if gesture.translation(in: seperator).x > 0 {
261 | displayAllItems()
262 | } else {
263 | displaySelectedItems()
264 | }
265 | }
266 |
267 | func displayAllItems() {
268 | if !isSelectingMode {
269 | seperatorCenterXConstraint.constant = seperatorWidthOffset
270 |
271 | allItemsTableLeadingConstraint.constant = 0
272 | selectedItemsTableTrailingConstraint.constant += seperatorCenterXConstraint.constant * 2
273 |
274 | animateStateTransition()
275 | isSelectingMode = true
276 | }
277 | }
278 |
279 | fileprivate func displaySelectedItems() {
280 | if isSelectingMode {
281 | seperatorCenterXConstraint.constant = -seperatorWidthOffset
282 |
283 | allItemsTableLeadingConstraint.constant += seperatorCenterXConstraint.constant * 2
284 | selectedItemsTableTrailingConstraint.constant = 5
285 |
286 | animateStateTransition()
287 | isSelectingMode = false
288 | }
289 | }
290 |
291 | private func animateStateTransition() {
292 | UIView.animate(withDuration: 0.3,
293 | delay: 0,
294 | usingSpringWithDamping: 1,
295 | initialSpringVelocity: 1,
296 | options: .curveEaseInOut,
297 | animations: {
298 | self.layoutIfNeeded()
299 | },
300 | completion: nil)
301 | }
302 |
303 | fileprivate var cellReuseId = "Cell"
304 | func register(nib: UINib, for cellReuseIdentifier: String) {
305 | cellReuseId = cellReuseIdentifier
306 | allItemsTable.register(nib, forCellReuseIdentifier: cellReuseId)
307 | selectedItemsTable.register(nib, forCellReuseIdentifier: cellReuseId)
308 | }
309 |
310 | func register(anyClass: AnyClass?, for cellReuseIdentifier: String) {
311 | cellReuseId = cellReuseIdentifier
312 | allItemsTable.register(anyClass, forCellReuseIdentifier: cellReuseId)
313 | selectedItemsTable.register(anyClass, forCellReuseIdentifier: cellReuseId)
314 | }
315 |
316 | func reloadAllItemsTable() {
317 | allItemsTable.reloadData()
318 | }
319 |
320 | func putBackInAllItemsTable(at index: Int, selectedItemAt selectedItemIndex: Int) {
321 |
322 | let indexPathToRemove = IndexPath(item: selectedItemIndex, section: 0)
323 | let newIndexPath = IndexPath(item: index, section: 0)
324 |
325 | cellTransitioner.unselectionTransition(in: self,
326 | fromTableView: selectedItemsTable,
327 | fromIndexPath: indexPathToRemove,
328 | toTableView: allItemsTable,
329 | toIndexPath: newIndexPath)
330 | }
331 |
332 | func removeFromSelected(at index: Int) {
333 | let indexPath = IndexPath(item: index, section: 0)
334 | selectedItemsTable.deleteRows(at: [indexPath], with: .right)
335 | }
336 |
337 | func addToSelectedItemsTable(at index: Int) {
338 | let count = selectedItemsTable.numberOfRows(inSection: 0)
339 | let newIndexPath = IndexPath(item: count, section: 0)
340 | let indexPath = IndexPath(item: index, section: 0)
341 | cellTransitioner.selectionTransition(in: self,
342 | fromTableView: allItemsTable,
343 | fromIndexPath: indexPath,
344 | toTableView: selectedItemsTable,
345 | toIndexPath: newIndexPath)
346 | }
347 | }
348 |
349 | extension MultiSelectionTableView : UITableViewDataSource {
350 |
351 | public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
352 | guard let dataSource = dataSource else { return 0 }
353 |
354 | if tableView == allItemsTable {
355 | return dataSource.allItemsCount
356 | } else {
357 | return dataSource.selectedItemsCount
358 | }
359 | }
360 |
361 | public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
362 | let cell: UITableViewCell?
363 |
364 | if tableView == allItemsTable {
365 | cell = dataSource?.cell(for: indexPath, inAllItemsTable: tableView)
366 | } else {
367 | cell = dataSource?.cell(for: indexPath, inSelectedItemsTable: tableView)
368 | }
369 | return cell ?? tableView.dequeueReusableCell(withIdentifier: cellReuseId) ?? UITableViewCell()
370 | }
371 | }
372 |
373 | extension MultiSelectionTableView : UITableViewDelegate {
374 |
375 |
376 | public func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
377 | guard supportsPagination,
378 | let dataSource = dataSource
379 | else { return }
380 |
381 | if tableView == allItemsTable {
382 | if indexPath.row == dataSource.allItemsCount - 1 - paginationNotificationRowIndex {
383 | sendActions(for: .scrollReachingEnd)
384 | }
385 | }
386 | }
387 | }
388 |
389 | fileprivate extension UIColor {
390 | static var defaultTableBackground: UIColor {
391 | UIColor(red: 25/255, green: 25/255, blue: 25/255, alpha: 1)
392 | }
393 |
394 | static var defaultCellPulseColor: UIColor {
395 | UIColor(red: 121/255, green: 2/255, blue: 188/255, alpha: 0.3)
396 | }
397 | }
398 |
399 | fileprivate extension UIView {
400 |
401 | typealias EdgesMargin = (top: CGFloat?, right: CGFloat?, bottom: CGFloat?, left: CGFloat?)
402 |
403 | func pinToEdges(
404 | of view: UIView,
405 | top: CGFloat = 0,
406 | left: CGFloat = 0,
407 | bottom: CGFloat = 0,
408 | right: CGFloat = 0
409 | ) {
410 | let boundAttributes: [NSLayoutConstraint.Attribute] = [.top, .right, .bottom, .left]
411 | let constants: [CGFloat] = [top, bottom, right, left]
412 |
413 | for i in 0..<4 {
414 | anchor(view, with: boundAttributes[i], withConstant: constants[i])
415 | }
416 | }
417 |
418 | private func anchor(
419 | _ view : UIView,
420 | with attribute: NSLayoutConstraint.Attribute,
421 | withConstant constant: CGFloat = 0
422 | ) {
423 |
424 | view.topAnchor.constraint(equalTo: topAnchor, constant: constant).isActive = true
425 | view.leftAnchor.constraint(equalTo: leftAnchor, constant: constant).isActive = true
426 | view.bottomAnchor.constraint(equalTo: bottomAnchor, constant: constant).isActive = true
427 | view.rightAnchor.constraint(equalTo: rightAnchor, constant: constant).isActive = true
428 |
429 | }
430 | }
431 |
--------------------------------------------------------------------------------
/Source/TableViewHeader.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TableViewHeader.swift
3 | // MultiSelectionTableView
4 | //
5 | // Created by Nuno Gonçalves on 08/12/16.
6 | //
7 | //
8 |
9 | import UIKit
10 |
11 | final class TableViewHeader : UIView {
12 |
13 | let title: String
14 |
15 | init(title: String) {
16 | self.title = title
17 |
18 | super.init(frame: CGRect(x: 0, y: 0, width: 100, height: 30))
19 |
20 | let label = UILabel()
21 | addSubview(label)
22 | label.text = title
23 | label.textColor = .gray
24 | label.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
25 | label.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 20).isActive = true
26 | label.translatesAutoresizingMaskIntoConstraints = false
27 | }
28 |
29 | @available(*, unavailable)
30 | required init?(coder aDecoder: NSCoder) {
31 | fatalError("init(coder:) has not been implemented")
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/Source/UIControlEvents.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIControlEvents.swift
3 | // Pods
4 | //
5 | // Created by Nuno Gonçalves on 15/12/16.
6 | //
7 | //
8 |
9 | public extension UIControl.Event {
10 |
11 | static var itemSelected: UIControl.Event {
12 | return UIControl.Event(rawValue: 1 << 30)
13 | }
14 |
15 | static var itemUnselected: UIControl.Event {
16 | return UIControl.Event(rawValue: 1 << 31)
17 | }
18 |
19 | static var scrollReachingEnd: UIControl.Event {
20 | return UIControl.Event(rawValue: 1 << 29)
21 | }
22 |
23 | }
24 |
--------------------------------------------------------------------------------