UIScrollView 中的 UIPageViewController 显示全屏,即使设置了 CGRect - returns 以在用户滚动时立即更正帧值
UIPageViewController within a UIScrollView appears fullscreen even though CGRect is set - returns to correct frame values as soon as user scrolls
我有一个名为 SwipingPhotosController 的 UIPageViewController,用户可以在其中水平滑动以查看图像。我已经在 SwipingPhotosController 中实现了这个 UIScrollView 通过给它 CGRect 值。然后我编写了一个函数,当用户向上或向下滚动时,它基本上可以实现放大和缩小的 弹性 header 效果 。
一切正常,除了当我尝试 在 SwipingPhotosController 下添加另一个视图时,一旦加载控制器,图像出现全屏。只要我稍微滚动一下,它就会 returns 到准确的位置。
这是我在模拟器中按 运行 时得到的结果 - 全屏显示 pageviewcontroller:
稍微滚动一下,视图就会恢复正常
注意:仅当我在 imageView 下方添加 nameLabel 时才会出现此错误 (swipingPhotosController.view)
class ProfileController: UIViewController, UIScrollViewDelegate {
var user: User! {
didSet{
swipingPhotosController.user = user
}
}
lazy var scrollProfileView: UIScrollView = {
let sv = UIScrollView()
sv.delegate = self
sv.backgroundColor = TDGSettings
sv.alwaysBounceVertical = true
sv.contentInsetAdjustmentBehavior = .never
return sv
}()
let nameLabel: UILabel = {
let label = UILabel()
label.font = .systemFont(ofSize: 12, weight: .bold)
label.textColor = .white
label.numberOfLines = 2
label.textAlignment = .left
return label
}()
let swipingPhotosController = SwipingPhotosController(transitionStyle: .scroll, navigationOrientation: .horizontal)
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = TDGSettings
setupViews()
}
fileprivate func setupViews() {
view.addSubview(scrollProfileView)
scrollProfileView.fillSuperview()
let imageView = swipingPhotosController.view!
scrollProfileView.addSubview(imageView)
let blurEffect = UIBlurEffect(style: .systemThinMaterialDark)
let visualEffectView = UIVisualEffectView(effect: blurEffect)
view.addSubview(visualEffectView)
visualEffectView.anchor(top: view.topAnchor, leading: view.leadingAnchor, bottom: view.safeAreaLayoutGuide.topAnchor, trailing: view.trailingAnchor)
scrollProfileView.addSubview(nameLabel)
nameLabel.anchor(top: imageView.bottomAnchor, leading: view.leadingAnchor, bottom: nil, trailing: view.trailingAnchor, padding: .init(top: 16, left: 16, bottom: 0, right: 16))
nameLabel.text = "First Name"
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
let imageView = swipingPhotosController.view!
imageView.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: view.frame.width)
}
//STRETCHY HEADER EFFECT
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let changeY = -scrollView.contentOffset.y
var width = view.frame.width + changeY * 2
width = max(view.frame.width, width)
let imageView = swipingPhotosController.view!
imageView.frame = CGRect(x: min(0, -changeY), y: min(0, -changeY), width: width, height: width)
}
}
根据要求为 SwipingPhotosController 添加代码
import Foundation
import LBTATools
class SwipingPhotosController: UIPageViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate {
var user: User! {
didSet{
controllers = user.swipingUrls.map({ (url) -> UIViewController in
let photoController = PhotosController(imageUrl: url)
return photoController
})
setViewControllers([controllers.first!], direction: .forward, animated: false, completion: nil)
}
}
var controllers = [UIViewController]()
override func viewDidLoad() {
super.viewDidLoad()
dataSource = self
delegate = self
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
let index = self.controllers.firstIndex(where: {[=12=] == viewController}) ?? 0
if index == 0 {return nil}
return controllers[index - 1]
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
let index = self.controllers.firstIndex(where: {[=12=] == viewController}) ?? 0
if index == controllers.count - 1 {return nil}
return controllers[index + 1]
}
}
class PhotosController: UIViewController {
let imageView = UIImageView()
init(imageUrl: String) {
if let url = URL(string: imageUrl){
imageView.sd_setImage(with: url)
}
super.init(nibName: nil, bundle: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
imageView.contentMode = .scaleAspectFill
view.addSubview(imageView)
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
imageView.fillSuperview()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
问题是您正在 viewWillLayoutSubviews()
中设置 swipingPhotosController
的框架 --- 但视图尚未布局。
您需要在 viewDidLayoutSubviews()
中执行此操作(已执行 而不是 ).
但是,viewDidLayoutSubviews()
被调用了很多次,特别是因为您在 scrollViewDidScroll()
中再次更改了框架。
因此,您需要设置一个标志,仅在 viewDidLayoutSubviews()
中设置一次框架(或再次,如果 scrollView 框架已更改)。
不确定您如何或何时设置 SwipingPhotosController
的属性,我这样做是为了测试:
// add a class property
var savedScrollViewWidth: CGFloat = 0.0
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if scrollProfileView.frame.width != savedScrollViewWidth {
savedScrollViewWidth = scrollProfileView.frame.width
let imageView = swipingPhotosController.view!
imageView.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: view.frame.width)
}
}
看看这是否能让您的布局正常工作。
我有一个名为 SwipingPhotosController 的 UIPageViewController,用户可以在其中水平滑动以查看图像。我已经在 SwipingPhotosController 中实现了这个 UIScrollView 通过给它 CGRect 值。然后我编写了一个函数,当用户向上或向下滚动时,它基本上可以实现放大和缩小的 弹性 header 效果 。
一切正常,除了当我尝试 在 SwipingPhotosController 下添加另一个视图时,一旦加载控制器,图像出现全屏。只要我稍微滚动一下,它就会 returns 到准确的位置。
这是我在模拟器中按 运行 时得到的结果 - 全屏显示 pageviewcontroller:
稍微滚动一下,视图就会恢复正常
注意:仅当我在 imageView 下方添加 nameLabel 时才会出现此错误 (swipingPhotosController.view)
class ProfileController: UIViewController, UIScrollViewDelegate {
var user: User! {
didSet{
swipingPhotosController.user = user
}
}
lazy var scrollProfileView: UIScrollView = {
let sv = UIScrollView()
sv.delegate = self
sv.backgroundColor = TDGSettings
sv.alwaysBounceVertical = true
sv.contentInsetAdjustmentBehavior = .never
return sv
}()
let nameLabel: UILabel = {
let label = UILabel()
label.font = .systemFont(ofSize: 12, weight: .bold)
label.textColor = .white
label.numberOfLines = 2
label.textAlignment = .left
return label
}()
let swipingPhotosController = SwipingPhotosController(transitionStyle: .scroll, navigationOrientation: .horizontal)
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = TDGSettings
setupViews()
}
fileprivate func setupViews() {
view.addSubview(scrollProfileView)
scrollProfileView.fillSuperview()
let imageView = swipingPhotosController.view!
scrollProfileView.addSubview(imageView)
let blurEffect = UIBlurEffect(style: .systemThinMaterialDark)
let visualEffectView = UIVisualEffectView(effect: blurEffect)
view.addSubview(visualEffectView)
visualEffectView.anchor(top: view.topAnchor, leading: view.leadingAnchor, bottom: view.safeAreaLayoutGuide.topAnchor, trailing: view.trailingAnchor)
scrollProfileView.addSubview(nameLabel)
nameLabel.anchor(top: imageView.bottomAnchor, leading: view.leadingAnchor, bottom: nil, trailing: view.trailingAnchor, padding: .init(top: 16, left: 16, bottom: 0, right: 16))
nameLabel.text = "First Name"
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
let imageView = swipingPhotosController.view!
imageView.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: view.frame.width)
}
//STRETCHY HEADER EFFECT
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let changeY = -scrollView.contentOffset.y
var width = view.frame.width + changeY * 2
width = max(view.frame.width, width)
let imageView = swipingPhotosController.view!
imageView.frame = CGRect(x: min(0, -changeY), y: min(0, -changeY), width: width, height: width)
}
}
根据要求为 SwipingPhotosController 添加代码
import Foundation
import LBTATools
class SwipingPhotosController: UIPageViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate {
var user: User! {
didSet{
controllers = user.swipingUrls.map({ (url) -> UIViewController in
let photoController = PhotosController(imageUrl: url)
return photoController
})
setViewControllers([controllers.first!], direction: .forward, animated: false, completion: nil)
}
}
var controllers = [UIViewController]()
override func viewDidLoad() {
super.viewDidLoad()
dataSource = self
delegate = self
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
let index = self.controllers.firstIndex(where: {[=12=] == viewController}) ?? 0
if index == 0 {return nil}
return controllers[index - 1]
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
let index = self.controllers.firstIndex(where: {[=12=] == viewController}) ?? 0
if index == controllers.count - 1 {return nil}
return controllers[index + 1]
}
}
class PhotosController: UIViewController {
let imageView = UIImageView()
init(imageUrl: String) {
if let url = URL(string: imageUrl){
imageView.sd_setImage(with: url)
}
super.init(nibName: nil, bundle: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
imageView.contentMode = .scaleAspectFill
view.addSubview(imageView)
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
imageView.fillSuperview()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
问题是您正在 viewWillLayoutSubviews()
中设置 swipingPhotosController
的框架 --- 但视图尚未布局。
您需要在 viewDidLayoutSubviews()
中执行此操作(已执行 而不是 ).
但是,viewDidLayoutSubviews()
被调用了很多次,特别是因为您在 scrollViewDidScroll()
中再次更改了框架。
因此,您需要设置一个标志,仅在 viewDidLayoutSubviews()
中设置一次框架(或再次,如果 scrollView 框架已更改)。
不确定您如何或何时设置 SwipingPhotosController
的属性,我这样做是为了测试:
// add a class property
var savedScrollViewWidth: CGFloat = 0.0
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if scrollProfileView.frame.width != savedScrollViewWidth {
savedScrollViewWidth = scrollProfileView.frame.width
let imageView = swipingPhotosController.view!
imageView.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: view.frame.width)
}
}
看看这是否能让您的布局正常工作。