[英]Scrolling scrollview based on pan gesture translation
我想知道如何將平移手勢y翻譯轉換為等效於滾動視圖內容y偏移量。
因此,在此我可以通過平移手勢翻譯滾動我的滾動視圖,重現自然滾動視圖滾動的相同效果。
let scrollYOffset: CGFloat = panGesture.translation(in: view).y
當我這樣做時,它導致滾動非常生澀,因為 scrollYOffset 值在拖動時從 0 變為 54,然后變為 124。
我想要的是使用平移手勢平滑自然地滾動滾動視圖。
您可能會問為什么我不使用自然滾動視圖及其委托 -
func scrollViewDidScroll(_ scrollView: UIScrollView)
答案是我正在嘗試構建一個應用程序,用戶可以在其中拖動屏幕上的對象,並且它后面的滾動視圖應該滾動那么多。
任何人都可以幫助我將平移手勢y翻譯轉換為滾動視圖y內容偏移量。
任何幫助將不勝感激。 謝謝。
方法translation:
返回先前迭代和當前迭代之間的差異,因此您應該將翻譯添加到現有結果中,就像這樣。
@objc private func handleGesture(panGestureRecognizer: UIPanGestureRecognizer) {
switch panGestureRecognizer.state {
case .begin:
break
case .changed:
let translation = panGestureRecognizer.translation(in: self.view)
panGestureRecognizer.setTranslation(.zero, in: self.view)
let newYOffset = currentOffset + translation.y
case .ended:
break
}
}
有多種方法可以獲得“平滑的可拖動視圖”——但是,為了您的目的,一個可能的選擇是使用UISlider
:
如果需要,您可以通過設置拇指和軌跡圖像來更改滑塊的外觀。
示例代碼(用於該屏幕截圖):
class SliderScrollViewController: UIViewController, UIScrollViewDelegate {
let scrollView: UIScrollView = UIScrollView()
let slider: UISlider = UISlider()
override func viewDidLoad() {
super.viewDidLoad()
[scrollView, slider].forEach {
view.addSubview($0)
$0.translatesAutoresizingMaskIntoConstraints = false
}
// horizontal stack view to hold scroll content
let stack = UIStackView()
stack.distribution = .fillEqually
stack.spacing = 8
stack.translatesAutoresizingMaskIntoConstraints = false
// add 20 views to the stack view for horizontal scrolling
for i in 1...20 {
let v = UILabel()
v.textAlignment = .center
v.backgroundColor = .yellow
v.text = "View \(i)"
stack.addArrangedSubview(v)
}
stack.arrangedSubviews.first?.widthAnchor.constraint(equalToConstant: 120).isActive = true
scrollView.addSubview(stack)
let g = view.safeAreaLayoutGuide
let svCLG = scrollView.contentLayoutGuide
let svFLG = scrollView.frameLayoutGuide
NSLayoutConstraint.activate([
scrollView.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
scrollView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
scrollView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
scrollView.heightAnchor.constraint(equalToConstant: 120.0),
slider.topAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: 20.0),
slider.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
slider.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
stack.topAnchor.constraint(equalTo: svCLG.topAnchor, constant: 8.0),
stack.leadingAnchor.constraint(equalTo: svCLG.leadingAnchor, constant: 8.0),
stack.trailingAnchor.constraint(equalTo: svCLG.trailingAnchor, constant: -8.0),
stack.bottomAnchor.constraint(equalTo: svCLG.bottomAnchor, constant: 8.0),
stack.heightAnchor.constraint(equalTo: svFLG.heightAnchor, constant: -16.0),
])
slider.addTarget(self, action: #selector(self.sliderChanged(_:)), for: .valueChanged)
// so we can see the frame of the scroll view
scrollView.backgroundColor = .green
// set scrollView delegate
scrollView.delegate = self
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
// we need to update the slider position when scroll view is manually scrolled
let w = scrollView.contentSize.width - scrollView.frame.size.width
let pct = scrollView.contentOffset.x / w
slider.value = Float(pct)
}
@objc func sliderChanged(_ sender: UISlider) -> Void {
// set scroll view's content offset x based on position of slider
let w = scrollView.contentSize.width - scrollView.frame.size.width
let x = w * CGFloat(sender.value)
scrollView.contentOffset.x = x
}
}
編輯
以為我會更多地玩這個……這個例子將滾動視圖的內容作為UIImage
抓取,將其用於滑塊后面的“背景”圖像,並為滑塊的拇指創建一個輪廓圖像:
完整代碼:
class SliderScrollViewController: UIViewController, UIScrollViewDelegate {
let scrollView: UIScrollView = UIScrollView()
let scrollContentView: UIView = UIView()
let slider: UISlider = UISlider()
let sliderBKG: UIImageView = UIImageView()
override func viewDidLoad() {
super.viewDidLoad()
[scrollView, sliderBKG, slider].forEach {
view.addSubview($0)
$0.translatesAutoresizingMaskIntoConstraints = false
}
// horizontal stack view to hold scroll content
let stack = UIStackView()
stack.distribution = .fillEqually
stack.spacing = 8
stack.translatesAutoresizingMaskIntoConstraints = false
// add 20 views to the stack view for horizontal scrolling
for i in 1...20 {
let v = UILabel()
v.textAlignment = .center
v.backgroundColor = UIColor(red: 0.0, green: 1.0, blue: 1.0 - (CGFloat(i) * 0.05), alpha: 1.0)
v.text = "View \(i)"
stack.addArrangedSubview(v)
}
stack.arrangedSubviews.first?.widthAnchor.constraint(equalToConstant: 120).isActive = true
scrollContentView.translatesAutoresizingMaskIntoConstraints = false
scrollContentView.addSubview(stack)
scrollView.addSubview(scrollContentView)
let g = view.safeAreaLayoutGuide
let svCLG = scrollView.contentLayoutGuide
let svFLG = scrollView.frameLayoutGuide
NSLayoutConstraint.activate([
scrollView.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
scrollView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
scrollView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
scrollView.heightAnchor.constraint(equalToConstant: 120.0),
slider.topAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: 20.0),
slider.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
slider.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
slider.heightAnchor.constraint(equalToConstant: 40.0),
sliderBKG.topAnchor.constraint(equalTo: slider.topAnchor, constant: 6.0),
sliderBKG.leadingAnchor.constraint(equalTo: slider.leadingAnchor, constant: 0.0),
sliderBKG.trailingAnchor.constraint(equalTo: slider.trailingAnchor, constant: 0.0),
sliderBKG.bottomAnchor.constraint(equalTo: slider.bottomAnchor, constant: -6.0),
scrollContentView.topAnchor.constraint(equalTo: svCLG.topAnchor, constant: 0.0),
scrollContentView.leadingAnchor.constraint(equalTo: svCLG.leadingAnchor, constant: 0.0),
scrollContentView.trailingAnchor.constraint(equalTo: svCLG.trailingAnchor, constant: 0.0),
scrollContentView.bottomAnchor.constraint(equalTo: svCLG.bottomAnchor, constant: 0.0),
scrollContentView.heightAnchor.constraint(equalTo: svFLG.heightAnchor, constant: 0.0),
stack.topAnchor.constraint(equalTo: scrollContentView.topAnchor, constant: 8.0),
stack.leadingAnchor.constraint(equalTo: scrollContentView.leadingAnchor, constant: 8.0),
stack.trailingAnchor.constraint(equalTo: scrollContentView.trailingAnchor, constant: -8.0),
stack.bottomAnchor.constraint(equalTo: scrollContentView.bottomAnchor, constant: -8.0),
])
slider.addTarget(self, action: #selector(self.sliderChanged(_:)), for: .valueChanged)
// set slider images to clear images
slider.setThumbImage(UIImage(), for: [])
slider.setMinimumTrackImage(UIImage(), for: [])
slider.setMaximumTrackImage(UIImage(), for: [])
// so we can see the frame of the scroll view
scrollView.backgroundColor = .green
scrollContentView.backgroundColor = .orange
// set scrollView delegate
scrollView.delegate = self
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// after view has appeared (so we know auto-layout has finished)
// grab the content of the scroll view as a UIImage
let img = scvSnapshot()
// set it to the "background" image view behind the slider
sliderBKG.image = img
updateThumbImage()
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
coordinator.animate(alongsideTransition: { _ in
}, completion: {
_ in
self.updateThumbImage()
})
}
func updateThumbImage() -> Void {
// generate a proportional width bordered clear image
let w = self.scrollView.frame.width * (self.scrollView.frame.width / self.scrollView.contentSize.width)
let rect = CGRect(origin: .zero, size: CGSize(width: w, height: 40.0))
let renderer = UIGraphicsImageRenderer(size: rect.size)
let img = renderer.image { rc in
rc.cgContext.setFillColor(UIColor.clear.cgColor)
rc.cgContext.setStrokeColor(UIColor.red.cgColor)
rc.cgContext.setLineWidth(2)
rc.cgContext.addRect(rect)
rc.cgContext.drawPath(using: .fillStroke)
}
// set the slider's custom thumb image
self.slider.setThumbImage(img, for: [])
}
func scvSnapshot() -> UIImage {
let renderer = UIGraphicsImageRenderer(bounds: scrollContentView.bounds)
return renderer.image { rendererContext in
scrollContentView.layer.render(in: rendererContext.cgContext)
}
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
// we need to update the slider position when scroll view is manually scrolled
let w = scrollView.contentSize.width - scrollView.frame.size.width
let pct = scrollView.contentOffset.x / w
slider.value = Float(pct)
}
@objc func sliderChanged(_ sender: UISlider) -> Void {
// set scroll view's content offset x based on position of slider
let w = scrollView.contentSize.width - scrollView.frame.size.width
let x = w * CGFloat(sender.value)
scrollView.contentOffset.x = x
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.