How to trigger refresh in Supplementary view of UICollectionView - ios

We are using UICollectionView to draw a calendar. The supplementary view is used for the date at the top of a column that represents a day in the calendar. Each calendar is a section in the UICollectionView. We want the current day highlighted. We have that working by
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
var view: UICollectionReusableView?
if kind == WeeklyCollectionElementKindDayColumnHeader {
let dayColumnHeader: WeeklyDayColumnHeader? = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: WeeklyDayColumnHeaderReuseIdentifier, for: indexPath) as? WeeklyDayColumnHeader
let day: Date? = collectionViewCalendarLayout?.dateForDayColumnHeader(at: indexPath)
let currentDay: Date? = currentTimeComponents(for: self.collectionView!, layout: collectionViewCalendarLayout!)
let startOfDay: Date? = Calendar.current.startOfDay(for: day!)
let startOfCurrentDay: Date? = Calendar.current.startOfDay(for: currentDay!)
dayColumnHeader?.day = day
dayColumnHeader?.isCurrentDay = startOfDay == startOfCurrentDay
view = dayColumnHeader
}
else if kind == WeeklyCollectionElementKindTimeRowHeader {
let timeRowHeader: WeeklyTimeRowHeader? = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: WeeklyTimeRowHeaderReuseIdentifier, for: indexPath) as? WeeklyTimeRowHeader
timeRowHeader?.time = collectionViewCalendarLayout?.dateForTimeRowHeader(at: indexPath)
view = timeRowHeader
}
return view!
}
which with this
class WeeklyDayColumnHeader: UICollectionReusableView {
var day: Date? {
didSet {
var dateFormatter: DateFormatter?
if dateFormatter == nil {
dateFormatter = DateFormatter()
dateFormatter?.dateFormat = "E d"
}
self.title!.text = dateFormatter?.string(from: day!)
setNeedsLayout()
}
}
var isCurrentDay: Bool = false {
didSet {
if isCurrentDay {
title?.textColor = UIColor.white
title?.font = UIFont.boldSystemFont(ofSize: CGFloat(16.0))
titleBackground?.backgroundColor = UIColor(red:0.99, green:0.22, blue:0.21, alpha:1.0)
}
else {
title?.font = UIFont.systemFont(ofSize: CGFloat(16.0))
title?.textColor = UIColor.black
titleBackground?.backgroundColor = UIColor.clear
}
}
}
set the background we want the first time I come into this view. But if I have this view open and the date changes, how do I update the header? Where should I move the logic that sets the date, and how do I "invalidate" or whatever I need to do?

Use the UIApplicationSignificantTimeChangeNotification in order to notify you of time changes.
Use the NotificationCenter to implement this. Something like this:
NotificationCenter.defaultCenter().addObserver(self, selector: "dayChanged:", name: UIApplicationSignificantTimeChangeNotification, object: nil)
Then you need a function to handle the change:
func dayChanged(notification: NSNotification){
// trigger the changes you need here
}

Related

how to change button icon only if time in json response is updated?

I have to change a button icon only if the time in response changes. So I created a timer function which hits api every 30 seconds and reloads table. Here is the code:
class HomeViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var refreshed = false
override func viewDidLoad() {
super.viewDidLoad()
table.dataSource = self
table.delegate = self
self.getPosts()
timer = Timer.scheduledTimer(withTimeInterval: 30.0, repeats: true) { (timer) in
self.refreshed = true
self.getPosts()
}
}
//TableView Datasource
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let data = posts[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "HomeTableViewCell", for: indexPath) as! HomeTableViewCell
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ" // Pass any format here you want according to your date response
let convDate = dateFormatter.date(from: data.postUpdatedAt ?? "")
if refreshed == false {
cell.timeBeforeRefreshInterval = convDate
}
else {
cell.timeAfterRefreshInterval = convDate
if cell.timeAfterRefreshInterval == cell.timeBeforeRefreshInterval {
cell.button.setImage(UIImage(named: "1"), for: .normal)
}
else {
cell.button.setImage(UIImage(named: "2"), for: .normal)
cell.timeBeforeRefreshInterval = cell.timeAfterRefreshInterval
refreshed = false
}
}
return cell
}
}
class HomeTableViewCell: UITableViewCell {
var timeBeforeRefreshInterval : Date?
var timeAfterRefreshInterval : Date?
}
First I created variable to store date response in HomeViewController but it will always store the response of last cell created so I added those variables in HomeTableVIewCell. However its still not working. Is there any better logic to compare times of post and update your cell according to that? All I wanna do is compare time, if someone posts something in group, time gets updated and I need to change button icon to show someone has posted something new.

var late don't woking

the text must be red when the variable beats == "true"
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! InstallmentTableViewCell
if self.switchInstallmentToPay == true {
if let installment = PaymentManager.paymentPlan?.unpaidInstallments![indexPath.row] {
if let id = installment.id, let paymentDue = installment.paymentDue, let description = installment.numberDescription, let method = installment.paymentMethodDescription, let expectedPayment = installment.expectedPayment, let actualPayment = installment.actualPayment, let payable = installment.payable, let late = installment.late {
cell.load(id: id, paymentDue: paymentDue, description: description, method: method, expectedPayment: expectedPayment, actualPayment: actualPayment, payable: payable, late: late)
if installment.payable! {
cell.accessoryType = .checkmark
cell.tintColor = UIColor.lighterGray
cell.isUserInteractionEnabled = true
if installment.late! {
cell.lbDescription.textColor = UIColor.danger // not working
}
}else{
cell.accessoryType = .none
//cell.tintColor = UIColor.lightGray
cell.isUserInteractionEnabled = false
}
}
}
}else{
if let installment = PaymentManager.paymentPlan?.paidInstallments![indexPath.row] {
if let id = installment.id, let paymentDue = installment.paymentDue, let description = installment.numberDescription, let method = installment.paymentMethodDescription, let expectedPayment = installment.expectedPayment, let actualPayment = installment.actualPayment, let payable = installment.payable, let late = installment.late {
cell.load(id: id, paymentDue: paymentDue, description: description, method: method, expectedPayment: expectedPayment, actualPayment: actualPayment, payable: payable, late: late)
cell.accessoryType = .none
cell.isUserInteractionEnabled = false
cell.lbDescription.textColor = UIColor.black // not working
cell.tintColor = UIColor.lighterGray
}
}
}
return cell
}
This code is difficult to read, and there's a lot of redundancy. If you're using a storyboard, I suggest making separate dynamic cells for the paid and unpaid installments. Both cells' class type can stay InstallmentTableViewCell, as you're just duplicating the cells' views, not their logic. The various elements' colors & styles can be set right in the storyboard's cell prototype, and then your tableView(_:cellForRowAt:indexPath) can be simplified to just
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellID = switchInstallmentToPay ? "unpaidCell" : "paidCell"
let cell = tableView.dequeueReusableCell(withIdentifier: cellID, for: indexPath) as! InstallmentTableViewCell
cell.load(...)
return cell
}
I would also recommend changing cell.load() to take an installment argument and setting the cells' properties there instead of cluttering the caller with multiple if lets.

cell in UICollectionView returning nil when trying to change UITextField in that cell from the controller

I have a UICollectionViewController with a datePicker. When the date is changed, it calls this function:
var cell: InfoSection?
#objc func didChangeDate(sender: UIDatePicker) {
let myDateFormatter: DateFormatter = DateFormatter()
myDateFormatter.dateFormat = "MM/dd/yyyy hh:mm"
let mySelectedDate: String = myDateFormatter.string(from: sender.date)
cell?.dateTextField.text! = mySelectedDate as String
print("mySelected Date is: ", mySelectedDate as String)
print("The content of dateTextField is: ", cell?.dateTextField.text!)
}
InfoSection is my cell that contains the textField.
cell is showing up as nil at this line:
cell?.dateTextField.text! = mySelectedDate as String
I'm sure there's a fairly obvious solution I'm missing here. I've tried force unwrapping it - it crashes. I've tried setting the variable cell to a value of = InfoSection() - it prints the date in the "context of dateTextField" statement, but doesn't change the textField.
And some SO answers said to change the data in a cell through cellForItemAt indexPath which makes sense, but I don't know how to call cell?.dateTextField.text! = mySelectedDate as String in cellForItemAt whenever func didChangeDate is called.
Any help would be amazing! And if you need any other info let me know, thanks!
Your should store your selected date as variable and reload your collectionView like this way:
var mySelectedDate: String?
#objc func didChangeDate(sender: UIDatePicker) {
let myDateFormatter: DateFormatter = DateFormatter()
myDateFormatter.dateFormat = "MM/dd/yyyy hh:mm"
mySelectedDate = myDateFormatter.string(from: sender.date)
collectionView.reloadData()
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CellIdentifier", for: indexPath) as! InfoSection
cell.dateTextField.text = mySelectedDate
return cell
}

Access Parent tableViewCell from Child CollectionViewCell

Image Depicting One tableViewCell. Inside One tableViewCell there is some content and One CollectionView(Gray color area showing dates).
Depending on dates in collection view and their scrolling I have to show Year and Month("December 2018" : It is static in image).
This is my JTAppleCalender cellForItemAt method :
func calendar(_ calendar: JTAppleCalendarView, cellForItemAt date: Date, cellState: CellState, indexPath: IndexPath) -> JTAppleCell {
let cell = calendar.dequeueReusableJTAppleCell(withReuseIdentifier: "CalenderCell", for: indexPath) as! CalenderCell
cell.lblDate.text = cellState.text
formatter.dateFormat = "EEE"
cell.lblDay.text = formatter.string(from: cellState.date)
formatter.dateFormat = "MMMM YYYY"
print(formatter.string(from: cellState.date))
formatter.dateFormat = "dd-MM-yyyy"
print(formatter.string(from: cellState.date))
if(pickup_dates .contains(formatter.string(from: cellState.date))){
let index = pickup_dates.index(of: formatter.string(from: cellState.date))
let dictinfoDelivery = self.infoDelivery.object(at: index) as! NSDictionary
cell.lblPrice.text = dictinfoDelivery .value(forKey: "total_price_format") as? String
cell.isUserInteractionEnabled = true
}
else{
cell.lblPrice.text = ""
cell.isUserInteractionEnabled = false
}
return cell
}
I am trying to access tableView label("December 2018") from inside of this method but was unable to do so.
How to access this TableViewCell label from child collectionViewCell?
My Requirement is to Change 2-3 contents in tableViewCell (Price,quantity) wrt Date Selected in Child CollectionView.
please try this
guard let lbl = (self.yourTableView.cellForRow(at: [NSIndexPath(row: 0, section: 0) as IndexPath]) as! yourCellNameInTableView).labelName else {
return
}
lbl.text = "Your text"
Please pass your row number and section number. It may helps to you.Thanks you

Adding Multiple Countdown Timers to UICollectionView

My goal is to have a unique (hh:mm:ss) countdown timer for each populated uicollectionviewcell within a uicollectionview.
Below is the (abbreviated) code I've been able to compile, but am stuck on getting it to really work in any capacity...any guidance would be appreciated.
class VC: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
var timer = Timer()
var secondsRemaining: Int?
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath as IndexPath) as! ExampleCollectionViewCell
let expirationDate = Calendar.current.date(byAdding: .hour, value: 24, to: startDate)
secondsRemaining = expirationDate?.seconds(from: startDate)
timer = Timer(timeInterval: 1, target: self, selector: #selector(countdown), userInfo: nil, repeats: true)
return cell
}
#objc func countdown() {
secondsRemaining = secondsRemaining! - 1
if secondsRemaining == 0 {
print("CELL TIME HAS EXPIRED!")
timer.invalidate()
} else {
let hours = secondsRemaining! / 3600
let minutes = (secondsRemaining! % 3600) / 60
let seconds = (secondsRemaining! % 3600) % 60
let countdownLabelString = String(format: "%02d:%02d:%02d", hours, minutes, seconds)
//i need to take this countdownLabelString and set it to the
//countdownLabel.text that is an element of each cell. i tried
//doing all of this in the cell subclass, but no luck...
}
}
}
Collection view cells are not made to update constantly. If you want them to update, you have to tell them to update.
If you want each cell to show a different amount of time remaining, you need a data model that saves a separate expirationDate for each indexPath you display. If your collection view is a simple one-dimensional list of items, like a table view, then you could just use a 1-dimensional array. If your collection view shows a grid of cells you might need a 2 dimensional array of model objects.
Each time you add an item to your data model, include an expirationDate value. (The Date when that item expires.)
Then have your timer call the collection view's reloadData method each time it fires.
In your collectionView(collectionView:cellForItemAt:) method, use the indexPath to figure out which item in your data model you are displaying. Look up the expirationDate for that indexPath, calculate the time remaining from now until your expiration date, and display that information in the cell. (Use the Calendar method dateComponents(_:from:to:) to calculate the number of hours, minutes, and seconds between now and your expiration date.)
EDIT
DO NOT decrement a counter every time your timer fires. Do math on the difference between your expiration date and the current date using dateComponents(_:from:to:)
EDIT #2:
A sample collection view controller that does something like what you want is below:
import UIKit
private let reuseIdentifier = "aCell"
class DatesCollectionVC: UICollectionViewController {
weak var timer: Timer?
var dates: [Date] = []
lazy var componentsFormatter: DateComponentsFormatter = {
let formatter = DateComponentsFormatter()
formatter.allowedUnits = [.hour, .minute, .second]
return formatter
}()
override func viewDidLoad() {
super.viewDidLoad()
for _ in 1...100 {
let interval = Double(arc4random_uniform( 10000)) + 120
dates.append(Date().addingTimeInterval(interval))
}
timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { timer in
self.collectionView?.reloadData()
}
}
// MARK: - UICollectionViewDataSource
override func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return dates.count
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as? MyCell else {
return UICollectionViewCell()
}
let now = Date()
let expireDate = dates[indexPath.row]
guard expireDate > now else {
cell.intervalLabel.text = "00"
return cell
}
let desiredComponents: Set<Calendar.Component> = [.hour, .minute, .second]
let components = Calendar.current.dateComponents(desiredComponents, from: now, to: expireDate)
cell.intervalLabel.text = self.componentsFormatter.string(from: components)
return cell
}
}
The code above looks like this:

Resources