SwiftUI实现图片轮播器

实现效果

实现一个简单的图片轮播器,在SwiftUI中整合UIKit的UIPageViewController和UIPageControl。

Model

struct Landmark: Identifiable {
    var id = UUID()
    var name: String
    var park: String
    var imageName: String
    var image: Image {
        Image(imageName)
    }
}


let datas = [
    Landmark(name: "Turtle Rock", park: "Joshua Tree National Park", imageName: "turtlerock_feature"),
    Landmark(name: "St.Mary Lake", park: "Glacier National Park", imageName: "stmarylake_feature"),
    Landmark(name: "Charley Rivers", park: "Charley Rivers National Preserve", imageName: "charleyrivers_feature")
]

Card

struct Card: View {
    var landmark: Landmark
    
    var body: some View {
        landmark.image
            .resizable()
            .aspectRatio(1.5, contentMode: .fit)
            .overlay(TextOverlay(landmark: landmark))
    }
}

struct Card_Previews: PreviewProvider {
    static var previews: some View {
        Card(landmark: datas[0])
    }
}

struct TextOverlay: View {
    var landmark: Landmark
    var gradient: LinearGradient {
        LinearGradient(
            gradient: Gradient(
                colors: [Color.black.opacity(0.6), Color.black.opacity(0)]),
            startPoint: .bottom,
            endPoint: .center)
    }
    var body: some View {
        ZStack(alignment: .bottomLeading) {
            Rectangle()
                .fill(gradient)
            VStack(alignment: .leading) {
                Text(landmark.name)
                    .foregroundColor(.white)
                    .font(.title)
                    .bold()
                Text(landmark.park)
                    .foregroundColor(.white)
            }
            .padding()
        }
    }
}

简单的图片和文字结合的卡牌。

PageController

struct PageViewController: UIViewControllerRepresentable {
    
    // 轮播控制器当前页
    @Binding var currentPage: Int
    
    var controllers: [UIViewController]
    
    // 创建协调器
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    
    // 创建controller
    func makeUIViewController(context: Context) -> UIPageViewController {
        let pageViewController = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil)
        // 协调器遵守协议
        pageViewController.dataSource = context.coordinator
        pageViewController.delegate = context.coordinator
        return pageViewController
    }
    
    // 更新controller
    func updateUIViewController(_ uiViewController: UIPageViewController, context: Context) {
        uiViewController.setViewControllers([controllers[currentPage]], direction: .forward, animated: true, completion: nil)
    }
    
    class Coordinator: NSObject, UIPageViewControllerDataSource, UIPageViewControllerDelegate {
        var parent: PageViewController
        
        init(_ pageViewController: PageViewController) {
            parent = pageViewController
        }
        
        // MARK: - UIPageViewControllerDataSource
        
        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];
        }
        
        // MARK: - UIPageViewControllerDelegate
        
        func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
            if
                completed,
                let viewController = pageViewController.viewControllers?.first,
                let index = parent.controllers.firstIndex(of: viewController) {
                parent.currentPage = index
            }
        }
    }
}

在SwiftUI中使用UIKit中的ViewController需要实现UIViewControllerRepresentable协议,实现makeUIViewControllerupdateUIViewController方法。使用协调器Coordinator来实现控制器的数据源和代理。通过context.coordinator来实现UIKit与SwiftUI的整合。

PageView

struct PageView<Page: View>: View {
    
    @State var currentPage: Int = 0
    
    var viewControllers: [UIHostingController<Page>]
    
    // 初始化方法,利用SwiftUI视图创建UIHostingController
    init(_ views: [Page]) {
        self.viewControllers = views.map { UIHostingController(rootView: $0) }
    }
    
    var body: some View {
        ZStack(alignment: .bottomTrailing) {
            PageViewController(currentPage: $currentPage, controllers: viewControllers)
            PageControl(numberOfPages: viewControllers.count, currentPage: $currentPage)
                .frame(width: CGFloat(viewControllers.count * 18))
                .padding()
        }
    }
}

struct PageView_Previews: PreviewProvider {
    static var previews: some View {
        PageView(datas.map { Card(landmark: $0) })
    }
}

通过传入SwiftUI的Views利用整合的PageViewController创建轮播器,PageView为可以供SwiftUI视图调用的视图。PageView向PageViewController和PageControl传递currentPage属性来实现图片和小圆点的切换,同样他们通过绑定currentPage属性完成修改。

PageControl

struct PageControl: UIViewRepresentable {
    
    var numberOfPages: Int
    
    @Binding var currentPage: Int
    
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    
    func makeUIView(context: Context) -> UIPageControl {
        let control = UIPageControl()
        control.numberOfPages = numberOfPages
        control.addTarget(context.coordinator, action: #selector(Coordinator.updateCurrentPage(sender:)), for: .valueChanged)
        return control
    }
    
    func updateUIView(_ uiView: UIPageControl, context: Context) {
        uiView.currentPage = currentPage
    }
    
    class Coordinator: NSObject {
        var parent: PageControl
        init(_ pageControl: PageControl) {
            parent = pageControl
        }
        
        // pageControl触发事件
        @objc func updateCurrentPage(sender: UIPageControl) {
            parent.currentPage = sender.currentPage
        }
    }
}

UIKit的UIView类视图需要实现UIViewRepresentable协议,与UIViewControllerRepresentable协议使用方法相同。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值