uicollectionviewcell labels misaligned after reload - ios

I tried following code to render simple collection view on screen.
But subviews in contentview do not algin properly
I am coding in swift 2.3 with Xcode 7.3.1
Whys is this happening?
Here is the code
import Foundation
class CustomerOverdueViewController : BaseViewController , UICollectionViewDelegateFlowLayout {
#IBOutlet weak var collectionView: UICollectionView!
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var lblTotalOverdue: UILabel!
#IBOutlet weak var lblInvestedFunds: UILabel!
#IBOutlet weak var segmentedControl: UISegmentedControl!
var rangeArray = NSArray()
var selectedOverdueIndex = 0
var selectedColor = UIColor()
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Customer Overdue"
}
#IBAction func segValueChanged(sender: UISegmentedControl) {
}
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 6
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let reuseIdentifier = String("CustomerOverdueCollectionViewCell")
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as! CustomerOverdueCollectionViewCell
return cell
}
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath){
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize
{
return CGSize(width: collectionView.frame.size.width/2 - 10, height: collectionView.frame.size.height/3 - 10)
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAtIndex section: Int) -> UIEdgeInsets
{
return UIEdgeInsets(top: 5, left: 5, bottom: 0, right: 5)
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAtIndex section: Int) -> CGFloat
{
return 10.0
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAtIndex section: Int) -> CGFloat
{
return 10.0
}
}

Finally I got the solution
Override layoutSubviews in Custom Cell
override func layoutSubviews() {
super.layoutSubviews()
self.layoutIfNeeded()
}
and add following lines in cellForItemAtIndexpath
cell.setNeedsLayout()
cell.layoutIfNeeded()

It seems that in the initial call the data is used when the table is first drawn (from ViewWillLoad). However when you refresh the data, the constraints are not recalculated.
Normally this is done via setNeedsLayout and/or layoutIfNeeded
You can try:
[view layoutIfNeeded];

Related

UICollectionView won't center

I have a fullscreen UICollectionView with cells. I have checked the width and height, and the view covers the entire screen. However, when I enabled paging, the view was no longer centered. How can I set the center of the cell or the collection view, so it does take the entire screen?
import UIKit
class ShowPictures: UIViewController, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {
var PICTURES: [Picture] = []
#IBAction func swipeAway(_ sender: Any) {
self.dismiss(animated: true, completion: nil)
}
override var prefersStatusBarHidden: Bool {
return true
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 1
}
private func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return UIScreen.main.bounds.size
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return self.PICTURES.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let item: Messages.MessageAdapter.Item.Picture = self.PICTURES[indexPath.section]
let cell: Cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! Cell
return cell
}
#IBAction func CloseController(_ sender: Any) {
dismiss(animated: true, completion: nil)
}
}
class Cell: UICollectionViewCell, UIScrollViewDelegate {
#IBOutlet weak var _image: UIImageView!
#IBOutlet weak var _scroll: UIScrollView!
override func layoutSubviews() {
self._scroll.delegate = self
self._scroll.zoomScale = CGFloat(integerLiteral: 1)
}
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return self._image
}
}
Add the following delegate method to remove the inset:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return .zero
}

Center Label Based on View

I want to center a label based on a view. The label is inside a collectionView and I want to center this label based on a view that is NOT inside the collectionView at runtime.
What I tried to do is change the constant of the label's constraint to the view's frame midY but it doesn't work:
// THIS DOES NOT WORK. I want the `textLabel` centered inside the `yellowView`.
cell.textLabelCenterYVerticalConstraint.constant = yellowView.frame.midY
Here is the screen:
Here is the Storyboard:
This is my collectionView code
class ViewController: UIViewController {
#IBOutlet weak var collectionView: UICollectionView!
#IBOutlet weak var yellowView: UIView!
let imageName = ["1", "2", "3"]
override func viewDidLoad() {
super.viewDidLoad()
collectionView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 5)
}
}
// MARK: - UICollectionViewDataSource
extension ViewController: UICollectionViewDataSource {
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return imageName.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellID", for: indexPath) as! MyCollectionViewCell
let index = indexPath.row
let images = imageName[index]
cell.backgroundImageView.image = UIImage(named: images)
// THIS DOES NOT WORK. I want the `textLabel` centered inside the `yellowView`.
cell.textLabelCenterYVerticalConstraint.constant = yellowView.frame.midY
return cell
}
}
// MARK: - UICollectionViewDelegateFlowLayout
extension ViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: view.bounds.width, height: collectionView.bounds.height)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 0.0, left: 5.0, bottom: 0.0, right: 5.0)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 10.0
}
}
This is my cell code
class MyCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var backgroundImageView: UIImageView!
#IBOutlet weak var textLabel: UILabel!
#IBOutlet weak var textLabelCenterYVerticalConstraint: NSLayoutConstraint!
}
Please see the comment in the code // THIS DOES NOT WORK. Any suggestions?

Display UICollectionView with 2 columns of equal size square cells

I'm trying to accomplish the following:
I've followed the answer in this question, but I get the image below:
This is the code for the collection view controller:
class MainMenuViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
#IBOutlet var collectionView: UICollectionView!
#IBOutlet weak var dateTimeLabel: MarqueeLabel!
#IBOutlet weak var iconButton: UIButton!
#IBOutlet var imageView: UIImageView!
var iconButtonTimer: Timer!
var upDateTimer: Timer!
var statusString: String!
var alertTitle: String!
var networkStatusString: String!
override func viewDidLoad() {
super.viewDidLoad()
collectionView.dataSource = self
collectionView.delegate = self
collectionView.register(UINib.init(nibName: "MainMenuCell", bundle: nil), forCellWithReuseIdentifier: "MainMenuCell")
collectionView.collectionViewLayout = MainMenuLayout()
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 8
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 2
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
print(#function)
let padding: CGFloat = 50
let collectionViewSize = collectionView.frame.size.width - padding
return CGSize(width: collectionViewSize/2, height: collectionViewSize/2)
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MainMenuCell", for: indexPath)
cell.backgroundColor = UIColor.red
return cell
}
}
And this is the storyboard...
Conform to UICollectionViewDelegateFlowLayout and implement
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsetsMake(10, 10, 10, 10)
}
Also comment this line
collectionView.collectionViewLayout = MainMenuLayout()
as no need for a custom layout to accomplish what you want

Adding header to the tableview with dynamic heigh in swift

I have a UIView(TableHeaderView) which has few labels and collection view. One of the label text is set on run time. So its height is unknown. I'm setting this view as the header for table view.
let tableHeaderView = CommentsTableHeaderView.instanceFromNib() as! CommentsTableHeaderView
commentsTable.tableHeaderView = tableHeaderView
In CommentsTableHeaderview class I have the following code.
I have set postDesc label's number of lines to 0. If the text comes in few lines, its cutting off. I want this to be dynamic and display all text.
class CommentsTableHeaderView: UIView,UICollectionViewDelegate,UICollectionViewDataSource,UICollectionViewDelegateFlowLayout {
#IBOutlet weak var postDesc: UILabel!
#IBOutlet weak var profileName: UILabel!
#IBOutlet weak var profileImage: UIImageView!
#IBOutlet weak var imageCollectionView: UICollectionView!
#IBOutlet weak var numberOfComments: UILabel!
class func instanceFromNib() -> UIView {
return UINib(nibName: "CommentsTableHeaderView", bundle: nil).instantiate(withOwner: nil, options: nil)[0] as! UIView
}
override func awakeFromNib() {
imageCollectionView.delegate = self
imageCollectionView.dataSource = self
imageCollectionView.register(UINib(nibName: "InnerCollectionCell", bundle: nil), forCellWithReuseIdentifier: "InnerCollectionCell")
postDesc.lineBreakMode = .byWordWrapping
postDesc.numberOfLines = 0
postDesc.text = "hi"
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
print("inside collection view")
return 4
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
print("inside cell")
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "InnerCollectionCell", for: indexPath) as! InnerCollectionCell
cell.cellImageView.image = UIImage(named: "Recipe.jpeg")
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize{
print("inside size\(collectionView.frame.width)")
return CGSize(width: imageCollectionView.frame.width, height: 200)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets{
return UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 10)
}
}
You need a way to resize your UILabel according to the text size. Do not put height constraints on UILabel. Rather pin its bottom to the bottom view's top.
Do that programmatic resizing after viewDidLoad() - maybe in viewWillAppear() or viewDidAppear(), before you do your first reloadData() call.
Here is a nice tutorial how this should be done - it suggests doing the resizing inside viewDidLayoutSubviews(), which should also be fine.
you need 2 collectionViewDelegate methods:
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
return CGSize(width: x, height: x)
}
if you wish to have a dynamic height in referenceSizeForHeaderInSection height field, set your height according to screen size for ex, UIScreen.main.bounds.height * 0.09

uicollectionview - data automatically load without calling reloaddata

I got two questions.
I am wondering why my collection view automatically load data without calling imageCollectionView.reloadData().
Solved. See comment
Why func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize is not called? I didn't see the print("collectionViewLayout called") I am trying to modify the cell's size so the cell's height equals to the collection view height
Solved. See comment
Here is the codes
class ProductInternalDetailVC: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
var selectedProduct: Product?
#IBOutlet weak var imageCollectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
imageCollectionView.delegate = self
imageCollectionView.dataSource = self
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 2
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath as IndexPath) as! ProductInternalDetailCVC
if indexPath.row == 0 {
cell.productImage.image = selectedProduct!.image1
} else {
cell.productImage.image = selectedProduct!.image2
}
cell.productImage.frame = CGRect(x: 1, y: 1, width: cell.frame.size.width-2, height: cell.frame.size.height-2)
return cell
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
//return CGSize(width: collectionView.frame.size.height-1, height: collectionView.frame.size.height-1)
print("collectionViewLayout called")
return CGSize(width: 10, height: 10)
}
}
class ProductInternalDetailCVC: UICollectionViewCell {
#IBOutlet weak var productImage: UIImageView!
}
Thanks for the help.
Question 2: The function signature has changed a bit. Change the function signature with this:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
...
}
And don't forget to implement the UICollectionViewDelegateFlowLayout protocol.
Question 1:
When entering the ViewController your collectionView loads the datasource of the collectionView automatically if this is your question.for example, make changes on datasource without changing the view (means without calling viewDidLoad method) you will not see the changes until you call imageCollectionView.reloadData().

Resources