How do I reload cells in UICollectionView without reloading the header? - ios

I am trying to update the content of the cells in a UICollectionView based on what the user inputs into a textField in the header. I have looked at other similar questions here but none of their answers have seemed to work.I even tried implementing an extension to the UICollectionView which goes through each cell in the section trying to reload its data but that leads to a crash as the number of cells are changing after the user enters into the textField
Function I attempted to solve the problem, I have only one section so I hard coded in that section
#objc func textFieldDidChange(){
guard(!(feedSearchBar.text?.isEmpty)!) else{
VC.currentTagsArray = VC.genericTagsArray
VC.feedScreenCollectionView.reloadData()
return
}
VC.currentTagsArray = VC.genericTagsArray.filter({letter -> Bool in
if feedSearchBar.text!.count > letter.count{
return false
}
let stringRange = letter.index(letter.startIndex, offsetBy: feedSearchBar.text!.count)
let subword = letter[..<stringRange]
return subword.lowercased().contains(feedSearchBar.text!.lowercased())
})
if VC.currentTagsArray.isEmpty{
VC.currentTagsArray.insert(feedSearchBar.text!, at: 0)
}
VC.feedScreenCollectionView.reloadItems(inSection: 0)
}
extension UICollectionView{
func reloadItems(inSection section:Int = 0) {
print("made it to reload")
for i in 0..<self.numberOfItems(inSection: section){
self.reloadItems(at: [IndexPath(item: i, section: section)])
}
}
}
Here is the code in its entirety from the project
import UIKit
class ViewController: UIViewController,UICollectionViewDelegateFlowLayout,UICollectionViewDelegate,UICollectionViewDataSource,printDelegateWorkedDelegate,updateCollectionView{
func updateCollectionView() {
self.feedScreenCollectionView.reloadData()
}
func printDelegateWorkedDelegate() {
print("The delegate worked")
}
var genericTagsArray:[String] = ["tag1","tag2","tag3","tag4","tag5","tag6","tag7","tag8","tag9","tag10","tag11","tag12","A","B","C","D","E","F","G","Ab","Abc","za","tag1","tag2","tag3","tag4","tag5","tag6","tag7","tag8","tag9","tag10","tag11","tag12","A","B","C","D","E","F","G","Ab","Abc","za"]
var currentTagsArray:[String] = [String]()
var tagsSelected:[String] = [String]()
let keyboardSlider = KeyboardSlider()
var header:feedViewHeader = feedViewHeader()
#IBOutlet weak var feedScreenCollectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
keyboardSlider.subscribeToKeyboardNotifications(view: view)
currentTagsArray = genericTagsArray
let viewTapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(feedViewHeader.viewTapped(gestureRecognizer:)))
viewTapGestureRecognizer.cancelsTouchesInView = false
self.feedScreenCollectionView.addGestureRecognizer(viewTapGestureRecognizer)
feedScreenCollectionView.delegate = self
//
feedScreenCollectionView.dataSource = self
//
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 5, left: 1, bottom: 0, right: 1)
layout.minimumLineSpacing = 0
layout.headerReferenceSize = CGSize(width: 50, height: 75)
layout.sectionHeadersPinToVisibleBounds = true
feedScreenCollectionView.collectionViewLayout = layout
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
return CGSize(width: 50, height: 75)
}
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
header = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionElementKindSectionHeader, withReuseIdentifier: "feedViewHeader", for: indexPath) as! feedViewHeader
header.VC = self
return header
}
//
//Data Source
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return currentTagsArray.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "feedViewCell", for: indexPath) as! feedViewCell
cell.feedImageView.backgroundColor = .blue
cell.feedImageView.clipsToBounds = true
cell.feedImageView.layer.cornerRadius = CGFloat((cell.feedImageView.frame.width)/5)
cell.feedLabel.text = currentTagsArray[indexPath.item]
return cell
}
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, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let collectionViewWidth = collectionView.bounds.width/4.0
let collectionViewHeight = collectionViewWidth
return CGSize(width: collectionViewWidth-4, height: collectionViewHeight+25)
}
var lastContentOffset:CGFloat = 0
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if self.lastContentOffset > self.feedScreenCollectionView.contentOffset.y && self.feedScreenCollectionView.contentOffset.y > 0 && self.feedScreenCollectionView.contentOffset.y < self.feedScreenCollectionView.frame.maxY {
self.lastContentOffset = scrollView.contentOffset.y
header.isHidden = false
}
else if (self.lastContentOffset < self.feedScreenCollectionView.contentOffset.y) && (self.feedScreenCollectionView.contentOffset.y < self.feedScreenCollectionView.frame.maxY) && (self.feedScreenCollectionView.contentOffset.y > 0) {
print("you scrolled down,content offSet: \(scrollView.contentOffset.y)->\(self.feedScreenCollectionView.contentOffset.y)")
header.isHidden = true
}
else{
self.lastContentOffset = scrollView.contentOffset.y
print("content offSet: \(scrollView.contentOffset.y)")
print("Nothing happened")
// self.headerDelegate?.hideHeaderView(hide: true)
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardFrameChangeNotification(notification:)), name: NSNotification.Name.UIKeyboardWillChangeFrame, object: nil)
}
var offsetY:CGFloat = 0
#objc func keyboardFrameChangeNotification(notification: Notification) {
}
}
class feedViewCell:UICollectionViewCell{
#IBOutlet weak var feedImageView: UIImageView!
#IBOutlet weak var feedLabel: UILabel!
let keyboardSlider = KeyboardSlider()
override func awakeFromNib() {
super.awakeFromNib()
feedLabel.translatesAutoresizingMaskIntoConstraints = false
feedImageView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
feedImageView.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
feedImageView.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true
feedImageView.bottomAnchor.constraint(equalTo: self.feedLabel.topAnchor).isActive = true
feedImageView.translatesAutoresizingMaskIntoConstraints = false
feedLabel.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
feedLabel.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true
feedLabel.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
feedLabel.topAnchor.constraint(equalTo: self.feedImageView.bottomAnchor).isActive = true
feedLabel.textAlignment = .center
}
}
class feedViewHeader:UICollectionReusableView,UITextFieldDelegate,UICollectionViewDelegate{
#IBOutlet weak var feedSearchBar: UITextField!
var delegateWorked:printDelegateWorkedDelegate?
var updateCV:updateCollectionView?
var VC:ViewController!
var collectionView:UICollectionView?
var stringToBeSet = "String to be set"
override func awakeFromNib() {
super.awakeFromNib()
feedSearchBar.delegate = self
feedSearchBar.autocorrectionType = .no
feedSearchBar.keyboardType = .default
feedSearchBar.addTarget(self, action: #selector(feedViewHeader.textFieldDidChange), for: .editingChanged)
self.feedSearchBar.borderStyle = .roundedRect
self.feedSearchBar.layer.borderColor = UIColor.black.cgColor
self.feedSearchBar.layer.borderWidth = 4
var searchBarHeight = self.feedSearchBar.bounds.height
self.feedSearchBar.placeholder = "Tap To Search"
self.feedSearchBar.returnKeyType = .search
self.feedSearchBar.rightViewMode = .always
}
#objc func viewTapped(gestureRecognizer:UIGestureRecognizer){
if feedSearchBar.isFirstResponder{
feedSearchBar.resignFirstResponder()
}
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
VC.feedScreenCollectionView.reloadData()
//VC.feedScreenCollectionView.reloadSections([0])
return true
}
/// Helper to dismiss keyboard
#objc func didStopEditing() {
}
func textFieldDidEndEditing(_ textField: UITextField) {
UIView.setAnimationCurve(UIViewAnimationCurve.easeInOut)
UIView.animate(withDuration: 0.2) {
self.VC.view.frame.origin.y = 0
}
}
#objc func textFieldDidChange(){
guard(!(feedSearchBar.text?.isEmpty)!) else{
VC.currentTagsArray = VC.genericTagsArray
VC.feedScreenCollectionView.reloadData()
return
}
VC.currentTagsArray = VC.genericTagsArray.filter({letter -> Bool in
if feedSearchBar.text!.count > letter.count{
return false
}
let stringRange = letter.index(letter.startIndex, offsetBy: feedSearchBar.text!.count)
let subword = letter[..<stringRange]
return subword.lowercased().contains(feedSearchBar.text!.lowercased())
})
if VC.currentTagsArray.isEmpty{
VC.currentTagsArray.insert(feedSearchBar.text!, at: 0)
}
VC.feedScreenCollectionView.reloadItems(inSection: 0)
}
}
extension Notification.Name{
static let showKeyboard = Notification.Name("showKeyboard")
}
class KeyboardSlider: NSObject {
// variables to hold and process information from the view using this class
weak var view: UIView?
#objc func keyboardWillShow(notification: NSNotification) {
// method to move keyboard up
// view?.frame.origin.y = 0 - getKeyboardHeight(notification as Notification)
print("made it to keyboard will show")
}
func getKeyboardHeight(_ notification:Notification) -> CGFloat {
// get exact height of keyboard on all devices and convert to float value to return for use
let userInfo = notification.userInfo
let keyboardSize = userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue
return keyboardSize.cgRectValue.height
}
func subscribeToKeyboardNotifications(view: UIView) {
// assigning view to class' counterpart
self.view = view
// when UIKeyboardWillShow do keyboardWillShow function
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(notification:)), name: .UIKeyboardWillShow, object: nil)
}
func unsubscribeFromKeyboardNotifications() {
NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillShow, object: nil)
}
}
class blankView:UICollectionReusableView{
}
extension UICollectionView{
func reloadItems(inSection section:Int = 0) {
print("made it to reload")
for i in 0..<self.numberOfItems(inSection: section){
self.reloadItems(at: [IndexPath(item: i, section: section)])
}
}
}

You can call the method
Objective-C
- (void)reloadItemsAtIndexPaths:(NSArray *)indexPaths
Swift
func reloadItems(at indexPaths: [IndexPath])
Where you can pass an array of indexPaths that you want to update.
More info in the Apple Documentation

Related

Can't pass data from cell to UIView

I want to change value of temperature in UIView (blue square), when I tapping on cell in collection view (date). I am trying to use delegate for it, but i can't do it. I want to change value of selectedIndex and pass this value to UIView in viewModel.weather[selectedIndex]
ViewController with collectionView
protocol WeekCityWeatherViewControllerDelegate {
func reloadWeatherData()
}
class WeekCityWeatherViewController: UIViewController {
var delegate: WeekCityWeatherViewControllerDelegate?
...
extension WeekCityWeatherViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 7
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "dateCell", for: indexPath) as! DateWeekWeatherScreenCell
cell.layer.cornerRadius = 5
if selectedIndex == indexPath.row {
cell.backgroundColor = UIColor.blue
cell.dateLabel.textColor = UIColor.white
} else {
cell.backgroundColor = UIColor.white
cell.dateLabel.textColor = UIColor.black
}
if let dateInt = viewModel.weather.first?.week.daily[indexPath.item].dt {
let timeInterval = TimeInterval(dateInt)
let myNSDate = Date(timeIntervalSince1970: timeInterval)
let dateFormatter2 = DateFormatter()
dateFormatter2.dateFormat = "dd/MM E"
dateFormatter2.locale = Locale(identifier: "ru_RU")
let dateString = dateFormatter2.string(from: myNSDate)
cell.dateLabel.text = dateString
}
return cell
}
}
extension WeekCityWeatherViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collectionView.frame.width / 5, height: collectionView.frame.height)
}
}
extension WeekCityWeatherViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
selectedIndex = indexPath.row
self.dateCollectionView.reloadData()
delegate?.reloadWeatherData()
}
}
UIView
class DayView: UIView, WeekCityWeatherViewControllerDelegate {
var viewModel: GeneralViewModel
var selectedIndex: Int
var currentIndex: Int
init(frame: CGRect, viewModel: GeneralViewModel, selectedIndex: Int, currentIndex: Int) {
self.viewModel = viewModel
self.selectedIndex = selectedIndex
self.currentIndex = currentIndex
super.init(frame: frame)
createSubviews()
reloadWeatherData()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func reloadWeatherData() {
self.reloadInputViews()
}
private func createSubviews() {
let weekCityWeatherViewController = WeekCityWeatherViewController(viewModel: viewModel, currentIndex: currentIndex)
weekCityWeatherViewController.delegate = self
let tempLabel = UILabel()
tempLabel.textColor = .black
tempLabel.translatesAutoresizingMaskIntoConstraints = false
tempLabel.font = UIFont(name: "Rubik-Regular", size: 30)
if let maxTemp = viewModel.weather[selectedIndex].week.daily[0].temp.max {
tempLabel.text = String(format: "%.0f", maxTemp) + " " + "°"
}
addSubview(tempLabel)
NSLayoutConstraint.activate([
tempLabel.centerXAnchor.constraint(equalTo: centerXAnchor),
tempLabel.topAnchor.constraint(equalTo: topAnchor, constant: 10),
tempLabel.heightAnchor.constraint(equalToConstant: 50),
tempLabel.widthAnchor.constraint(equalToConstant: 50),
])
}
}
create a protocol and send the target cell as a parameter in the trigger method.
Here is the steps;
// 1. protocol and trigger method
protocol ItemTableViewCellDelegate: AnyObject {
func doneButtonTapped(_ cell: ItemTableViewCell)
}
// 2. implement delegate and send the trigger method in your case
class ItemTableViewCell: UITableViewCell {
weak var delegate: ItemTableViewCellDelegate?
//...
}
// MARK: - Implementation
extension ViewController: ItemTableViewCellDelegate {
func doneButtonTapped(_ cell: ItemTableViewCell) {
// 3. find the index of item you tapped the button, via cell as a parameter
guard let indexPath = tableView.indexPath(for: cell) else { return }
var item = MainScreenModel.dummyModel[indexPath.row]
// 4. reload the cell by indexpath
tableView.reloadRows(at: [indexPath], with: .automatic)
}
}

How to track a CollectionView cell by time in Swift

I've been working on a feature to detect when a user sees a post and when he doesn't. When the user does see the post I turn the cell's background into green, when it doesn't then it stays red. Now after doing that I notice that I turn on all the cells into green even tho the user only scroll-down the page, so I added a timer but I couldn't understand how to use it right so I thought myself maybe you guys have a suggestion to me cause I'm kinda stuck with it for like two days :(
Edit: Forgot to mention that a cell marks as seen if it passes the minimum length which is 2 seconds.
Here's my Code:
My VC(CollectionView):
import UIKit
class ViewController: UIViewController,UIScrollViewDelegate {
var impressionEventStalker: ImpressionStalker?
var impressionTracker: ImpressionTracker?
var indexPathsOfCellsTurnedGreen = [IndexPath]() // All the read "posts"
#IBOutlet weak var collectionView: UICollectionView!{
didSet{
collectionView.contentInset = UIEdgeInsets(top: 20, left: 0, bottom: 0, right: 0)
impressionEventStalker = ImpressionStalker(minimumPercentageOfCell: 0.70, collectionView: collectionView, delegate: self)
}
}
func registerCollectionViewCells(){
let cellNib = UINib(nibName: CustomCollectionViewCell.nibName, bundle: nil)
collectionView.register(cellNib, forCellWithReuseIdentifier: CustomCollectionViewCell.reuseIdentifier)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
collectionView.delegate = self
collectionView.dataSource = self
registerCollectionViewCells()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
impressionEventStalker?.stalkCells()
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
impressionEventStalker?.stalkCells()
}
}
// MARK: CollectionView Delegate + DataSource Methods
extension ViewController: UICollectionViewDelegateFlowLayout, UICollectionViewDataSource{
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 100
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let customCell = collectionView.dequeueReusableCell(withReuseIdentifier: CustomCollectionViewCell.reuseIdentifier, for: indexPath) as? CustomCollectionViewCell else {
fatalError()
}
customCell.textLabel.text = "\(indexPath.row)"
if indexPathsOfCellsTurnedGreen.contains(indexPath){
customCell.cellBackground.backgroundColor = .green
}else{
customCell.cellBackground.backgroundColor = .red
}
return customCell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 150, height: 225)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 20) // Setting up the padding
}
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
//Start The Clock:
if let trackableCell = cell as? TrackableView {
trackableCell.tracker = ImpressionTracker(delegate: trackableCell)
trackableCell.tracker?.start()
}
}
func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
//Stop The Clock:
(cell as? TrackableView)?.tracker?.stop()
}
}
// MARK: - Delegate Method:
extension ViewController:ImpressionStalkerDelegate{
func sendEventForCell(atIndexPath indexPath: IndexPath) {
guard let customCell = collectionView.cellForItem(at: indexPath) as? CustomCollectionViewCell else {
return
}
customCell.cellBackground.backgroundColor = .green
indexPathsOfCellsTurnedGreen.append(indexPath) // We append all the visable Cells into an array
}
}
my ImpressionStalker:
import Foundation
import UIKit
protocol ImpressionStalkerDelegate:NSObjectProtocol {
func sendEventForCell(atIndexPath indexPath:IndexPath)
}
protocol ImpressionItem {
func getUniqueId()->String
}
class ImpressionStalker: NSObject {
//MARK: Variables & Constants
let minimumPercentageOfCell: CGFloat
weak var collectionView: UICollectionView?
static var alreadySentIdentifiers = [String]()
weak var delegate: ImpressionStalkerDelegate?
//MARK: Initializer
init(minimumPercentageOfCell: CGFloat, collectionView: UICollectionView, delegate:ImpressionStalkerDelegate ) {
self.minimumPercentageOfCell = minimumPercentageOfCell
self.collectionView = collectionView
self.delegate = delegate
}
// Checks which cell is visible:
func stalkCells() {
for cell in collectionView!.visibleCells {
if let visibleCell = cell as? UICollectionViewCell & ImpressionItem {
let visiblePercentOfCell = percentOfVisiblePart(ofCell: visibleCell, inCollectionView: collectionView!)
if visiblePercentOfCell >= minimumPercentageOfCell,!ImpressionStalker.alreadySentIdentifiers.contains(visibleCell.getUniqueId()){ // >0.70 and not seen yet then...
guard let indexPath = collectionView!.indexPath(for: visibleCell), let delegate = delegate else {
continue
}
delegate.sendEventForCell(atIndexPath: indexPath) // send the cell's index since its visible.
ImpressionStalker.alreadySentIdentifiers.append(visibleCell.getUniqueId()) // to avoid double events to show up.
}
}
}
}
// Func Which Calculate the % Of Visible of each Cell:
private func percentOfVisiblePart(ofCell cell:UICollectionViewCell, inCollectionView collectionView:UICollectionView) -> CGFloat{
guard let indexPathForCell = collectionView.indexPath(for: cell),
let layoutAttributes = collectionView.layoutAttributesForItem(at: indexPathForCell) else {
return CGFloat.leastNonzeroMagnitude
}
let cellFrameInSuper = collectionView.convert(layoutAttributes.frame, to: collectionView.superview)
let interSectionRect = cellFrameInSuper.intersection(collectionView.frame)
let percentOfIntersection: CGFloat = interSectionRect.height/cellFrameInSuper.height
return percentOfIntersection
}
}
ImpressionTracker:
import Foundation
import UIKit
protocol ViewTracker {
init(delegate: TrackableView)
func start()
func pause()
func stop()
}
final class ImpressionTracker: ViewTracker {
private weak var viewToTrack: TrackableView?
private var timer: CADisplayLink?
private var startedTimeStamp: CFTimeInterval = 0
private var endTimeStamp: CFTimeInterval = 0
init(delegate: TrackableView) {
viewToTrack = delegate
setupTimer()
}
func setupTimer() {
timer = (viewToTrack as? UIView)?.window?.screen.displayLink(withTarget: self, selector: #selector(update))
timer?.add(to: RunLoop.main, forMode: .default)
timer?.isPaused = true
}
func start() {
guard viewToTrack != nil else { return }
timer?.isPaused = false
startedTimeStamp = CACurrentMediaTime() // Current Time in seconds.
}
func pause() {
guard viewToTrack != nil else { return }
timer?.isPaused = true
endTimeStamp = CACurrentMediaTime()
print("Im paused!")
}
func stop() {
timer?.isPaused = true
timer?.invalidate()
}
#objc func update() {
guard let viewToTrack = viewToTrack else {
stop()
return
}
guard viewToTrack.precondition() else {
startedTimeStamp = 0
endTimeStamp = 0
return
}
endTimeStamp = CACurrentMediaTime()
trackIfThresholdCrossed()
}
private func trackIfThresholdCrossed() {
guard let viewToTrack = viewToTrack else { return }
let elapsedTime = endTimeStamp - startedTimeStamp
if elapsedTime >= viewToTrack.thresholdTimeInSeconds() {
viewToTrack.viewDidStayOnViewPortForARound()
startedTimeStamp = endTimeStamp
}
}
}
my customCell:
import UIKit
protocol TrackableView: NSObject {
var tracker: ViewTracker? { get set }
func thresholdTimeInSeconds() -> Double //Takes care of the screen's time, how much "second" counts.
func viewDidStayOnViewPortForARound() // Counter for how long the "Post" stays on screen.
func precondition() -> Bool // Checks if the View is full displayed so the counter can go on fire.
}
class CustomCollectionViewCell: UICollectionViewCell {
var tracker: ViewTracker?
static let nibName = "CustomCollectionViewCell"
static let reuseIdentifier = "customCell"
#IBOutlet weak var cellBackground: UIView!
#IBOutlet weak var textLabel: UILabel!
var numberOfTimesTracked : Int = 0 {
didSet {
self.textLabel.text = "\(numberOfTimesTracked)"
}
}
override func awakeFromNib() {
super.awakeFromNib()
cellBackground.backgroundColor = .red
layer.borderWidth = 0.5
layer.borderColor = UIColor.lightGray.cgColor
}
override func prepareForReuse() {
super.prepareForReuse()
print("Hello")
tracker?.stop()
tracker = nil
}
}
extension CustomCollectionViewCell: ImpressionItem{
func getUniqueId() -> String {
return self.textLabel.text!
}
}
extension CustomCollectionViewCell: TrackableView {
func thresholdTimeInSeconds() -> Double { // every 2 seconds counts as a view.
return 2
}
func viewDidStayOnViewPortForARound() {
numberOfTimesTracked += 1 // counts for how long the view stays on screen.
}
func precondition() -> Bool {
let screenRect = UIScreen.main.bounds
let viewRect = convert(bounds, to: nil)
let intersection = screenRect.intersection(viewRect)
return intersection.height == bounds.height && intersection.width == bounds.width
}
}
The approach you probably want to use...
In you posted code, you've created an array of "read posts":
var indexPathsOfCellsTurnedGreen = [IndexPath]() // All the read "posts"
Assuming your real data will have multiple properties, such as:
struct TrackPost {
var message: String = ""
var postAuthor: String = ""
var postDate: Date = Date()
// ... other stuff
}
add another property to track whether or not it has been "seen":
struct TrackPost {
var message: String = ""
var postAuthor: String = ""
var postDate: Date = Date()
// ... other stuff
var hasBeenSeen: Bool = false
}
Move all of your "tracking" code out of the controller, and instead add a Timer to your cell class.
When the cell appears:
if hasBeenSeen for that cell's Data is false
start a 2-second timer
if the timer elapses, the cell has been visible for 2 seconds, so set hasBeenSeen to true (use a closure or protocol / delegate pattern to tell the controller to update the data source) and change the backgroundColor
if the cell is scrolled off-screen before the timer elapses, stop the timer and don't tell the controller anything
if hasBeenSeen is true to begin with, don't start the 2-second timer
Now, your cellForItemAt code will look something like this:
let p: TrackPost = myData[indexPath.row]
customCell.authorLabel.text = p.postAuthor
customCell.dateLabel.text = myDateFormat(p.postDate) // formatted as a string
customCell.textLabel.text = p.message
// setting hasBeenSeen in your cell should also set the backgroundColor
// and will be used so the cell knows whether or not to start the timer
customCell.hasBeenSeen = p.hasBeenSeen
// this will be called by the cell if the timer elapsed
customCell.wasSeenCallback = { [weak self] in
guard let self = self else { return }
self.myData[indexPath.item].hasBeenSeen = true
}
What about a much simpler approach like:
func scrollViewDidScroll(_ scrollView: UIScrollView) {
for subview in collectionView!.visibleCells {
if /* check visible percentage */ {
if !(subview as! TrackableCollectionViewCell).timerRunning {
(subview as! TrackableCollectionViewCell).startTimer()
}
} else {
if (subview as! TrackableCollectionViewCell).timerRunning {
(subview as! TrackableCollectionViewCell).stopTimer()
}
}
}
}
With a Cell-Class extended by:
class TrackableCollectionViewCell {
static let minimumVisibleTime: TimeInterval = 2.0
var timerRunning: Bool = true
private var timer: Timer = Timer()
func startTimer() {
if timerRunning {
return
}
timerRunning = true
timer = Timer.scheduledTimer(withTimeInterval: minimumVisibleTime, repeats: false) { (_) in
// mark cell as seen
}
}
func stopTimer() {
timerRunning = false
timer.invalidate()
}
}

UICollection View "Reload Data" After Scrolling Crash

I have made a collection view with cells arranged in rows in columns using a great tutorial. I have added a button to a toolbar in the main view controller that calls collectionView.reloadData() as I want a user to be able to edit values which will in turn update the datasource and then reload the collection view to show the updates.
Running this on a simulator it works, but if any scrolling takes place it causes this crash *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 0 beyond bounds for empty array'. If no scrolling has taken place then calling collectionView.reloadData() works. I can't find where this empty array is which is causing the crash. I have tried printing all the arrays that are used in the code in the console but none appear to be empty. Have tried commenting out various lines of code to try and narrow down where the problem is, it seems to be something in the override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? block. I have also tried reseting the collection view frame co-ordinates to 0 before reload data is called but that also didn't work. Have been stuck going round in circles for a few days which no luck. Any suggestions as to where I am going wrong would be hugely appreciated! My code so far is below (please excuse the long winded explanation and code);
View Controller
import UIKit
class ViewController: UIViewController {
// variable to contain cell indexpaths sent from collectionViewFlowLayout.
var cellIndexPaths = [IndexPath] ()
#IBOutlet weak var collectionView: UICollectionView!
#IBOutlet weak var collectionViewFlowLayout: UICollectionViewFlowLayout! {
didSet {
collectionViewFlowLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
}
}
#IBAction func editButtonPressed(_ sender: UIButton) {
collectionView.reloadData()
}
#IBAction func doneButtonPressed(_ sender: UIButton) {
}
override func viewDidLoad() {
super.viewDidLoad()
collectionView.delegate = self
collectionView.dataSource = self
}
}
extension ViewController:UICollectionViewDataSource {
func numberOfSections(in collectionView: UICollectionView) -> Int {
2
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
5
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! collectionViewCell
cell.backgroundColor = .green
cell.textLabel.text = "\(indexPath) - AAAABBBBCCCC"
return cell
}
}
extension ViewController:UICollectionViewDelegate, IndexPathDelegate {
func getIndexPaths(indexPathArray: Array<IndexPath>) {
cellIndexPaths = indexPathArray.uniqueValues
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
var cellArray = [UICollectionViewCell] ()
print(cellIndexPaths)
for indexPathItem in cellIndexPaths {
if let cell = collectionView.cellForItem(at: indexPathItem) {
if indexPathItem.section == indexPath.section {
cellArray.append(cell)
}
}
for cells in cellArray {
cells.backgroundColor = .red
Timer.scheduledTimer(withTimeInterval: 0.2, repeats: false) { (timer) in
cells.backgroundColor = .green
}
}
}
}
}
extension ViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let cell = collectionView.cellForItem(at: indexPath)
if let safeCell = cell {
let cellSize = CGSize(width: safeCell.frame.width, height: safeCell.frame.height)
return cellSize
} else {
return CGSize (width: 300, height: 100)
}
}
}
extension Array where Element: Hashable {
var uniqueValues: [Element] {
var allowed = Set(self)
return compactMap { allowed.remove($0) }
}
}
Flow Layout
import UIKit
protocol IndexPathDelegate {
func getIndexPaths(indexPathArray: Array<IndexPath>)
}
class collectionViewFlowLayout: UICollectionViewFlowLayout {
override var collectionViewContentSize: CGSize {
return CGSize(width: 10000000, height: 100000)
}
override func prepare() {
setupAttributes()
indexItemDelegate()
}
// MARK: - ATTRIBUTES FOR ALL CELLS
private var allCellAttributes: [[UICollectionViewLayoutAttributes]] = []
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
var layoutAttributes = [UICollectionViewLayoutAttributes]()
for rowAttrs in allCellAttributes {
for itemAttrs in rowAttrs where rect.intersects(itemAttrs.frame) {
layoutAttributes.append(itemAttrs)
}
}
return layoutAttributes
}
// MARK: - SETUP ATTRIBUTES
var cellIndexPaths = [IndexPath] ()
private func setupAttributes() {
allCellAttributes = []
var xOffset: CGFloat = 0
var yOffset: CGFloat = 0
for row in 0..<rowsCount {
var rowAttrs: [UICollectionViewLayoutAttributes] = []
xOffset = 0
for col in 0..<columnsCount(in: row) {
let itemSize = size(forRow: row, column: col)
let indexPath = IndexPath(row: row, column: col)
let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
attributes.frame = CGRect(x: xOffset, y: yOffset, width: itemSize.width, height: itemSize.height).integral
rowAttrs.append(attributes)
xOffset += itemSize.width
cellIndexPaths.append(indexPath)
}
yOffset += rowAttrs.last?.frame.height ?? 0.0
allCellAttributes.append(rowAttrs)
}
}
// MARK: - CONVERT SECTIONS TO ROWS, ITEMS TO COLUMNS
private var rowsCount: Int {
return collectionView!.numberOfSections
}
private func columnsCount(in row: Int) -> Int {
return collectionView!.numberOfItems(inSection: row)
}
// MARK: - GET CELL SIZE
private func size(forRow row: Int, column: Int) -> CGSize {
guard let delegate = collectionView?.delegate as? UICollectionViewDelegateFlowLayout,
let size = delegate.collectionView?(collectionView!, layout: self, sizeForItemAt: IndexPath(row: row, column: column)) else {
assertionFailure("Implement collectionView(_,layout:,sizeForItemAt: in UICollectionViewDelegateFlowLayout")
return .zero
}
return size
}
private func indexItemDelegate () {
let delegate = collectionView?.delegate as? IndexPathDelegate
delegate?.getIndexPaths(indexPathArray: cellIndexPaths)
}
}
// MARK: - INDEX PATH EXTENSION
//creates index path with rows and columns instead of sections and items
private extension IndexPath {
init(row: Int, column: Int) {
self = IndexPath(item: column, section: row)
}
}
Collection Cell
import UIKit
class collectionViewCell: UICollectionViewCell {
#IBOutlet weak var textLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
contentView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
contentView.leftAnchor.constraint(equalTo: leftAnchor),
contentView.rightAnchor.constraint(equalTo: rightAnchor),
contentView.topAnchor.constraint(equalTo: topAnchor),
contentView.bottomAnchor.constraint(equalTo: bottomAnchor)
])
}
}
I have managed to get around the issue by using the UICollectionView.reloadSections(sections: IndexSet) method. This doesn't cause any crashes. I loop through all sections and add each section to an IndexSet variable then use that in the reload sections method like this;
var indexSet = IndexSet()
let rowCount = collectionView.numberOfSections
for row in 0..<rowCount {
indexSet = [row]
collectionView.reloadSections(indexSet)
}

Implementing search function in UICollectionView in a UICollectionViewCell

I want to implement a search function in UICollectionView in a UICollectionViewCell.
What i'm trying to achieve is a swipe-able pages with search bar.
Previously i achieved multiple pages with a search bar but not able to swipe and to match with my Android application i need to make it swipe-able.
This is the tutorial i followed on how to make it swipe-able
https://www.letsbuildthatapp.com/course_video?id=75
To make this sound less confusing, the main UICollectionView let's call it MainFrame and the MainFrame stores the two UICollectionViewCell which contains their own UICollectionView let's call it ChildFrame.
So i'm able to parse search text from MainFrame to the ChildFrame and filter the data array in the ChildFrame. Checking it on the console log, everything is working as intended. The filtered result is correct. But my ChildFrame is not updating to the latest data array. Here's the code below.
Here's the code for ChildFrame
class ChildFrameCell1: UICollectionViewCell, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
let cellId = "cellId"
var dataArray = [CustomObject]()
var filteredData = [CustomObject]()
var isFiltering: Bool = false
var mainFrame: MainFrame?
lazy var collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 4, left: 0, bottom: 0, right: 0)
layout.minimumInteritemSpacing = 0.0
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.backgroundColor = .white
cv.dataSource = self
cv.delegate = self
return cv
}()
override init(frame: CGRect) {
super.init(frame: frame)
populateCollectionView()
collectionView.register(CustomCell.self, forCellWithReuseIdentifier: cellId)
addSubview(collectionView)
collectionView.anchor(top: self.topAnchor, left: self.leftAnchor, bottom: self.bottomAnchor, right: self.rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func searchTypeAndText(type: String, text: String, filtering: Bool) {
isFiltering = filtering
filteredData = dataArray.filter({( object : CustomObject) -> Bool in
if type == "type" {
return object.type.lowercased().contains(text.lowercased())
} else {
return object.type.lowercased().contains(text.lowercased())
}
})
DispatchQueue.main.async {
self.collectionView.reloadData()
self.collectionView.layoutSubviews()
}
}
func populateCollectionView() {
// load data into dataArray
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if isFiltering {
// during searching this will be triggered and return the correct count for the filteredData
return filteredData.count
} else {
return dataArray.count
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! CustomCell
var object: CustomObject
// isFiltering is always false even when searching
if isFiltering {
// never called
object = filteredData[indexPath.row]
} else {
// always called even when searching
object = dataArray[indexPath.row]
}
// do cell things here
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collectionView.frame.size.width - 8, height: 120)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 4
}
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
cell.contentView.layer.masksToBounds = true
let radius = cell.contentView.layer.cornerRadius
cell.layer.shadowPath = UIBezierPath(roundedRect: cell.bounds, cornerRadius: radius).cgPath
}
}
Here's the code for MainFrame
class MainFrame: UICollectionViewController, UICollectionViewDelegateFlowLayout {
let searchController = UISearchController(searchResultsController: nil)
let cellId = "cellId"
let childFrameCell1Id = "cell1Id"
let childFrameCell2Id = "cell2Id"
var childFrameCell1: ChildFrameCell1!
var childFrameCell2: ChildFrameCell2!
lazy var topBar: CustomTopBar = {
let tb = CustomTopBar()
tb.mainFrame = self
return tb
}() // Top bar sliding indicator
override func viewDidLoad() {
super.viewDidLoad()
configureView()
configureSearchBar()
configureCollectionView()
setupTopBar()
}
func configureCollectionView() {
if let flowLayout = collectionView?.collectionViewLayout as? UICollectionViewFlowLayout {
flowLayout.scrollDirection = .horizontal
flowLayout.minimumLineSpacing = 0
flowLayout.minimumInteritemSpacing = 0
}
collectionView.backgroundColor = .white
collectionView.register(ChildFrameCell1.self, forCellWithReuseIdentifier: childFrameCell1Id)
collectionView.register(ChildFrameCell2.self, forCellWithReuseIdentifier: childFrameCell2Id)
collectionView.translatesAutoresizingMaskIntoConstraints = false
collectionView.isPagingEnabled = true
collectionView.showsHorizontalScrollIndicator = false
collectionView.collectionViewLayout.invalidateLayout()
collectionView.contentInset = UIEdgeInsets(top: 45, left: 0, bottom: 0, right: 0)
collectionView.scrollIndicatorInsets = UIEdgeInsets(top: 45, left: 0, bottom: 0, right: 0)
collectionView.contentInsetAdjustmentBehavior = UIScrollView.ContentInsetAdjustmentBehavior.never
childFrameCell1 = ChildFrameCell1()
childFrameCell2 = ChildFrameCell2()
}
func setupTopBar() {
// add top bar
}
func configureView() {
// set up view
}
func configureSearchBar() {
searchController.searchResultsUpdater = self
searchController.obscuresBackgroundDuringPresentation = false
searchController.searchBar.placeholder = "Search"
searchController.searchBar.tintColor = .white
searchController.searchBar.barTintColor = .white
if let tf = searchController.searchBar.value(forKey: "searchField") as? UITextField {
tf.textColor = UIColor.white
if let backgroundView = tf.subviews.first {
backgroundView.backgroundColor = .white
backgroundView.layer.cornerRadius = 10
backgroundView.clipsToBounds = true
}
}
navigationItem.searchController = searchController
definesPresentationContext = true
searchController.searchBar.scopeButtonTitles = ["Type1", "Type2"]
searchController.searchBar.delegate = self
}
func searchBarIsEmpty() -> Bool {
return searchController.searchBar.text?.isEmpty ?? true
}
func isFiltering() -> Bool {
let searchBarScopeIsFiltering = searchController.searchBar.selectedScopeButtonIndex != 0
return searchController.isActive && (!searchBarIsEmpty() || searchBarScopeIsFiltering)
}
func filterContentForSearchText(_ searchText: String, scope: String = "Location") {
let visibleRect = CGRect(origin: collectionView.contentOffset, size: collectionView.bounds.size)
let visiblePoint = CGPoint(x: visibleRect.midX, y: visibleRect.midY)
let visibleIndexPath = collectionView.indexPathForItem(at: visiblePoint)
if visibleIndexPath?.row == 0 {
// find the current view and parse search text into childFrame
childFrameCell1.searchTypeAndText(type: scope, text: searchText, filtering: isFiltering())
} else {
childFrameCell2.searchTypeAndText(type: scope, text: searchText, filtering: isFiltering())
}
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 2 // 2 pages
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let id: String
currentView = indexPath.item
if currentView == 0 {
id = childFrameCell1Id
} else {
id = childFrameCell2Id
}
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: id, for: indexPath) as! ChildFrameCell1
cell.mainFrame = self
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: view.frame.width, height: view.frame.height - 50)
}
}
extension MainFrame: UISearchResultsUpdating {
func updateSearchResults(for searchController: UISearchController) {
let searchBar = searchController.searchBar
let scope = searchBar.scopeButtonTitles![searchBar.selectedScopeButtonIndex]
filterContentForSearchText(searchController.searchBar.text!, scope: scope)
}
}
}
extension MainFrame: UISearchBarDelegate {
func searchBar(_ searchBar: UISearchBar, selectedScopeButtonIndexDidChange selectedScope: Int) {
filterContentForSearchText(searchBar.text!, scope: searchBar.scopeButtonTitles![selectedScope])
}
}
ChildFrameCell2 is a subclass of ChildFrameCell1
When i enter text in the search bar in the MainFrame, it doesn't reload the collectionView to the new filteredData even if filteredData contains data.
I've been trying many ways to do this but still unable to reload the collectionView.
I even tried using DispatchGroup but the result is still the same. Filtering is not an issue, everything works accordingly except for reloading the collectionView.
Thank you for reading this lengthy post.
If you need the code from the MainFrame do let me know.

Segue not performing swift

This is the code form the 'Opgeslagen Rooster' Viewcontroller
class vcSavedTimetable: UIViewController {
#IBOutlet weak var noTimetableSavedLabel: UILabel!
var timetable: Timetable!
override func viewWillAppear(_ animated: Bool) {
prepareView()
}
override func viewWillDisappear(_ animated: Bool) {
noTimetableSavedLabel.isHidden = true
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showSavedTimetable"{
let viewController = segue.destination as! vcTimetableShow
viewController.timetable = self.timetable
viewController.willSave = false
}
}
func prepareView(){
let savedTimetable = getTimetableFromUserDefaults()
guard (savedTimetable != nil) else {
noTimetableSavedLabel.isHidden = false
return
}
self.timetable = savedTimetable!
performSegue(withIdentifier: "showSavedTimetable", sender: nil)
}
func getTimetableFromUserDefaults()->Timetable?{
let timetableID = UserDefaults.standard.string(forKey: "savedTimetableID")
if isConnectedToNetwork(){
if let id = timetableID{
let table = Timetable(timetableID: id)
return table
}
}
let val = UserDefaults.standard.data(forKey: "savedTimetable")
if let data = val{
let table = NSKeyedUnarchiver.unarchiveObject(with: data) as! Timetable
return table
}
return nil
}
}
This is the code of the destination viewcontroller 'Rooster Bekijken'. The viewdidload() function should be executed, which doesn't happen. I tested it using breakpoints
//
// vcTimetableShowCollectionViewController.swift
// Ostrea Rooster App
//
// Created by Giel-Jan Looij on 14-12-16.
// Copyright © 2016 GJ-Computers. All rights reserved.
//
import UIKit
private let reuseIdentifier = "Cell"
class vcTimetableShow: UICollectionViewController, UICollectionViewDelegateFlowLayout {
weak var timetable: Timetable?
var willSave = false
//UIVIEWCONTROLLER
override func viewDidLoad() {
super.viewDidLoad()
collectionView?.delegate = self
collectionView?.register( UINib(nibName: "timetableViewCell", bundle: nil), forCellWithReuseIdentifier: "lessonEntry")
NotificationCenter.default.addObserver(self, selector: #selector(self.dataDidArrive(_:)), name: .didFetchTimetable, object: nil)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
deinit {
NotificationCenter.default.removeObserver(self)
}
//METHODES
func dataDidArrive(_ notification: NSNotification){
collectionView?.reloadData()
if let cv = self.collectionView {
cv.layoutIfNeeded()
let indexPath = IndexPath(item: 0, section: getDayOfWeek()-1)
if let attributes = cv.layoutAttributesForSupplementaryElement(ofKind: UICollectionElementKindSectionHeader, at: indexPath) {
let topOfHeader = CGPoint(x: 0, y: attributes.frame.origin.y - cv.contentInset.top)
cv.setContentOffset(topOfHeader, animated:true)
}
}
if willSave {
UserDefaults.standard.set(timetable!.timetableID, forKey: "savedTimetableID")
let encodedTimetable = NSKeyedArchiver.archivedData(withRootObject: self.timetable!)
UserDefaults.standard.set(encodedTimetable, forKey: "savedTimetable")
UserDefaults.standard.synchronize()
}
}
func getDayOfWeek()->Int {
let myCalendar = Calendar(identifier: .gregorian)
let weekDay = myCalendar.component(.weekday, from: Date())
if weekDay > 6 || weekDay < 2{
return 5
}
return weekDay-1
}
//UICOLLECTIONVIEW DATASOURCE
override func numberOfSections(in collectionView: UICollectionView) -> Int {
return timetable!.days.count
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return timetable!.days[section+1]!.hour.count * 2
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if indexPath.item % 2 == 0{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "hourIndicator", for: indexPath)
let dayLabel = cell.viewWithTag(1) as! UILabel
let dayIndex = indexPath.section + 1
let hourIndex = indexPath.item/2 + 1
let cellHour = timetable!.days[dayIndex]!.hour[hourIndex]!
dayLabel.text = String(cellHour.hourID)
cell.layer.borderColor = UIColor.black.cgColor
cell.layer.borderWidth = 2
return cell
}else{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "lessonEntry", for: indexPath) as! timetableViewCell
let dayIndex = indexPath.section + 1
let hourIndex = indexPath.item/2 + 1
let cellHour = timetable!.days[dayIndex]!.hour[hourIndex]!
cell.cellHour = cellHour
cell.prepareCell()
return cell
}
}
override func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
let cell = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "dayHeaderCell", for: indexPath as IndexPath)
let dayIndex = indexPath.section + 1
let dayName = timetable!.days[dayIndex]!.dayName
let day = cell.viewWithTag(1) as! UILabel
day.text = dayName
if timetable!.days[dayIndex]!.dayID == getDayOfWeek(){
cell.backgroundColor = UIColor.lightGray
}
return cell
}
//UICOLLECTIONVIEW DELEGATE
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
if indexPath.item % 2 == 0{
let dayIndex = indexPath.section + 1
let hourIndex = indexPath.item/2 + 1
let cellHour = timetable!.days[dayIndex]!.hour[hourIndex]!
var cellHeight: CGFloat = 0
for _ in cellHour.lessons{
cellHeight += 23
}
if cellHour.notice != ""{
cellHeight += 23
}
return CGSize(width: 50, height: cellHeight)
}else{
let dayIndex = indexPath.section + 1
let hourIndex = indexPath.item/2 + 1
let cellHour = timetable!.days[dayIndex]!.hour[hourIndex]!
var cellHeight: CGFloat = 0
for _ in cellHour.lessons{
cellHeight += 23
}
if cellHour.notice != ""{
cellHeight += 23
}
return CGSize(width: 226.0, height: cellHeight)
}
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 1
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
let edgeInset: CGFloat = (collectionView.frame.width - (226.0 + 50.0))/2
return UIEdgeInsets.init(top: 0, left: edgeInset, bottom: 30, right: edgeInset)
}
}
I'm trying to segue from 'Opgeslagen Rooster' to 'Rooster Bekijken', but it doesn't work. It is running in the main thread (thread 1), so that isn't the problem. It gets to prepare(for segue...), does everything. After that the view should change right? But that doesn't happen. The viewdidload method of the 'Rooster Bekijken' ViewController doesn't get called. It hangs after the last line in the pepare(for segue ....) function.
Why doesn't it change to the other viewcontroller? It does work when im accessing the 'Rooster Bekijken' viewcontroller form the other viewcontroller which segue's to it.

Resources