The problem is that the text labels are missing on some cells and when I click the like button, the text label is supposed to update the label but instead, the whole collection view disappears. The text labels are fetching the numbers from firebase.
Here is the relevant code:
HomeController.swift:
class HomeController: UICollectionViewController, UICollectionViewDelegateFlowLayout, HomePostCellDelegate {
var hpc: HomePostCell!
let cellId = "cellId"
override func viewDidLoad() {
super.viewDidLoad()
collectionView?.register(HomePostCell.self, forCellWithReuseIdentifier: cellId)
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! HomePostCell
self.hpc = cell
cell.post = posts[indexPath.item]
cell.delegate = self
return cell
}
#objc func reload(likesLabelNotification: Notification) {
guard let likesLabel = likesLabelNotification.userInfo?["likesLabelInfo"] as? UILabel else { return }
guard let indexPath = collectionView?.indexPath(for: hpc) else { return }
let post = self.posts[indexPath.item]
guard let postId = post.id else { return }
let postUserId = post.user.uid
let numOfLikesRef = FIRDatabase.database().reference().child("likes").child(postId)
numOfLikesRef.observe(.value, with: { (snapshot: FIRDataSnapshot!) in
likesLabel.isHidden = false
let numOfChildrens = snapshot.childrenCount
likesLabel.text = "\(numOfChildrens)"
}, withCancel: { (error) in
print("failed to fetch num of posts: ",error)
})
self.posts[indexPath.item] = post
self.collectionView?.reloadData()
}
func didPressShareButton(for cell: HomePostCell) {
guard let indexPath = collectionView?.indexPath(for: cell) else { return }
let post = self.posts[indexPath.item]
guard let url = NSURL(string: post.videoUrl) else { return }
let activityViewController = UIActivityViewController(
activityItems: ["Check out this video I found on Vlogger: \(url)"],applicationActivities: nil)
present(activityViewController, animated: true, completion: nil)
}
func didLike(for cell: HomePostCell) {
guard let indexPath = collectionView?.indexPath(for: cell) else { return }
var post = self.posts[indexPath.item]
guard let postId = post.id else { return }
guard let uid = FIRAuth.auth()?.currentUser?.uid else { return }
let values = [uid : post.hasLiked == true ? 0 : 1]
FIRDatabase.database().reference().child("likes").child(postId).updateChildValues(values) { (err, _) in
if let err = err {
print("Failed to like post", err)
return
}
post.hasLiked = !post.hasLiked
self.posts[indexPath.item] = post
self.collectionView?.reloadData()
}
}
HomePostCell.swift:
protocol HomePostCellDelegate {
func didLike(for cell: HomePostCell)
}
class HomePostCell: UICollectionViewCell {
var delegate: HomePostCellDelegate?
lazy var likeButton: UIButton = {
let button = UIButton(type: .system)
button.setImage(#imageLiteral(resourceName: "heart_unselected").withRenderingMode(.alwaysOriginal), for: .normal)
button.addTarget(self, action: #selector(handleLike), for: .touchUpInside)
return button
}()
#objc func handleLike() {
delegate?.didLike(for: self)
}
lazy var likesLabel: UILabel = {
let label = UILabel()
label.font = UIFont(name: "AvenirNext-Regular", size: 20)
label.textColor = UIColor.black
label.isHidden = true
let userInfo = ["likesLabelInfo": label]
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "refresh"), object: nil, userInfo: userInfo)
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
}
static let homePostCellNotificationName = NSNotification.Name(rawValue: "homePostCellRaw")
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Please comment below if you have any questions!
Thanks in advance!
Related
I'd like to show the CollectionView inside the B ViewController using the A ViewController's button. Image.
The information in the image is in the json file.
Gets the information and invokes the image in the Asset file.
There's no problem getting the data in json.
But nothing appears. Using the buttons in the A ViewController,
What's the problem?
The bottom is my code.
Thank you.
A ViewController
//MARK: 4. #objc Button Action
#objc func topHand(){
let cham = ChampViewViewController()
cham.modalPresentationStyle = .fullScreen
present(cham, animated: true, completion: nil)
}
B ViewController
class ChampViewViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource{
var nameArrayCount = 0
var nameArray = [String]()
private var collectionView : UICollectionView?
override func viewDidLoad() {
super.viewDidLoad()
let indenti = "top"
let layout = UICollectionViewLayout()
getJson(line: indenti)
collectionView = UICollectionView(frame: .zero,collectionViewLayout: layout)
collectionView?.delegate = self
collectionView?.dataSource = self
collectionView?.register(ChamCellCollectionViewCell.self, forCellWithReuseIdentifier: ChamCellCollectionViewCell.identifier)
guard let collectionsView = collectionView else { return }
view.addSubview(collectionsView)
collectionsView.frame = view.bounds
}
private func getJson(line:String){
let cellUrl = Bundle.main.url(forResource: line, withExtension: "json")
let cellData = NSData(contentsOf: cellUrl!)
do {
let modelJson = try JSONSerialization.jsonObject(with: cellData! as Data, options: .allowFragments ) as! NSArray
var models : [Model] = []
modelJson.forEach { json in
guard let dic = json as? [String : AnyObject] else {
return
}
let newModel = Model(name: dic["이름"] as! String,
line: dic["주라인"] as! String, type:
dic["성향"] as! String,
hp: dic["체력"] as! Int,
hpRe: dic["추가체력"] as! Int,
attackPower: dic["공격력"] as! Double,
attackPowerRe: dic["추가공격력"] as! Double,
attackSpeed: dic["공속"] as! Double,
attackSpeedRe: dic["추가공속"] as! Double,
defensive: dic["방어력"] as! Double,
defensiveRe: dic["추가방어력"] as! Double,
magicDefensive: dic["마저"] as! Double,
magicDefensiveRe: dic["추가마저"] as! Double,
row: dic["row"] as! Int,
column: dic["column"] as! Int)
models.append(newModel)
}
for data in models {
let key = data.name
nameArray.append(key)
}
nameArrayCount = nameArray.count
}catch {
fatalError()
}
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return nameArrayCount
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ChamCellCollectionViewCell.identifier, for: indexPath) as? ChamCellCollectionViewCell else {
fatalError()
}
cell.imageConfigure(with: UIImage(named:"가렌"))
return cell
}
}
B ViewController CollectionViewCell Class
import UIKit
class ChamCellCollectionViewCell : UICollectionViewCell{
static let identifier = "ChamCellCollectionViewCell"
private let imageView : UIImageView = {
let image = UIImageView()
image.contentMode = .scaleAspectFit
return image
}()
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func imageConfigure(with image : UIImage?) {
imageView.image = image
}
override func layoutSubviews() {
super.layoutSubviews()
imageView.frame = contentView.bounds
}
override func prepareForReuse() {
super.prepareForReuse()
imageView.image = nil
}
}
Add this code to getJson() before the catch block:
DispatchQueue.main.async { [weak self] in
self?.collectionView?.reloadData()
}
Try with a didSet for your nameArrayCount.
var nameArrayCount = 0{
didSet{
collectionView.reloadData()
}
}
I've been trying to tackle this issue for hours, I know my code isn't the fanciest but i just can't seem to pin point the problem. I have an array of NSmanagedobject that has attributes in it. if the attribute of "isComplete" is true i want the background color of my cell to be green. I have a custom view that creates a new nsmanaged object and adds it to the tableview. where by default it should add a white background cell. I know there's a lot of code to explain but it's been hours and i just can't figure out why my tableview is loading the cells data and configuration incorrectly. this is my view controller with the tableview inside.
import CoreData
import UIKit
var coreTasks: [NSManagedObject] = []
var taskAdditionView:TaskAdditionView!
let appDel : AppDelegate = UIApplication.shared.delegate as! AppDelegate
let context: NSManagedObjectContext = appDel.persistentContainer.viewContext
class HomeViewController: UIViewController {
#IBOutlet var addTaskFunction: UIBarButtonItem!
#IBOutlet var homeTableView: UITableView!
var todaysdeadlineLabel: UILabel!
#IBAction func addTaskFunctions(_ sender: UIBarButtonItem) {
animateIn()
addTaskFunction.isEnabled = false
}
func animateIn(){
taskAdditionView = TaskAdditionView() // the view where i create a new nsmanaged object
view.addSubview(taskAdditionView)
view.bringSubviewToFront(taskAdditionView)
}
func tableviewsConstraints(){
homeTableView.translatesAutoresizingMaskIntoConstraints = false
homeTableView.layer.cornerRadius = 4
homeTableView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 20).isActive = true
homeTableView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -20).isActive = true
homeTableView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 120).isActive = true
homeTableView.heightAnchor.constraint(equalToConstant: homeTableView.rowHeight * 3).isActive = true
homeTableView.layer.borderWidth = 0.5
homeTableView.layer.borderColor = UIColor
.black.cgColor
}
override func viewDidLoad() {
super.viewDidLoad()
loadTasks()
tableviewsConstraints()
NotificationCenter.default.addObserver(self, selector: #selector(reloadTable), name: NSNotification.Name(rawValue: "reloadTableNotification"), object: nil)
homeTableView.delegate = self
homeTableView.dataSource = self
UNUserNotificationCenter.current().requestAuthorization(options: [.alert,.sound,.badge]) { (didallow, error) in
}
}
#objc func reloadTable(notification:Notification){
animateOut()
DispatchQueue.main.async {
self.homeTableView.reloadData()
self.addTaskFunction.isEnabled = true
print("reloadTable() fired!")
}
}
}
extension HomeViewController: UITableViewDelegate,UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return coreTasks.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let task = coreTasks[indexPath.row] // creating a new task from the already stored task depending on the indexpath.row if indexPath.row is 3 then the task is tasks[3]
let cell = tableView.dequeueReusableCell(withIdentifier: "taskCell") as! CustomCell // setting the identifier ( we have already set in the storyboard, the class of our cells to be our custom cell)
cell.setTask(task: task) // this changes the label and date text since an instance of the task contains both the task and the date
print("CellData Task :", task.value(forKey: "isComplete") as! Bool, task.value(forKey: "name") as! String)
if (task.value(forKey: "isComplete") as! Bool == true){
cell.labelsToYellow()
cell.backgroundColor = Colors.greencomplete
cell.selectionStyle = .none
}
return cell
}
// Leading Swipe Action
func tableView(_ tableView: UITableView, leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let task = coreTasks[indexPath.row]
let complete = markComplete(at: indexPath)
if !(task.value(forKey: "isComplete") as! Bool){
return UISwipeActionsConfiguration(actions: [complete])
} else {
return UISwipeActionsConfiguration(actions: [])
}
}
// Mark as Completed Task
func markComplete(at: IndexPath) -> UIContextualAction {
let df = DateFormatter()
df.dateFormat = "dd-MM-yyyy" // assigning the date format
let now = df.string(from: Date()) // extracting the date with the given format
let cell = homeTableView.cellForRow(at: at) as! CustomCell
let task = coreTasks[at.row]
let completeActionImage = UIImage(named: "AddTask")?.withTintColor(.white)
let action = UIContextualAction(style: .normal, title: "Complete") { (action, view, completion) in
task.setValue(!(task.value(forKey: "isComplete") as! Bool), forKey: "isComplete")
self.homeTableView.cellForRow(at: at)?.backgroundColor = task.value(forKey: "isComplete") as! Bool ? Colors.greencomplete : .white
cell.backgroundColor = Colors.greencomplete
cell.labelsToYellow()
task.setValue("Finished " + now, forKey: "date")
do {
try
context.save()
self.homeTableView.reloadData()
} catch {
print("Markcomplete save error")
}
// cell.displayIcons(task: task)
completion(true)
}
//action.image = #imageLiteral(resourceName: "AddTask")
action.image = completeActionImage
action.backgroundColor = Colors.greencomplete
return action
}
// Trailing Swipe Actions
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let task = coreTasks[indexPath.row]
let important = importantAction(at: indexPath)
let delete = deleteAction(at: indexPath)
if task.value(forKey: "isComplete") as? Bool == true {
return UISwipeActionsConfiguration(actions: [delete])
} else {
return UISwipeActionsConfiguration(actions: [delete,important])
}
}
// Delete Action
func deleteAction(at: IndexPath) -> UIContextualAction {
// remove !!!! from coredata memory as well not just array
let deleteActionImage = UIImage(named: "Delete")?.withTintColor(.white)
let action = UIContextualAction(style: .destructive , title: "Delete") { (action, view, completion) in
let objectToDelete = coreTasks.remove(at: at.row)
context.delete(objectToDelete)
self.homeTableView.deleteRows(at: [at], with: .automatic)
do {
try
context.save()
} catch {
print("Problem while saving")
}
completion(true)
}
action.image = deleteActionImage
action.backgroundColor = Colors.reddelete
return action
}
func loadTasks(){
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Tasks")
do {
coreTasks = try context.fetch(request) as! [NSManagedObject]
print("loadTasks() fired!")
} catch let error as NSError {
print("Could not fetch. \(error), \(error.userInfo)")
}
}
}
and this is my taskAddition view
import UserNotifications
import UIKit
import CoreData
class TaskAdditionView: UIView {
var importanceSegmentControl: CustomSegmentControl!
var headerLabel:UILabel!
var taskTextField: CustomTextField!
var submitButton:CustomButton!
var reminderSwitch: UISwitch!
var datePicker: UIDatePicker!
var dateSelected: Date?
var importanceValue: Int16 = 0
override init(frame: CGRect) {
super.init(frame: frame)
} // initiliaze the view like this TaskAdditionView()
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
func setupSwitchPicker(){
reminderSwitch = UISwitch()
reminderSwitch.transform = CGAffineTransform(rotationAngle: CGFloat.pi/2)
datePicker = UIDatePicker()
datePicker.minimumDate = Date()
datePicker.addTarget(self, action: #selector(pickingDate(sender:)), for: .valueChanged)
}
#objc func pickingDate(sender: UIDatePicker){
self.dateSelected = sender.date
print("Date selected: \(dateSelected)")
}
func setupLabel(){
headerLabel = UILabel()
headerLabel?.text = "Add Task"
headerLabel.textAlignment = .center
headerLabel.textColor = .black
headerLabel?.font = UIFont(name: "AvenirNext-Bold", size: 30.0)
headerLabel?.backgroundColor = UIColor.clear
}
#objc func indexChanged(control : CustomSegmentControl) {
// This all works fine and it prints out the value of 3 on any click
switch control.selectedIndex {
case 0:
importanceValue = 0
print(importanceValue)
case 1:
importanceValue = 1
print(importanceValue)
case 2:
importanceValue = 2
print(importanceValue)
default:
break;
} //Switch
} // indexChanged for the Segmented Control
func setupSegmentControl(){
importanceSegmentControl = CustomSegmentControl()
importanceSegmentControl.addTarget(self, action: #selector(indexChanged(control:)),for: UIControl.Event.valueChanged)
}
func setupButton(){
let myAttributes = [ NSAttributedString.Key.font: UIFont(name: "AvenirNext-DemiBold", size: 18.0)! , NSAttributedString.Key.foregroundColor: UIColor.white ]
let myTitle = "Add"
let myAttributedTitle = NSAttributedString(string: myTitle, attributes: myAttributes)
submitButton = CustomButton()
submitButton.setAttributedTitle(myAttributedTitle, for: .normal)
submitButton.addTarget(self, action: #selector(submitFunction(sender:)), for: .touchUpInside)
}
// Submit Function
#objc func submitFunction(sender: CustomButton){
print("Worked")
submitButton.shake()
NotificationCenter.default.post(name: NSNotification.Name("reloadTableNotification") , object: nil)
addTask()
if (reminderSwitch.isOn){
setupNotification()
print(dateSelected)
}
NSLayoutConstraint.deactivate(self.constraints)
removeFromSuperview()
}
func setupTextField(){
taskTextField = CustomTextField()
}
func setupConstraints(){
setupLabel()
setupTextField()
setupSegmentControl()
setupButton()
setupSwitchPicker()
addSubview(headerLabel!)
addSubview(importanceSegmentControl!)
addSubview(taskTextField)
addSubview(submitButton)
reminderSwitch.transform = reminderSwitch.transform.rotated(by: -(.pi/2))
addSubview(reminderSwitch)
addSubview(datePicker)
}
override func didMoveToSuperview() {
setupConstraints()
}
override func removeFromSuperview() {
for view in self.subviews{
view.removeFromSuperview()
}
NSLayoutConstraint.deactivate(self.constraints)
removeAllConstraintsFromView(view: self)
}
func addTask(){
let df = DateFormatter()
df.dateFormat = "dd-MM-yyyy" // assigning the date format
let now = reminderSwitch.isOn ? "Deadline " + df.string(from: dateSelected!) : df.string(from: Date()) // extracting the date with the given format
print("Reminder Switch is ON: ", reminderSwitch.isOn)
// Adding a task to the array
let entity =
NSEntityDescription.entity(forEntityName: "Tasks",
in: context)!
let newTask = NSManagedObject(entity: entity, insertInto: context)
newTask.setValue(taskTextField.text!, forKey: "name")
newTask.setValue(false, forKey: "isComplete")
newTask.setValue(now, forKey: "date")
newTask.setValue(importanceValue, forKey: "importValue")
do {
try
context.save()
coreTasks.append(newTask)
print("addTask() fired!")
} catch {
print("Problem while saving")
}
}
func setupNotification(){
let currentDate = Date()
let interval = dateSelected?.timeIntervalSince(currentDate)
print(interval)
let notifcation = UNMutableNotificationContent()
notifcation.title = "Task Reminder"
notifcation.subtitle = taskTextField.text ?? "Empty"
notifcation.badge = 1
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: interval!, repeats: false)
let request = UNNotificationRequest(identifier: "taskReminder", content: notifcation, trigger: trigger)
UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
taskTextField.endEditing(true)
}
}
func removeAllConstraintsFromView(view: UIView) { for c in view.constraints { view.removeConstraint(c) } }
extension UIView {
func removeAllConstraints() {
let superViewConstraints = superview?.constraints.filter{ $0.firstItem === self || $0.secondItem === self } ?? []
superview?.removeConstraints(superViewConstraints + constraints)
}
}
and this is my custom tableview cell
import UIKit
import CoreData
class CustomCell: UITableViewCell {
#IBOutlet var taskLabel: UILabel!
#IBOutlet var dateLabel: UILabel!
func setTask(task: NSManagedObject ){
taskLabel.text = task.value(forKey: "name") as? String
dateLabel.text = task.value(forKey: "date") as? String
}
func labelsToYellow() {
taskLabel.textColor = .white
dateLabel.textColor = .white
}
func labelsToBlack() {
taskLabel.textColor = .black
dateLabel.textColor = .black
}
Ideally When i create a new task of type nsmanaged object via the task addition. my tableview that is populate by an array of nsmanagedobject should add the task in a cell with a background color white and the task labels accordingly. I have a contextual action that marks a task complete and makes the cell background green. weirdly enough it was working at some point. Now randomly sometimes the task cell is created with a green background and sometime the labels are blank or when i scroll down or up all the labels turn green. I'd really appreciate some help.
I've had this issue before, because TableViewCells are re-used you need to ensure you set the background regardless of if it is default or not.
So when you are adding in the code to set the background to green, add an else statement or before the query set the cell background to white/your default color Issue with UITableViewCells Repeating Content
I have a checkout flow using a UICollectionViewController and a full page UICell. After the last cell(the last item) is displayed I want to add a summary page of all the items that have been added to the shopping cart along with a total price. I tried to check if the last item = itemsArray.count - 1 however it keeps printing "last item" in the cell before last. Also in the last cell, it prints "not last".
class CollectionViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout, PostCellDelegate {
var totalPrice = Float()
private var hiddenRows = Set<Int>()
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! PostCell
cell.finalLabel.text = String(totalPrice)
cell.delegate = self
let item = itemsArr[indexPath.row]
cell.set(name: item.name, brand: item.brand, price: item.price)
if indexPath.row == itemsArr.count - 1 {
print("last item")
}else {
print("not last")
}
if hiddenRows.contains(indexPath.row) {
cell.myButton.isHidden = true
cell.removeButton.isHidden = false
}else{
cell.removeButton.isHidden = true
cell.myButton.isHidden = false
}
cell.finalLabel.text = String(totalPrice)
return cell
}
#objc func addTapped(cell: PostCell) {
guard let indexPath = self.collectionView.indexPath(for: cell) else {return}
hiddenRows.insert(indexPath.row)
cell.removeButton.isHidden = false
let item = itemsArr[indexPath.row]
print(item.price)
totalPrice += Float(item.price) ?? 0
cell.finalLabel.text = String(totalPrice)
}
#objc func removeButtonTapped(cell: PostCell) {
guard let indexPath = self.collectionView.indexPath(for: cell) else {return}
hiddenRows.insert(indexPath.row)
cell.myButton.isHidden = false
let item = itemsArr[indexPath.row]
totalPrice -= Float(item.price) ?? 0
cell.finalLabel.text = String(totalPrice)
}
}
protocol PostCellDelegate {
func removeButtonTapped(cell: PostCell)
func addTapped(cell: PostCell)
func didPressButton(_ tag: Int)
}
class PostCell: UICollectionViewCell {
var delegate: PostCellDelegate?
func set(name: String, brand: String, price: String){
nameLabel.text = name
brandLabel.text = brand
priceLabel.text = price
}
override init(frame: CGRect) {
super.init(frame: frame)
self.myButton.addTarget(self, action: #selector(addButtonTapped(sender:)), for: .touchUpInside)
self.removeButton.addTarget(self, action: #selector(subButtonTapped(sender:)), for: .touchUpInside)
setupCellConstraints()
}
#objc func buttonPressed(_ sender: UIButton) {
delegate?.didPressButton(sender.tag)
}
#objc func addButtonTapped(sender: UIButton){
self.delegate?.addTapped(cell: self)
sender.isHidden = true
}
#objc func subButtonTapped(sender: UIButton){
self.delegate?.removeButtonTapped(cell: self)
sender.isHidden = true
}
}
Yes I have already tried these from other similar questions but they didn't work:
var frame = CGRect.zero
frame.size.height = .leastNormalMagnitude
tableView.tableHeaderView = UIView(frame: frame)
and
self.automaticallyAdjustsScrollViewInsets = false;
definesPresentationContext = true
Have a look at the extra space I am trying to remove below search bar:
Screenshot
Maybe you can point out an improvement in my code for the same:
import UIKit
class SelectCountryViewController: UITableViewController, UISearchBarDelegate, UISearchResultsUpdating {
struct CellStruct
{
var countryName : String
var countryFlag : String
var countryDialCode : String
}
var cellDatas = [CellStruct]()
var filteredCellDatas = [CellStruct]()
var searchController : UISearchController!
var resultsController = UITableViewController()
var refreshController = UIRefreshControl()
var searchTextField : UITextField!
var searchLoaded = false
var isSearching = false
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.delegate = self;
var frame = CGRect.zero
frame.size.height = .leastNormalMagnitude
tableView.tableHeaderView = UIView(frame: frame)
self.automaticallyAdjustsScrollViewInsets = false;
//tableView.separatorStyle = UITableViewCellSeparatorStyle.singleLine
definesPresentationContext = true
configureSearchController()
let cancelButton = UIBarButtonItem(title: "Cancel", style: .plain, target: self, action: #selector(cancel))
navigationItem.leftBarButtonItem = cancelButton
//self.navigationItem.title = "Select Country"
let searchButton: UIBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: self, action: #selector(searchButtonAction))
searchButton.image = UIImage(named: "search")
self.navigationItem.rightBarButtonItem = searchButton
refreshController.attributedTitle = NSAttributedString(string: "")
refreshController.addTarget(self, action: #selector(refreshSelector), for: .valueChanged)
tableView.addSubview(refreshController)
guard let path = Bundle.main.path(forResource: "countries", ofType: "json")
else
{
return
}
let url = URL(fileURLWithPath: path)
do
{
let data = try Data (contentsOf: url)
let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers)
print(json)
guard let array = json as? [Any] else { return}
for info in array {
guard let userDict = info as? [String: Any] else { return}
guard let code = userDict["code"] as? String else { print("No code found"); return}
guard let dialCode = userDict["dial_code"] as? String else { print("No dial code found"); return}
guard let name = userDict["name"] as? String else { print("No name found"); return}
print("We have: ", code, dialCode, name)
cellDatas.append(CellStruct(countryName: name, countryFlag: code, countryDialCode: dialCode))
}
}
catch
{
print(error)
}
}
func configureSearchController()
{
resultsController.tableView.delegate = self
resultsController.tableView.dataSource = self
self.searchController = UISearchController(searchResultsController: self.resultsController)
//self.tableView.tableHeaderView = self.searchController.searchBar
self.searchController.searchResultsUpdater = self
self.searchController.dimsBackgroundDuringPresentation = false
searchController.searchBar.delegate = self
self.searchController.searchBar.scopeButtonTitles = []
for subView in searchController.searchBar.subviews {
for subViewOne in subView.subviews {
if subViewOne is UITextField {
searchTextField = subViewOne as! UITextField
subViewOne.backgroundColor = UIColor.white
break
}
}
}
self.automaticallyAdjustsScrollViewInsets = false;
extendedLayoutIncludesOpaqueBars = true
definesPresentationContext = true
}
func searchBarShouldBeginEditing(_ searchBar: UISearchBar) -> Bool {
// tableView.setContentOffset(self.navigationItem, animated: true)
searchController.searchBar.barTintColor = UIColor.white
//searchController.searchBar.layer.borderColor = UIColor.white.cgColor
searchTextField.backgroundColor = UIColor.searchBarTextFieldGrey()
return true
}
func searchBarTextDidEndEditing(_ searchBar: UISearchBar) {
self.searchController.searchBar.showsCancelButton = false
// searchController.searchBar.barTintColor = nil
searchTextField.backgroundColor = UIColor.white
searchController.searchBar.barTintColor = nil
}
override func viewWillDisappear(_ animated: Bool) {
self.navigationController?.navigationBar.shadowImage = nil
self.navigationController?.navigationBar.backIndicatorImage = nil
}
func updateSearchResults(for searchController: UISearchController) {
//tableView.separatorStyle = UITableViewCellSeparatorStyle.none
if searchController.searchBar.text! == "" {
filteredCellDatas = cellDatas
} else {
// Filter the results
filteredCellDatas = cellDatas.filter { $0.countryName.lowercased().contains(searchController.searchBar.text!.lowercased()) }
}
resultsController.tableView.reloadData()
// tableView.separatorStyle = .none
// tableView.separatorStyle = UITableViewCellSeparatorStyle.singleLine
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if tableView == resultsController.tableView
{
isSearching = true
return filteredCellDatas.count
}
else
{
isSearching = false
return cellDatas.count
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
var cell = tableView.dequeueReusableCell(withIdentifier: "Cell")
cell?.separatorInset.left = 15
if cell == nil {
cell = UITableViewCell(style: .value1, reuseIdentifier: "Cell")
cell?.separatorInset.left = 0
}
if tableView == resultsController.tableView
{
cell?.textLabel?.text = filteredCellDatas[indexPath.row].countryName
cell?.detailTextLabel?.text = filteredCellDatas[indexPath.row].countryDialCode
cell?.imageView?.image = UIImage (named: filteredCellDatas[indexPath.row].countryFlag)
}
else
{
cell?.textLabel?.text = cellDatas[indexPath.row].countryName
cell?.detailTextLabel?.text = cellDatas[indexPath.row].countryDialCode
cell?.imageView?.image = UIImage (named: cellDatas[indexPath.row].countryFlag)
}
cell?.textLabel?.textColor = UIColor.labelGray2()
cell?.detailTextLabel?.textColor = UIColor.labelGray2()
cell?.textLabel?.font = UIFont(name:"SF Pro Text", size:15)
cell?.detailTextLabel?.font = UIFont(name:"SF Pro Text", size:15)
return cell!
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print(indexPath.row, indexPath.section)
// let currentCell = tableView.cellForRow(at: indexPath)
if(isSearching)
{
print(filteredCellDatas[indexPath.row].countryFlag)
UserDefaults.standard.set(filteredCellDatas[indexPath.row].countryFlag, forKey: "preferredCountry")
if searchController.isActive {
DispatchQueue.main.async {
self.searchController.dismiss(animated: true, completion: {
self.performSegue(withIdentifier: "unWindFromSelectCountry", sender: nil)
})
}
} else {
// Play segue, dismiss or pop ...
self.performSegue(withIdentifier: "unWindFromSelectCountry", sender: nil)
}
}
else
{
print(cellDatas[indexPath.row].countryFlag)
UserDefaults.standard.set(cellDatas[indexPath.row].countryFlag, forKey: "preferredCountry")
self.performSegue(withIdentifier: "unWindFromSelectCountry", sender: nil)
}
}
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
if section == 0 {
return CGFloat.leastNormalMagnitude
}
return tableView.sectionHeaderHeight
}
#objc func cancel(){
navigationController?.popViewController(animated: true)
}
#objc func refreshSelector()
{
if(!searchLoaded)
{
searchLoaded = true
self.tableView.tableHeaderView = searchController.searchBar
print( "Got ya")
}
refreshController.endRefreshing()
}
#objc func searchButtonAction() {
if(!searchLoaded)
{
searchLoaded = true
self.tableView.tableHeaderView = searchController.searchBar
// self.navigationItem.titleView = searchController.searchBar
}
self.searchController.searchBar.becomeFirstResponder()
self.searchController.searchBar.text = ""
// self.navigationItem.rightBarButtonItem = nil
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
As said the table view is of style grouped. I am configuring search bar from code. And updating results therefore using code in the same table view.
Thanks in advance
Maybe it is not a tableHeaderView I presume that it could be particular Section HeaderView try with this:
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
if section == 0 {
return CGFloat.leastNormalMagnitude
}
return tableView.sectionHeaderHeight
}
Code is hiding header for first section in the table
I have sliderCollectionViewController in UICollectionViewCell, try to loading data from json web, all data is loading without image. Here I like to load images in slideCollectionViewCell which created in a collectionViewCell.
import UIKit
import Foundation
**DescriptionObject**
`class Description: NSObject {
var id: Int?
var product_id: Int?
var myDescription: String?
var product_description: String?
var all_images: [String]?
}
**DescriptionCollectionViewController with slideCollectionViewController**
class DescriptionCollectionView: UICollectionViewController, UICollectionViewDelegateFlowLayout{
var arrDescription = [Description]()
**json request**
func loadDescription(){
ActivityIndicator.customActivityIndicatory(self.view, startAnimate: true)
let url = URL(string: ".........")
URLSession.shared.dataTask(with:url!) { (urlContent, response, error) in
if error != nil {
print(error ?? 0)
}
else {
do {
let json = try JSONSerialization.jsonObject(with: urlContent!) as! [String:Any]
let myProducts = json["products"] as? [String: Any]
let myData = myProducts?["data"] as? [[String:Any]]
myData?.forEach { dt in
let oProduct = Description()
oProduct.id = dt["id"] as? Int
oProduct.product_id = dt["product_id"] as? Int
oProduct.myDescription = dt["description"] as? String
oProduct.product_description = dt["product_description"] as? String
if let allImages = dt["all_images"] as? [[String:Any]] {
oProduct.all_images = allImages.flatMap { $0["image"] as? String }
}
self.arrDescription.append(oProduct)
}
} catch let error as NSError {
print(error)
}
}
DispatchQueue.main.async(execute: {
ActivityIndicator.customActivityIndicatory(self.view, startAnimate: false)
self.collectionView?.reloadData()
})
}.resume()
}
fileprivate let cellId = "cellId"
fileprivate let descriptionCellId = "descriptionCellId"
override func viewDidLoad() {
super.viewDidLoad()
self.loadDescription()
collectionView?.register(DescriptionCell.self, forCellWithReuseIdentifier: descriptionCellId)
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return arrDescription.count
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: descriptionCellId, for: indexPath) as! DescriptionCell
cell.descriptionOb = arrDescription[indexPath.item]
return cell
}
**DescriptionCollectionViewCell**
class DescriptionCell: UICollectionViewCell, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
var descriptionOb: Description!{
didSet{
descriptionTextView.text = descriptionOb?.myDescription
couponTextView.text = descriptionOb?.product_description
slideCollectionView.reloadData()
}
}
override init(frame: CGRect) {
super.init(frame: frame)
setupCell()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
let descriptionTextView: UITextView = {
let textview = UITextView()
textview.text = "Description is the pattern of development "
return textview
}()
let couponTextView: UITextView = {
let textview = UITextView()
textview.text = "Description is the pattern of development "
return textview
}()
fileprivate let cellId = "cellId"
lazy var slideCollectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.backgroundColor = UIColor.clear
return cv
}()
func setupCell() {
slideCollectionView.dataSource = self
slideCollectionView.delegate = self
slideCollectionView.isPagingEnabled = true
slideCollectionView.register(SlideCell.self, forCellWithReuseIdentifier: cellId)
addSubview(slideCollectionView)
addSubview(descriptionTextView)
addSubview(couponTextView)
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if let count = descriptionOb?.all_images?.count{
return count
}
return 0
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! SlideCell
if let imageName = descriptionOb?.all_images?[indexPath.item] {
cell.imageView.image = UIImage(named: imageName)
}
return cell
}
}
**SlideCollectionViewCell**
class SlideCell: UICollectionViewCell{
override init(frame: CGRect) {
super.init(frame: frame)
setupCellSlider()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
let imageView: CustomImageView = {
let iv = CustomImageView()
iv.contentMode = .scaleAspectFill
iv.image = UIImage(named: "defaultImage3")
iv.backgroundColor = UIColor.green
return iv
}()
func setupCellSlider() {
backgroundColor = .green
addSubview(imageView)
}
}`
**Image Extension**
let imageCache = NSCache<AnyObject, AnyObject>()
class CustomImageView: UIImageView {
var imageUrlString: String?
func loadImageUsingUrlString(_ urlString: String) {
imageUrlString = urlString
guard let urlEncoded = urlString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else {
print("Encoding not done")
return
}
let url = URL(string: urlEncoded)
image = nil
if let imageFromCache = imageCache.object(forKey: urlString as AnyObject) as? UIImage {
self.image = imageFromCache
return
}
if let url = url {
URLSession.shared.dataTask(with: url, completionHandler: {(myData, respones, error) in
if error != nil {
print(error ?? 0)
return
}
if let myData = myData {
DispatchQueue.main.async(execute: {
let imageToCache = UIImage(data: myData)
if self.imageUrlString == urlString {
self.image = imageToCache
}
if let imageToCache = imageToCache {
imageCache.setObject(imageToCache, forKey: urlString as AnyObject)
}
})
}
}).resume()
}
}
}
json web data
You should use the method in UIImageView subclass CustomImageView
so instead of
cell.imageView.image = UIImage(named: imageName)
try this:
cell.imageView.loadImageUsingUrlString(imageName)
in you cellForItem method of DescriptionCell