我一直在浏览每个beta版本的文档,但是还没有找到制作传统的分页ScrollView的方法。我对AppKit不熟悉,所以我想知道SwiftUI中是否不存在此控件,因为它主要是UIKit构造。无论如何,有没有人举这个例子,或者有人可以告诉我这绝对是不可能的,所以我可以停止寻找自己的东西并滚动自己的东西了。
答案 0 :(得分:4)
不确定这是否对您的问题有所帮助,但是暂时在Apple致力于在SwiftUI中添加分页视图时,我编写了utility library,使您在使用UIPageViewController
时拥有SwiftUI的感觉隐藏在引擎盖下。
您可以像这样使用它:
Pages {
Text("Page 1")
Text("Page 2")
Text("Page 3")
Text("Page 4")
}
或者,如果您的应用程序中有模型列表,则可以这样使用它:
struct Car {
var model: String
}
let cars = [Car(model: "Ford"), Car(model: "Ferrari")]
ModelPages(cars) { index, car in
Text("The \(index) car is a \(car.model)")
.padding(50)
.foregroundColor(.white)
.background(Color.blue)
.cornerRadius(10)
}
答案 1 :(得分:3)
您现在可以使用TabView并将.tabViewStyle设置为PageTabViewStyle()
TabView {
View1()
View2()
View3()
}
.tabViewStyle(PageTabViewStyle())
答案 2 :(得分:1)
从Beta 3开始,没有用于页面调度的本机SwiftUI API。我已提出反馈,建议您也这样做。他们将ScrollView
API从Beta 2更改为Beta 3,看到进一步的更新我不会感到惊讶。
现在可以包装UIScrollView
以便提供此功能。不幸的是,您必须将UIScrollView包装在UIViewController
中,并进一步包装在UIViewControllerRepresentable
中以支持SwiftUI内容。
class UIScrollViewViewController: UIViewController {
lazy var scrollView: UIScrollView = {
let v = UIScrollView()
v.isPagingEnabled = true
return v
}()
var hostingController: UIHostingController<AnyView> = UIHostingController(rootView: AnyView(EmptyView()))
override func viewDidLoad() {
super.viewDidLoad()
self.view.addSubview(self.scrollView)
self.pinEdges(of: self.scrollView, to: self.view)
self.hostingController.willMove(toParent: self)
self.scrollView.addSubview(self.hostingController.view)
self.pinEdges(of: self.hostingController.view, to: self.scrollView)
self.hostingController.didMove(toParent: self)
}
func pinEdges(of viewA: UIView, to viewB: UIView) {
viewA.translatesAutoresizingMaskIntoConstraints = false
viewB.addConstraints([
viewA.leadingAnchor.constraint(equalTo: viewB.leadingAnchor),
viewA.trailingAnchor.constraint(equalTo: viewB.trailingAnchor),
viewA.topAnchor.constraint(equalTo: viewB.topAnchor),
viewA.bottomAnchor.constraint(equalTo: viewB.bottomAnchor),
])
}
}
struct UIScrollViewWrapper<Content: View>: UIViewControllerRepresentable {
var content: () -> Content
init(@ViewBuilder content: @escaping () -> Content) {
self.content = content
}
func makeUIViewController(context: Context) -> UIScrollViewViewController {
let vc = UIScrollViewViewController()
vc.hostingController.rootView = AnyView(self.content())
return vc
}
func updateUIViewController(_ viewController: UIScrollViewViewController, context: Context) {
viewController.hostingController.rootView = AnyView(self.content())
}
}
然后使用它:
var body: some View {
GeometryReader { proxy in
UIScrollViewWrapper {
VStack {
ForEach(0..<1000) { _ in
Text("Hello world")
}
}
.frame(width: proxy.size.width) // This ensures the content uses the available width, otherwise it will be pinned to the left
}
}
}
答案 3 :(得分:1)
Apple的官方教程对此进行了举例说明。我觉得很容易理解并且适合我的情况。我真的建议您检查一下并尝试了解如何与UIKit交互。由于SwiftUI太年轻,因此目前还无法涵盖UIKit中的所有功能。与UIKit进行接口应该可以解决大部分需求。
https://developer.apple.com/tutorials/swiftui/interfacing-with-uikit
答案 4 :(得分:1)
结帐SwiftUIPager。这是在SwiftUI本机组件之上构建的寻呼机:
答案 5 :(得分:0)
这里是在swiftUI中使用scrollView的示例,您可以在foreach循环中使用customView并相应地设置垂直或水平滚动行为。试试这个:-
var body: some View {
VStack {
ScrollView (alwaysBounceHorizontal: true, showsHorizontalIndicator: false, showsVerticalIndicator: false ) {
VStack(spacing: 15){
ForEach(0...100) {_ in
Text("test")
}
}.padding()
}
}
}
答案 6 :(得分:0)
您只需使用.onAppear()
跟踪状态即可加载下一页。
struct YourListView : View {
@ObservedObject var viewModel = YourViewModel()
let numPerPage = 50
var body: some View {
NavigationView {
List(viewModel.items) { item in
NavigationLink(destination: DetailView(item: item)) {
ItemRow(item: item)
.onAppear {
if self.shouldLoadNextPage(currentItem: item) {
self.viewModel.fetchItems(limitPerPage: self.numPerPage)
}
}
}
}
.navigationBarTitle(Text("Items"))
.onAppear {
guard self.viewModel.items.isEmpty else { return }
self.viewModel.fetchItems(limitPerPage: self.numPerPage)
}
}
}
private func shouldLoadNextPage(currentItem item: Item) -> Bool {
let currentIndex = self.viewModel.items.firstIndex(where: { $0.id == item.id } )
let lastIndex = self.viewModel.items.count - 1
let offset = 5 //Load next page when 5 from bottom, adjust to meet needs
return currentIndex == lastIndex - offset
}
}
class YourViewModel: ObservableObject {
@Published private(set) items = [Item]()
// add whatever tracking you need for your paged API like next/previous and count
private(set) var fetching = false
private(set) var next: String?
private(set) var count = 0
func fetchItems(limitPerPage: Int = 30, completion: (([Item]?) -> Void)? = nil) {
// Do your stuff here based on the API rules for paging like determining the URL etc...
if items.count == 0 || items.count < count {
let urlString = next ?? "https://somePagedAPI?limit=/(limitPerPage)"
fetchNextItems(url: urlString, completion: completion)
} else {
completion?(pokemon)
}
}
private func fetchNextItems(url: String, completion: (([Item]?) -> Void)?) {
guard !fetching else { return }
fetching = true
Networking.fetchItems(url: url) { [weak self] (result) in
DispatchQueue.main.async { [weak self] in
self?.fetching = false
switch result {
case .success(let response):
if let count = response.count {
self?.count = count
}
if let newItems = response.results {
self?.items += newItems
}
self?.next = response.next
case .failure(let error):
// Error state tracking not implemented but would go here...
os_log("Error fetching data: %@", error.localizedDescription)
}
}
}
}
}
进行修改以适合您正在调用的任何API,并根据您的应用程序体系结构处理错误。
答案 7 :(得分:0)
如果您想利用PageTabViewStyle
中新的TabView
,但需要垂直分页滚动视图,则可以使用.rotationEffect()
之类的效果修饰符。
使用此方法,我编写了一个名为library的VerticalTabView ?,只需将现有的TabView
更改为VTabView
即可使TabView垂直。