Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3aa04958d3 | ||
|
|
43808c6d33 | ||
|
|
84f19193e3 | ||
|
|
a2c6b4afbf | ||
|
|
2f74142383 | ||
|
|
a4df164649 | ||
|
|
729f3b7c5b | ||
|
|
959f922708 | ||
|
|
656aa95fea | ||
|
|
0f00fde7e6 | ||
|
|
f9654a5358 | ||
|
|
dd43c0251f | ||
|
|
b3f881281e | ||
|
|
c17d24bcee | ||
|
|
b1853f8e2a | ||
|
|
58e672ef87 | ||
|
|
de040b348b | ||
|
|
01bd7502ca | ||
|
|
8b09ecc842 | ||
|
|
6738ca13cb | ||
|
|
19ba6c05aa | ||
|
|
3184fff1e1 |
+1
-1
@@ -2,5 +2,5 @@ osx_image: xcode7.3
|
||||
language: objective-c
|
||||
|
||||
xcode_project: DemoExpandingCollection/DemoExpandingCollection.xcodeproj
|
||||
xcode_scheme: DemoExpandingCollection
|
||||
xcode_scheme: ExpandingCollection
|
||||
xcode_sdk: iphonesimulator
|
||||
|
||||
@@ -493,6 +493,7 @@
|
||||
TargetAttributes = {
|
||||
848596271CF585F3000BCC74 = {
|
||||
CreatedOnToolsVersion = 7.3.1;
|
||||
DevelopmentTeam = LEAZS7L33U;
|
||||
};
|
||||
8485963B1CF585F3000BCC74 = {
|
||||
CreatedOnToolsVersion = 7.3.1;
|
||||
@@ -500,6 +501,7 @@
|
||||
};
|
||||
8499D8131D001B82004B5B37 = {
|
||||
CreatedOnToolsVersion = 7.3.1;
|
||||
DevelopmentTeam = LEAZS7L33U;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -810,7 +812,7 @@
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.ramotion.ExpandingCollection;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.ramotion.dev;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
@@ -833,7 +835,7 @@
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.ramotion.ExpandingCollection;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.ramotion.dev;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
@@ -879,6 +881,7 @@
|
||||
8499D81E1D001B82004B5B37 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
|
||||
@@ -55,4 +55,4 @@ extension AppDelegate {
|
||||
NSShadowAttributeName: shadow
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -9,8 +9,9 @@
|
||||
import UIKit
|
||||
|
||||
extension UICollectionView {
|
||||
|
||||
func getReusableCellWithIdentifier<T: UICollectionViewCell where T: CollectionViewCellIdentifiable>(indexPath: NSIndexPath) -> T {
|
||||
guard let cell = self.dequeueReusableCellWithReuseIdentifier(T.cellIdentifier, forIndexPath: indexPath) as? T else {
|
||||
guard let cell = dequeueReusableCellWithReuseIdentifier(T.cellIdentifier, forIndexPath: indexPath) as? T else {
|
||||
fatalError("Couldn't instantiate view controller with identifier \(T.cellIdentifier) ")
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ extension UIStoryboard {
|
||||
}
|
||||
|
||||
func instantiateViewController<T: UIViewController where T: StoryboardIdentifiable>() -> T {
|
||||
guard let viewController = self.instantiateViewControllerWithIdentifier(T.storyboardIdentifier) as? T else {
|
||||
guard let viewController = instantiateViewControllerWithIdentifier(T.storyboardIdentifier) as? T else {
|
||||
fatalError("Couldn't instantiate view controller with identifier \(T.storyboardIdentifier) ")
|
||||
}
|
||||
|
||||
|
||||
+11
-6
@@ -14,7 +14,8 @@ class DemoTableViewController: ExpandingTableViewController {
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
configureNavBar()
|
||||
tableView.backgroundView = UIImageView(image: UIImage.Asset.BackgroundImage.image)
|
||||
let image1 = UIImage.Asset.BackgroundImage.image
|
||||
tableView.backgroundView = UIImageView(image: image1)
|
||||
}
|
||||
}
|
||||
// MARK: Helpers
|
||||
@@ -33,8 +34,10 @@ extension DemoTableViewController {
|
||||
|
||||
@IBAction func backButtonHandler(sender: AnyObject) {
|
||||
// buttonAnimation
|
||||
for case let viewController as DemoViewController in navigationController!.viewControllers {
|
||||
if case let rightButton as AnimatingBarButton = viewController.navigationItem.rightBarButtonItem {
|
||||
let viewControllers: [DemoViewController?] = navigationController?.viewControllers.map { $0 as? DemoViewController } ?? []
|
||||
|
||||
for viewController in viewControllers {
|
||||
if let rightButton = viewController?.navigationItem.rightBarButtonItem as? AnimatingBarButton {
|
||||
rightButton.animationSelected(false)
|
||||
}
|
||||
}
|
||||
@@ -49,8 +52,10 @@ extension DemoTableViewController {
|
||||
override func scrollViewDidScroll(scrollView: UIScrollView) {
|
||||
if scrollView.contentOffset.y < -25 {
|
||||
// buttonAnimation
|
||||
for case let viewController as DemoViewController in navigationController!.viewControllers {
|
||||
if case let rightButton as AnimatingBarButton = viewController.navigationItem.rightBarButtonItem {
|
||||
let viewControllers: [DemoViewController?] = navigationController?.viewControllers.map { $0 as? DemoViewController } ?? []
|
||||
|
||||
for viewController in viewControllers {
|
||||
if let rightButton = viewController?.navigationItem.rightBarButtonItem as? AnimatingBarButton {
|
||||
rightButton.animationSelected(false)
|
||||
}
|
||||
}
|
||||
@@ -59,4 +64,4 @@ extension DemoTableViewController {
|
||||
|
||||
scrollOffsetY = scrollView.contentOffset.y
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-2
@@ -19,6 +19,5 @@ class DemoCollectionViewCell: BasePageCollectionCell {
|
||||
customTitle.layer.shadowRadius = 2
|
||||
customTitle.layer.shadowOffset = CGSize(width: 0, height: 3)
|
||||
customTitle.layer.shadowOpacity = 0.2
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+11
-18
@@ -41,7 +41,7 @@ extension DemoViewController {
|
||||
}
|
||||
|
||||
private func fillCellIsOpeenArry() {
|
||||
for _ in 0..<items.count {
|
||||
for _ in items {
|
||||
cellsIsOpen.append(false)
|
||||
}
|
||||
}
|
||||
@@ -75,15 +75,12 @@ extension DemoViewController {
|
||||
|
||||
func swipeHandler(sender: UISwipeGestureRecognizer) {
|
||||
let indexPath = NSIndexPath(forRow: currentIndex, inSection: 0)
|
||||
guard case let cell as DemoCollectionViewCell = collectionView?.cellForItemAtIndexPath(indexPath) else {
|
||||
return
|
||||
}
|
||||
|
||||
guard let cell = collectionView?.cellForItemAtIndexPath(indexPath) as? DemoCollectionViewCell else { return }
|
||||
// double swipe Up transition
|
||||
if cell.isOpened == true && sender.direction == .Up {
|
||||
pushToViewController(getViewController())
|
||||
|
||||
if case let rightButton as AnimatingBarButton = navigationItem.rightBarButtonItem {
|
||||
if let rightButton = navigationItem.rightBarButtonItem as? AnimatingBarButton {
|
||||
rightButton.animationSelected(true)
|
||||
}
|
||||
}
|
||||
@@ -108,28 +105,25 @@ extension DemoViewController {
|
||||
extension DemoViewController {
|
||||
|
||||
func collectionView(collectionView: UICollectionView, willDisplayCell cell: UICollectionViewCell, forItemAtIndexPath indexPath: NSIndexPath) {
|
||||
guard case let cell as DemoCollectionViewCell = cell else {
|
||||
return
|
||||
}
|
||||
let index = indexPath.row % 4
|
||||
guard let cell = cell as? DemoCollectionViewCell else { return }
|
||||
|
||||
let index = indexPath.row % items.count
|
||||
let info = items[index]
|
||||
cell.backgroundImageView?.image = UIImage(named: info.imageName)
|
||||
cell.customTitle.text = info.title
|
||||
cell.cellIsOpen(cellsIsOpen[indexPath.row], animated: false)
|
||||
cell.cellIsOpen(cellsIsOpen[index], animated: false)
|
||||
}
|
||||
|
||||
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
|
||||
guard case let cell as DemoCollectionViewCell = collectionView.cellForItemAtIndexPath(indexPath) else {
|
||||
return
|
||||
}
|
||||
if currentIndex != indexPath.row { return }
|
||||
|
||||
guard let cell = collectionView.cellForItemAtIndexPath(indexPath) as? DemoCollectionViewCell
|
||||
where currentIndex == indexPath.row else { return }
|
||||
|
||||
if cell.isOpened == false {
|
||||
cell.cellIsOpen(true)
|
||||
} else {
|
||||
pushToViewController(getViewController())
|
||||
|
||||
if case let rightButton as AnimatingBarButton = navigationItem.rightBarButtonItem {
|
||||
if let rightButton = navigationItem.rightBarButtonItem as? AnimatingBarButton {
|
||||
rightButton.animationSelected(true)
|
||||
}
|
||||
}
|
||||
@@ -148,4 +142,3 @@ extension DemoViewController {
|
||||
return collectionView.dequeueReusableCellWithReuseIdentifier(String(DemoCollectionViewCell), forIndexPath: indexPath)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-5
@@ -58,10 +58,8 @@ extension AnimatingBarButton {
|
||||
}
|
||||
|
||||
private func configureImageView(imageView: UIImageView, imageName: String) {
|
||||
guard let customView = self.customView else {
|
||||
return
|
||||
}
|
||||
|
||||
guard let customView = customView else { return }
|
||||
|
||||
imageView.translatesAutoresizingMaskIntoConstraints = false
|
||||
imageView.image = UIImage(named: imageName)
|
||||
imageView.contentMode = .ScaleAspectFit
|
||||
@@ -85,5 +83,4 @@ extension AnimatingBarButton {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+4
-4
@@ -9,12 +9,12 @@
|
||||
import UIKit
|
||||
|
||||
protocol Rotatable {
|
||||
|
||||
|
||||
func rotateAnimationFrom(fromItem: UIView, toItem: UIView, duration: Double)
|
||||
}
|
||||
|
||||
extension Rotatable {
|
||||
|
||||
|
||||
func rotateAnimationFrom(fromItem: UIView, toItem: UIView, duration: Double) {
|
||||
|
||||
let fromRotate = animationFrom(0, to: M_PI, key: "transform.rotation", duration: duration)
|
||||
@@ -36,7 +36,7 @@ extension Rotatable {
|
||||
$0.fromValue = from
|
||||
$0.toValue = to
|
||||
$0.fillMode = kCAFillModeForwards
|
||||
$0.removedOnCompletion = false;
|
||||
$0.removedOnCompletion = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-2
@@ -28,9 +28,8 @@ class DemoExpandingCollectionTests: XCTestCase {
|
||||
|
||||
func testPerformanceExample() {
|
||||
// This is an example of a performance test case.
|
||||
self.measureBlock {
|
||||
measureBlock {
|
||||
// Put the code you want to measure the time of here.
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
[](http://twitter.com/Ramotion)
|
||||
[](https://cocoapods.org/pods/expanding-collection)
|
||||
[](http://cocoapods.org/pods/expanding-collection)
|
||||
[](https://github.com/Ramotion/expanding-collection)
|
||||
[](https://travis-ci.org/Ramotion/elastic-pull-to-update)
|
||||
[](https://codebeat.co/projects/github-com-ramotion-expanding-collection)
|
||||
|
||||
@@ -24,8 +25,13 @@ Just add the Source folder to your project.
|
||||
|
||||
or use [CocoaPods](https://cocoapods.org) with Podfile:
|
||||
``` ruby
|
||||
pod 'expanding-collection', '~> 0.0.2'
|
||||
pod 'expanding-collection'
|
||||
```
|
||||
or [Carthage](https://github.com/Carthage/Carthage) users can simply add to their `Cartfile`:
|
||||
```
|
||||
github "Ramotion/expanding-collection"
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```swift
|
||||
@@ -46,7 +52,7 @@ import expanding_collection
|
||||
- add any desired uiviews to frontView
|
||||
|
||||
3) Adding BackView
|
||||
- repeat step 3 (connect outlets to `@IBOutlet weak var backContainerView: UIView!`, `@IBOutlet weak var backConstraintY: NSLayoutConstraint!`)
|
||||
- repeat step 2 (connect outlets to `@IBOutlet weak var backContainerView: UIView!`, `@IBOutlet weak var backConstraintY: NSLayoutConstraint!`)
|
||||
|
||||
4) Cell example [DemoCell](https://github.com/Ramotion/expanding-collection/tree/master/DemoExpandingCollection/DemoExpandingCollection/ViewControllers/DemoViewController/Cells)
|
||||
|
||||
@@ -64,8 +70,8 @@ override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
// register cell
|
||||
let nib = UINib(nibName: "CellIdentifier", bundle: nil)
|
||||
collectionView?.registerNib(nib, forCellWithReuseIdentifier: String(DemoCollectionViewCell))
|
||||
let nib = UINib(nibName: "NibName", bundle: nil)
|
||||
collectionView?.registerNib(nib, forCellWithReuseIdentifier: "CellIdentifier")
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -8,16 +8,14 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
/// Base class for UITableViewcontroller which have back transition method
|
||||
public class ExpandingTableViewController: UITableViewController {
|
||||
|
||||
// MARK: Vars
|
||||
|
||||
/// The height of the table view header
|
||||
public var headerHeight: CGFloat = 236
|
||||
var transitionDriver: TransitionDriver?
|
||||
|
||||
public required init?(coder aDecoder: NSCoder) {
|
||||
super.init(coder: aDecoder)
|
||||
}
|
||||
var transitionDriver: TransitionDriver?
|
||||
}
|
||||
|
||||
// MARK: Helpers
|
||||
@@ -31,10 +29,14 @@ extension ExpandingTableViewController {
|
||||
return view.takeSnapshot(CGRect(origin: backImageOrigin, size: backImageSize))
|
||||
}
|
||||
}
|
||||
// MARK: Public
|
||||
|
||||
// MARK: Methods
|
||||
|
||||
extension ExpandingTableViewController {
|
||||
|
||||
/**
|
||||
Pops the top view controller from the navigation stack and updates the display with custom animation.
|
||||
*/
|
||||
public func popTransitionAnimation() {
|
||||
guard let transitionDriver = self.transitionDriver else {
|
||||
return
|
||||
|
||||
@@ -8,9 +8,11 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
/// Base class for UICollectionViewCell
|
||||
public class BasePageCollectionCell: UICollectionViewCell {
|
||||
|
||||
@IBInspectable public var openSize: CGFloat = 40
|
||||
/// Animation oposition offset when cell is open
|
||||
@IBInspectable public var yOffset: CGFloat = 40
|
||||
|
||||
// MARK: Constants
|
||||
|
||||
@@ -25,23 +27,43 @@ public class BasePageCollectionCell: UICollectionViewCell {
|
||||
|
||||
// MARK: Vars
|
||||
|
||||
@IBOutlet weak var frontContainerView: UIView!
|
||||
@IBOutlet weak var backContainerView: UIView!
|
||||
/// The view used as the face of the cell must connectid from xib or storyboard.
|
||||
@IBOutlet public weak var frontContainerView: UIView!
|
||||
/// The view used as the back of the cell must connectid from xib or storyboard.
|
||||
@IBOutlet public weak var backContainerView: UIView!
|
||||
|
||||
@IBOutlet weak var backConstraintY: NSLayoutConstraint!
|
||||
@IBOutlet weak var frontConstraintY: NSLayoutConstraint!
|
||||
/// constraints for backContainerView must connectid from xib or storyboard
|
||||
@IBOutlet public weak var backConstraintY: NSLayoutConstraint!
|
||||
/// constraints for frontContainerView must connectid from xib or storyboard
|
||||
@IBOutlet public weak var frontConstraintY: NSLayoutConstraint!
|
||||
|
||||
var shadowView: UIView?
|
||||
|
||||
/// A Boolean value that indicates whether the cell is opened.
|
||||
public var isOpened = false
|
||||
|
||||
// MARK: inits
|
||||
|
||||
/**
|
||||
Initializes a UICollectionViewCell from data in a given unarchiver.
|
||||
|
||||
- parameter aDecoder: An unarchiver object.
|
||||
|
||||
- returns: An initialized UICollectionViewCell object.
|
||||
*/
|
||||
required public init?(coder aDecoder: NSCoder) {
|
||||
super.init(coder: aDecoder)
|
||||
|
||||
configureOutletFromDecoder(aDecoder)
|
||||
}
|
||||
|
||||
/**
|
||||
Initializes and returns a newly allocated view object with the specified frame rectangle.
|
||||
|
||||
- parameter frame: The frame rectangle for the view
|
||||
|
||||
- returns: An initialized UICollectionViewCell object.
|
||||
*/
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
|
||||
@@ -70,18 +92,24 @@ extension BasePageCollectionCell {
|
||||
|
||||
extension BasePageCollectionCell {
|
||||
|
||||
/**
|
||||
Open or close cell.
|
||||
|
||||
- parameter isOpen: Contains the value true if the cell should display open state, if false should display close state.
|
||||
- parameter animated: Set to true if the change in selection state is animated.
|
||||
*/
|
||||
public func cellIsOpen(isOpen: Bool, animated: Bool = true) {
|
||||
if isOpen == isOpened { return }
|
||||
|
||||
frontConstraintY.constant = isOpen == true ? -bounds.size.height / 5 : 0
|
||||
backConstraintY.constant = isOpen == true ? bounds.size.height / 5 - openSize / 2 : 0
|
||||
backConstraintY.constant = isOpen == true ? bounds.size.height / 5 - yOffset / 2 : 0
|
||||
|
||||
if let widthConstant = backContainerView.getConstraint(.Width) {
|
||||
widthConstant.constant = isOpen == true ? contentView.bounds.size.width + openSize : contentView.bounds.size.width
|
||||
widthConstant.constant = isOpen == true ? contentView.bounds.size.width + yOffset : contentView.bounds.size.width
|
||||
}
|
||||
|
||||
if let heightConstant = backContainerView.getConstraint(.Height) {
|
||||
heightConstant.constant = isOpen == true ? contentView.bounds.size.height + openSize : contentView.bounds.size.height
|
||||
heightConstant.constant = isOpen == true ? contentView.bounds.size.height + yOffset : contentView.bounds.size.height
|
||||
}
|
||||
|
||||
if animated == true {
|
||||
|
||||
@@ -8,17 +8,28 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
/// UIViewController with UICollectionView with custom transition method
|
||||
public class ExpandingViewController: UIViewController {
|
||||
|
||||
/// The default size to use for cells.
|
||||
public var itemSize = CGSize(width: 256, height: 335)
|
||||
|
||||
/// The collection view object managed by this view controller.
|
||||
public var collectionView: UICollectionView?
|
||||
|
||||
private var transitionDriver: TransitionDriver?
|
||||
|
||||
/// Index of current cell
|
||||
public var currentIndex: Int {
|
||||
guard let collectionView = self.collectionView else { return 0 }
|
||||
|
||||
let startOffset = (collectionView.bounds.size.width - itemSize.width) / 2
|
||||
return Int((collectionView.contentOffset.x + startOffset) / (itemSize.width + 25))
|
||||
guard let collectionLayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout else {
|
||||
return 0
|
||||
}
|
||||
|
||||
let minimumLineSpacing = collectionLayout.minimumLineSpacing
|
||||
return Int((collectionView.contentOffset.x + startOffset + itemSize.width / 2) / (itemSize.width + minimumLineSpacing))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +47,11 @@ extension ExpandingViewController {
|
||||
|
||||
public extension ExpandingViewController {
|
||||
|
||||
/**
|
||||
Pushes a view controller onto the receiver’s stack and updates the display with custom animation.
|
||||
|
||||
- parameter viewController: The table view controller to push onto the stack.
|
||||
*/
|
||||
func pushToViewController(viewController: ExpandingTableViewController) {
|
||||
guard let collectionView = self.collectionView else {
|
||||
return
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Pod::Spec.new do |s|
|
||||
s.name = 'expanding-collection'
|
||||
s.version = '0.0.2'
|
||||
s.version = '0.0.6'
|
||||
s.summary = 'Transition animtion from CollectionView to TableView'
|
||||
s.license = 'MIT'
|
||||
s.homepage = 'https://github.com/Ramotion/expanding-collection'
|
||||
|
||||
Reference in New Issue
Block a user