使用 UIImage 滚动 UIScrollView
Ever scrolling UIScrollView with UIImage
我的应用程序中有一个水平的 UIScrollview,它有 1 个很长的 UIImageView 开始。我有一个计时器和动画来创建滚动视图下的图像自动滚动的错觉。图像结束后,我会动态地将相似的图像添加到滚动视图中,因此它看起来应该像图像在重复一样。
这就是我希望它们在滚动视图下的显示方式:image1|image2|image3|image4......这些图像将从右向左滚动。在您登录之前,它在 Behance 的 iphone 应用程序中的具体工作方式。
这是我的代码(在故事板中我有滚动视图和一个 UIIMageview 已经添加)。
override func viewDidAppear(_ animated: Bool) {
Timer.scheduledTimer(timeInterval: 0.6, target: self, selector: #selector(scrollImage), userInfo: nil, repeats: true)
}
@objc func scrollImage() {
offSet.x = offSet.x + CGFloat(20)
UIView.animate(withDuration: 1.0, animations: {
self.behanceView.setContentOffset(self.offSet, animated: false)
})
}
func addImagetoScrollView() {
let imageView = UIImageView(image:UIImage(named:"Landing_Scrollable"))
print(imageCount*Int(self.behanceView.contentSize.width)+100)
imageView.frame = CGRect(x:imageCount*Int(self.behanceView.contentSize.width), y: 0, width: 875, height: 502)
self.behanceView.contentSize = CGSize(width: imageView.bounds.size.width * CGFloat(imageCount), height: imageView.bounds.size.height)
self.behanceView.addSubview(imageView)
imageCount+=1
}
extension ViewController: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let scrollViewWidth = scrollView.frame.size.width;
let scrollOffset = scrollView.contentOffset.x;
print(imageCount*Int(self.behanceView.contentSize.width - scrollViewWidth))
if scrollOffset >= CGFloat(imageCount*Int(self.behanceView.contentSize.width - scrollViewWidth)) {
self.addImagetoScrollView()
}
}
}
但是当我看到它在运行时,它做了一些奇怪的事情并且动画全部关闭。
有人可以帮忙吗。
谢谢,
我从未见过“Behance”应用程序,但我猜你是在问如何无限期地在屏幕上设置无缝平铺背景图像的动画,如下所示:
(Pattern image by Evan Eckard.)
我在演示中使用了 1 秒的动画持续时间,但您可能希望在真实应用中使用更长的持续时间。
你不应该为此使用计时器。 Core Animation 可以为您执行动画,让它执行动画更流畅、更高效。 (你可能认为 Core Animation 正在执行你的动画,因为你正在使用 UIView
动画,但我相信动画滚动视图的 contentOffset
确实 而不是 使用 Core Animation 因为滚动视图必须在每个动画步骤中调用其委托的 scrollViewDidScroll
。)
您也不应该为此使用滚动视图。 UIScrollView
存在以允许用户滚动。由于您不允许用户滚动,因此不应使用 UIScrollView
.
设置背景的方法如下:
创建两个 个相同的图像视图(编号为 0 和 1),显示相同的图像。确保每个图像视图都足够大以填满屏幕。
将图像视图 0 的左边缘放在根视图的左边缘。将 image view 1 的左边缘放在 image view 0 的右边缘。由于每个 image view 都足够大以填满屏幕,因此 image view 1 将完全从屏幕的右边缘开始。
动画图像视图 0 的 transform.translation.x
从 0 到 -imageView.bounds.size.width
。这将使它向左滑动恰好是它自己的宽度,所以当动画结束时,图像视图 0 的右边缘位于屏幕的左边缘(因此图像视图 0 完全不在屏幕的左边缘) ).将动画的 repeatCount
设置为 .infinity
.
将相同的动画添加到图像视图 1。因此图像视图 1 在图像视图 0 离开时出现在屏幕上,正好覆盖图像视图 0 的动画显示的像素。
两个动画同时结束。当它们结束时,图像视图 1 恰好位于图像视图 0 开始时的位置。由于两个动画都设置为无限重复,因此它们都立即重新开始。当 image view 0 的动画重新开始时,image view 0 立即跳回到它的起始位置,也就是 image view 1 结束的位置。由于两个图像视图显示相同的图像,因此屏幕上的像素不会改变。这使得动画循环无缝。
这是我的代码:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
for imageView in imageViews {
imageView.image = patternImage
imageView.contentMode = .scaleToFill
view.addSubview(imageView)
}
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let bounds = view.bounds
let patternSize = patternImage.size
// Scale the image up if necessary to be at least as big as the screen on both axes.
// But make sure scale is at least 1 so I don't shrink the image if it's larger than the screen.
let scale = max(1 as CGFloat, bounds.size.width / patternSize.width, bounds.size.height / patternSize.height)
let imageFrame = CGRect(x: 0, y: 0, width: scale * patternSize.width, height: scale * patternSize.height)
for (i, imageView) in imageViews.enumerated() {
imageView.frame = imageFrame.offsetBy(dx: CGFloat(i) * imageFrame.size.width, dy: 0)
let animation = CABasicAnimation(keyPath: "transform.translation.x")
animation.fromValue = 0
animation.toValue = -imageFrame.size.width
animation.duration = 1
animation.repeatCount = .infinity
animation.timingFunction = CAMediaTimingFunction(name: .linear)
// The following line prevents iOS from removing the animation when the app goes to the background.
animation.isRemovedOnCompletion = false
imageView.layer.add(animation, forKey: animation.keyPath)
}
}
private let imageViews: [UIImageView] = [.init(), .init()]
private let patternImage = #imageLiteral(resourceName: "pattern")
}
我的应用程序中有一个水平的 UIScrollview,它有 1 个很长的 UIImageView 开始。我有一个计时器和动画来创建滚动视图下的图像自动滚动的错觉。图像结束后,我会动态地将相似的图像添加到滚动视图中,因此它看起来应该像图像在重复一样。
这就是我希望它们在滚动视图下的显示方式:image1|image2|image3|image4......这些图像将从右向左滚动。在您登录之前,它在 Behance 的 iphone 应用程序中的具体工作方式。
这是我的代码(在故事板中我有滚动视图和一个 UIIMageview 已经添加)。
override func viewDidAppear(_ animated: Bool) {
Timer.scheduledTimer(timeInterval: 0.6, target: self, selector: #selector(scrollImage), userInfo: nil, repeats: true)
}
@objc func scrollImage() {
offSet.x = offSet.x + CGFloat(20)
UIView.animate(withDuration: 1.0, animations: {
self.behanceView.setContentOffset(self.offSet, animated: false)
})
}
func addImagetoScrollView() {
let imageView = UIImageView(image:UIImage(named:"Landing_Scrollable"))
print(imageCount*Int(self.behanceView.contentSize.width)+100)
imageView.frame = CGRect(x:imageCount*Int(self.behanceView.contentSize.width), y: 0, width: 875, height: 502)
self.behanceView.contentSize = CGSize(width: imageView.bounds.size.width * CGFloat(imageCount), height: imageView.bounds.size.height)
self.behanceView.addSubview(imageView)
imageCount+=1
}
extension ViewController: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let scrollViewWidth = scrollView.frame.size.width;
let scrollOffset = scrollView.contentOffset.x;
print(imageCount*Int(self.behanceView.contentSize.width - scrollViewWidth))
if scrollOffset >= CGFloat(imageCount*Int(self.behanceView.contentSize.width - scrollViewWidth)) {
self.addImagetoScrollView()
}
}
}
但是当我看到它在运行时,它做了一些奇怪的事情并且动画全部关闭。
有人可以帮忙吗。
谢谢,
我从未见过“Behance”应用程序,但我猜你是在问如何无限期地在屏幕上设置无缝平铺背景图像的动画,如下所示:
(Pattern image by Evan Eckard.)
我在演示中使用了 1 秒的动画持续时间,但您可能希望在真实应用中使用更长的持续时间。
你不应该为此使用计时器。 Core Animation 可以为您执行动画,让它执行动画更流畅、更高效。 (你可能认为 Core Animation 正在执行你的动画,因为你正在使用 UIView
动画,但我相信动画滚动视图的 contentOffset
确实 而不是 使用 Core Animation 因为滚动视图必须在每个动画步骤中调用其委托的 scrollViewDidScroll
。)
您也不应该为此使用滚动视图。 UIScrollView
存在以允许用户滚动。由于您不允许用户滚动,因此不应使用 UIScrollView
.
设置背景的方法如下:
创建两个 个相同的图像视图(编号为 0 和 1),显示相同的图像。确保每个图像视图都足够大以填满屏幕。
将图像视图 0 的左边缘放在根视图的左边缘。将 image view 1 的左边缘放在 image view 0 的右边缘。由于每个 image view 都足够大以填满屏幕,因此 image view 1 将完全从屏幕的右边缘开始。
动画图像视图 0 的
transform.translation.x
从 0 到-imageView.bounds.size.width
。这将使它向左滑动恰好是它自己的宽度,所以当动画结束时,图像视图 0 的右边缘位于屏幕的左边缘(因此图像视图 0 完全不在屏幕的左边缘) ).将动画的repeatCount
设置为.infinity
.将相同的动画添加到图像视图 1。因此图像视图 1 在图像视图 0 离开时出现在屏幕上,正好覆盖图像视图 0 的动画显示的像素。
两个动画同时结束。当它们结束时,图像视图 1 恰好位于图像视图 0 开始时的位置。由于两个动画都设置为无限重复,因此它们都立即重新开始。当 image view 0 的动画重新开始时,image view 0 立即跳回到它的起始位置,也就是 image view 1 结束的位置。由于两个图像视图显示相同的图像,因此屏幕上的像素不会改变。这使得动画循环无缝。
这是我的代码:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
for imageView in imageViews {
imageView.image = patternImage
imageView.contentMode = .scaleToFill
view.addSubview(imageView)
}
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let bounds = view.bounds
let patternSize = patternImage.size
// Scale the image up if necessary to be at least as big as the screen on both axes.
// But make sure scale is at least 1 so I don't shrink the image if it's larger than the screen.
let scale = max(1 as CGFloat, bounds.size.width / patternSize.width, bounds.size.height / patternSize.height)
let imageFrame = CGRect(x: 0, y: 0, width: scale * patternSize.width, height: scale * patternSize.height)
for (i, imageView) in imageViews.enumerated() {
imageView.frame = imageFrame.offsetBy(dx: CGFloat(i) * imageFrame.size.width, dy: 0)
let animation = CABasicAnimation(keyPath: "transform.translation.x")
animation.fromValue = 0
animation.toValue = -imageFrame.size.width
animation.duration = 1
animation.repeatCount = .infinity
animation.timingFunction = CAMediaTimingFunction(name: .linear)
// The following line prevents iOS from removing the animation when the app goes to the background.
animation.isRemovedOnCompletion = false
imageView.layer.add(animation, forKey: animation.keyPath)
}
}
private let imageViews: [UIImageView] = [.init(), .init()]
private let patternImage = #imageLiteral(resourceName: "pattern")
}