English | 简体中文
CardCarousel
is a powerful yet easy-to-use carousel component that you can even configure with spells.
-
Supports method chaining
-
Supports setting page dimensions in a manner similar to
NSCollectionLayoutSize
-
Supports multiple loop modes
-
Supports setting page alignment when scrolling stops
-
Supports setting scroll direction
-
Supports setting scroll animation effects for automatic scrolling
-
Supports setting page transition effects
-
Supports setting pagination threshold
-
Supports setting the deceleration rate of pages during sliding
-
Supports
SwiftUI
-
Supports configuration through spells
-
And more...
In Xcode, select File
> Add Package Dependencies...
, paste https://github.com/YuLeiFuYun/CardCarousel.git
.
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '12.0'
use_frameworks!
target '<Your Target Name>' do
pod 'CardCarousel', '~> 2.0'
end
- iOS 12+
- Swift 5.9+
- Xcode 15+
- Simple use
import CardCarousel
let cardCarousel = CardCarousel(frame: ...).add(to: view)
// For local images, you can directly assign an array of UIImage objects.
cardCarousel.items = [UIImage]
// Before using remote images in CardCarousel, set up the ImageLoadingManager with appropriate loading, prefetching, and cancellation behaviors. This is typically done using a third-party library like SDWebImage or Kingfisher.
// Here’s an example of how you might configure the ImageLoadingManager:
import Kingfisher
ImageLoadingManager.shared.configure { url, imageView, placeholder, completion in
imageView.kf.setImage(with: url, placeholder: placeholder) { _ in
completion()
}
} prefetch: { urls in
ImagePrefetcher(urls: urls).start()
} cancel: { urls in
ImagePrefetcher(urls: urls).stop()
}
// After configuring ImageLoadingManager, you can set the items of CardCarousel to be an array of URL strings:
cardCarousel.items = [
"https://example.com/image1.jpg",
"https://example.com/image2.jpg",
"https://example.com/image3.jpg"
]
- Custom cell
CardCarousel(items: items) { (cell: CustomCell, index: Int, itemIdentifier: Item) in
cell.imageView.kf.setImage(with: url)
cell.indexLabel.backgroundColor = itemIdentifier.color
cell.indexLabel.text = itemIdentifier.index
}
.cardLayoutSize(widthDimension: .fractionalWidth(0.7), heightDimension: .fractionalHeight(0.7))
.cardTransformMode(.liner(minimumAlpha: 0.3))
.cardCornerRadius(10)
.add(to: view, layoutConstraints: { cardCarouselView, superView in
NSLayoutConstraint.activate([
cardCarouselView.leadingAnchor.constraint(equalTo: superView.leadingAnchor),
cardCarouselView.trailingAnchor.constraint(equalTo: superView.trailingAnchor),
cardCarouselView.topAnchor.constraint(equalTo: superView.topAnchor, constant: 100),
cardCarouselView.heightAnchor.constraint(equalToConstant: 200)
])
})
- SwiftUI View
CardCarousel(items: items) { index, itemIdentifier in
HStack {
Text(itemIdentifier)
.font(.system(size: 18))
Spacer()
}
}
.scrollDirection(.topToBottom)
.add(to: view)
- Custom page control
public protocol CardCarouselPageControlType: UIView {
var numberOfPages: Int { get set }
var currentPage: Int { get }
}
public protocol CardCarouselNormalPageControlType: CardCarouselPageControlType {
var currentPage: Int { get set }
}
public protocol CardCarouselContinousPageControlType: CardCarouselPageControlType {
var progress: Double { get set }
}
extension UIPageControl: CardCarouselNormalPageControlType { }
Example:
extension CustomPageControl: CardCarouselContinousPageControlType {
...
}
CardCarousel(itemsPublisher: $items) { index, itemIdentifier in
Text(itemIdentifier.text)
.font(.title)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(itemIdentifier.color)
}
.cardLayoutSize(widthDimension: .fractionalWidth(0.9), heightDimension: .absolute(200))
.minimumLineSpacing(20)
.scrollStopAlignment(.head(offset: 10))
.pageControl(makePageControl: {
let pageControl = CustomPageControl()
pageControl.currentPageTintColor = .white
pageControl.tintColor = .green
pageControl.radius = 4
pageControl.padding = 15
return pageControl
}, position: .centerXBottom(offset: CGPoint(x: 0, y: -10)))
.add(to: view)
struct Content: View {
@State var items = [
"飞光飞光 劝尔一杯酒",
"吾不识青天高 黄地厚",
"惟见月寒日暖 来煎人寿",
"食熊则肥 食蛙则瘦",
]
var body: some View {
CardCarouselView($items, content: { index, itemIdentifier in
if index.isMultiple(of: 2) {
ZStack {
Color.blue
Text(itemIdentifier)
}
} else {
Text(itemIdentifier)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.yellow)
.clipShape(Capsule())
}
})
.scrollMode(.automatic(timeInterval: 3))
.cardTransformMode(.coverflow(minimumAlpha: 0.3))
.cardLayoutSize(widthDimension: .fractionalWidth(0.7), heightDimension: .fractionalHeight(0.7))
.minimumLineSpacing(-20)
.cardCornerRadius(10)
// The larger the value, the further the slide after the user releases the drag, default is 0.9924.
.decelerationRate(0.999)
.onCardSelected({ index in
print(index)
})
.onCardChanged({ index in
print(index)
})
.frame(height: 200)
}
}
For spells in the styles of 高级动物
and 催妆曲
, please separate function names, parameter names, and arguments with full-width commas. Separate multiple spells (i.e., multiple function calls) with spaces.
- 动物协鸣
CardCarousel(咒语: "汪咕呦汪叽嗡呜汪叽 喵呜 呜啾 嘎啾", 施法材料: items, 作用域: CGRect(x: 0, y: 100, width: 393, height: 200))
.法术目标(view)
// The effect is equivalent to
CardCarousel(frame: CGRect(x: 0, y: 100, width: 393, height: 200), items: items)
.cardLayoutSize(widthDimension: .fractionalWidth(0.7), heightDimension: .fractionalHeight(0.7))
.cardTransformMode(.liner)
.scrollDirection(.rightToLeft)
.loopMode(.rollback)
.add(to: view)
- 高级动物
// Same effect as above
CardCarousel(咒语: "矛盾,自私,好色,爱喜,无聊,善良,爱喜 贪婪,真诚 善变,暗淡 无奈,埋怨", 施法材料: items, 作用域: CGRect(x: 0, y: 100, width: 393, height: 200))
.法术目标(view)
- 催妆曲
// Same effect as above
CardCarousel(咒语: "醒呀,画眉在杏枝上歌,画眉人不起是因何,黛棕,远峰尖滴着新黛,正好蘸来描画双蛾,黛棕 晨鸡声呖呖在相催,日神也捧着金镜 画眉在杏枝上歌,她对着如镜的池塘 远峰尖滴着新黛,春莺儿衔了额黄归", 施法材料: items, 作用域: CGRect(x: 0, y: 100, width: 393, height: 200))
.法术目标(view)
- 大威天龙
let 白素贞 = view
CardCarousel(咒语: "大威天龙", 施法材料: items)
.法术目标(白素贞)
// he effect is equivalent to
CardCarousel(items: items)
.minimumLineSpacing(10)
.pageControl(makePageControl: { UIPageControl() }, position: .centerXBottom)
.add(to: view)
public protocol CardCarouselInterface {
/// Card layout size, filling the entirety of the super view by default.
///
/// 动物协鸣:汪;0-9:["汪", "啾", "喵", "咩", "哞", "呱", "嘎", "叽", "吱", "嘶"]
/// widthDimension:咕;heightDimension:嗡
/// fractionalWidth:呦;fractionalHeight:呜;absolute:嗷;inset:嘤
///
/// 高级动物:矛盾;0-9:["爱", "贪", "嗔", "痴", "恨", "苦", "忧", "喜", "怨", "怒"]
/// widthDimension:自私;heightDimension:无聊
/// fractionalWidth:好色;fractionalHeight:善良;absolute:博爱;inset:诡辩
///
/// 催妆曲:醒呀;0-9:["黛", "墨", "碧", "朱", "紫", "黄", "蓝", "棕", "灰", "白"]
/// widthDimension:画眉在杏枝上歌;heightDimension:远峰尖滴着新黛
/// fractionalWidth:画眉人不起是因何;fractionalHeight:正好蘸来描画双蛾;absolute:春莺儿衔了额黄归;inset:起呀
func cardLayoutSize(widthDimension: CardLayoutDimension, heightDimension: CardLayoutDimension) -> Self
/// Minimum card spacing, default 0.
///
/// 动物协鸣:啾;0-9:["汪", "啾", "喵", "咩", "哞", "呱", "嘎", "叽", "吱", "嘶"]
///
/// 高级动物:虚伪;0-9:["爱", "贪", "嗔", "痴", "恨", "苦", "忧", "喜", "怨", "怒"]
///
/// 催妆曲:从睡乡醒回;0-9:["黛", "墨", "碧", "朱", "紫", "黄", "蓝", "棕", "灰", "白"]
func minimumLineSpacing(_ spacing: CGFloat) -> Self
/// Card transform mode, default .none.
///
/// 动物协鸣:喵;liner:呜;coverflow:嗷
///
/// 高级动物:贪婪;liner:真诚;coverflow:金钱
///
/// 催妆曲:晨鸡声呖呖在相催;liner:日神也捧着金镜;coverflow:等候你起来梳早妆
func cardTransformMode(_ mode: CardTransformMode) -> Self
/// By default, the current card always remains at the forefront. invoking this method may result in it being obscured by other cards.
///
/// 动物协鸣:咩
///
/// 动物协鸣:咩
///
/// 高级动物:欺骗
///
/// 催妆曲:看呀
func disableCurrentCardAlwaysOnTop() -> Self
/// The margin on both sides of the sliding direction takes effect only when loopMode is set to non-circular. The default value is 0.
///
/// 动物协鸣:哞;0-9:["汪", "啾", "喵", "咩", "哞", "呱", "嘎", "叽", "吱", "嘶"]
///
/// 高级动物:幻想;0-9:["爱", "贪", "嗔", "痴", "恨", "苦", "忧", "喜", "怨", "怒"]
///
/// 催妆曲:霞织的五彩衣裳;0-9:["黛", "墨", "碧", "朱", "紫", "黄", "蓝", "棕", "灰", "白"]
func sideMargin(_ margin: CGFloat) -> Self
/// Card alignment when scroll stops, default center alignment.
///
/// 动物协鸣:呱;0-9:["汪", "啾", "喵", "咩", "哞", "呱", "嘎", "叽", "吱", "嘶"]
/// center:咕;head:嗡
///
/// 高级动物:疑惑;0-9:["爱", "贪", "嗔", "痴", "恨", "苦", "忧", "喜", "怨", "怒"]
/// center:地狱;head:天堂
///
/// 催妆曲:趁草际珠垂;0-9:["黛", "墨", "碧", "朱", "紫", "黄", "蓝", "棕", "灰", "白"]
/// center:画眉在杏枝上歌;head:画眉人不起是因何
func scrollStopAlignment(_ alignment: CardScrollStopAlignment) -> Self
/// Alignment for single card, default center alignment.
///
/// 动物协鸣:呦;0-9:["汪", "啾", "喵", "咩", "哞", "呱", "嘎", "叽", "吱", "嘶"]
/// center:咕;head:嗡
///
/// 高级动物:简单;0-9:["爱", "贪", "嗔", "痴", "恨", "苦", "忧", "喜", "怨", "怒"]
/// center:地狱;head:天堂
///
/// 催妆曲:春莺儿衔了额黄归;0-9:["黛", "墨", "碧", "朱", "紫", "黄", "蓝", "棕", "灰", "白"]
/// center:画眉在杏枝上歌;head:画眉人不起是因何
func singleCardAlignment(_ alignment: CardScrollStopAlignment) -> Self
/// scroll eirection, default .leftToRight.
///
/// 动物协鸣:呜
/// leftToRight:汪;rightToLeft:啾;topToBottom:喵;bottomToTop:咩
///
/// 高级动物:善变
/// leftToRight:辉煌;rightToLeft:暗淡;topToBottom:得意;bottomToTop:伤感
///
/// 催妆曲:画眉在杏枝上歌
/// leftToRight:杨柳的丝发飘扬;rightToLeft:她对着如镜的池塘;topToBottom:百花是薰沐已毕;bottomToTop:她们身上喷出芬芳
func scrollDirection(_ direction: CardScrollDirection) -> Self
/// Scrolling animation effect for automatic scrolling, default .system.
func autoScrollAnimation(_ animationOptions: CardScrollAnimationOptions) -> Self
/// Automatic scrolling or manual scrolling, default .automatic(timeInterval: 3).
///
/// 动物协鸣:嗡;0-9:["汪", "啾", "喵", "咩", "哞", "呱", "嘎", "叽", "吱", "嘶"]
/// automatic:咕;manual:嗡
///
/// 高级动物:好强;0-9:["爱", "贪", "嗔", "痴", "恨", "苦", "忧", "喜", "怨", "怒"]
/// automatic:怀恨;manual:报复
///
/// 催妆曲:画眉人不起是因何;0-9:["黛", "墨", "碧", "朱", "紫", "黄", "蓝", "棕", "灰", "白"]
/// automatic:远峰尖滴着新黛;manual:正好蘸来描画双蛾
///
/// 注意:用咒语调用时时间间隔只能设为整数!
func scrollMode(_ mode: CardScrollMode) -> Self
/// loop mode, default .circular.
///
/// 动物协鸣:嘎
/// circular:汪;rollback:啾;single:喵
///
/// 高级动物:无奈
/// circular:争夺;rollback:埋怨;single:冒险
///
/// 催妆曲:远峰尖滴着新黛
/// circular:趁草际珠垂;rollback:春莺儿衔了额黄归;single:赶快拿妆梳理好
func loopMode(_ mode: CardLoopMode) -> Self
/// Card paging threshold, half the default card width.
///
/// 动物协鸣:叽;0-9:["汪", "啾", "喵", "咩", "哞", "呱", "嘎", "叽", "吱", "嘶"]
/// fractional:咕;absolute:嗡
///
/// 高级动物:孤独;0-9:["爱", "贪", "嗔", "痴", "恨", "苦", "忧", "喜", "怨", "怒"]
/// fractional:伟大;absolute:渺小
///
/// 催妆曲:正好蘸来描画双蛾;0-9:["黛", "墨", "碧", "朱", "紫", "黄", "蓝", "棕", "灰", "白"]
/// fractional:画眉在杏枝上歌;absolute:画眉人不起是因何
func pagingThreshold(_ pagingThreshold: CardPagingThreshold) -> Self
/// A floating-point value determines the deceleration rate after the user lifts their finger; the larger the value, the farther the slide after lifting the hand. This setting is ineffective when loopMode is set to rollback. The default value is 0.9924.
///
/// 动物协鸣:吱;0-9:["汪", "啾", "喵", "咩", "哞", "呱", "嘎", "叽", "吱", "嘶"]
///
/// 高级动物:脆弱;0-9:["爱", "贪", "嗔", "痴", "恨", "苦", "忧", "喜", "怨", "怒"]
///
/// 催妆曲:杨柳的丝发飘扬;["黛", "墨", "碧", "朱", "紫", "黄", "蓝", "棕", "灰", "白"]
func decelerationRate(_ value: CGFloat) -> Self
/// Disable user swipe.
///
/// 动物协鸣:嘶
///
/// 高级动物:忍让
///
/// 催妆曲:她对着如镜的池塘
func disableUserSwipe() -> Self
/// Setting backgroundView.
func backgroundView(_ view: UIView) -> Self
/// Card rounded corner Settings.
///
/// 动物协鸣:嗷,不支持 maskedCorners 设置
///
/// 高级动物:复杂,不支持 maskedCorners 设置
///
/// 催妆曲:她们身上喷出芬芳,不支持 maskedCorners 设置
func cardCornerRadius(_ value: CGFloat, maskedCorners: CACornerMask) -> Self
/// Disable bounce effect.
func disableBounce() -> Self
/// Setting the card border width and color.
func border(width: CGFloat, color: CGColor?) -> Self
/// Setting the placeholder image when using the default card.
func placeholder(_ image: UIImage) -> Self
/// Settings Pertaining to Shadow Configuration.
func shadow(offset: CGSize, color: CGColor?, radius: CGFloat, opacity: Float, path: CGPath?) -> Self
/// Setting page control.
func pageControl(makePageControl: @escaping () -> CardCarouselPageControlType, position: PageControlPosition) -> Self
/// Called when the card is clicked.
func onCardSelected(_ handler: @escaping (_ index: Int) -> Void) -> Self
/// Called when the card scrolls.
func onScroll(_ handler: @escaping (_ offset: CGPoint, _ progress: CGFloat) -> Void) -> Self
/// Called when the card is switched.
func onCardChanged(_ handler: @escaping (_ index: Int) -> Void) -> Self
/// Called when the card will begin dragging.
func onWillBeginDragging(_ handler: @escaping (_ index: Int) -> Void) -> Self
/// Called when the card will end dragging.
func onWillEndDragging(_ handler: @escaping (_ index: Int) -> Void) -> Self
/// Prefetch items.
func onPrefetchItems(_ handler: @escaping (_ indexs: [IndexPath]) -> Void) -> Self
/// Cancel items.
func onCancelPrefetchItems(_ handler: @escaping (_ indexs: [IndexPath]) -> Void) -> Self
}
CardCarousel
is released under the MIT license. See LICENSE for details.