Skip to content

Instantly share code, notes, and snippets.

@agelessman
Created July 6, 2020 09:56
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save agelessman/3bf1d4f4d7fb24495972ba962777874f to your computer and use it in GitHub Desktop.
Save agelessman/3bf1d4f4d7fb24495972ba962777874f to your computer and use it in GitHub Desktop.
//
// ContentView.swift
// ScrollViewDemo
//
// Created by MC on 2020/7/5.
//
import SwiftUI
struct ContentView: View {
var body: some View {
Example3()
}
}
struct Example4: View {
@State private var offsetY: CGFloat = 0
var body: some View {
VStack {
Spacer()
Text("offseetX: \(offsetY)")
Spacer()
HStack {
Button("offseetY: 0") {
self.offsetY = 0
}
.border(Color.green)
Button("offsetY: 100") {
self.offsetY = 100
}
.border(Color.green)
Button("offsetY: 200") {
self.offsetY = 200
}
.border(Color.green)
}
Spacer()
ScrollView(.vertical) {
LazyVStack(spacing: 20) {
ForEach(0..<10) { index in
Text("\(index)")
.frame(width: 200, height: 140)
.background(index == 6 ? Color.green : Color.orange.opacity(0.5))
.cornerRadius(/*@START_MENU_TOKEN@*/3.0/*@END_MENU_TOKEN@*/)
}
}
}
.scrollOffsetY(self.$offsetY)
Spacer()
}
}
}
struct Example3: View {
@State private var offsetX: CGFloat = 0
var body: some View {
VStack {
Spacer()
Text("offseetX: \(offsetX)")
Spacer()
HStack {
Button("offseetX: 0") {
self.offsetX = 0
}
.border(Color.green)
Button("offseetX: 100") {
self.offsetX = 100
}
.border(Color.green)
Button("offseetX: 200") {
self.offsetX = 200
}
.border(Color.green)
}
Spacer()
ScrollView(.horizontal) {
LazyHStack {
ForEach(0..<10) { index in
Text("\(index)")
.frame(width: 100, height: 240)
.background(index == 6 ? Color.green : Color.orange.opacity(0.5))
.cornerRadius(/*@START_MENU_TOKEN@*/3.0/*@END_MENU_TOKEN@*/)
}
}
}
.scrollOffsetX(self.$offsetX)
.frame(height: 300)
Spacer()
}
}
}
extension View {
func scrollOffsetX(_ offsetX: Binding<CGFloat>) -> some View {
return MyScrollViewControllerRepresentable(offset: offsetX, isOffsetX: true, content: self)
}
}
extension View {
func scrollOffsetY(_ offsetY: Binding<CGFloat>) -> some View {
return MyScrollViewControllerRepresentable(offset: offsetY, isOffsetX: false, content: self)
}
}
struct MyScrollViewControllerRepresentable<Content>: UIViewControllerRepresentable where Content: View {
var offset: Binding<CGFloat>
let isOffsetX: Bool
var content: Content
func makeUIViewController(context: Context) -> MyScrollViewUIHostingController<Content> {
MyScrollViewUIHostingController(offset: offset, isOffsetX:isOffsetX, rootView: content)
}
func updateUIViewController(_ uiViewController: MyScrollViewUIHostingController<Content>, context: Context) {
uiViewController.scroll(to: offset.wrappedValue, animated: true)
}
}
class MyScrollViewUIHostingController<Content>: UIHostingController<Content> where Content: View {
var offset: Binding<CGFloat>
let isOffsetX: Bool
var showed = false
var sv: UIScrollView?
init(offset: Binding<CGFloat>, isOffsetX: Bool, rootView: Content) {
self.offset = offset
self.isOffsetX = isOffsetX
super.init(rootView: rootView)
}
@objc dynamic required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidAppear(_ animated: Bool) {
/// 保证设置一次监听
if showed {
return
}
showed = true
/// 查找UIScrollView
sv = findScrollView(in: view)
/// 设置监听
sv?.addObserver(self,
forKeyPath: #keyPath(UIScrollView.contentOffset),
options: [.old, .new],
context: nil)
/// 滚动到指定位置
scroll(to: offset.wrappedValue, animated: false)
super.viewDidAppear(animated)
}
func scroll(to position: CGFloat, animated: Bool = true) {
if let s = sv {
if position != (self.isOffsetX ? s.contentOffset.x : s.contentOffset.y) {
let offset = self.isOffsetX ? CGPoint(x: position, y: 0) : CGPoint(x: 0, y: position)
sv?.setContentOffset(offset, animated: animated)
}
}
}
override func observeValue(forKeyPath keyPath: String?,
of object: Any?,
change: [NSKeyValueChangeKey: Any]?,
context: UnsafeMutableRawPointer?) {
if keyPath == #keyPath(UIScrollView.contentOffset) {
if let s = self.sv {
DispatchQueue.main.async {
self.offset.wrappedValue = self.isOffsetX ? s.contentOffset.x : s.contentOffset.y
}
}
}
}
func findScrollView(in view: UIView?) -> UIScrollView? {
if view?.isKind(of: UIScrollView.self) ?? false {
return view as? UIScrollView
}
for subview in view?.subviews ?? [] {
if let sv = findScrollView(in: subview) {
return sv
}
}
return nil
}
}
struct Example2: View {
var body: some View {
ScrollViewRepresentable()
.frame(width: 200, height: 100)
}
struct ScrollViewRepresentable: UIViewRepresentable {
func makeUIView(context: Context) -> UIScrollView {
let scrollView = UIScrollView()
scrollView.backgroundColor = UIColor.green
return scrollView
}
func updateUIView(_ uiView: UIScrollView, context: Context) {}
}
}
struct Example1: View {
var body: some View {
ScrollView(.horizontal) {
ScrollViewReader { scrollViewProxy in
LazyHStack(spacing: 10) {
VStack {
Spacer()
Text(".leading")
.frame(width: 80, height: 40, alignment: /*@START_MENU_TOKEN@*/ .center/*@END_MENU_TOKEN@*/)
.foregroundColor(.white)
.background(Color.green)
.onTapGesture {
withAnimation {
scrollViewProxy.scrollTo(6, anchor: .leading)
}
}
Spacer()
Text(".center")
.frame(width: 80, height: 40, alignment: .center)
.foregroundColor(.white)
.background(Color.green)
.onTapGesture {
withAnimation {
scrollViewProxy.scrollTo(6, anchor: .center)
}
}
Spacer()
Text(".trailing")
.frame(width: 80, height: 40, alignment: /*@START_MENU_TOKEN@*/ .center/*@END_MENU_TOKEN@*/)
.foregroundColor(.white)
.background(Color.green)
.onTapGesture {
withAnimation {
scrollViewProxy.scrollTo(6, anchor: .trailing)
}
}
Spacer()
}
.frame(width: 100, height: 240)
ForEach(0..<10) { index in
Text("\(index)")
.frame(width: 100, height: 240)
.background(index == 6 ? Color.green : Color.orange.opacity(0.5))
.cornerRadius(/*@START_MENU_TOKEN@*/3.0/*@END_MENU_TOKEN@*/)
}
}
.padding(.horizontal, /*@START_MENU_TOKEN@*/10/*@END_MENU_TOKEN@*/)
}
}
.frame(height: 300, alignment: /*@START_MENU_TOKEN@*/ .center/*@END_MENU_TOKEN@*/)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment