├── .gitignore ├── GXCardView-Swift.gif ├── GXCardView-Swift.podspec ├── GXCardView-Swift ├── GXCardCell.swift ├── GXCardLayout.swift └── GXCardView.swift ├── GXCardViewSample ├── GXCardViewSample.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── GXCardViewSample │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ └── Contents.json │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── GXCardTestCell.swift │ ├── GXCardTestCell.xib │ ├── Info.plist │ ├── SceneDelegate.swift │ ├── ViewController.swift │ └── images │ │ ├── banner0.jpeg │ │ ├── banner1.jpeg │ │ └── banner2.jpeg ├── GXCardViewSampleTests │ ├── GXCardViewSampleTests.swift │ └── Info.plist └── GXCardViewSampleUITests │ ├── GXCardViewSampleUITests.swift │ └── Info.plist ├── LICENSE ├── QQ.jpeg └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## User settings 6 | xcuserdata/ 7 | 8 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) 9 | *.xcscmblueprint 10 | *.xccheckout 11 | 12 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) 13 | build/ 14 | DerivedData/ 15 | *.moved-aside 16 | *.pbxuser 17 | !default.pbxuser 18 | *.mode1v3 19 | !default.mode1v3 20 | *.mode2v3 21 | !default.mode2v3 22 | *.perspectivev3 23 | !default.perspectivev3 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | 28 | ## App packaging 29 | *.ipa 30 | *.dSYM.zip 31 | *.dSYM 32 | 33 | ## Playgrounds 34 | timeline.xctimeline 35 | playground.xcworkspace 36 | 37 | # Swift Package Manager 38 | # 39 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 40 | # Packages/ 41 | # Package.pins 42 | # Package.resolved 43 | # *.xcodeproj 44 | # 45 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata 46 | # hence it is not needed unless you have added a package configuration file to your project 47 | # .swiftpm 48 | 49 | .build/ 50 | 51 | # CocoaPods 52 | # 53 | # We recommend against adding the Pods directory to your .gitignore. However 54 | # you should judge for yourself, the pros and cons are mentioned at: 55 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 56 | # 57 | # Pods/ 58 | # 59 | # Add this line if you want to avoid checking in source code from the Xcode workspace 60 | # *.xcworkspace 61 | 62 | # Carthage 63 | # 64 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 65 | # Carthage/Checkouts 66 | 67 | Carthage/Build/ 68 | 69 | # Accio dependency management 70 | Dependencies/ 71 | .accio/ 72 | 73 | # fastlane 74 | # 75 | # It is recommended to not store the screenshots in the git repo. 76 | # Instead, use fastlane to re-generate the screenshots whenever they are needed. 77 | # For more information about the recommended setup visit: 78 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 79 | 80 | fastlane/report.xml 81 | fastlane/Preview.html 82 | fastlane/screenshots/**/*.png 83 | fastlane/test_output 84 | 85 | # Code Injection 86 | # 87 | # After new code Injection tools there's a generated folder /iOSInjectionProject 88 | # https://github.com/johnno1962/injectionforxcode 89 | 90 | iOSInjectionProject/ 91 | -------------------------------------------------------------------------------- /GXCardView-Swift.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsyhei/GXCardView-Swift/fab086a10be79218d312b11304c4496c6d7ed121/GXCardView-Swift.gif -------------------------------------------------------------------------------- /GXCardView-Swift.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod spec lint GXCardView-Swift.podspec' to ensure this is a 3 | # valid spec and to remove all comments including this before submitting the spec. 4 | # 5 | # To learn more about Podspec attributes see http://docs.cocoapods.org/specification.html 6 | # To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/ 7 | # 8 | 9 | Pod::Spec.new do |s| 10 | 11 | s.name = "GXCardView-Swift" 12 | s.version = "1.1.3" 13 | s.swift_version = "4.2" 14 | s.summary = "一个卡片式布局,类似(探探附近/QQ颜值匹配)等..." 15 | s.homepage = "https://github.com/gsyhei/GXCardView-Swift" 16 | s.license = { :type => "MIT", :file => "LICENSE" } 17 | s.author = { "Gin" => "279694479@qq.com" } 18 | s.platform = :ios, "9.0" 19 | s.source = { :git => "https://github.com/gsyhei/GXCardView-Swift.git", :tag => "1.1.3" } 20 | s.requires_arc = true 21 | s.source_files = "GXCardView-Swift" 22 | s.frameworks = "Foundation","UIKit" 23 | 24 | end 25 | -------------------------------------------------------------------------------- /GXCardView-Swift/GXCardCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GXCardCell.swift 3 | // GXCardViewSample 4 | // 5 | // Created by Gin on 2020/9/8. 6 | // Copyright © 2020 gin. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | fileprivate extension CGPoint { 12 | static func += (lhs: inout CGPoint, rhs: CGPoint) { 13 | lhs = CGPoint(x: lhs.x + rhs.x, y: lhs.y + rhs.y) 14 | } 15 | } 16 | 17 | public extension GXCardCell { 18 | @objc enum SwipeDirection: Int { 19 | case none = 0 20 | case left = 1 21 | case right = 2 22 | } 23 | } 24 | 25 | protocol GXCardCellDelagate: NSObjectProtocol { 26 | func cardCell(_ cell: GXCardCell, willRemoveAt direction: GXCardCell.SwipeDirection) 27 | func cardCell(_ cell: GXCardCell, didRemoveAt direction: GXCardCell.SwipeDirection) 28 | func cardCell(_ cell: GXCardCell, didMoveAt point: CGPoint, direction: GXCardCell.SwipeDirection) 29 | } 30 | 31 | public class GXCardCell: UICollectionViewCell { 32 | private var originalTransform: CGAffineTransform = .identity 33 | private var currentPoint: CGPoint = .zero 34 | weak var delegate: GXCardCellDelagate? 35 | weak var cardView: GXCardView! 36 | var maxAngle: CGFloat = 0 37 | var maxRemoveDistance: CGFloat = 0 38 | var isPanAnimatedEnd: Bool = false 39 | 40 | public convenience override init(frame: CGRect = .zero) { 41 | self.init(frame: frame) 42 | self.addPanGestureRecognizer() 43 | } 44 | 45 | public override func awakeFromNib() { 46 | super.awakeFromNib() 47 | self.addPanGestureRecognizer() 48 | } 49 | 50 | private func addPanGestureRecognizer() { 51 | let pan = UIPanGestureRecognizer(target: self, action: #selector(self.panGestureRecognizer(pan:))) 52 | self.addGestureRecognizer(pan) 53 | } 54 | 55 | @objc private func panGestureRecognizer(pan: UIPanGestureRecognizer) { 56 | guard self.cardView.isShowFirst(cell: self) else { return } 57 | switch pan.state { 58 | case .began: 59 | self.currentPoint = .zero 60 | self.originalTransform = self.transform 61 | case .changed: 62 | let movePoint = pan.translation(in: pan.view) 63 | self.didPanStateChanged(move: movePoint) 64 | pan.setTranslation(.zero, in: pan.view) 65 | case .ended: 66 | self.didPanStateEnded() 67 | default: 68 | self.restorePosition() 69 | } 70 | } 71 | } 72 | 73 | public extension GXCardCell { 74 | func remove(swipe direction: SwipeDirection) { 75 | self.remove(direction: direction, isPan: false) 76 | } 77 | } 78 | 79 | fileprivate extension GXCardCell { 80 | func degreesToRadians(angle: CGFloat) -> CGFloat { 81 | return (angle / 180.0 * CGFloat.pi) 82 | } 83 | func transform(direction: SwipeDirection) -> CGAffineTransform { 84 | switch direction { 85 | case .left: 86 | return self.originalTransform.rotated(by: -degreesToRadians(angle: self.maxAngle)) 87 | case .right: 88 | return self.originalTransform.rotated(by: degreesToRadians(angle: self.maxAngle)) 89 | default: return self.originalTransform 90 | } 91 | } 92 | func endCenter(direction: SwipeDirection, view: UIView) -> CGPoint { 93 | let rect: CGRect = self.cardView.rectCardViewForCell(self) 94 | let centerY: CGFloat = view.center.y + rect.origin.y - view.frame.origin.y 95 | switch direction { 96 | case .left: 97 | let endCenterX = -(GX_ScreenWidth*0.5 + self.frame.width) 98 | return CGPoint(x: endCenterX, y: centerY) 99 | case .right: 100 | let endCenterX = GX_ScreenWidth*0.5 + self.frame.width*1.5 101 | return CGPoint(x: endCenterX, y: centerY) 102 | default: return .zero 103 | } 104 | } 105 | func didPanStateChanged(move point: CGPoint) { 106 | self.currentPoint += point 107 | var moveScale: CGFloat = self.currentPoint.x / self.maxRemoveDistance 108 | if abs(moveScale) > 1.0 { 109 | moveScale = (moveScale > 0) ? 1.0 : -1.0 110 | } 111 | let angle = degreesToRadians(angle: self.maxAngle) * moveScale 112 | let transRotation = self.originalTransform.rotated(by: angle) 113 | self.transform = transRotation.translatedBy(x: self.currentPoint.x, y: self.currentPoint.y) 114 | 115 | if (self.currentPoint.x < -self.maxRemoveDistance) { 116 | self.delegate?.cardCell(self, didMoveAt: self.currentPoint, direction: .left) 117 | } 118 | else if (self.currentPoint.x > self.maxRemoveDistance) { 119 | self.delegate?.cardCell(self, didMoveAt: self.currentPoint, direction: .right) 120 | } 121 | else { 122 | self.delegate?.cardCell(self, didMoveAt: self.currentPoint, direction: .none) 123 | } 124 | } 125 | func didPanStateEnded() { 126 | self.cardView.isUserInteractionEnabled = (false || !self.isPanAnimatedEnd) 127 | if (self.currentPoint.x < -self.maxRemoveDistance) { 128 | self.remove(direction: .left, isPan: true) 129 | } 130 | else if (self.currentPoint.x > self.maxRemoveDistance) { 131 | self.remove(direction: .right, isPan: true) 132 | } 133 | else { 134 | self.restorePosition() 135 | } 136 | } 137 | func restorePosition() { 138 | UIView.animate( 139 | withDuration: GX_SpringDuration, delay: 0, 140 | usingSpringWithDamping: GX_SpringWithDamping, 141 | initialSpringVelocity: GX_SpringVelocity, 142 | options: .curveEaseOut, 143 | animations: { 144 | self.transform = self.originalTransform 145 | }, completion: { (finished) in 146 | self.cardView.isUserInteractionEnabled = true 147 | }) 148 | } 149 | func didRemove(direction: SwipeDirection) { 150 | self.transform = self.originalTransform 151 | self.removeFromSuperview() 152 | self.delegate?.cardCell(self, didRemoveAt: direction) 153 | } 154 | func remove(direction: SwipeDirection, isPan stateEnded: Bool) { 155 | self.delegate?.cardCell(self, willRemoveAt: direction) 156 | let snapshotView = self.snapshotView(afterScreenUpdates: true) ?? UIView() 157 | snapshotView.transform = self.transform 158 | self.cardView.addSubview(snapshotView) 159 | let toCenter = self.endCenter(direction: direction, view: snapshotView) 160 | snapshotView.center = CGPoint(x: snapshotView.center.x, y: toCenter.y) 161 | self.didRemove(direction: direction) 162 | 163 | let toTransform: CGAffineTransform = self.transform(direction: direction) 164 | UIView.animateKeyframes(withDuration: GX_DefaultDuration, delay: 0, options: .calculationModeLinear, animations: { 165 | UIView.addKeyframe(withRelativeStartTime: 0.0, relativeDuration: 1.0) { 166 | snapshotView.center = toCenter 167 | } 168 | if !stateEnded { 169 | UIView.addKeyframe(withRelativeStartTime: 0.0, relativeDuration: 0.5) { 170 | snapshotView.transform = toTransform 171 | } 172 | } 173 | }, completion: { (finished) in 174 | snapshotView.removeFromSuperview() 175 | self.cardView.isUserInteractionEnabled = true 176 | }) 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /GXCardView-Swift/GXCardLayout.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UICardLayout.swift 3 | // GXCardViewSample 4 | // 5 | // Created by Gin on 2020/9/7. 6 | // Copyright © 2020 gin. All rights reserved. 7 | // 8 | 9 | // 10 | // UICardLayout.swift 11 | // GXCardViewSample 12 | // 13 | // Created by Gin on 2020/9/7. 14 | // Copyright © 2020 gin. All rights reserved. 15 | // 16 | 17 | import UIKit 18 | 19 | let GX_DefaultDuration: TimeInterval = 0.25 20 | let GX_SpringDuration: TimeInterval = 0.5 21 | let GX_SpringWithDamping: CGFloat = 0.5 22 | let GX_SpringVelocity: CGFloat = 0.8 23 | let GX_ScreenWidth = UIScreen.main.bounds.size.width 24 | let GX_ScreenHeight = UIScreen.main.bounds.size.height 25 | 26 | public class GXCardLayout: UICollectionViewLayout { 27 | /// 卡片可见数量(默认3) 28 | open var visibleCount: Int = 3 29 | /// 卡片与卡片之间的insets(从上至下,正负皆可) 30 | open var cardInsets: UIEdgeInsets = UIEdgeInsets(top: 10, left: 10, bottom: -10, right: 10) 31 | /// 侧滑最大角度(默认15°) 32 | open var maxAngle: CGFloat = 15.0 33 | /// 是否重复(默认false) 34 | open var isRepeat: Bool = false 35 | /// 最大移除距离(默认屏幕的1/4) 36 | open var maxRemoveDistance: CGFloat = GX_ScreenWidth * 0.25 37 | /// 是否需要移除动画结束才可以再次拖拽 38 | open var isPanAnimatedEnd: Bool = false 39 | } 40 | 41 | public extension GXCardLayout { 42 | 43 | override func prepare() { 44 | self.collectionView?.bounces = false 45 | self.collectionView?.clipsToBounds = false 46 | self.collectionView?.isPagingEnabled = true 47 | self.collectionView?.isScrollEnabled = false 48 | self.collectionView?.showsVerticalScrollIndicator = false 49 | self.collectionView?.showsHorizontalScrollIndicator = false 50 | } 51 | 52 | override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool { 53 | return true 54 | } 55 | 56 | override var collectionViewContentSize: CGSize { 57 | return CGSize(width: self.size.width, height: CGFloat(self.number + 1) * self.size.height) 58 | } 59 | 60 | override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { 61 | var attributes: [UICollectionViewLayoutAttributes] = [] 62 | let index: Int = Int(ceil(self.offset.y / self.size.height)) 63 | var showMax: Int = index + self.visibleCount 64 | if !self.isRepeat && (showMax > self.number) { 65 | showMax = self.number 66 | } 67 | for i in index.. UICollectionViewLayoutAttributes? { 77 | let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath) 78 | let firstIndex: Int = Int(ceil(self.offset.y / self.size.height)) 79 | var index = (indexPath.item - firstIndex) 80 | if index < 0 { index = (self.number - firstIndex + indexPath.item) % self.visibleCount } 81 | var percent = (self.offset.y.truncatingRemainder(dividingBy:self.size.height) / self.size.height) 82 | if index == self.visibleCount - 1 { 83 | attributes.alpha = (percent > 0) ? percent : 1.0 84 | } 85 | if percent < 1 && percent > 0 { percent = 1 - percent } 86 | let indexPercent: CGFloat = CGFloat(index) + percent 87 | let insets: UIEdgeInsets = self.cardCellInsets(for: indexPercent) 88 | let size = self.bounds.inset(by: insets).size 89 | let transform = CGAffineTransform(scaleX: size.width/self.size.width, y: size.height/self.size.height) 90 | attributes.frame = self.bounds 91 | attributes.transform = transform 92 | let centerX = attributes.center.x + (insets.left - insets.right)/2 93 | let centerY = attributes.center.y + (insets.top - insets.bottom)/2 94 | attributes.center = CGPoint(x: centerX, y: centerY) 95 | 96 | return attributes 97 | } 98 | } 99 | 100 | fileprivate extension GXCardLayout { 101 | var size: CGSize { 102 | return self.collectionView?.frame.size ?? .zero 103 | } 104 | var number: Int { 105 | return self.collectionView?.numberOfItems(inSection: 0) ?? 0 106 | } 107 | var offset: CGPoint { 108 | return self.collectionView?.contentOffset ?? .zero 109 | } 110 | var bounds: CGRect { 111 | return self.collectionView?.bounds ?? .zero 112 | } 113 | func cardCellInsets(for indexPercent: CGFloat) -> UIEdgeInsets { 114 | var insets: UIEdgeInsets = .zero 115 | if self.cardInsets.top < 0 { 116 | insets.top -= self.cardInsets.top * (CGFloat(self.visibleCount - 1) - indexPercent) 117 | } 118 | else { 119 | insets.top = self.cardInsets.top * indexPercent 120 | } 121 | if self.cardInsets.left < 0 { 122 | insets.left -= self.cardInsets.left * (CGFloat(self.visibleCount - 1) - indexPercent) 123 | } 124 | else { 125 | insets.left = self.cardInsets.left * indexPercent 126 | } 127 | if self.cardInsets.right < 0 { 128 | insets.right -= self.cardInsets.right * (CGFloat(self.visibleCount - 1) - indexPercent) 129 | } 130 | else { 131 | insets.right = self.cardInsets.right * indexPercent 132 | } 133 | if self.cardInsets.bottom < 0 { 134 | insets.bottom -= self.cardInsets.bottom * (CGFloat(self.visibleCount - 1) - indexPercent) 135 | } 136 | else { 137 | insets.bottom = self.cardInsets.bottom * indexPercent 138 | } 139 | return insets 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /GXCardView-Swift/GXCardView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GXCardCView.swift 3 | // GXCardViewSample 4 | // 5 | // Created by Gin on 2020/9/8. 6 | // Copyright © 2020 gin. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public protocol GXCardCViewDataSource: NSObjectProtocol { 12 | func numberOfItems(in cardView: GXCardView) -> Int 13 | func cardView(_ cardView: GXCardView, cellForItemAt indexPath: IndexPath) -> GXCardCell 14 | } 15 | 16 | @objc public protocol GXCardCViewDelegate: NSObjectProtocol { 17 | @objc optional func cardView(_ cardView: GXCardView, didSelectItemAt index: Int) 18 | @objc optional func cardView(_ cardView: GXCardView, willRemove cell: GXCardCell, forItemAt index: Int, direction: GXCardCell.SwipeDirection) 19 | @objc optional func cardView(_ cardView: GXCardView, didRemove cell: GXCardCell, forItemAt index: Int, direction: GXCardCell.SwipeDirection) 20 | @objc optional func cardView(_ cardView: GXCardView, didRemoveLast cell: GXCardCell, forItemAt index: Int, direction: GXCardCell.SwipeDirection) 21 | @objc optional func cardView(_ cardView: GXCardView, didDisplay cell: GXCardCell, forItemAt index: Int) 22 | @objc optional func cardView(_ cardView: GXCardView, didMove cell: GXCardCell, forItemAt index: Int, move point: CGPoint, direction: GXCardCell.SwipeDirection) 23 | } 24 | 25 | public class GXCardView: UIView { 26 | public weak var dataSource: GXCardCViewDataSource? 27 | public weak var delegate: GXCardCViewDelegate? 28 | public var cardLayout: GXCardLayout! 29 | public var collectionView: UICollectionView! 30 | 31 | convenience init(frame: CGRect, cardLayout layout: GXCardLayout) { 32 | self.init(frame:frame) 33 | self.setCardLayout(cardLayout: layout) 34 | } 35 | 36 | func setCardLayout(cardLayout layout: GXCardLayout) { 37 | self.cardLayout = layout 38 | self.collectionView = UICollectionView(frame: self.bounds, collectionViewLayout: self.cardLayout) 39 | self.collectionView.backgroundColor = .clear 40 | self.collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight] 41 | self.collectionView.dataSource = self 42 | self.collectionView.delegate = self 43 | self.addSubview(self.collectionView) 44 | } 45 | } 46 | 47 | public extension GXCardView { 48 | final func register(classCellType: T.Type) { 49 | let cellID = String(describing: classCellType) 50 | self.collectionView.register(classCellType, forCellWithReuseIdentifier: cellID) 51 | } 52 | final func register(nibCellType: T.Type) { 53 | let cellID = String(describing: nibCellType) 54 | let nib = UINib.init(nibName: cellID, bundle: nil) 55 | self.collectionView.register(nib, forCellWithReuseIdentifier: cellID) 56 | } 57 | final func dequeueReusableCell(for indexPath: IndexPath, cellType: T.Type = T.self) -> T { 58 | let cellID = String(describing: cellType) 59 | let bareCell = self.collectionView.dequeueReusableCell(withReuseIdentifier: cellID, for: indexPath) 60 | guard let cell = bareCell as? T else { 61 | fatalError( 62 | "Failed to dequeue a cell with identifier \(cellID) matching type \(cellType.self). " 63 | + "Check that the reuseIdentifier is set properly in your XIB/Storyboard " 64 | + "and that you registered the cell beforehand" 65 | ) 66 | } 67 | return cell 68 | } 69 | final func setContentOffset(_ contentOffset: CGPoint, animated: Bool) { 70 | self.collectionView.performBatchUpdates({ 71 | }) { [weak self] (finished) in 72 | self?.collectionView.setContentOffset(contentOffset, animated: animated) 73 | } 74 | } 75 | final func scrollToItem(at index: Int, animated: Bool) { 76 | if animated && index > 0 { 77 | let currentIndex: Int = Int(round(self.collectionView.contentOffset.y / self.collectionView.frame.height)) 78 | if abs(currentIndex - index) > 1 { 79 | let offsetY: CGFloat = CGFloat(index - 1) * self.collectionView.frame.height 80 | self.setContentOffset(CGPoint(x: 0, y: offsetY), animated: false) 81 | } 82 | } 83 | let offsetY: CGFloat = CGFloat(index) * self.collectionView.frame.height 84 | self.setContentOffset(CGPoint(x: 0, y: offsetY), animated: animated) 85 | } 86 | final func removeTopCardViewCell(swipe direction: GXCardCell.SwipeDirection) { 87 | let index: Int = Int(round(self.collectionView.contentOffset.y / self.collectionView.frame.height)) 88 | let topCell = self.collectionView.cellForItem(at: IndexPath(item: index, section: 0)) as? GXCardCell 89 | topCell?.remove(swipe: direction) 90 | } 91 | final func invalidateLayout() { 92 | self.collectionView.collectionViewLayout.invalidateLayout() 93 | } 94 | final func reloadData() { 95 | self.collectionView.reloadData() 96 | } 97 | final func isShowFirst(cell: GXCardCell) -> Bool { 98 | let index = self.collectionView.indexPath(for: cell)?.item ?? 0 99 | let firstIndex = Int(ceil(self.collectionView.contentOffset.y/self.collectionView.frame.height)) 100 | return index == firstIndex 101 | } 102 | final func rectCardViewForCell(_ cell: GXCardCell) -> CGRect { 103 | return self.collectionView.convert(cell.frame, to: self) 104 | } 105 | } 106 | 107 | extension GXCardView: UICollectionViewDataSource, UICollectionViewDelegate { 108 | // MARK: - UICollectionViewDataSource 109 | public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 110 | return self.dataSource?.numberOfItems(in: self) ?? 0 111 | } 112 | public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 113 | let cell = self.dataSource?.cardView(self, cellForItemAt: indexPath) 114 | cell?.isPanAnimatedEnd = self.cardLayout.isPanAnimatedEnd 115 | cell?.maxRemoveDistance = self.cardLayout.maxRemoveDistance 116 | cell?.maxAngle = self.cardLayout.maxAngle 117 | cell?.cardView = self 118 | cell?.delegate = self 119 | return cell ?? UICollectionViewCell() 120 | } 121 | // MARK: - UICollectionViewDelegate 122 | public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { 123 | if (delegate?.responds(to: #selector(delegate?.cardView(_:didSelectItemAt:))) ?? false) { 124 | self.delegate?.cardView?(self, didSelectItemAt: indexPath.item) 125 | } 126 | } 127 | public func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { 128 | if (delegate?.responds(to: #selector(delegate?.cardView(_:didDisplay:forItemAt:))) ?? false) { 129 | self.delegate?.cardView?(self, didDisplay: cell as! GXCardCell, forItemAt: indexPath.item) 130 | } 131 | } 132 | } 133 | 134 | extension GXCardView: UIScrollViewDelegate { 135 | // MARK: - UIScrollViewDelegate 136 | public func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) { 137 | guard self.cardLayout.isRepeat else { return } 138 | let index: Int = Int(round(scrollView.contentOffset.y / scrollView.frame.height)) 139 | let lastIndex = (self.dataSource?.numberOfItems(in: self) ?? 0) 140 | if index == lastIndex { 141 | self.scrollToItem(at: 0, animated: false) 142 | } 143 | } 144 | public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { 145 | guard self.cardLayout.isRepeat else { return } 146 | let index: Int = Int(round(scrollView.contentOffset.y / scrollView.frame.height)) 147 | let lastIndex = (self.dataSource?.numberOfItems(in: self) ?? 0) 148 | if index == lastIndex { 149 | self.scrollToItem(at: 0, animated: false) 150 | } 151 | } 152 | } 153 | 154 | extension GXCardView: GXCardCellDelagate { 155 | func cardCell(_ cell: GXCardCell, willRemoveAt direction: GXCardCell.SwipeDirection) { 156 | let index = self.collectionView.indexPath(for: cell)?.item ?? 0 157 | if (delegate?.responds(to: #selector(delegate?.cardView(_:willRemove:forItemAt:direction:))) ?? false) { 158 | self.delegate?.cardView?(self, willRemove: cell, forItemAt: index, direction: direction) 159 | } 160 | } 161 | func cardCell(_ cell: GXCardCell, didRemoveAt direction: GXCardCell.SwipeDirection) { 162 | let index = self.collectionView.indexPath(for: cell)?.item ?? 0 163 | let lastIndex = (self.dataSource?.numberOfItems(in: self) ?? 0) - 1 164 | 165 | if self.cardLayout.isRepeat { 166 | self.scrollToItem(at: index + 1, animated: true) 167 | } 168 | else { 169 | if index <= lastIndex { 170 | self.scrollToItem(at: index + 1, animated: true) 171 | } 172 | } 173 | 174 | if (delegate?.responds(to: #selector(delegate?.cardView(_:didRemove:forItemAt:direction:))) ?? false) { 175 | self.delegate?.cardView?(self, didRemove: cell, forItemAt: index, direction: direction) 176 | } 177 | if index == lastIndex { 178 | if (delegate?.responds(to: #selector(delegate?.cardView(_:didRemoveLast:forItemAt:direction:))) ?? false) { 179 | self.delegate?.cardView?(self, didRemoveLast: cell, forItemAt: index, direction: direction) 180 | } 181 | } 182 | } 183 | func cardCell(_ cell: GXCardCell, didMoveAt point: CGPoint, direction: GXCardCell.SwipeDirection) { 184 | if (delegate?.responds(to: #selector(delegate?.cardView(_:didMove:forItemAt:move:direction:))) ?? false) { 185 | let index = self.collectionView.indexPath(for: cell)?.item ?? 0 186 | self.delegate?.cardView?(self, didMove: cell, forItemAt: index, move: point, direction: direction) 187 | } 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /GXCardViewSample/GXCardViewSample.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 33034C3924DCCE6A00585225 /* banner2.jpeg in Resources */ = {isa = PBXBuildFile; fileRef = 33034C3624DCCE6A00585225 /* banner2.jpeg */; }; 11 | 33034C3A24DCCE6A00585225 /* banner1.jpeg in Resources */ = {isa = PBXBuildFile; fileRef = 33034C3724DCCE6A00585225 /* banner1.jpeg */; }; 12 | 33034C3B24DCCE6A00585225 /* banner0.jpeg in Resources */ = {isa = PBXBuildFile; fileRef = 33034C3824DCCE6A00585225 /* banner0.jpeg */; }; 13 | 3323EC072507EAA100BD24ED /* GXCardCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3323EC042507EAA000BD24ED /* GXCardCell.swift */; }; 14 | 3323EC082507EAA100BD24ED /* GXCardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3323EC052507EAA000BD24ED /* GXCardView.swift */; }; 15 | 3323EC092507EAA100BD24ED /* GXCardLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3323EC062507EAA000BD24ED /* GXCardLayout.swift */; }; 16 | 3330854A2506F65E00549452 /* GXCardTestCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 333085482506F65E00549452 /* GXCardTestCell.swift */; }; 17 | 3330854B2506F65E00549452 /* GXCardTestCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 333085492506F65E00549452 /* GXCardTestCell.xib */; }; 18 | 33D6DD2424DA53B0008C7964 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33D6DD2324DA53B0008C7964 /* AppDelegate.swift */; }; 19 | 33D6DD2624DA53B0008C7964 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33D6DD2524DA53B0008C7964 /* SceneDelegate.swift */; }; 20 | 33D6DD2824DA53B0008C7964 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33D6DD2724DA53B0008C7964 /* ViewController.swift */; }; 21 | 33D6DD2B24DA53B0008C7964 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 33D6DD2924DA53B0008C7964 /* Main.storyboard */; }; 22 | 33D6DD2D24DA53B2008C7964 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33D6DD2C24DA53B2008C7964 /* Assets.xcassets */; }; 23 | 33D6DD3024DA53B2008C7964 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 33D6DD2E24DA53B2008C7964 /* LaunchScreen.storyboard */; }; 24 | 33D6DD3B24DA53B2008C7964 /* GXCardViewSampleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33D6DD3A24DA53B2008C7964 /* GXCardViewSampleTests.swift */; }; 25 | 33D6DD4624DA53B2008C7964 /* GXCardViewSampleUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33D6DD4524DA53B2008C7964 /* GXCardViewSampleUITests.swift */; }; 26 | /* End PBXBuildFile section */ 27 | 28 | /* Begin PBXContainerItemProxy section */ 29 | 33D6DD3724DA53B2008C7964 /* PBXContainerItemProxy */ = { 30 | isa = PBXContainerItemProxy; 31 | containerPortal = 33D6DD1824DA53B0008C7964 /* Project object */; 32 | proxyType = 1; 33 | remoteGlobalIDString = 33D6DD1F24DA53B0008C7964; 34 | remoteInfo = GXCardViewSample; 35 | }; 36 | 33D6DD4224DA53B2008C7964 /* PBXContainerItemProxy */ = { 37 | isa = PBXContainerItemProxy; 38 | containerPortal = 33D6DD1824DA53B0008C7964 /* Project object */; 39 | proxyType = 1; 40 | remoteGlobalIDString = 33D6DD1F24DA53B0008C7964; 41 | remoteInfo = GXCardViewSample; 42 | }; 43 | /* End PBXContainerItemProxy section */ 44 | 45 | /* Begin PBXFileReference section */ 46 | 33034C3624DCCE6A00585225 /* banner2.jpeg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = banner2.jpeg; sourceTree = ""; }; 47 | 33034C3724DCCE6A00585225 /* banner1.jpeg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = banner1.jpeg; sourceTree = ""; }; 48 | 33034C3824DCCE6A00585225 /* banner0.jpeg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = banner0.jpeg; sourceTree = ""; }; 49 | 3323EC042507EAA000BD24ED /* GXCardCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GXCardCell.swift; sourceTree = ""; }; 50 | 3323EC052507EAA000BD24ED /* GXCardView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GXCardView.swift; sourceTree = ""; }; 51 | 3323EC062507EAA000BD24ED /* GXCardLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GXCardLayout.swift; sourceTree = ""; }; 52 | 333085482506F65E00549452 /* GXCardTestCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GXCardTestCell.swift; sourceTree = ""; }; 53 | 333085492506F65E00549452 /* GXCardTestCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = GXCardTestCell.xib; sourceTree = ""; }; 54 | 33D6DD2024DA53B0008C7964 /* GXCardViewSample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = GXCardViewSample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 55 | 33D6DD2324DA53B0008C7964 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 56 | 33D6DD2524DA53B0008C7964 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; 57 | 33D6DD2724DA53B0008C7964 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 58 | 33D6DD2A24DA53B0008C7964 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 59 | 33D6DD2C24DA53B2008C7964 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 60 | 33D6DD2F24DA53B2008C7964 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 61 | 33D6DD3124DA53B2008C7964 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 62 | 33D6DD3624DA53B2008C7964 /* GXCardViewSampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = GXCardViewSampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 63 | 33D6DD3A24DA53B2008C7964 /* GXCardViewSampleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GXCardViewSampleTests.swift; sourceTree = ""; }; 64 | 33D6DD3C24DA53B2008C7964 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 65 | 33D6DD4124DA53B2008C7964 /* GXCardViewSampleUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = GXCardViewSampleUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 66 | 33D6DD4524DA53B2008C7964 /* GXCardViewSampleUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GXCardViewSampleUITests.swift; sourceTree = ""; }; 67 | 33D6DD4724DA53B2008C7964 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 68 | /* End PBXFileReference section */ 69 | 70 | /* Begin PBXFrameworksBuildPhase section */ 71 | 33D6DD1D24DA53B0008C7964 /* Frameworks */ = { 72 | isa = PBXFrameworksBuildPhase; 73 | buildActionMask = 2147483647; 74 | files = ( 75 | ); 76 | runOnlyForDeploymentPostprocessing = 0; 77 | }; 78 | 33D6DD3324DA53B2008C7964 /* Frameworks */ = { 79 | isa = PBXFrameworksBuildPhase; 80 | buildActionMask = 2147483647; 81 | files = ( 82 | ); 83 | runOnlyForDeploymentPostprocessing = 0; 84 | }; 85 | 33D6DD3E24DA53B2008C7964 /* Frameworks */ = { 86 | isa = PBXFrameworksBuildPhase; 87 | buildActionMask = 2147483647; 88 | files = ( 89 | ); 90 | runOnlyForDeploymentPostprocessing = 0; 91 | }; 92 | /* End PBXFrameworksBuildPhase section */ 93 | 94 | /* Begin PBXGroup section */ 95 | 33034C3524DCCE6A00585225 /* images */ = { 96 | isa = PBXGroup; 97 | children = ( 98 | 33034C3624DCCE6A00585225 /* banner2.jpeg */, 99 | 33034C3724DCCE6A00585225 /* banner1.jpeg */, 100 | 33034C3824DCCE6A00585225 /* banner0.jpeg */, 101 | ); 102 | path = images; 103 | sourceTree = ""; 104 | }; 105 | 3323EC032507EAA000BD24ED /* GXCardView-Swift */ = { 106 | isa = PBXGroup; 107 | children = ( 108 | 3323EC042507EAA000BD24ED /* GXCardCell.swift */, 109 | 3323EC052507EAA000BD24ED /* GXCardView.swift */, 110 | 3323EC062507EAA000BD24ED /* GXCardLayout.swift */, 111 | ); 112 | name = "GXCardView-Swift"; 113 | path = "../../GXCardView-Swift"; 114 | sourceTree = ""; 115 | }; 116 | 33D6DD1724DA53B0008C7964 = { 117 | isa = PBXGroup; 118 | children = ( 119 | 33D6DD2224DA53B0008C7964 /* GXCardViewSample */, 120 | 33D6DD3924DA53B2008C7964 /* GXCardViewSampleTests */, 121 | 33D6DD4424DA53B2008C7964 /* GXCardViewSampleUITests */, 122 | 33D6DD2124DA53B0008C7964 /* Products */, 123 | ); 124 | sourceTree = ""; 125 | }; 126 | 33D6DD2124DA53B0008C7964 /* Products */ = { 127 | isa = PBXGroup; 128 | children = ( 129 | 33D6DD2024DA53B0008C7964 /* GXCardViewSample.app */, 130 | 33D6DD3624DA53B2008C7964 /* GXCardViewSampleTests.xctest */, 131 | 33D6DD4124DA53B2008C7964 /* GXCardViewSampleUITests.xctest */, 132 | ); 133 | name = Products; 134 | sourceTree = ""; 135 | }; 136 | 33D6DD2224DA53B0008C7964 /* GXCardViewSample */ = { 137 | isa = PBXGroup; 138 | children = ( 139 | 3323EC032507EAA000BD24ED /* GXCardView-Swift */, 140 | 33D6DD2324DA53B0008C7964 /* AppDelegate.swift */, 141 | 33D6DD2524DA53B0008C7964 /* SceneDelegate.swift */, 142 | 33D6DD2724DA53B0008C7964 /* ViewController.swift */, 143 | 333085482506F65E00549452 /* GXCardTestCell.swift */, 144 | 333085492506F65E00549452 /* GXCardTestCell.xib */, 145 | 33D6DD2924DA53B0008C7964 /* Main.storyboard */, 146 | 33034C3524DCCE6A00585225 /* images */, 147 | 33D6DD2C24DA53B2008C7964 /* Assets.xcassets */, 148 | 33D6DD2E24DA53B2008C7964 /* LaunchScreen.storyboard */, 149 | 33D6DD3124DA53B2008C7964 /* Info.plist */, 150 | ); 151 | path = GXCardViewSample; 152 | sourceTree = ""; 153 | }; 154 | 33D6DD3924DA53B2008C7964 /* GXCardViewSampleTests */ = { 155 | isa = PBXGroup; 156 | children = ( 157 | 33D6DD3A24DA53B2008C7964 /* GXCardViewSampleTests.swift */, 158 | 33D6DD3C24DA53B2008C7964 /* Info.plist */, 159 | ); 160 | path = GXCardViewSampleTests; 161 | sourceTree = ""; 162 | }; 163 | 33D6DD4424DA53B2008C7964 /* GXCardViewSampleUITests */ = { 164 | isa = PBXGroup; 165 | children = ( 166 | 33D6DD4524DA53B2008C7964 /* GXCardViewSampleUITests.swift */, 167 | 33D6DD4724DA53B2008C7964 /* Info.plist */, 168 | ); 169 | path = GXCardViewSampleUITests; 170 | sourceTree = ""; 171 | }; 172 | /* End PBXGroup section */ 173 | 174 | /* Begin PBXNativeTarget section */ 175 | 33D6DD1F24DA53B0008C7964 /* GXCardViewSample */ = { 176 | isa = PBXNativeTarget; 177 | buildConfigurationList = 33D6DD4A24DA53B2008C7964 /* Build configuration list for PBXNativeTarget "GXCardViewSample" */; 178 | buildPhases = ( 179 | 33D6DD1C24DA53B0008C7964 /* Sources */, 180 | 33D6DD1D24DA53B0008C7964 /* Frameworks */, 181 | 33D6DD1E24DA53B0008C7964 /* Resources */, 182 | ); 183 | buildRules = ( 184 | ); 185 | dependencies = ( 186 | ); 187 | name = GXCardViewSample; 188 | productName = GXCardViewSample; 189 | productReference = 33D6DD2024DA53B0008C7964 /* GXCardViewSample.app */; 190 | productType = "com.apple.product-type.application"; 191 | }; 192 | 33D6DD3524DA53B2008C7964 /* GXCardViewSampleTests */ = { 193 | isa = PBXNativeTarget; 194 | buildConfigurationList = 33D6DD4D24DA53B2008C7964 /* Build configuration list for PBXNativeTarget "GXCardViewSampleTests" */; 195 | buildPhases = ( 196 | 33D6DD3224DA53B2008C7964 /* Sources */, 197 | 33D6DD3324DA53B2008C7964 /* Frameworks */, 198 | 33D6DD3424DA53B2008C7964 /* Resources */, 199 | ); 200 | buildRules = ( 201 | ); 202 | dependencies = ( 203 | 33D6DD3824DA53B2008C7964 /* PBXTargetDependency */, 204 | ); 205 | name = GXCardViewSampleTests; 206 | productName = GXCardViewSampleTests; 207 | productReference = 33D6DD3624DA53B2008C7964 /* GXCardViewSampleTests.xctest */; 208 | productType = "com.apple.product-type.bundle.unit-test"; 209 | }; 210 | 33D6DD4024DA53B2008C7964 /* GXCardViewSampleUITests */ = { 211 | isa = PBXNativeTarget; 212 | buildConfigurationList = 33D6DD5024DA53B2008C7964 /* Build configuration list for PBXNativeTarget "GXCardViewSampleUITests" */; 213 | buildPhases = ( 214 | 33D6DD3D24DA53B2008C7964 /* Sources */, 215 | 33D6DD3E24DA53B2008C7964 /* Frameworks */, 216 | 33D6DD3F24DA53B2008C7964 /* Resources */, 217 | ); 218 | buildRules = ( 219 | ); 220 | dependencies = ( 221 | 33D6DD4324DA53B2008C7964 /* PBXTargetDependency */, 222 | ); 223 | name = GXCardViewSampleUITests; 224 | productName = GXCardViewSampleUITests; 225 | productReference = 33D6DD4124DA53B2008C7964 /* GXCardViewSampleUITests.xctest */; 226 | productType = "com.apple.product-type.bundle.ui-testing"; 227 | }; 228 | /* End PBXNativeTarget section */ 229 | 230 | /* Begin PBXProject section */ 231 | 33D6DD1824DA53B0008C7964 /* Project object */ = { 232 | isa = PBXProject; 233 | attributes = { 234 | LastSwiftUpdateCheck = 1160; 235 | LastUpgradeCheck = 1160; 236 | ORGANIZATIONNAME = gin; 237 | TargetAttributes = { 238 | 33D6DD1F24DA53B0008C7964 = { 239 | CreatedOnToolsVersion = 11.6; 240 | }; 241 | 33D6DD3524DA53B2008C7964 = { 242 | CreatedOnToolsVersion = 11.6; 243 | TestTargetID = 33D6DD1F24DA53B0008C7964; 244 | }; 245 | 33D6DD4024DA53B2008C7964 = { 246 | CreatedOnToolsVersion = 11.6; 247 | TestTargetID = 33D6DD1F24DA53B0008C7964; 248 | }; 249 | }; 250 | }; 251 | buildConfigurationList = 33D6DD1B24DA53B0008C7964 /* Build configuration list for PBXProject "GXCardViewSample" */; 252 | compatibilityVersion = "Xcode 9.3"; 253 | developmentRegion = en; 254 | hasScannedForEncodings = 0; 255 | knownRegions = ( 256 | en, 257 | Base, 258 | ); 259 | mainGroup = 33D6DD1724DA53B0008C7964; 260 | productRefGroup = 33D6DD2124DA53B0008C7964 /* Products */; 261 | projectDirPath = ""; 262 | projectRoot = ""; 263 | targets = ( 264 | 33D6DD1F24DA53B0008C7964 /* GXCardViewSample */, 265 | 33D6DD3524DA53B2008C7964 /* GXCardViewSampleTests */, 266 | 33D6DD4024DA53B2008C7964 /* GXCardViewSampleUITests */, 267 | ); 268 | }; 269 | /* End PBXProject section */ 270 | 271 | /* Begin PBXResourcesBuildPhase section */ 272 | 33D6DD1E24DA53B0008C7964 /* Resources */ = { 273 | isa = PBXResourcesBuildPhase; 274 | buildActionMask = 2147483647; 275 | files = ( 276 | 33034C3B24DCCE6A00585225 /* banner0.jpeg in Resources */, 277 | 33034C3A24DCCE6A00585225 /* banner1.jpeg in Resources */, 278 | 33034C3924DCCE6A00585225 /* banner2.jpeg in Resources */, 279 | 3330854B2506F65E00549452 /* GXCardTestCell.xib in Resources */, 280 | 33D6DD3024DA53B2008C7964 /* LaunchScreen.storyboard in Resources */, 281 | 33D6DD2D24DA53B2008C7964 /* Assets.xcassets in Resources */, 282 | 33D6DD2B24DA53B0008C7964 /* Main.storyboard in Resources */, 283 | ); 284 | runOnlyForDeploymentPostprocessing = 0; 285 | }; 286 | 33D6DD3424DA53B2008C7964 /* Resources */ = { 287 | isa = PBXResourcesBuildPhase; 288 | buildActionMask = 2147483647; 289 | files = ( 290 | ); 291 | runOnlyForDeploymentPostprocessing = 0; 292 | }; 293 | 33D6DD3F24DA53B2008C7964 /* Resources */ = { 294 | isa = PBXResourcesBuildPhase; 295 | buildActionMask = 2147483647; 296 | files = ( 297 | ); 298 | runOnlyForDeploymentPostprocessing = 0; 299 | }; 300 | /* End PBXResourcesBuildPhase section */ 301 | 302 | /* Begin PBXSourcesBuildPhase section */ 303 | 33D6DD1C24DA53B0008C7964 /* Sources */ = { 304 | isa = PBXSourcesBuildPhase; 305 | buildActionMask = 2147483647; 306 | files = ( 307 | 3323EC072507EAA100BD24ED /* GXCardCell.swift in Sources */, 308 | 33D6DD2824DA53B0008C7964 /* ViewController.swift in Sources */, 309 | 3330854A2506F65E00549452 /* GXCardTestCell.swift in Sources */, 310 | 3323EC092507EAA100BD24ED /* GXCardLayout.swift in Sources */, 311 | 3323EC082507EAA100BD24ED /* GXCardView.swift in Sources */, 312 | 33D6DD2424DA53B0008C7964 /* AppDelegate.swift in Sources */, 313 | 33D6DD2624DA53B0008C7964 /* SceneDelegate.swift in Sources */, 314 | ); 315 | runOnlyForDeploymentPostprocessing = 0; 316 | }; 317 | 33D6DD3224DA53B2008C7964 /* Sources */ = { 318 | isa = PBXSourcesBuildPhase; 319 | buildActionMask = 2147483647; 320 | files = ( 321 | 33D6DD3B24DA53B2008C7964 /* GXCardViewSampleTests.swift in Sources */, 322 | ); 323 | runOnlyForDeploymentPostprocessing = 0; 324 | }; 325 | 33D6DD3D24DA53B2008C7964 /* Sources */ = { 326 | isa = PBXSourcesBuildPhase; 327 | buildActionMask = 2147483647; 328 | files = ( 329 | 33D6DD4624DA53B2008C7964 /* GXCardViewSampleUITests.swift in Sources */, 330 | ); 331 | runOnlyForDeploymentPostprocessing = 0; 332 | }; 333 | /* End PBXSourcesBuildPhase section */ 334 | 335 | /* Begin PBXTargetDependency section */ 336 | 33D6DD3824DA53B2008C7964 /* PBXTargetDependency */ = { 337 | isa = PBXTargetDependency; 338 | target = 33D6DD1F24DA53B0008C7964 /* GXCardViewSample */; 339 | targetProxy = 33D6DD3724DA53B2008C7964 /* PBXContainerItemProxy */; 340 | }; 341 | 33D6DD4324DA53B2008C7964 /* PBXTargetDependency */ = { 342 | isa = PBXTargetDependency; 343 | target = 33D6DD1F24DA53B0008C7964 /* GXCardViewSample */; 344 | targetProxy = 33D6DD4224DA53B2008C7964 /* PBXContainerItemProxy */; 345 | }; 346 | /* End PBXTargetDependency section */ 347 | 348 | /* Begin PBXVariantGroup section */ 349 | 33D6DD2924DA53B0008C7964 /* Main.storyboard */ = { 350 | isa = PBXVariantGroup; 351 | children = ( 352 | 33D6DD2A24DA53B0008C7964 /* Base */, 353 | ); 354 | name = Main.storyboard; 355 | sourceTree = ""; 356 | }; 357 | 33D6DD2E24DA53B2008C7964 /* LaunchScreen.storyboard */ = { 358 | isa = PBXVariantGroup; 359 | children = ( 360 | 33D6DD2F24DA53B2008C7964 /* Base */, 361 | ); 362 | name = LaunchScreen.storyboard; 363 | sourceTree = ""; 364 | }; 365 | /* End PBXVariantGroup section */ 366 | 367 | /* Begin XCBuildConfiguration section */ 368 | 33D6DD4824DA53B2008C7964 /* Debug */ = { 369 | isa = XCBuildConfiguration; 370 | buildSettings = { 371 | ALWAYS_SEARCH_USER_PATHS = NO; 372 | CLANG_ANALYZER_NONNULL = YES; 373 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 374 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 375 | CLANG_CXX_LIBRARY = "libc++"; 376 | CLANG_ENABLE_MODULES = YES; 377 | CLANG_ENABLE_OBJC_ARC = YES; 378 | CLANG_ENABLE_OBJC_WEAK = YES; 379 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 380 | CLANG_WARN_BOOL_CONVERSION = YES; 381 | CLANG_WARN_COMMA = YES; 382 | CLANG_WARN_CONSTANT_CONVERSION = YES; 383 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 384 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 385 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 386 | CLANG_WARN_EMPTY_BODY = YES; 387 | CLANG_WARN_ENUM_CONVERSION = YES; 388 | CLANG_WARN_INFINITE_RECURSION = YES; 389 | CLANG_WARN_INT_CONVERSION = YES; 390 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 391 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 392 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 393 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 394 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 395 | CLANG_WARN_STRICT_PROTOTYPES = YES; 396 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 397 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 398 | CLANG_WARN_UNREACHABLE_CODE = YES; 399 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 400 | COPY_PHASE_STRIP = NO; 401 | DEBUG_INFORMATION_FORMAT = dwarf; 402 | ENABLE_STRICT_OBJC_MSGSEND = YES; 403 | ENABLE_TESTABILITY = YES; 404 | GCC_C_LANGUAGE_STANDARD = gnu11; 405 | GCC_DYNAMIC_NO_PIC = NO; 406 | GCC_NO_COMMON_BLOCKS = YES; 407 | GCC_OPTIMIZATION_LEVEL = 0; 408 | GCC_PREPROCESSOR_DEFINITIONS = ( 409 | "DEBUG=1", 410 | "$(inherited)", 411 | ); 412 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 413 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 414 | GCC_WARN_UNDECLARED_SELECTOR = YES; 415 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 416 | GCC_WARN_UNUSED_FUNCTION = YES; 417 | GCC_WARN_UNUSED_VARIABLE = YES; 418 | IPHONEOS_DEPLOYMENT_TARGET = 13.6; 419 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 420 | MTL_FAST_MATH = YES; 421 | ONLY_ACTIVE_ARCH = YES; 422 | SDKROOT = iphoneos; 423 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 424 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 425 | }; 426 | name = Debug; 427 | }; 428 | 33D6DD4924DA53B2008C7964 /* Release */ = { 429 | isa = XCBuildConfiguration; 430 | buildSettings = { 431 | ALWAYS_SEARCH_USER_PATHS = NO; 432 | CLANG_ANALYZER_NONNULL = YES; 433 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 434 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 435 | CLANG_CXX_LIBRARY = "libc++"; 436 | CLANG_ENABLE_MODULES = YES; 437 | CLANG_ENABLE_OBJC_ARC = YES; 438 | CLANG_ENABLE_OBJC_WEAK = YES; 439 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 440 | CLANG_WARN_BOOL_CONVERSION = YES; 441 | CLANG_WARN_COMMA = YES; 442 | CLANG_WARN_CONSTANT_CONVERSION = YES; 443 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 444 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 445 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 446 | CLANG_WARN_EMPTY_BODY = YES; 447 | CLANG_WARN_ENUM_CONVERSION = YES; 448 | CLANG_WARN_INFINITE_RECURSION = YES; 449 | CLANG_WARN_INT_CONVERSION = YES; 450 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 451 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 452 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 453 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 454 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 455 | CLANG_WARN_STRICT_PROTOTYPES = YES; 456 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 457 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 458 | CLANG_WARN_UNREACHABLE_CODE = YES; 459 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 460 | COPY_PHASE_STRIP = NO; 461 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 462 | ENABLE_NS_ASSERTIONS = NO; 463 | ENABLE_STRICT_OBJC_MSGSEND = YES; 464 | GCC_C_LANGUAGE_STANDARD = gnu11; 465 | GCC_NO_COMMON_BLOCKS = YES; 466 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 467 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 468 | GCC_WARN_UNDECLARED_SELECTOR = YES; 469 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 470 | GCC_WARN_UNUSED_FUNCTION = YES; 471 | GCC_WARN_UNUSED_VARIABLE = YES; 472 | IPHONEOS_DEPLOYMENT_TARGET = 13.6; 473 | MTL_ENABLE_DEBUG_INFO = NO; 474 | MTL_FAST_MATH = YES; 475 | SDKROOT = iphoneos; 476 | SWIFT_COMPILATION_MODE = wholemodule; 477 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 478 | VALIDATE_PRODUCT = YES; 479 | }; 480 | name = Release; 481 | }; 482 | 33D6DD4B24DA53B2008C7964 /* Debug */ = { 483 | isa = XCBuildConfiguration; 484 | buildSettings = { 485 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 486 | CODE_SIGN_STYLE = Automatic; 487 | DEVELOPMENT_TEAM = L54KZN9KMH; 488 | INFOPLIST_FILE = GXCardViewSample/Info.plist; 489 | IPHONEOS_DEPLOYMENT_TARGET = 13.4; 490 | LD_RUNPATH_SEARCH_PATHS = ( 491 | "$(inherited)", 492 | "@executable_path/Frameworks", 493 | ); 494 | PRODUCT_BUNDLE_IDENTIFIER = com.gin.GXCardViewSample; 495 | PRODUCT_NAME = "$(TARGET_NAME)"; 496 | SWIFT_VERSION = 5.0; 497 | TARGETED_DEVICE_FAMILY = "1,2"; 498 | }; 499 | name = Debug; 500 | }; 501 | 33D6DD4C24DA53B2008C7964 /* Release */ = { 502 | isa = XCBuildConfiguration; 503 | buildSettings = { 504 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 505 | CODE_SIGN_STYLE = Automatic; 506 | DEVELOPMENT_TEAM = L54KZN9KMH; 507 | INFOPLIST_FILE = GXCardViewSample/Info.plist; 508 | IPHONEOS_DEPLOYMENT_TARGET = 13.4; 509 | LD_RUNPATH_SEARCH_PATHS = ( 510 | "$(inherited)", 511 | "@executable_path/Frameworks", 512 | ); 513 | PRODUCT_BUNDLE_IDENTIFIER = com.gin.GXCardViewSample; 514 | PRODUCT_NAME = "$(TARGET_NAME)"; 515 | SWIFT_VERSION = 5.0; 516 | TARGETED_DEVICE_FAMILY = "1,2"; 517 | }; 518 | name = Release; 519 | }; 520 | 33D6DD4E24DA53B2008C7964 /* Debug */ = { 521 | isa = XCBuildConfiguration; 522 | buildSettings = { 523 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 524 | BUNDLE_LOADER = "$(TEST_HOST)"; 525 | CODE_SIGN_STYLE = Automatic; 526 | DEVELOPMENT_TEAM = L54KZN9KMH; 527 | INFOPLIST_FILE = GXCardViewSampleTests/Info.plist; 528 | IPHONEOS_DEPLOYMENT_TARGET = 13.6; 529 | LD_RUNPATH_SEARCH_PATHS = ( 530 | "$(inherited)", 531 | "@executable_path/Frameworks", 532 | "@loader_path/Frameworks", 533 | ); 534 | PRODUCT_BUNDLE_IDENTIFIER = com.gin.GXCardViewSampleTests; 535 | PRODUCT_NAME = "$(TARGET_NAME)"; 536 | SWIFT_VERSION = 5.0; 537 | TARGETED_DEVICE_FAMILY = "1,2"; 538 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/GXCardViewSample.app/GXCardViewSample"; 539 | }; 540 | name = Debug; 541 | }; 542 | 33D6DD4F24DA53B2008C7964 /* Release */ = { 543 | isa = XCBuildConfiguration; 544 | buildSettings = { 545 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 546 | BUNDLE_LOADER = "$(TEST_HOST)"; 547 | CODE_SIGN_STYLE = Automatic; 548 | DEVELOPMENT_TEAM = L54KZN9KMH; 549 | INFOPLIST_FILE = GXCardViewSampleTests/Info.plist; 550 | IPHONEOS_DEPLOYMENT_TARGET = 13.6; 551 | LD_RUNPATH_SEARCH_PATHS = ( 552 | "$(inherited)", 553 | "@executable_path/Frameworks", 554 | "@loader_path/Frameworks", 555 | ); 556 | PRODUCT_BUNDLE_IDENTIFIER = com.gin.GXCardViewSampleTests; 557 | PRODUCT_NAME = "$(TARGET_NAME)"; 558 | SWIFT_VERSION = 5.0; 559 | TARGETED_DEVICE_FAMILY = "1,2"; 560 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/GXCardViewSample.app/GXCardViewSample"; 561 | }; 562 | name = Release; 563 | }; 564 | 33D6DD5124DA53B2008C7964 /* Debug */ = { 565 | isa = XCBuildConfiguration; 566 | buildSettings = { 567 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 568 | CODE_SIGN_STYLE = Automatic; 569 | DEVELOPMENT_TEAM = L54KZN9KMH; 570 | INFOPLIST_FILE = GXCardViewSampleUITests/Info.plist; 571 | LD_RUNPATH_SEARCH_PATHS = ( 572 | "$(inherited)", 573 | "@executable_path/Frameworks", 574 | "@loader_path/Frameworks", 575 | ); 576 | PRODUCT_BUNDLE_IDENTIFIER = com.gin.GXCardViewSampleUITests; 577 | PRODUCT_NAME = "$(TARGET_NAME)"; 578 | SWIFT_VERSION = 5.0; 579 | TARGETED_DEVICE_FAMILY = "1,2"; 580 | TEST_TARGET_NAME = GXCardViewSample; 581 | }; 582 | name = Debug; 583 | }; 584 | 33D6DD5224DA53B2008C7964 /* Release */ = { 585 | isa = XCBuildConfiguration; 586 | buildSettings = { 587 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 588 | CODE_SIGN_STYLE = Automatic; 589 | DEVELOPMENT_TEAM = L54KZN9KMH; 590 | INFOPLIST_FILE = GXCardViewSampleUITests/Info.plist; 591 | LD_RUNPATH_SEARCH_PATHS = ( 592 | "$(inherited)", 593 | "@executable_path/Frameworks", 594 | "@loader_path/Frameworks", 595 | ); 596 | PRODUCT_BUNDLE_IDENTIFIER = com.gin.GXCardViewSampleUITests; 597 | PRODUCT_NAME = "$(TARGET_NAME)"; 598 | SWIFT_VERSION = 5.0; 599 | TARGETED_DEVICE_FAMILY = "1,2"; 600 | TEST_TARGET_NAME = GXCardViewSample; 601 | }; 602 | name = Release; 603 | }; 604 | /* End XCBuildConfiguration section */ 605 | 606 | /* Begin XCConfigurationList section */ 607 | 33D6DD1B24DA53B0008C7964 /* Build configuration list for PBXProject "GXCardViewSample" */ = { 608 | isa = XCConfigurationList; 609 | buildConfigurations = ( 610 | 33D6DD4824DA53B2008C7964 /* Debug */, 611 | 33D6DD4924DA53B2008C7964 /* Release */, 612 | ); 613 | defaultConfigurationIsVisible = 0; 614 | defaultConfigurationName = Release; 615 | }; 616 | 33D6DD4A24DA53B2008C7964 /* Build configuration list for PBXNativeTarget "GXCardViewSample" */ = { 617 | isa = XCConfigurationList; 618 | buildConfigurations = ( 619 | 33D6DD4B24DA53B2008C7964 /* Debug */, 620 | 33D6DD4C24DA53B2008C7964 /* Release */, 621 | ); 622 | defaultConfigurationIsVisible = 0; 623 | defaultConfigurationName = Release; 624 | }; 625 | 33D6DD4D24DA53B2008C7964 /* Build configuration list for PBXNativeTarget "GXCardViewSampleTests" */ = { 626 | isa = XCConfigurationList; 627 | buildConfigurations = ( 628 | 33D6DD4E24DA53B2008C7964 /* Debug */, 629 | 33D6DD4F24DA53B2008C7964 /* Release */, 630 | ); 631 | defaultConfigurationIsVisible = 0; 632 | defaultConfigurationName = Release; 633 | }; 634 | 33D6DD5024DA53B2008C7964 /* Build configuration list for PBXNativeTarget "GXCardViewSampleUITests" */ = { 635 | isa = XCConfigurationList; 636 | buildConfigurations = ( 637 | 33D6DD5124DA53B2008C7964 /* Debug */, 638 | 33D6DD5224DA53B2008C7964 /* Release */, 639 | ); 640 | defaultConfigurationIsVisible = 0; 641 | defaultConfigurationName = Release; 642 | }; 643 | /* End XCConfigurationList section */ 644 | }; 645 | rootObject = 33D6DD1824DA53B0008C7964 /* Project object */; 646 | } 647 | -------------------------------------------------------------------------------- /GXCardViewSample/GXCardViewSample.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /GXCardViewSample/GXCardViewSample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /GXCardViewSample/GXCardViewSample/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // GXCardViewSample 4 | // 5 | // Created by Gin on 2020/8/5. 6 | // Copyright © 2020 gin. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | 15 | 16 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 17 | // Override point for customization after application launch. 18 | return true 19 | } 20 | 21 | // MARK: UISceneSession Lifecycle 22 | 23 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { 24 | // Called when a new scene session is being created. 25 | // Use this method to select a configuration to create the new scene with. 26 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) 27 | } 28 | 29 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { 30 | // Called when the user discards a scene session. 31 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. 32 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return. 33 | } 34 | 35 | 36 | } 37 | 38 | -------------------------------------------------------------------------------- /GXCardViewSample/GXCardViewSample/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | } 93 | ], 94 | "info" : { 95 | "author" : "xcode", 96 | "version" : 1 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /GXCardViewSample/GXCardViewSample/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /GXCardViewSample/GXCardViewSample/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 | -------------------------------------------------------------------------------- /GXCardViewSample/GXCardViewSample/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 35 | 46 | 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 | 92 | 101 | 110 | 119 | 128 | 137 | 146 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | -------------------------------------------------------------------------------- /GXCardViewSample/GXCardViewSample/GXCardTestCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GXCardTestCell.swift 3 | // GXCardViewSample 4 | // 5 | // Created by Gin on 2020/9/8. 6 | // Copyright © 2020 gin. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class GXCardTestCell: GXCardCell { 12 | @IBOutlet weak var iconIView: UIImageView! 13 | @IBOutlet weak var numberLabel: UILabel! 14 | @IBOutlet weak var leftLabel: UILabel! 15 | @IBOutlet weak var rightLabel: UILabel! 16 | 17 | override func awakeFromNib() { 18 | super.awakeFromNib() 19 | self.layer.cornerRadius = 10.0 20 | self.layer.borderColor = UIColor.gray.cgColor 21 | self.layer.borderWidth = 1.0 22 | self.layer.masksToBounds = true 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /GXCardViewSample/GXCardViewSample/GXCardTestCell.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 | 32 | 41 | 51 | 62 | 71 | 80 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | -------------------------------------------------------------------------------- /GXCardViewSample/GXCardViewSample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UIApplicationSceneManifest 24 | 25 | UIApplicationSupportsMultipleScenes 26 | 27 | UISceneConfigurations 28 | 29 | UIWindowSceneSessionRoleApplication 30 | 31 | 32 | UISceneConfigurationName 33 | Default Configuration 34 | UISceneDelegateClassName 35 | $(PRODUCT_MODULE_NAME).SceneDelegate 36 | UISceneStoryboardFile 37 | Main 38 | 39 | 40 | 41 | 42 | UILaunchStoryboardName 43 | LaunchScreen 44 | UIMainStoryboardFile 45 | Main 46 | UIRequiredDeviceCapabilities 47 | 48 | armv7 49 | 50 | UISupportedInterfaceOrientations 51 | 52 | UIInterfaceOrientationPortrait 53 | UIInterfaceOrientationLandscapeLeft 54 | UIInterfaceOrientationLandscapeRight 55 | 56 | UISupportedInterfaceOrientations~ipad 57 | 58 | UIInterfaceOrientationPortrait 59 | UIInterfaceOrientationPortraitUpsideDown 60 | UIInterfaceOrientationLandscapeLeft 61 | UIInterfaceOrientationLandscapeRight 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /GXCardViewSample/GXCardViewSample/SceneDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.swift 3 | // GXCardViewSample 4 | // 5 | // Created by Gin on 2020/8/5. 6 | // Copyright © 2020 gin. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class SceneDelegate: UIResponder, UIWindowSceneDelegate { 12 | 13 | var window: UIWindow? 14 | 15 | 16 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { 17 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. 18 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. 19 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). 20 | guard let _ = (scene as? UIWindowScene) else { return } 21 | } 22 | 23 | func sceneDidDisconnect(_ scene: UIScene) { 24 | // Called as the scene is being released by the system. 25 | // This occurs shortly after the scene enters the background, or when its session is discarded. 26 | // Release any resources associated with this scene that can be re-created the next time the scene connects. 27 | // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead). 28 | } 29 | 30 | func sceneDidBecomeActive(_ scene: UIScene) { 31 | // Called when the scene has moved from an inactive state to an active state. 32 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. 33 | } 34 | 35 | func sceneWillResignActive(_ scene: UIScene) { 36 | // Called when the scene will move from an active state to an inactive state. 37 | // This may occur due to temporary interruptions (ex. an incoming phone call). 38 | } 39 | 40 | func sceneWillEnterForeground(_ scene: UIScene) { 41 | // Called as the scene transitions from the background to the foreground. 42 | // Use this method to undo the changes made on entering the background. 43 | } 44 | 45 | func sceneDidEnterBackground(_ scene: UIScene) { 46 | // Called as the scene transitions from the foreground to the background. 47 | // Use this method to save data, release shared resources, and store enough scene-specific state information 48 | // to restore the scene back to its current state. 49 | } 50 | 51 | 52 | } 53 | 54 | -------------------------------------------------------------------------------- /GXCardViewSample/GXCardViewSample/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // GXCardViewSample 4 | // 5 | // Created by Gin on 2020/8/5. 6 | // Copyright © 2020 gin. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController { 12 | @IBOutlet weak var topLabel: UILabel! 13 | @IBOutlet weak var leftLabel: UILabel! 14 | @IBOutlet weak var rightLabel: UILabel! 15 | @IBOutlet weak var bottomLabel: UILabel! 16 | @IBOutlet weak var cardView: GXCardView! 17 | var cellCount: Int = 10 18 | 19 | private lazy var cardLayout: GXCardLayout = { 20 | let layout = GXCardLayout() 21 | layout.visibleCount = 4 22 | layout.maxAngle = 15.0 23 | layout.isRepeat = false 24 | layout.isPanAnimatedEnd = false //必须动画结束才可再次拖拽,为true时可不停的拖拽 25 | layout.maxRemoveDistance = self.view.frame.width/4 26 | layout.cardInsets = UIEdgeInsets(top: 10, left: 10, bottom: -10, right: 10) 27 | 28 | return layout 29 | }() 30 | 31 | override func viewDidLoad() { 32 | super.viewDidLoad() 33 | 34 | self.cardView.dataSource = self 35 | self.cardView.delegate = self 36 | self.cardView.setCardLayout(cardLayout: self.cardLayout) 37 | self.cardView.register(nibCellType: GXCardTestCell.self) 38 | self.cardView.reloadData() 39 | } 40 | 41 | @IBAction func leftButtonClicked(_ sender: Any?) { 42 | self.cardView.removeTopCardViewCell(swipe: .left) 43 | } 44 | 45 | @IBAction func rightButtonClick(_ sender: Any?) { 46 | self.cardView.removeTopCardViewCell(swipe: .right) 47 | } 48 | 49 | @IBAction func updateButtonClick(_ sender: Any?) { 50 | self.cardView.scrollToItem(at: self.cellCount/2, animated: true) 51 | } 52 | 53 | @IBAction func topChange(_ sender: UISlider) { 54 | self.topLabel.text = String(Int(sender.value)) 55 | self.cardLayout.cardInsets.top = CGFloat(sender.value) 56 | self.cardView.invalidateLayout() 57 | } 58 | 59 | @IBAction func leftChange(_ sender: UISlider) { 60 | self.leftLabel.text = String(Int(sender.value)) 61 | self.cardLayout.cardInsets.left = CGFloat(sender.value) 62 | self.cardView.invalidateLayout() 63 | } 64 | 65 | @IBAction func rightChange(_ sender: UISlider) { 66 | self.rightLabel.text = String(Int(sender.value)) 67 | self.cardLayout.cardInsets.right = CGFloat(sender.value) 68 | self.cardView.invalidateLayout() 69 | } 70 | 71 | @IBAction func bottomChange(_ sender: UISlider) { 72 | self.bottomLabel.text = String(Int(sender.value)) 73 | self.cardLayout.cardInsets.bottom = CGFloat(sender.value) 74 | self.cardView.invalidateLayout() 75 | } 76 | } 77 | 78 | extension ViewController: GXCardCViewDataSource, GXCardCViewDelegate { 79 | // MARK: - GXCardViewDataSource 80 | func numberOfItems(in cardView: GXCardView) -> Int { 81 | return self.cellCount 82 | } 83 | func cardView(_ cardView: GXCardView, cellForItemAt indexPath: IndexPath) -> GXCardCell { 84 | let cell = cardView.dequeueReusableCell(for: indexPath, cellType: GXCardTestCell.self) 85 | cell.iconIView.image = UIImage(named: String(format: "banner%d.jpeg", indexPath.item%3)) 86 | cell.numberLabel.text = String(indexPath.item) 87 | cell.leftLabel.isHidden = true 88 | cell.rightLabel.isHidden = true 89 | 90 | return cell 91 | } 92 | 93 | // MARK: - GXCardViewDelegate 94 | func cardView(_ cardView: GXCardView, didRemoveLast cell: GXCardCell, forItemAt index: Int, direction: GXCardCell.SwipeDirection) { 95 | NSLog("didRemove forRowAtIndex = %d, direction = %d", index, direction.rawValue) 96 | if !cardView.cardLayout.isRepeat { 97 | cardView.reloadData() 98 | cardView.scrollToItem(at: 0, animated: false) 99 | } 100 | } 101 | func cardView(_ cardView: GXCardView, willRemove cell: GXCardCell, forItemAt index: Int, direction: GXCardCell.SwipeDirection) { 102 | NSLog("willRemove forRowAtIndex = %d, direction = %d", index, direction.rawValue) 103 | if let toCell = cell as? GXCardTestCell { 104 | toCell.leftLabel.isHidden = !(direction == .right) 105 | toCell.rightLabel.isHidden = !(direction == .left) 106 | } 107 | } 108 | func cardView(_ cardView: GXCardView, didRemove cell: GXCardCell, forItemAt index: Int, direction: GXCardCell.SwipeDirection) { 109 | NSLog("didRemove forRowAtIndex = %d, direction = %d", index, direction.rawValue) 110 | if !cardView.cardLayout.isRepeat && index == 9 { 111 | self.cellCount = 20 112 | cardView.reloadData() 113 | } 114 | } 115 | func cardView(_ cardView: GXCardView, didMove cell: GXCardCell, forItemAt index: Int, move point: CGPoint, direction: GXCardCell.SwipeDirection) { 116 | NSLog("move point = %@, direction = %ld", point.debugDescription, direction.rawValue) 117 | if let toCell = cell as? GXCardTestCell { 118 | toCell.leftLabel.isHidden = !(direction == .right) 119 | toCell.rightLabel.isHidden = !(direction == .left) 120 | } 121 | } 122 | func cardView(_ cardView: GXCardView, didDisplay cell: GXCardCell, forItemAt index: Int) { 123 | NSLog("didDisplay forRowAtIndex = %d", index) 124 | } 125 | func cardView(_ cardView: GXCardView, didSelectItemAt index: Int) { 126 | NSLog("didSelectItemAt index = %d", index) 127 | } 128 | } 129 | 130 | -------------------------------------------------------------------------------- /GXCardViewSample/GXCardViewSample/images/banner0.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsyhei/GXCardView-Swift/fab086a10be79218d312b11304c4496c6d7ed121/GXCardViewSample/GXCardViewSample/images/banner0.jpeg -------------------------------------------------------------------------------- /GXCardViewSample/GXCardViewSample/images/banner1.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsyhei/GXCardView-Swift/fab086a10be79218d312b11304c4496c6d7ed121/GXCardViewSample/GXCardViewSample/images/banner1.jpeg -------------------------------------------------------------------------------- /GXCardViewSample/GXCardViewSample/images/banner2.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsyhei/GXCardView-Swift/fab086a10be79218d312b11304c4496c6d7ed121/GXCardViewSample/GXCardViewSample/images/banner2.jpeg -------------------------------------------------------------------------------- /GXCardViewSample/GXCardViewSampleTests/GXCardViewSampleTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GXCardViewSampleTests.swift 3 | // GXCardViewSampleTests 4 | // 5 | // Created by Gin on 2020/8/5. 6 | // Copyright © 2020 gin. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class GXCardViewSampleTests: XCTestCase { 12 | 13 | override func setUpWithError() throws { 14 | // Put setup code here. This method is called before the invocation of each test method in the class. 15 | } 16 | 17 | override func tearDownWithError() throws { 18 | // Put teardown code here. This method is called after the invocation of each test method in the class. 19 | } 20 | 21 | func testExample() throws { 22 | // This is an example of a functional test case. 23 | // Use XCTAssert and related functions to verify your tests produce the correct results. 24 | } 25 | 26 | func testPerformanceExample() throws { 27 | // This is an example of a performance test case. 28 | self.measure { 29 | // Put the code you want to measure the time of here. 30 | } 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /GXCardViewSample/GXCardViewSampleTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /GXCardViewSample/GXCardViewSampleUITests/GXCardViewSampleUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GXCardViewSampleUITests.swift 3 | // GXCardViewSampleUITests 4 | // 5 | // Created by Gin on 2020/8/5. 6 | // Copyright © 2020 gin. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class GXCardViewSampleUITests: XCTestCase { 12 | 13 | override func setUpWithError() throws { 14 | // Put setup code here. This method is called before the invocation of each test method in the class. 15 | 16 | // In UI tests it is usually best to stop immediately when a failure occurs. 17 | continueAfterFailure = false 18 | 19 | // 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. 20 | } 21 | 22 | override func tearDownWithError() throws { 23 | // Put teardown code here. This method is called after the invocation of each test method in the class. 24 | } 25 | 26 | func testExample() throws { 27 | // UI tests must launch the application that they test. 28 | let app = XCUIApplication() 29 | app.launch() 30 | 31 | // Use recording to get started writing UI tests. 32 | // Use XCTAssert and related functions to verify your tests produce the correct results. 33 | } 34 | 35 | func testLaunchPerformance() throws { 36 | if #available(macOS 10.15, iOS 13.0, tvOS 13.0, *) { 37 | // This measures how long it takes to launch your application. 38 | measure(metrics: [XCTOSSignpostMetric.applicationLaunch]) { 39 | XCUIApplication().launch() 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /GXCardViewSample/GXCardViewSampleUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Gin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /QQ.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gsyhei/GXCardView-Swift/fab086a10be79218d312b11304c4496c6d7ed121/QQ.jpeg -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GXCardView 2 | 卡片式布局(探探附近/QQ配对),可以设置卡片堆叠效果,可以设置循环效果。 3 | 有建议可以联系QQ交流群:1101980843,喜欢就给个star哦,谢谢关注! 4 |

5 | 6 |

7 | 8 | 先上demo菜单效果 9 | -- 10 |

11 | 12 |

13 | 14 | Requirements 15 | -- 16 |

17 | 18 | 19 | 20 |

21 | 22 | 关于会变形的问题 23 | -- 24 | 这里要说明一下。为了使动画平滑故而使用的CGAffineTransform,那么想要使内容不变形必须按照实际情况设置cardInsets: UIEdgeInsets。 25 | 假设Card的CGSize(w, h), 那么设置的 (top + bottom) / w == (left + right) / h,这样每个cardCell的长宽比例相等才能不变形。 26 | 27 | Usage in you Podfile: 28 | -- 29 | 30 | ``` 31 | pod 'GXCardView-Swift' 32 | ``` 33 | * 其它版本 [OC版本](https://github.com/gsyhei/GXCardView) 34 | ``` 35 | pod 'GXCardView' 36 | ``` 37 | GXCardViewDataSource 38 | -- 39 | 40 | ```swift 41 | func numberOfItems(in cardView: GXCardView) -> Int 42 | func cardView(_ cardView: GXCardView, cellForItemAt indexPath: IndexPath) -> GXCardCell 43 | ``` 44 | 45 | GXCardViewDelegate 46 | -- 47 | 48 | ```swift 49 | @objc optional func cardView(_ cardView: GXCardView, didSelectItemAt index: Int) 50 | @objc optional func cardView(_ cardView: GXCardView, didRemove cell: GXCardCell, forItemAt index: Int, direction: GXCardCell.SwipeDirection) 51 | @objc optional func cardView(_ cardView: GXCardView, didRemoveLast cell: GXCardCell, forItemAt index: Int, direction: GXCardCell.SwipeDirection) 52 | @objc optional func cardView(_ cardView: GXCardView, didDisplay cell: GXCardCell, forItemAt index: Int) 53 | @objc optional func cardView(_ cardView: GXCardView, didMove cell: GXCardCell, forItemAt index: Int, move point: CGPoint, direction: GXCardCell.SwipeDirection) 54 | ``` 55 | 56 | 可以设置参数 57 | -- 58 | 59 | ```swift 60 | /// 卡片可见数量(默认3) 61 | open var visibleCount: Int = 3 62 | /// 卡片与卡片之间的insets(从上至下,正负皆可) 63 | open var cardInsets: UIEdgeInsets = UIEdgeInsets(top: 10, left: 10, bottom: -10, right: 10) 64 | /// 侧滑最大角度(默认15°) 65 | open var maxAngle: CGFloat = 15.0 66 | /// 是否重复(默认false) 67 | open var isRepeat: Bool = false 68 | /// 最大移除距离(默认屏幕的1/4) 69 | open var maxRemoveDistance: CGFloat = GX_ScreenWidth * 0.25 70 | /// 是否需要移除动画结束才可以再次拖拽 71 | open var isPanAnimatedEnd: Bool = false 72 | 73 | ``` 74 | 75 | License 76 | -- 77 | MIT 78 | 79 | 80 | --------------------------------------------------------------------------------