Reputation: 81
I'm trying to apply a .background modifier to my card which is represented by a PageViewController.
The red background is not applied to my card, because the card seems to have a default white background.
import SwiftUI
struct PageView<Page:View>: View {
var viewControllers: [UIHostingController<Page>]
init(_ views: [Page]) {
self.viewControllers = views.map { UIHostingController(rootView: $0)}
}
var body: some View {
ZStack {
Color.black
.edgesIgnoringSafeArea(.all)
PageViewController(controllers: viewControllers)
.background(Color.red)
}
}
}
struct PageView_Previews: PreviewProvider {
static var previews: some View {
PageView(cards.map { OnboardingCardView(card: $0) })
}
}
Each card come from a CardView and no background is applied to my card here.
Here's my pageViewController code :
import SwiftUI
struct PageViewController: UIViewControllerRepresentable {
var controllers: [UIViewController]
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
func makeUIViewController(context: Context) -> UIPageViewController {
let pageViewController = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal)
pageViewController.dataSource = context.coordinator
return pageViewController
}
func updateUIViewController(_ pageViewController: UIPageViewController, context: Context) {
pageViewController.setViewControllers([controllers[0]], direction: .forward, animated: true)
}
class Coordinator: NSObject, UIPageViewControllerDataSource {
var parent: PageViewController
init(_ pageViewController: PageViewController) {
self.parent = pageViewController
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let index = parent.controllers.firstIndex(of: viewController) else {
return nil
}
if index == 0 {
return parent.controllers.last
}
return parent.controllers[index-1]
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let index = parent.controllers.firstIndex(of: viewController) else {
return nil
}
if index + 1 == parent.controllers.count {
return parent.controllers.first
}
return parent.controllers[index+1]
}
}
}
What's the problem here ? Thanks a lot.
Upvotes: 0
Views: 630
Reputation: 699
Background is set in your UIHostingController for every exact card by default. You can set background color to .clear manually to make SwiftUI background makes effect:
struct PageView<Page:View>: View {
var viewControllers: [UIHostingController<Page>]
init(_ views: [Page]) {
self.viewControllers = views.map { UIHostingController(rootView: $0)}
for viewController in viewControllers{
viewController.view.backgroundColor = .clear
}
}
...
}
Upvotes: 1
Reputation: 699
Check my full SwiftUI
realisation of UIPageViewController
. It doesn't use UIKit
, so it would be easier to control.
import SwiftUI
extension OnboardingCard{
static var test: [OnboardingCard]{
[
OnboardingCard(image: "onboarding", title: "Card 1", description: "Card 1 description."),
OnboardingCard(image: "onboarding", title: "Card 2", description: "Card 2 description."),
OnboardingCard(image: "onboarding", title: "Card 3", description: "Card 4 description.")
]
}
}
struct ContentView: View {
var directionForward = true
@State var currentCard: OnboardingCard
var cards: [OnboardingCard]
@State var xOffset: CGFloat = 0
init(cards newCards: [OnboardingCard]){
self.cards = newCards
guard let startCard = newCards.first else {fatalError("no cards")}
self._currentCard = State(initialValue: startCard)
}
var body: some View {
GeometryReader{geometry in
HStack{
ForEach(self.cardsSorted()){card in
OnboardingCardView(card: card)
.frame(width: geometry.size.width)
}
}
.offset(x: self.xOffset, y: 0)
.gesture(DragGesture()
.onChanged(){value in
self.xOffset = value.location.x - value.startLocation.x
}
.onEnded(){value in
let inPercent = 100 * self.xOffset / (geometry.size.width / 2)
let changedCount = inPercent / 100
if changedCount < -1{//drag left to go to next one
guard let currentCardInd = self.cards.firstIndex(where: {$0.id == self.currentCard.id}) else{fatalError("cant find current card")}
let nextInd = self.cards.index(currentCardInd, offsetBy: Int(-changedCount))
if self.cards.indices.contains(nextInd){
self.currentCard = self.cards[nextInd]
self.xOffset = geometry.size.width + self.xOffset
}else{
guard let firstCard = self.cards.first else{fatalError("cant get first card")}
self.currentCard = firstCard
self.xOffset = geometry.size.width + self.xOffset
}
}else if changedCount > 1{//drag right to go to previos one
guard let currentCardInd = self.cards.firstIndex(where: {$0.id == self.currentCard.id}) else{fatalError("cant find current card")}
let previosInd = self.cards.index(currentCardInd, offsetBy: Int(-changedCount))
if self.cards.indices.contains(previosInd){
self.currentCard = self.cards[previosInd]
self.xOffset = -geometry.size.width + self.xOffset
}else{
guard let lastCard = self.cards.last else{fatalError("cant get last card")}
self.currentCard = lastCard
self.xOffset = -geometry.size.width + self.xOffset
}
}
withAnimation(.easeInOut(duration: 0.5)){
self.xOffset = 0
}
})
}
}
func cardsSorted() -> [OnboardingCard]{
guard let currentCardInd = self.cards.firstIndex(where: {$0.id == self.currentCard.id}) else{fatalError("cant find current card")}
guard let firstInd = self.cards.firstIndex(where: {$0.id == cards.first?.id}) else{fatalError("cant find current card")}
guard let lastInd = self.cards.firstIndex(where: {$0.id == cards.last?.id}) else{fatalError("cant find current card")}
var nextInd = self.cards.index(after: currentCardInd)
if self.cards.indices.contains(nextInd) == false{
nextInd = firstInd
}
var previosInd = self.cards.index(before: currentCardInd)
if self.cards.indices.contains(previosInd) == false{
previosInd = lastInd
}
return [self.cards[previosInd], self.cards[currentCardInd],self.cards[nextInd]]
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView(cards: OnboardingCard.test)
}
}
it have less code, and it have transparent logic. You can modify checks to adjust switch sensitivity if you want (by editing compare if changedCount > 1
). You can use gesture end point predictions to calculate and adjust animation speed or even to switch more than one card at once. It's fully up to you. Or you can just use if as is.
Upvotes: 0