UICollectionView ScrollTo Crashing - swift

I am attempting to have my UICollectionView in my custom UIView, be able to scroll to a page when a button is pressed in the main VC. Unfortunately everytime I call the method to do it in the MainVC, it crashes, # to scrollToItem .
here is my MainVC.
let mainView = MainViewsHome()
In the ViewWillAppear:
/** Setting up the bottom half **/
mainView.frame = CGRect(x:self.view.frame.width * 0, y:self.view.frame.height / 6.2, width:self.view.frame.width,height:self.view.frame.height / 1.1925)
mainView.backgroundColor = UIColor.clear
self.view.addSubview(mainView)
&& My custom UIView
class MainViewsHome: UIView, UIGestureRecognizerDelegate, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
var cellId = "Cell"
var collectionView : UICollectionView!
override init(frame: CGRect) {
super.init(frame: CGRect(x:0, y:0, width:UIScreen.main.bounds.width, height: UIScreen.main.bounds.height / 1.3))
/**Creation of the View **/
let flowLayout : UICollectionViewFlowLayout = UICollectionViewFlowLayout()
flowLayout.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
flowLayout.scrollDirection = .horizontal
let collectionView = UICollectionView(frame: CGRect(x:self.frame.width * 0,y:self.frame.height * 0,width:self.frame.width,height: self.frame.height), collectionViewLayout: flowLayout)
collectionView.register(uploadGenreSelectionCVC.self, forCellWithReuseIdentifier: cellId)
collectionView.isPagingEnabled = true
collectionView.delegate = self
collectionView.dataSource = self
collectionView.backgroundColor = UIColor.purple
self.addSubview(collectionView)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func moveToPage() {
print("we are moving to page")
self.collectionView.scrollToItem(at: 2 as IndexPath, at: UICollectionViewScrollPosition.right, animated: true)
}
&& Then in my MainVC I am calling: mainView.moveToPage() Where am I going wrong with my understanding?

Problem is with 2 as IndexPath, IndexPath is not Integer so you need to make the object of IndexPath using its init init(item:section:). and after that scroll collectionView to specific item this way.
func moveToPage() {
let indexPath = IndexPath(item: 2, section: 0) //Change section from 0 to other if you are having multiple sections
self.collectionView.scrollToItem(at: indexPath, at: .right, animated: true)
}
Note: CollectionView's items start at 0 index so if you want to scroll to 2nd item then make indexPath using IndexPath(item: 1, section: 0) because 2nd will scroll at 3rd cell of collectionView.

Related

Is there a way to give outer border in every section of uicollctionview?

Try to add outer border of every section in a collection view.
If i'm using cell.layer.border, it will also create an inner border. Is there a simple way to create outer border only for every section in collection view?
Try to created red border like image below
As Matt pointed out in the comments and the articles pointed out, you would need to make use of a DecorationView.
You can read up on this here
So to do this, you would have to follow these steps:
Create a custom UICollectionReusableView which would serve as your decoration view
Subclass UICollectionViewFlowLayout to create a custom layout
Override layoutAttributesForDecorationView and layoutAttributesForElements to figure out the frame of each section and place the decoration view in the section frame
Use the custom flow layout as the layout of your collection view
Here is that in code
Create the Decoration view, which is just a regular view with a border
class SectionBackgroundView : UICollectionReusableView {
static let DecorationViewKind = "SectionBackgroundIdentifier"
override init(frame: CGRect) {
super.init(frame: frame)
// Customize the settings to what you want
backgroundColor = .clear
layer.borderWidth = 5.0
layer.borderColor = UIColor.blue.cgColor
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Create a custom flow layout
class BorderedFlowLayout: UICollectionViewFlowLayout {
override init() {
super.init()
// Register your decoration view for the layout
register(SectionBackgroundView.self,
forDecorationViewOfKind: SectionBackgroundView.DecorationViewKind)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutAttributesForDecorationView(ofKind elementKind: String,
at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
if elementKind == SectionBackgroundView.DecorationViewKind {
guard let collectionView = collectionView else { return nil }
// Initialize a UICollectionViewLayoutAttributes for a DecorationView
let decorationAttributes
= UICollectionViewLayoutAttributes(forDecorationViewOfKind: SectionBackgroundView.DecorationViewKind,
with:indexPath)
// Set it behind other views
decorationAttributes.zIndex = 2
let numberOfItemsInSection
= collectionView.numberOfItems(inSection: indexPath.section)
// Get the first and last item in the section
let firstItem = layoutAttributesForItem(at: IndexPath(item: 0, section: indexPath.section))
let lastItem = layoutAttributesForItem(at: IndexPath(item: (numberOfItemsInSection - 1),
section: indexPath.section))
// The difference between the maxY of the last item and
// the the minY of the first item is the height of the section
let height = lastItem!.frame.maxY - firstItem!.frame.minY
// Set the frame of the decoration view for the section
decorationAttributes.frame = CGRect(x: 0,
y: firstItem!.frame.minY,
width: collectionView.bounds.width,
height: height)
return decorationAttributes
}
return nil
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
// Get all the UICollectionViewLayoutAttributes for the current view port
var attributes = super.layoutAttributesForElements(in: rect)
// Filter to get all the different sections
let sectionAttributes
= attributes?.filter { $0.indexPath.item == 0 } ?? []
// Loop through the different sections
for sectionAttribute in sectionAttributes {
// Create decoration attributes for the current section
if let decorationAttributes
= self.layoutAttributesForDecorationView(ofKind: SectionBackgroundView.DecorationViewKind,
at: sectionAttribute.indexPath) {
// Add the decoration attributes for a section if it is in the current viewport
if rect.intersects(decorationAttributes.frame) {
attributes?.append(decorationAttributes)
}
}
}
return attributes
}
}
Make use of the custom layout in your view controller
private func configureCollectionView() {
collectionView = UICollectionView(frame: CGRect.zero,
collectionViewLayout: createLayout())
collectionView.backgroundColor = .white
collectionView.register(UICollectionViewCell.self,
forCellWithReuseIdentifier: "cell")
// You can ignore the header and footer views as you probably already did this
collectionView.register(HeaderFooterView.self,
forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader,
withReuseIdentifier: HeaderFooterView.identifier)
collectionView.register(HeaderFooterView.self,
forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter,
withReuseIdentifier: HeaderFooterView.identifier)
collectionView.dataSource = self
collectionView.delegate = self
view.addSubview(collectionView)
}
private func createLayout() -> UICollectionViewFlowLayout {
let flowLayout = BorderedFlowLayout()
flowLayout.minimumLineSpacing = 10
flowLayout.minimumInteritemSpacing = 10
flowLayout.scrollDirection = .vertical
flowLayout.sectionInset = UIEdgeInsets(top: 10,
left: horizontalPadding,
bottom: 10,
right: horizontalPadding)
return flowLayout
}
Doing all of this should give you what you want
I have only posted the most important snippets. If for some reason you can't follow along, here is the full code to recreate the example

Self sizing dynamic UICollectionViewCell height improperly sizes itself after calling reloadData()

I have a UICollectionView that sizes cells heights automatically so depending on how much text is inside the cell it will size the height appropriately.
This works perfectly fine I can click all the buttons, scroll up or down, etc, but the problem is that when I call reloadData(), the collectionViewCell's constraints get screwed up and they stack on top of each other for some reason.
Here is a picture of the collectionView before reloadData() is called:
Here is a picture of the collectionVIew after I call reloadData():
Anyone possibly know why this is happening and how I can fix it?
Here is my code for the CustomCollectionView:
class CustomCollectionView: UICollectionView {
public let bottomRefresh = CollectionViewBottomRefresh()
init() {
let layout = UICollectionViewFlowLayout()
layout.minimumLineSpacing = 0
layout.minimumInteritemSpacing = 0
layout.scrollDirection = .vertical
layout.estimatedItemSize = CGSize(width: UIScreen.main.bounds.width, height: 50)
super.init(frame: .zero, collectionViewLayout: layout)
alwaysBounceVertical = true
backgroundColor = .systemBackground
delaysContentTouches = false
showsVerticalScrollIndicator = false
register(PostView.self, forCellWithReuseIdentifier: "post")
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func touchesShouldCancel(in view: UIView) -> Bool {
if view is UIButton || view is UITextField {
return true
}
return super.touchesShouldCancel(in: view)
}
}
Here is my code for CollectionViewCell:
class PostView: UICollectionViewCell, {
override init(frame: CGRect) {
super.init(frame: frame)
contentView.addSubview(commentsButton)
contentView.addSubview(kuduAppTeamDeleteButton)
contentView.addSubview(titleLabel)
contentView.addSubview(infoButton)
contentView.addSubview(imageViewButton)
contentView.addSubview(likeButton)
contentView.addSubview(followButton)
contentView.addSubview(profile)
contentView.addSubview(likeCount)
contentView.addSubview(date)
contentView.addSubview(line)
addConstraints()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
public func setupView(post: Post) {
guard let post = fb.posts.firstIndex(where: { p in p.id == post.id }) else { return }
self.post = post
guard let user = fb.users.firstIndex(where: { user in user.id == fb.posts[post].uid }) else { return }
self.user = user
if fb.currentUser.likes.contains(fb.posts[post].id) {
self.likeButton.setImage(UIImage(systemName: "hand.thumbsup.fill"), for: .normal)
self.likeButton.tintColor = UIColor.theme.blueColor
} else {
self.likeButton.setImage(UIImage(systemName: "hand.thumbsup"), for: .normal)
self.likeButton.tintColor = .label
}
let result = String(format: "%ld %#", locale: Locale.current, fb.posts[post].likeCount, "")
likeCount.text = result
//Button Actions
infoButton.addAction(infoButtonAction, for: .touchUpInside)
likeButton.addAction(likeButtonAction, for: .touchUpInside)
followButton.addAction(followButtonAction, for: .touchUpInside)
imageViewButton.addAction(imageViewButtonAction, for: .touchUpInside)
profile.addAction(profileAction, for: .touchUpInside)
commentsButton.addAction(commentsButtonAction, for: .touchUpInside)
kuduAppTeamDeleteButton.addAction(kuduAppTeamDeleteButtonAction, for: .touchUpInside)
//Date
let dateFormatter = DateFormatter()
dateFormatter.timeStyle = .none
dateFormatter.dateStyle = .long
let dateString = dateFormatter.string(from: fb.posts[post].date)
date.text = dateString
//Set follow button text
if self.fb.currentUser.following.contains(fb.users[user].id) {
self.followButton.label.text = "Unfollow"
} else {
self.followButton.label.text = "Follow"
}
//Set imageview image
imageViewButton.setImage(fb.posts[post].image, for: .normal)
imageViewButton.imageView!.contentMode = .scaleAspectFill
//Set user image
profile.usernameLabel.text = fb.users[user].username
profile.profileImage.image = fb.users[user].profileImage
if profile.profileImage.image == UIImage(systemName: "person.circle.fill") {
profile.profileImage.tintColor = UIColor.theme.accentColor
}
//Set post title
titleLabel.text = fb.posts[post].title
}
override func prepareForReuse() {
//Remove all actions
infoButton.removeAction(infoButtonAction, for: .touchUpInside)
likeButton.removeAction(likeButtonAction, for: .touchUpInside)
imageViewButton.removeAction(imageViewButtonAction, for: .touchUpInside)
profile.removeAction(profileAction, for: .touchUpInside)
commentsButton.removeAction(commentsButtonAction, for: .touchUpInside)
followButton.removeAction(followButtonAction, for: .touchUpInside)
kuduAppTeamDeleteButton.removeAction(kuduAppTeamDeleteButtonAction, for: .touchUpInside)
//Remove any other text or images
for subview in imageViewButton.subviews {
if let subview = subview as? UIImageView, subview.image == UIImage(systemName: "play.circle.fill") {
subview.removeFromSuperview()
}
}
imageViewButton.setImage(nil, for: .normal)
kuduAppTeamDeleteButton.color = nil
profile.profileImage.image = nil
titleLabel.text = nil
date.text = nil
self.followButton.label.text = nil
}
// Sets a requried width and a dynamic height that changes depending on what is in the cell. So we can have searchbar as first cell heigh 50, and post in other cells with height of view.bounds.width.
override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
let targetSize = CGSize(width: layoutAttributes.frame.width, height: 0)
layoutAttributes.frame.size = contentView.systemLayoutSizeFitting(targetSize, withHorizontalFittingPriority: .required, verticalFittingPriority: .fittingSizeLevel)
return layoutAttributes
}
private func addConstraints() {
kuduAppTeamDeleteButton.height(30)
kuduAppTeamDeleteButton.width(UIScreen.main.bounds.width / 4)
kuduAppTeamDeleteButton.bottom(to: commentsButton)
kuduAppTeamDeleteButton.leftToRight(of: commentsButton, offset: 5)
imageViewButton.width(UIScreen.main.bounds.width)
imageViewButton.height(UIScreen.main.bounds.width * 9/16)
imageViewButton.topToSuperview()
infoButton.leftToRight(of: titleLabel, offset: 6)
infoButton.topToBottom(of: imageViewButton, offset: 15)
infoButton.width(30)
infoButton.height(30)
titleLabel.horizontalToSuperview(insets: UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 40))
titleLabel.topToBottom(of: imageViewButton, offset: 5)
titleLabel.height(min: 50)
likeButton.topToBottom(of: titleLabel, offset: 10)
likeButton.trailingToSuperview(offset: 10)
likeButton.height(32)
likeButton.width(32)
profile.leadingToSuperview(offset: 5)
profile.topToBottom(of: titleLabel, offset: 10)
profile.widthToSuperview(multiplier: 0.4)
likeCount.trailingToLeading(of: likeButton, offset: -5)
likeCount.topToBottom(of: titleLabel, offset: 15)
followButton.topToBottom(of: titleLabel, offset: 5)
followButton.trailingToLeading(of: likeCount, offset: -10)
followButton.height(50)
followButton.width(UIScreen.main.bounds.width / 4)
date.bottom(to: commentsButton, offset: -5)
date.trailingToSuperview(offset: 5)
commentsButton.topToBottom(of: profile, offset: 10)
commentsButton.leadingToSuperview(offset: 5)
line.horizontalToSuperview()
line.bottom(to: commentsButton)
line.height(1)
contentView.bottom(to: line)
contentView.widthToSuperview()
}
}
Thanks in advance!
Finally figured how you can fix this problem if anyone else is having this issue.
So what I did was I decided to use a UITableView instead of a UICollectionView. When you change to a UITableView you can access the variableUITableView.automaticDimension.
Side note: You can only use a UITableView if you have one column.
So this is what I did for my UITableView:
class MyTableView: UITableView {
public let bottomRefresh = TableViewBottomRefresh()
init() {
super.init(frame: .zero, style: .plain)
rowHeight = UITableView.automaticDimension
estimatedRowHeight = 500
separatorStyle = .none
allowsSelection = false
delaysContentTouches = false
alwaysBounceVertical = true
showsVerticalScrollIndicator = false
register(MyTableViewCell.self, forCellReuseIdentifier: "MyCell")
}
}
Then you need to create a containerView that you can later put all the cells contents inside:
private let containerView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
Add contentView.addsubview(containerView) and put all cell contents inside the containerView so your cell can automatically size itself:
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
contentView.addSubview(containerView)
containerView.addSubview(profileImage)
containerView.addSubview(username)
containerView.addSubview(editProfileButton)
addConstraints()
}
addconstraints() func :
private func addConstraints() {
profileImage.topToSuperview()
profileImage.centerXToSuperview()
profileImage.widthToSuperview(multiplier: 1/2)
profileImage.height(UIScreen.main.bounds.width / 2)
username.topToBottom(of: profileImage, offset: 10)
username.horizontalToSuperview()
username.height(50)
editProfileButton.height(50)
editProfileButton.widthToSuperview(multiplier: 1/3)
editProfileButton.centerXToSuperview()
editProfileButton.topToBottom(of: username, offset: 10)
containerView.widthToSuperview()
containerView.bottom(to: editProfileButton)
}
Hopefully this helps someone! If anyone ever figures out how to call reloadData() with a dynamic collectionViewCell height without screwing up the cell constraints let me know! I tried for 2 weeks and still never found a solution :(.

CollectionView Cells are not visible

Using Swift 5.1.3, iOS13.3,
I am trying to display a horizontal CollectionViewController as a ChildView Controller.
The issue: All Cells are invisible !
The print-statement in the Code (shown with lots of !!!!!!!! in the comment below)
Test.CardHeaderCell: 0x7fa563709370; baseClass = UICollectionViewCell; frame = (1059 23; 343 279); hidden = YES; layer = <CALayer: 0x6000024de080>>
Here is the code:
override func viewDidLoad() {
// ...
let cardsHorizontalController = CardsHorizontalController()
self.addChild(cardsHorizontalController)
self.view.addSubview(cardsHorizontalController.view)
self.didMove(toParent: cardsHorizontalController)
cardsHorizontalController.view.translatesAutoresizingMaskIntoConstraints = false
cardsHorizontalController.view.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 70.0).isActive = true
cardsHorizontalController.view.heightAnchor.constraint(equalToConstant: 390.0).isActive = true
cardsHorizontalController.view.widthAnchor.constraint(equalToConstant: UIScreen.main.bounds.width).isActive = true
}
Here is the HorizontalController
class CardsHorizontalController: HorizontalSnappingController, UICollectionViewDelegateFlowLayout {
let cellId = "horizontalCardID"
override func viewDidLoad() {
super.viewDidLoad()
collectionView.backgroundColor = .green
collectionView.register(CardHeaderCell.self, forCellWithReuseIdentifier: cellId)
if let layout = collectionViewLayout as? UICollectionViewFlowLayout {
layout.scrollDirection = .horizontal
}
collectionView.showsHorizontalScrollIndicator = true
collectionView.showsVerticalScrollIndicator = false
// do the insets here instead of the optional "insetForSectionAt" delegate-method
// i.e. this helps to get the scrolled cells aligned in the middle of the screen once 1 cell wide scrolled...
collectionView.contentInset = .init(top: 65.0, left: 16.0, bottom: 0.0, right: 16.0)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return .init(width: view.frame.width - 32.0, height: 279.0)
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 17
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath)
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
print(cell)
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
return cell
}
}
And the Cell:
class CardHeaderCell: UICollectionViewCell {
let imageView = UIImageView(cornerRadius: 10.0)
override init(frame: CGRect) {
super.init(frame: frame)
// blur view
imageView.backgroundColor = UIColor(red: 0.086, green: 0.086, blue: 0.086, alpha: 0.35)
let visualEffectView = UIVisualEffectView(frame: imageView.frame)
visualEffectView.effect = UIView.customBlurEffect()
visualEffectView.autoresizingMask = UIView.AutoresizingMask(rawValue: UIView.AutoresizingMask.flexibleWidth.rawValue | UIView.AutoresizingMask.flexibleHeight.rawValue)
imageView.addSubview(visualEffectView)
let stackView = VerticalStackView(arrangedSubviews: [
imageView
], spacing: 12.0)
addSubview(stackView)
stackView.fillSuperview(padding: .init(top: 0.0, left: 0.0, bottom: 0.0, right: 0.0))
}
required init?(coder aDecoder: NSCoder) {
fatalError()
}
}
And for completeness reasons, here the rest of the custom classes...
class HorizontalSnappingController: UICollectionViewController {
init() {
let layout = BetterSnappingLayout()
layout.scrollDirection = .horizontal
super.init(collectionViewLayout: layout)
collectionView.decelerationRate = .fast
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class BetterSnappingLayout: UICollectionViewFlowLayout {
override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {
guard let collectionView = collectionView else {
return super.targetContentOffset(forProposedContentOffset: proposedContentOffset, withScrollingVelocity: velocity)
}
let nextX: CGFloat
if proposedContentOffset.x <= 0 || collectionView.contentOffset == proposedContentOffset {
nextX = proposedContentOffset.x
} else {
nextX = collectionView.contentOffset.x + (velocity.x > 0 ? collectionView.bounds.size.width : -collectionView.bounds.size.width)
}
let targetRect = CGRect(x: nextX, y: 0, width: collectionView.bounds.size.width, height: collectionView.bounds.size.height)
var offsetAdjustment = CGFloat.greatestFiniteMagnitude
let horizontalOffset = proposedContentOffset.x + collectionView.contentInset.left
let layoutAttributesArray = super.layoutAttributesForElements(in: targetRect)
layoutAttributesArray?.forEach({ (layoutAttributes) in
let itemOffset = layoutAttributes.frame.origin.x
if fabsf(Float(itemOffset - horizontalOffset)) < fabsf(Float(offsetAdjustment)) {
offsetAdjustment = itemOffset - horizontalOffset
}
})
return CGPoint(x: proposedContentOffset.x + offsetAdjustment, y: proposedContentOffset.y)
}
}
class VerticalStackView: UIStackView {
init(arrangedSubviews: [UIView], spacing: CGFloat = 0, alignment: UIStackView.Alignment = .center) {
super.init(frame: .zero)
arrangedSubviews.forEach({addArrangedSubview($0)})
self.spacing = spacing
self.alignment = alignment
self.axis = .vertical
}
required init(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
I finally found a solution:
The class VerticalStackView had a default Alignment property .center.
When I replace .center by .fill as a default value - then everything works as expected !
The usage of the VerticalStackView remains the same (i.e. default value for alignment applies):
let stackView = VerticalStackView(arrangedSubviews: [
imageView
], spacing: 12.0)

Nothing Occurring with ScrollToItem in UICollectionView

I have two collection views within a ViewController. One Collection view represents the tab bar at the top, the other represents pages on the bottom half of the screen. I have the second UICollectionView (on the bottom half) within a custom UIView (loading from another class). My objective is to have when I click on the tab bar, to move the bottom collectionView accordingly, with regards to the scrollToIndex. My issue is that when I click on the tab bar at the top, and call the function that is in my custom UIView, nothing occurs. Do I need to share information through a delegate/protocol? What am I doing incorrectly?
In my MainVC I have:
/** Setting up the top header **/
let topBar = UIView()
/** Setting up the connection to the Bottom Collection View **/
let mainView = MainViewsHome()
override func viewWillAppear(_ animated: Bool) {
self.view.layoutIfNeeded()
/**Setting up the header that the buttons lay on **/
topBar.frame = CGRect(x:self.view.frame.width * 0, y:self.view.frame.height * 0, width:self.view.frame.width,height:self.view.frame.height / 6.2)
topBar.backgroundColor = UIColor.red
self.view.addSubview(topBar)
/** Setting up the buttons in the header**/
setUpHeaderBarButtons()
/** Setting up the bottom half **/
mainView.frame = CGRect(x:self.view.frame.width * 0, y:self.view.frame.height / 6.2, width:self.view.frame.width,height:self.view.frame.height / 1.1925)
mainView.backgroundColor = UIColor.clear
self.view.addSubview(mainView)
mainView.delegate = self
}
& Then setting up the collectionView for the MainVC (the bar buttons)
func setUpHeaderBarButtons() {
let flowLayout = UICollectionViewFlowLayout()
let collectionView = UICollectionView(frame: CGRect(x: topBar.frame.width * 0, y:topBar.frame.height / 1.75, width: topBar.frame.width, height: topBar.frame.height / 3.6), collectionViewLayout: flowLayout)
collectionView.register(SelectionCVC.self, forCellWithReuseIdentifier: cellId)
collectionView.delegate = self
collectionView.dataSource = self
collectionView.backgroundColor = UIColor.clear
topBar.addSubview(collectionView)
}
&& Then the didSelect for the MainVC (The bar buttons)
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("selecting cell")
mainView.moveToPage()
}
&& Then my custom UIVIew with the bottom collection view that I wish to trigger a scrollToIndex
class MainViewsHome: UIView, UIGestureRecognizerDelegate, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
var cellId = "Cell"
var collectionView2 : UICollectionView!
override init(frame: CGRect) {
super.init(frame: CGRect(x:0, y:0, width:UIScreen.main.bounds.width, height: UIScreen.main.bounds.height / 1.27))
/**Creation of the View **/
let flowLayout2 : UICollectionViewFlowLayout = UICollectionViewFlowLayout()
flowLayout2.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
flowLayout2.scrollDirection = .horizontal
let collectionView2 = UICollectionView(frame: CGRect(x:self.frame.width * 0,y:self.frame.height * 0,width:self.frame.width,height: self.frame.height), collectionViewLayout: flowLayout2)
collectionView2.register(SelectionCVC.self, forCellWithReuseIdentifier: cellId)
collectionView2.isPagingEnabled = true
collectionView2.delegate = self
collectionView2.dataSource = self
collectionView2.backgroundColor = UIColor.purple
self.addSubview(collectionView2)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func moveToPage() {
// print("we are moving to page")
let indexPath = IndexPath(item: 2, section: 0) //Change section from 0 to other if you are having multiple sections
self.collectionView2?.scrollToItem(at: indexPath, at: [], animated: true)
}
}
where am i going wrong?
In the init of MainViewsHome instead of initializing instance property collectionView2 you are initializing completely new collectionView so you are not having reference in collectionView2.
It should be
self.collectionView2 = UICollectionView(frame: CGRect(x:self.frame.width * 0,y:self.frame.height * 0,width:self.frame.width,height: self.frame.height), collectionViewLayout: flowLayout2)
Instead of
let collectionView2 = UICollectionView(frame: CGRect(x:self.frame.width * 0,y:self.frame.height * 0,width:self.frame.width,height: self.frame.height), collectionViewLayout: flowLayout2)

Swift CollectionView Vertical Paging

I enabled paging on my collectionview and encountered the first issue of each swipe not stopping on a different cell, but page.
I then tried to enter code and handle the paging manually in ScrollViewWillEndDecelerating.
However my problem is the changing the collectionviews ContentOffset or scrolling to a Point both do not work unless called in LayoutSubiews. that is the only place where they effect the CollectionView
Each cell in my collection view is full screen. I handle the cell sizing in layout subviews.
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if let layout = customCollectionView.collectionViewLayout as? UICollectionViewFlowLayout {
let itemWidth = view.frame.width
let itemHeight = view.frame.height
layout.itemSize = CGSize(width: itemWidth, height: itemHeight)
layout.invalidateLayout()
}
let ip = IndexPath(row: 2, section: 0)
view.layoutIfNeeded()
customCollectionView.scrollToItem(at: ip, at: UICollectionViewScrollPosition.centeredVertically, animated: true)
let point = CGPoint(x: 50, y: 600)
customCollectionView.setContentOffset(point, animated: true)
}
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
let pageHeight = customCollectionView.bounds.size.height
let videoLength = CGFloat(videoArray.count)
let minSpace:CGFloat = 10
var cellToSwipe = (scrollView.contentOffset.y) / (pageHeight + minSpace) + 0.5
if cellToSwipe < 0 {
cellToSwipe = 0
}
else if (cellToSwipe >= videoLength){
cellToSwipe = videoLength - 1
}
let p = Int(cellToSwipe)
let roundedIP = round(Double(Int(cellToSwipe)))
let ip = IndexPath(row: Int(roundedIP), section: 0)
let ip = IndexPath(row: 2, section: 0)
view.layoutIfNeeded()
customCollectionView.scrollToItem(at: ip, at: UICollectionViewScrollPosition.centeredVertically, animated: true)
}
First off, I would recommend making your view controller class conform to UICollectionViewDelegateFlowLayout and use the following delegate method to size your UICollectionViewCells
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let size = CGSize(width: view.frame.width, height: view.frame.height)
return size
}
Then in your viewDidLoad call
customCollectionView.isPagingEnabled = true