Skip to content

Instantly share code, notes, and snippets.

@Codelaby
Last active June 28, 2023 09:57
Show Gist options
  • Save Codelaby/bd5a53d5ab7bbcb0550bdd4d0dc4977a to your computer and use it in GitHub Desktop.
Save Codelaby/bd5a53d5ab7bbcb0550bdd4d0dc4977a to your computer and use it in GitHub Desktop.
Ejemplos de TabView en SwiftUI
//
// CarouselView.swift
// Avatars
//
// Created by Codelaby on 28/6/23.
//
/*
https://swiftuilibrary.com/components/paginated-carousel
https://github.com/manuelduarte077/CustomCarouselList
https://stackoverflow.com/questions/75049237/slide-carousel-cards-on-cards-tap-in-swiftui
https://stackoverflow.com/questions/62864221/change-tabview-indicator-color-swiftui
https://www.appcoda.com/swiftui-tabview-paged-scrolling/
https://betterprogramming.pub/swipe-the-page-with-swiftuipager-981e379e3bbd
https://gist.github.com/beader/e1312aa5b88af30407bde407235fbe67
https://www.boltuix.com/2022/08/how-to-create-onboarding-with-swiftui.html
*/
import SwiftUI
struct ItemCard: Identifiable, Hashable {
let id = UUID()
let title: String
let color: Color
func hash(into hasher: inout Hasher) {
hasher.combine(id)
}
static func ==(lhs: ItemCard, rhs: ItemCard) -> Bool {
return lhs.id == rhs.id
}
}
struct CarouselView: View {
@State private var selectedPage = 0
var items: [ItemCard] = []
init() {
let colors: [Color] = [.orange, .green, .yellow, .teal, .red, .blue, .purple, .gray, .brown, .cyan, .mint, .pink]
let emojis: [String] = ["🐀", "🐂", "🐅", "🐇", "🐉", "🐍", "🐎", "🐐", "🐒", "🐓", "🐕", "🐖"]
for index in 0..<emojis.count {
let item = ItemCard(title: emojis[index], color: colors[index])
items.append(item)
}
//UIPageControl.appearance().currentPageIndicatorTintColor = .red
//UIPageControl.appearance().pageIndicatorTintColor = UIColor.black.withAlphaComponent(0.2)
}
var body: some View {
VStack {
HStack() {
Button("<") { if selectedPage > 0 {
withAnimation { selectedPage -= 1 }
}}
Spacer().frame(width: 40)
Button(">") { if (selectedPage < (items.count - 1)) {
withAnimation { selectedPage += 1 }
}}
}
TabView(selection: $selectedPage) {
ForEach(items.indices, id: \.self) { index in
Text(items[index].title)
.font(.system(size: 96))
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: 250)
.background(items[index].color)
.clipShape(RoundedRectangle(cornerRadius: 32))
.padding(.horizontal, 16)
.tabItem {
Text(items[index].title)
}
.tag(index)
}
}
.tabViewStyle(PageTabViewStyle())
.indexViewStyle(PageIndexViewStyle(backgroundDisplayMode: .always))
//.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: 340)
.border(.purple)
.onChange(of: selectedPage) { newValue in
print("New page: \(newValue)")
}
}
}
}
struct CarouselView_Previews: PreviewProvider {
static var previews: some View {
CarouselView()
}
}
//
// CarouselView.swift
// Avatars
//
// Created by Codelaby on 28/6/23.
//
/*
https://swiftuilibrary.com/components/paginated-carousel
https://github.com/manuelduarte077/CustomCarouselList
https://stackoverflow.com/questions/75049237/slide-carousel-cards-on-cards-tap-in-swiftui
https://stackoverflow.com/questions/62864221/change-tabview-indicator-color-swiftui
https://www.appcoda.com/swiftui-tabview-paged-scrolling/
https://betterprogramming.pub/swipe-the-page-with-swiftuipager-981e379e3bbd
https://gist.github.com/beader/e1312aa5b88af30407bde407235fbe67
https://www.boltuix.com/2022/08/how-to-create-onboarding-with-swiftui.html
*/
import SwiftUI
struct ItemCard: Identifiable, Hashable {
let id = UUID()
let title: String
let color: Color
func hash(into hasher: inout Hasher) {
hasher.combine(id)
}
static func ==(lhs: ItemCard, rhs: ItemCard) -> Bool {
return lhs.id == rhs.id
}
}
struct InfiniteTabPageView<Content: View>: View {
@GestureState private var translation: CGFloat = .zero
@State private var currentPage: Int = 0
@State private var offset: CGFloat = .zero
private let width: CGFloat
private let animationDuration: CGFloat = 0.25
let content: (_ page: Int) -> Content
init(width: CGFloat = 390, @ViewBuilder content: @escaping (_ page: Int) -> Content) {
self.width = width
self.content = content
}
private var dragGesture: some Gesture {
DragGesture(minimumDistance: 0)
.updating($translation) { value, state, _ in
let translation = min(width, max(-width, value.translation.width))
state = translation
}
.onEnded { value in
offset = min(width, max(-width, value.translation.width))
let predictEndOffset = value.predictedEndTranslation.width
withAnimation(.easeOut(duration: animationDuration)) {
if offset < -width / 2 || predictEndOffset < -width {
offset = -width
} else if offset > width / 2 || predictEndOffset > width {
offset = width
} else {
offset = 0
}
}
DispatchQueue.main.asyncAfter(deadline: .now() + animationDuration) {
if offset < 0 {
currentPage += 1
} else if offset > 0 {
currentPage -= 1
}
offset = 0
}
}
}
var body: some View {
ZStack {
content(pageIndex(currentPage + 2) - 1)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.offset(x: CGFloat(1 - offsetIndex(currentPage - 1)) * width)
content(pageIndex(currentPage + 1) + 0)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.offset(x: CGFloat(1 - offsetIndex(currentPage + 1)) * width)
content(pageIndex(currentPage + 0) + 1)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.offset(x: CGFloat(1 - offsetIndex(currentPage)) * width)
}
.contentShape(Rectangle())
.offset(x: translation)
.offset(x: offset)
.gesture(dragGesture)
.clipped()
}
private func pageIndex(_ x: Int) -> Int {
// 0 0 0 3 3 3 6 6 6 . . . 周期函数
// 用来决定 3 个 content 分别应该展示第几页
Int((CGFloat(x) / 3).rounded(.down)) * 3
}
private func offsetIndex(_ x: Int) -> Int {
// 0 1 2 0 1 2 0 1 2 ... 周期函数
// 用来决定静止状态 3 个 content 的摆放顺序
if x >= 0 {
return x % 3
} else {
return (x + 1) % 3 + 2
}
}
}
struct CarouselView: View {
@State private var selectedPage = 0
var items: [ItemCard] = []
init() {
let colors: [Color] = [.orange, .green, .yellow, .teal, .red, .blue, .purple, .gray, .brown, .cyan, .mint, .pink]
let emojis: [String] = ["🐀", "🐂", "🐅", "🐇", "🐉", "🐍", "🐎", "🐐", "🐒", "🐓", "🐕", "🐖"]
for index in 0..<emojis.count {
let item = ItemCard(title: emojis[index], color: colors[index])
items.append(item)
}
//UIPageControl.appearance().currentPageIndicatorTintColor = .red
//UIPageControl.appearance().pageIndicatorTintColor = UIColor.black.withAlphaComponent(0.2)
}
var body: some View {
VStack {
GeometryReader { geometry in
InfiniteTabPageView(width: geometry.size.width) { page in
let index = (page % items.count + items.count) % items.count
Text(items[index].title)
.font(.system(size: 96))
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: 250)
.background(items[index].color)
.clipShape(RoundedRectangle(cornerRadius: 32))
.padding(.horizontal, 16)
.tabItem {
Text(items[index].title)
}
.onTapGesture {
selectedPage = index
}
}
.border(.purple)
.onChange(of: selectedPage) { newValue in
print("New page: \(newValue)")
}
}
}
}
}
struct CarouselView_Previews: PreviewProvider {
static var previews: some View {
CarouselView()
}
}
//
// CarouselView.swift
// Avatars
//
// Created by Codelaby on 28/6/23.
//
/*
https://swiftuilibrary.com/components/paginated-carousel
https://github.com/manuelduarte077/CustomCarouselList
https://stackoverflow.com/questions/75049237/slide-carousel-cards-on-cards-tap-in-swiftui
https://stackoverflow.com/questions/62864221/change-tabview-indicator-color-swiftui
https://www.appcoda.com/swiftui-tabview-paged-scrolling/
https://betterprogramming.pub/swipe-the-page-with-swiftuipager-981e379e3bbd
https://gist.github.com/beader/e1312aa5b88af30407bde407235fbe67
https://www.boltuix.com/2022/08/how-to-create-onboarding-with-swiftui.html
*/
import SwiftUI
struct ItemCard: Identifiable, Hashable {
let id = UUID()
let title: String
let color: Color
func hash(into hasher: inout Hasher) {
hasher.combine(id)
}
static func ==(lhs: ItemCard, rhs: ItemCard) -> Bool {
return lhs.id == rhs.id
}
}
struct CarouselView: View {
@State private var selectedPage = 0
var items: [ItemCard] = []
init() {
let colors: [Color] = [.orange, .green, .yellow, .pink, .purple, .blue, .red, .gray, .brown, .cyan, .brown, .teal]
let emojis: [String] = ["🐀", "🐂", "🐅", "🐇", "🐉", "🐍", "🐎", "🐐", "🐒", "🐓", "🐕", "🐖"]
for index in 0..<emojis.count {
let item = ItemCard(title: emojis[index], color: colors[index])
items.append(item)
}
//UIPageControl.appearance().currentPageIndicatorTintColor = .red
//UIPageControl.appearance().pageIndicatorTintColor = UIColor.black.withAlphaComponent(0.2)
}
var body: some View {
VStack {
TabView(selection: $selectedPage) {
ForEach(items.indices, id: \.self) { index in
Text(items[index].title)
.font(.system(size: 96))
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: 250)
.background(items[index].color)
.clipShape(RoundedRectangle(cornerRadius: 32))
.padding(.horizontal, 16)
.tabItem {
Text(items[index].title)
}
.tag(index)
}
}
.tabViewStyle(PageTabViewStyle())
.indexViewStyle(PageIndexViewStyle(backgroundDisplayMode: .always))
//.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: 340)
.border(.purple)
.onChange(of: selectedPage) { newValue in
print("New page: \(newValue)")
}
}
}
}
struct CarouselView_Previews: PreviewProvider {
static var previews: some View {
CarouselView()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment