Last active
October 25, 2021 23:24
-
-
Save Arutyun2312/0fc17976193d94aead8547f323ce1255 to your computer and use it in GitHub Desktop.
Simple Scrollview created based on UIKit. Can control content offset and scroll direction. Note: to control content offset, set 'scrollOwner' to code.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// CustomScrollView.swift | |
// BearFitness | |
// | |
// Created by Arutyun Enfendzhyan on 05.10.21. | |
// | |
import SwiftUI | |
struct CustomScrollView: View { | |
@Binding var offset: CGFloat | |
@Binding var scrollOwner: ScrollOwner | |
var axis: Axis = .vertical | |
let content: AnyView | |
var body: some View { | |
GeometryReader { proxy in | |
Internal(offset: $offset, scrollOwner: $scrollOwner, axis: axis, content: .init( | |
Group { | |
switch axis { | |
case .horizontal: | |
content | |
.frame(height: proxy.size.height) | |
case .vertical: | |
content | |
.frame(width: proxy.size.width) | |
} | |
} | |
)) | |
} | |
} | |
private struct Internal: UIViewControllerRepresentable { | |
@Binding var offset: CGFloat | |
@Binding var scrollOwner: ScrollOwner | |
let axis: Axis | |
let content: AnyView | |
func makeUIViewController(context: Context) -> UIViewControllerType { | |
let controller = UIViewControllerType() | |
controller.hostingController.rootView = self.content | |
controller.onOffsetChanged = { offset = axis == .vertical ? $0.y : $0.x } | |
return controller | |
} | |
func updateUIViewController(_ controller: UIViewControllerType, context: Context) { | |
controller.scrollView.isScrollEnabled = self.scrollOwner == .user | |
if self.scrollOwner == .code { | |
let offset: CGPoint = { | |
switch axis { | |
case .horizontal: | |
return .init(x: self.offset.atMost(controller.scrollView.contentSize.width).atLeast(0), y: 0) | |
case .vertical: | |
return .init(x: 0, y: self.offset.atMost(controller.scrollView.contentSize.height).atLeast(0)) | |
} | |
}() | |
controller.scrollView.setContentOffset(offset, animated: true) | |
} else { | |
print("Content offset is controlled by user") | |
} | |
controller.hostingController.rootView = self.content | |
controller.scrollOwner = self.scrollOwner | |
} | |
class UIViewControllerType: UIViewController, UIScrollViewDelegate { | |
lazy var scrollView = UIScrollView() | |
var hostingController = UIHostingController(rootView: AnyView(EmptyView())) | |
var onOffsetChanged: (CGPoint) -> () = { _ in } | |
var scrollOwner: ScrollOwner = .user | |
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) | |
self.view.setNeedsUpdateConstraints() | |
self.view.updateConstraintsIfNeeded() | |
self.view.layoutIfNeeded() | |
self.scrollView.delegate = 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), | |
]) | |
} | |
func scrollViewDidScroll(_ scrollView: UIScrollView) { | |
if self.scrollOwner == .user { | |
self.onOffsetChanged(scrollView.contentOffset) | |
} | |
} | |
} | |
} | |
} | |
extension CustomScrollView { | |
init<Content: View>(offset: Binding<CGFloat>, scrollOwner: Binding<ScrollOwner>, axis: Axis = .vertical, @ViewBuilder content: () -> Content) { | |
self.init(offset: offset, scrollOwner: scrollOwner, axis: axis, content: .init(content())) | |
} | |
} | |
enum ScrollOwner { | |
case user, code | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment