I want to have this progression view (pod) inside each collectionviewcell. When I run the app, the progression is 0%, it should animate to 100% for testing, but it stays at 0%. Only when I swipe up and down (refresh the cells???) it starts animating to 100%. This is my code:
import UIKit
import MKRingProgressView
class myCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var ringView: UIView!
let ringsize = 150
var ringProgressView = RingProgressView()
override func layoutSubviews() {
ringProgressView = RingProgressView(frame: CGRect(x: 0, y: 0, width: ringsize, height: ringsize))
ringProgressView.startColor = .red
ringProgressView.endColor = .magenta
ringProgressView.ringWidth = CGFloat(ringsize) * 0.15
ringProgressView.progress = 0.0
ringProgressView.shadowOpacity = 0.5
ringView.addSubview(ringProgressView)
}
}
extension myCollectionViewCell{
func setProgress(progress: Double){
UIView.animate(withDuration: 0.5){
self.ringProgressView.progress = progress
}
}
}
import UIKit
private let reuseIdentifier = "Cell"
class myCollectionViewController: UICollectionViewController {
#IBOutlet var myCollectionView: UICollectionView!
var cellColor = true
override func viewDidLoad() {
super.viewDidLoad()
}
override func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of items
return 7
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! myCollectionViewCell
cell.setProgress(progress: 1.0)
return cell
}
}
Can someone help me? Thanks!!
Call myCollectionView.reloadData() inside viewDidAppear method.
Related
I want to put this ring progress view (CoocaPod) inside CollectionView Cells. One Progress View is in one cell. Somehow, the ring progress view is not showing... This is my code:
CollectionView Controller Class:
import UIKit
private let reuseIdentifier = "Cell"
class myCollectionViewController: UICollectionViewController {
var cellColor = true
override func viewDidLoad() {
super.viewDidLoad()
self.collectionView!.register(UICollectionViewCell.self, forCellWithReuseIdentifier: reuseIdentifier)
}
override func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of items
return 19
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath)
cell.backgroundColor = cellColor ? UIColor.red : UIColor.blue
cellColor = !cellColor
return cell
}
}
CollectionView Cell Class:
import UIKit
import MKRingProgressView
class myCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var ringView: UIView!
override func layoutSubviews() {
let ringProgressView = RingProgressView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
ringProgressView.startColor = .red
ringProgressView.endColor = .magenta
ringProgressView.ringWidth = 5
ringProgressView.progress = 1.0
ringView.addSubview(ringProgressView)
}
}
I selected myCollectionViewCell Class for Cell and myCollectionViewController for the ViewController at Main.storyboard
Does anyone can help me?
Thank you!
Uncommented the register line. Works now
import UIKit
class My_CustomTableViewCell: UITableViewCell {
#IBOutlet weak var collectionView: UICollectionView!
#IBOutlet weak var collectionViewHeight: NSLayoutConstraint!
#IBOutlet weak var nameLabel: UILabel!
#IBOutlet weak var numberLabel: UILabel!
#IBOutlet weak var imageV: UIImageView!
let collectionArray = generateRandomData()
var index = 0
override func awakeFromNib()
{
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool)
{
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
func getCellSize(_ targetSize: CGSize) -> CGSize
{
return CGSize(width: 44, height: 44)
}
override func prepareForReuse()
{
self.collectionView.contentOffset = .zero
}
override func systemLayoutSizeFitting(_ targetSize: CGSize) -> CGSize
{
// let frame = self.collectionView.frame
// self.collectionView.layoutIfNeeded()
//
// let height: CGFloat = self.collectionView.collectionViewLayout.collectionViewContentSize.height
// self.collectionViewHeight.constant = height
// self.collectionView.frame = CGRect(x: frame.minX, y: frame.minY, width: frame.width, height: height)
// return self.collectionView.collectionViewLayout.collectionViewContentSize
self.collectionViewHeight.constant = collectionView.collectionViewLayout.collectionViewContentSize.height
return self.contentView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
}
override func systemLayoutSizeFitting(_ targetSize: CGSize, withHorizontalFittingPriority horizontalFittingPriority: UILayoutPriority, verticalFittingPriority: UILayoutPriority) -> CGSize
{
self.collectionView.layoutIfNeeded()
self.collectionViewHeight.constant = collectionView.collectionViewLayout.collectionViewContentSize.height
return self.contentView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
}
}
extension My_CustomTableViewCell: UICollectionViewDelegate, UICollectionViewDataSource
{
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
{
let number = Int(self.numberLabel.text!)! + 10
return number
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollVCell", for: indexPath) as! My_CustomCollectionViewCell
// let number = Int(self.numberLabel.text!)!
cell.numberlabel.text = "\(indexPath.row)"
return cell
}
}
extension My_CustomTableViewCell: UICollectionViewDelegateFlowLayout
{
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
{
return CGSize(width: 44, height: 44)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat
{
return 8.0
}
}
import UIKit
class My_CustomViewController: UIViewController
{
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Enable automatic row auto layout calculations
self.tableView.rowHeight = UITableView.automaticDimension
// Set the estimatedRowHeight to a non-0 value to enable auto layout.
self.tableView.estimatedRowHeight = 44
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destination.
// Pass the selected object to the new view controller.
}
*/
}
extension My_CustomViewController: UITableViewDelegate, UITableViewDataSource
{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return 66
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "TbVCell", for: indexPath) as! My_CustomTableViewCell
cell.collectionView.tag = indexPath.row
cell.nameLabel.text = "\(indexPath.row)"
cell.numberLabel.text = "\(indexPath.row)"
cell.index = indexPath.row
print("cellForRowAt-----------\(indexPath.row)")
return cell
}
}
storyboard settings
here I am not able to get the required number of collection cells as expected by the numberOfItemsInSection function which is increasing with the tableviewcell number like 64 collectionview items for 64rth tableviewcell.
What I need to do to get exact matching of number of items with tableviewcell.
with none of the above variables I am able to keep track of the count to match.
Regards
I'm working on a requirement where I need to add the items in a UICollectionView dynamically.
Here is my code of ViewController
import UIKit
class ViewController: UIViewController {
enum Direction {
case Horizonatal
case Verticle
}
var enumDirection: Direction = .Verticle
var direction = "Verticle"
var SectionsAndRows = [Int]()
override func viewDidLoad() {
super.viewDidLoad()
SectionsAndRows.append(4)
SectionsAndRows.append(3)
SectionsAndRows.append(2)
SectionsAndRows.append(1)
//SectionsAndRows.append(5)
}
#IBOutlet var gridCollectionView: UICollectionView! {
didSet {
gridCollectionView.bounces = false
}
}
#IBOutlet var gridLayout: UICollectionViewFlowLayout! {
didSet {
//gridLayout.stickyRowsCount = 0
gridLayout.scrollDirection = .horizontal
//gridLayout.stickyColumnsCount = 0
gridLayout.minimumLineSpacing = 5
gridLayout.minimumInteritemSpacing = 5
}
}
}
// MARK: - Collection view data source and delegate methods
extension ViewController: UICollectionViewDataSource {
func numberOfSections(in collectionView: UICollectionView) -> Int {
return SectionsAndRows.count
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
print(SectionsAndRows[section])
return SectionsAndRows[section]
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CollectionViewCell.reuseID, for: indexPath) as? CollectionViewCell else {
return UICollectionViewCell()
}
print("Current Section ==\(indexPath.section) CurrentRow ===\(indexPath.row) and its rows count ==\(SectionsAndRows[indexPath.section])")
cell.titleLabel.text = ""
cell.btn.addTarget(self, action: #selector(handleAdd(sender:)), for: .touchUpInside)
cell.btn.tag = (indexPath.section * 1000) + indexPath.row
if enumDirection == .Verticle {
if indexPath.section == SectionsAndRows.count - 1 {
cell.btn.setTitle("+", for: .normal)
} else {
cell.btn.setTitle("\(indexPath)", for: .normal)
}
}
return cell
}
#objc func handleAdd(sender: UIButton) {
// Perform some opration
}
}
extension ViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 5
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 100, height: 100)
}
}
CollectionViewCell.swift
import UIKit
class CollectionViewCell: UICollectionViewCell {
static let reuseID = "CollectionViewCell"
#IBOutlet weak var btn: UIButton!
#IBOutlet weak var titleLabel: UILabel!
}
If you run the code, it will show a Collection of 1 row and 1 column in each row. If you uncomment the last line of viewDidLoad() (SectionsAndRows.append(5)) function, then it works fine.
My observation is that the last section of the CollectionView will have the highest number of a column. Is that correct or is this a bug of a CollectionView?
in my view controller I Am loading a custom CollectionViewCell with subclass. Based on the position of a cell's indexpath I want to format the text labels differently. I.e. first row has only one cell with bigger text, whereas the second has two cell with smaller text.
How can I access the indexpath from my UICollectionView in my UICollectionViewCell subclass? I tried a delegate protocol but this always returns nil.
Code below and Thanks so much!
Markus
UICollectionViewController:
import UIKit
protocol WorkoutDataViewControllerCVDataSource: AnyObject {
func workoutType(for workoutDataViewControllerCV: WorkoutDataViewControllerCV) -> WorkoutType
func workoutDistance(for workoutDataViewControllerCV: WorkoutDataViewControllerCV) -> Double
func workoutDuration(for workoutDataViewControllerCV: WorkoutDataViewControllerCV) -> Double
func workoutInstantVelocity(for workoutDataViewControllerCV: WorkoutDataViewControllerCV) -> Double
}
final class WorkoutDataViewControllerCV: UIViewController {
#IBOutlet weak var collectionView: UICollectionView!
weak var dataSource: WorkoutDataViewControllerCVDataSource!
private lazy var velocityFormatter = VelocityFormatter(dataSource: self, delegate: self)
private lazy var averageVelocityFormatter = VelocityFormatter(dataSource: self, delegate: self)
override func viewDidLoad() {
super.viewDidLoad()
self.collectionView.register(MeasurementCollectionViewCell.preferredNib, forCellWithReuseIdentifier: MeasurementCollectionViewCell.preferredReuseIdentifier)
}
}
// MARK: - Managing UICollectionView
extension WorkoutDataViewControllerCV: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 4
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Measurement Cell", for: indexPath)
return cell
}
}
extension WorkoutDataViewControllerCV: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize {
let availableWidth = self.view.frame.width
switch indexPath.row {
case 0: return CGSize(width: availableWidth, height: 150)
case 1: return CGSize(width: availableWidth/2.1, height: 150)
case 2: return CGSize(width: availableWidth/2.1, height: 150)
case 3: return CGSize(width: availableWidth, height: 150)
default:
return CGSize(width: availableWidth/2.1, height: 150)
}
}
}
// MARK: - Managing VelocityFormatter
extension WorkoutDataViewControllerCV: VelocityFormatterDataSource {
func duration(for velocityFormatter: VelocityFormatter) -> Double {
return dataSource.workoutDuration(for: self)
}
func distance(for velocityFormatter: VelocityFormatter) -> Double {
return dataSource.workoutDistance(for: self)
}
func instantVelocity(for velocityFormatter: VelocityFormatter) -> Double {
return dataSource.workoutInstantVelocity(for: self)
}
}
UICollectionViewCell.swift
import UIKit
final class MeasurementCollectionViewCell: UICollectionViewCell {
#IBOutlet private var measurementPropertyLabel: UILabel!
#IBOutlet private var measurementValueLabel: UILabel!
#IBOutlet private var measurementUnitLabel: UILabel!
static let preferredReuseIdentifier = "Measurement Cell"
static let preferredNib = UINib(nibName: "MeasurementCollectionViewCell", bundle: nil)
override func awakeFromNib() {
super.awakeFromNib()
updateMeasurement(property: "Speed", value: "100", unit: "km/h")
//measurementValueLabel.font = measurementValueLabel.font.monospacedDigitFont
}
func updateMeasurement(property: String, value: String, unit: String?) {
measurementPropertyLabel.text = property
measurementValueLabel.text = value
measurementUnitLabel.text = unit
}
}
Get the instance of cell in UICollectionView delegate method collectionView(_, didSelectItemAt _).
extension WorkoutDataViewControllerCV: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if let cell = collectionView.cellForItem(at: indexPath) as? MeasurementCollectionViewCell {
cell.selectedIndexPath(indexPath)
}
}
}
The indexPath will be passed as an argument in method selectedIndexPath to MeasurementCollectionViewCell from above method.
class MeasurementCollectionViewCell: UICollectionViewCell {
......
func selectedIndexPath(_ indexPath: IndexPath) {
//Do your business here.
}
}
You can use the responder chain to get the collection view of a cell with which you can get the index path. Just add these extensions in a new file called UICollectionViewCell+IndexPath.swift.
extension UIResponder {
func next<T: UIResponder>(_ type: T.Type) -> T? {
return next as? T ?? next?.next(type)
}
}
extension UICollectionViewCell {
var indexPath: IndexPath? {
return next(UICollectionView.self)?.indexPath(for: self)
}
}
Now inside your cell, you can use self.indexPath
Pretty straight forward way would be storing the indexPath into the subclass of UICollectionViewCell class. Assign it while returning from cellForRow at: index path. So now the subclassed collectionviewcell has access to the indexpath of it's own
I am trying to make a collectionView header become hidden when a button is tapped but I cant seem to access this property from inside of the IBAction function.
button function
var buttonPressed:Bool = true
#IBAction func changeView(_ sender: Any) {
if buttonPressed{
UIView.animate(withDuration: 0.05){
self.buttonPressed = !self.buttonPressed
print("Ive been expanded")
}
}
else{
UIView.animate(withDuration: 0.05){
self.buttonPressed = !self.buttonPressed
}
}
}
}
Code in its entirety
import UIKit
class ViewController: UIViewController,UICollectionViewDataSource,UICollectionViewDelegate {
#IBOutlet weak var animateCollectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
animateCollectionView.dataSource = self
animateCollectionView.delegate = self
animateCollectionView.backgroundColor = UIColor.blue
animateCollectionView.frame = CGRect(x: 0, y: 100, width: 600, height: 1000)
let layout = animateCollectionView.collectionViewLayout as? UICollectionViewFlowLayout
layout?.sectionHeadersPinToVisibleBounds = true
self.view.sendSubview(toBack: self.animateCollectionView)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 200
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = self.animateCollectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! customCell
cell.cellLabel.text = "\(indexPath.item)"
cell.backgroundColor = UIColor.orange
return cell
}
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
// returning the search bar for header
let header = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionElementKindSectionHeader, withReuseIdentifier: "headerCell", for: indexPath) as! customHeader
header.backgroundColor = UIColor.purple
return header
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
// if section is above search bar we need to make its height 0
if section == 0 {
return CGSize(width: 0, height: 0)
}
// for section header i.e. actual search bar
return CGSize(width: animateCollectionView.frame.width, height: 50)
}
var buttonPressed:Bool = true
#IBAction func changeView(_ sender: Any) {
if buttonPressed{
UIView.animate(withDuration: 0.05){
self.buttonPressed = !self.buttonPressed
print("Ive been expanded")
}
}
else{
UIView.animate(withDuration: 0.05){
}
}
}
}
class customCell :UICollectionViewCell{
#IBOutlet weak var cellLabel: UILabel!
}
class customHeader: UICollectionReusableView{
}
As #SPatel mentioned, set the size to zero.
Then set up a delegate method from the cell to the VC so that the VC knows to invalidate layouts.
For instance:
Cell Class
protocol HideHeaderViewDelegate {
func hideHeaderView(hide: Bool)
}
class HeaderView: UICollectionReusableView {
var delegate: HideHeaderViewDelegate?
#IBOutlet weak var hideButton: UIButton!
override func awakeFromNib() {
super.awakeFromNib()
}
#IBAction func hideButtonAction(_ sender: Any) {
self.frame.size = CGSize.zero
guard let delegate = delegate else { return }
delegate.hideHeaderView(hide: true)
}
}
View Controller
extension ViewController: HideHeaderView {
func hideHeaderView(hide: Bool) {
if hide == true {
print(hide)
// invalidate your layout here
}
}
}
Don't forget to set the delegate
func collectionView(_ collectionView: UICollectionView,
viewForSupplementaryElementOfKind kind: String,
at indexPath: IndexPath) -> UICollectionReusableView {
let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionElementKindSectionHeader,
withReuseIdentifier: "headerView", for: indexPath) as! HeaderView
headerView.delegate = self
return headerView
}