Expandable drop down with Multi select checkbox in Swift - ios

check / uncheck the check box by tapping the cell in table view and how to know which cell has checked or unchecked inside Expandable drop down in Swift.
VBExpandVC
class VBExpandVC: UIViewController,UITableViewDelegate,UITableViewDataSource {
#IBOutlet var myTableView: UITableView!
struct Notification:Codable {
let notification:[Headings]
}
struct Headings:Codable {
var name:String
var status:Int
}
var names = [Headings]()
var expandTableview:VBHeader = VBHeader()
var cell : VCExpandCell!
override func viewDidLoad() {
super.viewDidLoad()
getNotifications()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView?
{
expandTableview = Bundle.main.loadNibNamed("VBHeader", owner: self, options: nil)?[0] as! VBHeader
let layer = expandTableview.viewHeader.layer
layer.shadowColor = UIColor.black.cgColor
layer.shadowOffset = CGSize(width: 0, height: 1)
layer.shadowOpacity = 0.4
expandTableview.lblDate.text = self.names[section].name
expandTableview.btnExpand.tag = section
expandTableview.btnExpand.addTarget(self, action: #selector(VBExpandVC.headerCellButtonTapped(_sender:)), for: UIControl.Event.touchUpInside)
let str:String = "\(self.names[section].status)"//arrStatus[section] as! String
if str == "0"
{
UIView.animate(withDuration: 2) { () -> Void in
self.expandTableview.imgArrow.image = UIImage(named :"switch")
}
}
else
{
UIView.animate(withDuration: 2) { () -> Void in
self.expandTableview.imgArrow.image = UIImage(named :"switch2")
}
}
return expandTableview
}
#objc func headerCellButtonTapped(_sender: UIButton)
{
print("header tapped at:")
print(_sender.tag)
var str:String = "\(self.names[_sender.tag].status)"
if str == "0"
{
self.names[_sender.tag].status = 1
}
else
{
self.names[_sender.tag].status = 0
}
// myTableView.reloadData()
myTableView.reloadSections([_sender.tag], with: .none)
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat
{
//Return header height as per your header hieght of xib
return 40
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
let str:Int = (names[section].status)
if str == 0
{
return 0
}
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as? VCExpandCell
return cell;
}
func numberOfSections(in tableView: UITableView) -> Int
{
return self.names.count
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
{
//Return row height as per your cell in tableview
return 111
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("selected:\(indexPath.section)")
}
// getNotifications
func getNotifications(){
guard let url = URL(string: "https://www.json-generator.com/api/json/get/cgAhRPmZgy?indent=2") else {
return
}
var request = URLRequest(url: url)
request.httpMethod = "GET"
URLSession.shared.dataTask(with: request, completionHandler: { (data, response, error) in
guard let data = data, error == nil, response != nil else {
return
}
do {
let headings = try JSONDecoder().decode(Notification.self, from: data)
self.names = headings.notification
DispatchQueue.main.async {
self.myTableView.reloadData()
}
} catch {
print(error)
}
}).resume()
}
// End
}
VCExpandCell
class VCExpandCell: UITableViewCell {
#IBOutlet weak var btnMobile: UIButton!
#IBOutlet weak var btnEmail: UIButton!
#IBOutlet weak var btnSms: UIButton!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
#IBAction func btnMobileApp(_ sender: UIButton) {
print("mobile app checked")
print(sender.tag)
if sender.isSelected {
sender.isSelected = false
} else {
sender.isSelected = true
}
}
#IBAction func btnSMS(_ sender: UIButton) {
print("sms checked")
print(sender.tag)
if sender.isSelected {
sender.isSelected = false
} else {
sender.isSelected = true
}
}
#IBAction func btnEmail(_ sender: UIButton) {
print("email checked")
print(sender.tag)
if sender.isSelected {
sender.isSelected = false
} else {
sender.isSelected = true
}
}
}
enter image description here
In the above code, I have two major problems.
selected check box positions are changing when expanded the section and expanded another section
Unable to find selected check boxes by tapping the cell in table view inside Expand drop down.

Have a look ont his given url:
https://github.com/AssistoLab/DropDown

Related

Can't Click on button in tableView cell after reloadData

I have in cell implement 3 buttons and button action. After call tableView.reloadData() can't click to any button in cell.
In controller in func cellForRow call cell?.onbuttonTappedAdd and other. Afer add element I call tabelViewReloadData.
//TableViewCell
var onButtonTappedAdd : (() -> Void)? = nil
var onButtonTappedRemove : (() -> Void)? = nil
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
func configureCell(orderitem: OrderItem){
self.orderItem = orderitem!
}
func setButtonGradient(){
}
#IBAction func changeValue(_ sender: Any) {
}
#IBAction func btnAddValue(_ sender: Any) {
if let onButtonTappedAdd = self.onButtonTappedAdd
{
onButtonTappedAdd()
}
}
#IBAction func btnRemoveValue(_ sender: Any) {
if let onButtonTappedRemove = self.onButtonTappedRemove {
onButtonTappedRemove()
}
}
//ViewControll cellForRow
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableViewOrderItem.dequeueReusableCell(withIdentifier: "OrderItemCheckoutCell", for: indexPath)
as? OrderItemCheckoutCell
var orderItem = GlobalArray.orderArrayList[indexPath.section].ListOrderItem[indexPath.row]
cell?.configureCell(orderitem: orderItem)
cell?.onButtonTappedAdd = {
GlobalArray.orderArrayList[indexPath.section].ListOrderItem[indexPath.row].quantity = GlobalArray.orderArrayList[indexPath.section].ListOrderItem[indexPath.row].quantity! + (GlobalArray.orderArrayList[indexPath.section].ListOrderItem[indexPath.row].Product?.bigValue)!
self.tableViewOrderItem.reloadData()
self.setTotalInfo()
}
cell?.onButtonTappedRemove = {
GlobalArray.orderArrayList[indexPath.section].ListOrderItem[indexPath.row].quantity = GlobalArray.orderArrayList[indexPath.section].ListOrderItem[indexPath.row].quantity! - (GlobalArray.orderArrayList[indexPath.section].ListOrderItem[indexPath.row].Product?.bigValue)!
self.tableViewOrderItem.reloadData()
}
return cell!
}
Reload TableView Outside of the "cellforRow" method

UITableViewCell multiple select circle (Edit Control) coming randomly

I created a ViewController with a UITableView as a subView.
import UIKit
class ViewController: UIViewController {
private var tableDataSource = [
"Lorem Ipsum is simply du.",
"It is a long established .",
"Lorem Ipsum come",
"All the Lorem .",
"The standard ch.",
"The generated.",
"Various versions."
]
private var isReorderingEnabled = false
private var isDeleteEnabled = false
#IBOutlet private var reorderCellsBarButton: UIBarButtonItem!
#IBOutlet private var selectCellsBarButton: UIBarButtonItem! {
didSet {
selectCellsBarButton.tag = ButtonTags.Select
}
}
#IBOutlet private var deleteCellsBarButton: UIBarButtonItem! {
didSet {
deleteCellsBarButton.tag = ButtonTags.Delete
}
}
#IBOutlet weak private var tableView: UITableView! {
didSet {
tableView.dataSource = self
tableView.delegate = self
tableView.allowsMultipleSelectionDuringEditing = false
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 300
}
}
}
extension ViewController {
private struct ButtonTags {
static let Delete = 2
static let Select = 1
}
private struct LocalizedStrings {
static let EnableReorderingText = "Reorder"
static let DisableReorderingText = "Done"
static let EnableSelectionText = "Select"
static let ConfirmDeletionText = "Delete"
static let DisableDeletionText = "Cancel"
}
}
extension ViewController {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
setDeleteBarButton(hidden: true, animated: false)
}
}
// IBActions
extension ViewController {
#IBAction private func reorderCells(_ sender: UIBarButtonItem) {
isReorderingEnabled = !tableView.isEditing
setSelectBarButton(hidden: isReorderingEnabled)
sender.title = isReorderingEnabled ? LocalizedStrings.DisableReorderingText : LocalizedStrings.EnableReorderingText
tableView.setEditing(isReorderingEnabled, animated: true)
tableView.reloadData()
}
#IBAction private func deleteCells(_ sender: UIBarButtonItem) {
isDeleteEnabled = !tableView.isEditing
setDeleteBarButton(hidden: !isDeleteEnabled)
selectCellsBarButton.title = isDeleteEnabled ? LocalizedStrings.DisableDeletionText : LocalizedStrings.EnableSelectionText
if sender.tag == ButtonTags.Delete {
deleteSelectedRows()
}
tableView.allowsMultipleSelectionDuringEditing = isDeleteEnabled
tableView.setEditing(isDeleteEnabled, animated: true)
tableView.reloadData()
}
}
// Custom Helper methods
extension ViewController {
private func setDeleteBarButton(hidden: Bool, animated: Bool = true) {
navigationItem.setRightBarButtonItems([(hidden ? reorderCellsBarButton : deleteCellsBarButton)], animated: animated)
}
private func setSelectBarButton(hidden: Bool, animated: Bool = true) {
self.navigationItem.setLeftBarButton((hidden ? nil : selectCellsBarButton), animated: animated)
}
private func deleteSelectedRows() {
guard let selectedRows = tableView.indexPathsForSelectedRows else { return }
let indexes = selectedRows.map { $0.row }
tableDataSource.remove(indexes: indexes)
tableView.deleteRows(at: selectedRows, with: .fade)
}
}
extension ViewController: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return tableDataSource.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let customCell = tableView.dequeueReusableCell(withIdentifier: CustomTableViewCell.identifier, for: indexPath) as! CustomTableViewCell
customCell.setup(content: tableDataSource[indexPath.row], for : indexPath.row)
return customCell
}
}
// pragma - To Select/Edit/Move TableView Cells
extension ViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
tableDataSource.move(from: sourceIndexPath.row, to: destinationIndexPath.row)
let reloadingIndexPaths = IndexPath.createForNumbers(from: sourceIndexPath.row, to: destinationIndexPath.row)
DispatchQueue.main.async {
tableView.reloadRows(at: reloadingIndexPaths, with: .none)
}
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
tableDataSource.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .none)
}
}
}
// pragma - Settings for Edit/Move TableViewCell
extension ViewController {
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
print("Edit- \(tableView.isEditing)")
return tableView.isEditing
}
func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
print("Move- \(isReorderingEnabled)")
return isReorderingEnabled
}
func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCellEditingStyle {
print("Style- None")
return .none
}
func tableView(_ tableView: UITableView, shouldIndentWhileEditingRowAt indexPath: IndexPath) -> Bool {
print("indent- \(isDeleteEnabled)")
return isDeleteEnabled
}
}
In this, I have made three bar buttons,one for Reordering , and other two for Deletion and Cancellation.
So, for delete I have provided ability to select multiple rows.
But the problem is, when Reordering selected, the checkbox is still appearing(Not clickable though). And sometimes when Deletion selected, checkbox doesn't appear.
Cannot understand the absurd behaviour. Is this a bug of XCode ? Please help.
Added Content:
CustomTableViewCell class :
import UIKit
class CustomTableViewCell: UITableViewCell {
static let identifier = "customCellIdentifier"
#IBOutlet weak private var titleLabel: UILabel!
#IBOutlet weak private var backgroundImageView: UIImageView!
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
let selectionView = UIView()
selectionView.backgroundColor = UIColor.clear
selectedBackgroundView = selectionView
}
}
extension CustomTableViewCell {
func setup(content: String, for rowNumber: Int) {
titleLabel.text = content
let backgroundImageName = rowNumber % 2 == 0 ? ImageAssetNames.BlueMatte : ImageAssetNames.GreenThreads
backgroundView = UIImageView(image: UIImage(named: backgroundImageName))
}
}

Setting the state of a UiButton from a function in Swift

I have 7 Buttons to toggle the days in a week on and off. If, for example, the button for Sunday is pressed, an IBAction toggles the state automatically without any problems:
#IBAction func SoToggle(_ sender: UIButton) {
self.So.isSelected = !self.So.isSelected;
}
But when I try to set the state of that button from a function in the same class, I get an error saying:
unexpectedly found nil while unwrapping an Optional value
Here is the code used for setting "isSelected" to either true or false:
func setSo(state: Bool) {
self.So.isSelected = state
}
I don't get why this is not working, because it's pretty much the same code.
EDIT:
The function is called by the TableViewController and the buttons are located in a custom TableViewCell. I included the whole code, just to make things clear.
TableViewController:
class TableViewSettings: UITableViewController{
let sections = ["weekdays", "start time"]
var CellDays:TableViewCellDays = TableViewCellDays()
override func viewDidLoad() {
super.viewDidLoad()
self.view.layer.cornerRadius = 7
self.tableView.contentInset = UIEdgeInsets(top: 10,left: 0,bottom: 0,right: 0)
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return self.sections[section]
}
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return sections.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return 1
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if(indexPath.section == 0) {
return Bundle.main.loadNibNamed("TableViewCellDays", owner: self, options: nil)?.first as! TableViewCellDays
} else {
return Bundle.main.loadNibNamed("TableViewCellPicker", owner: self, options: nil)?.first as! TableViewCellPicker
}
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if(indexPath.section == 0) {
return 80
} else {
return 150
}
}
override func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 15
}
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 30
}
override func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
let v: UIView = UIView()
v.backgroundColor = .clear
return v;
}
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let cell = Bundle.main.loadNibNamed("TableViewCellHeader", owner: self, options: nil)?.first as! TableViewCellHeader
cell.mainLabel.text = sections[section]
return cell
}
func setSettingsButtons(settings: [String]) {
// if (settings[0] == "1") {
// CellDays.Mo.isSelected = true
// } else {CellDays.Mo.isSelected = false}
//
// if (settings[1] == "1") {
// CellDays.Di.isSelected = true
// } else {CellDays.Di.isSelected = false}
//
// if (settings[2] == "1") {
// CellDays.Mi.isSelected = true
// } else {CellDays.Mi.isSelected = false}
//
// if (settings[3] == "1") {
// CellDays.Do.isSelected = true
// } else {CellDays.Do.isSelected = false}
//
// if (settings[4] == "1") {
// CellDays.Fr.isSelected = true
// } else {CellDays.Fr.isSelected = false}
//
// if (settings[5] == "1") {
// CellDays.Sa.isSelected = true
// } else {CellDays.Sa.isSelected = false}
//
// if (settings[6] == "1") {
// CellDays.So.isSelected = true
// } else {CellDays.So.isSelected = false}
CellDays.setSo(state: true)
}
TableViewCell:
class TableViewCellDays: UITableViewCell {
#IBOutlet var Mo: UIButton!
#IBOutlet var Di: UIButton!
#IBOutlet var Mi: UIButton!
#IBOutlet var Do: UIButton!
#IBOutlet var Fr: UIButton!
#IBOutlet var Sa: UIButton!
#IBOutlet var So: UIButton!
#IBOutlet var MoLeading: NSLayoutConstraint!
#IBOutlet var DiLeading: NSLayoutConstraint!
#IBOutlet var MiLeading: NSLayoutConstraint!
#IBOutlet var DoLeading: NSLayoutConstraint!
#IBOutlet var FrLeading: NSLayoutConstraint!
#IBOutlet var SaLeading: NSLayoutConstraint!
#IBOutlet var SoLeading: NSLayoutConstraint!
var offset: CGFloat = 10
override func awakeFromNib() {
super.awakeFromNib()
// init Button states
Mo.isSelected = false
Di.isSelected = false
Mi.isSelected = false
Do.isSelected = false
Fr.isSelected = false
Sa.isSelected = false
So.isSelected = false
let screenWidth = UIScreen.main.bounds.width
let screenWidthCenter = screenWidth/2
let frameWidth = Do.frame.width
// Offset Settings depending on Device
offset = screenWidth / 37
print(offset)
// X Coordinate Settings
MoLeading.constant = screenWidthCenter - 3.5 * frameWidth - 3.75 * offset
DiLeading.constant = screenWidthCenter - 2.5 * frameWidth - 2.75 * offset
MiLeading.constant = screenWidthCenter - 1.5 * frameWidth - 1.75 * offset
DoLeading.constant = screenWidthCenter - 0.5 * frameWidth - 0.75 * offset
FrLeading.constant = screenWidthCenter + 0.5 * frameWidth + 0.25 * offset
SaLeading.constant = screenWidthCenter + 1.5 * frameWidth + 1.25 * offset
SoLeading.constant = screenWidthCenter + 2.5 * frameWidth + 2.25 * offset
}
#IBAction func MoToggle(_ sender: UIButton) {
self.Mo.isSelected = !self.Mo.isSelected;
}
#IBAction func DiToggle(_ sender: UIButton) {
self.Di.isSelected = !self.Di.isSelected;
}
#IBAction func MiToggle(_ sender: UIButton) {
self.Mi.isSelected = !self.Mi.isSelected;
}
#IBAction func DoToggle(_ sender: UIButton) {
self.Do.isSelected = !self.Do.isSelected;
}
#IBAction func FrToggle(_ sender: UIButton) {
self.Fr.isSelected = !self.Fr.isSelected;
}
#IBAction func SaToggle(_ sender: UIButton) {
self.Sa.isSelected = !self.Sa.isSelected;
}
#IBAction func SoToggle(_ sender: UIButton) {
self.So.isSelected = !self.So.isSelected;
}
func setSo(state: Bool) {
self.Mo.isSelected = state
}
}
First you have to register the nib for table view to be able to use it.
Add in viewDidLoad:
self.tableView.register(UINib(nibName:"TableViewCellDays", bundle:nil), forCellReuseIdentifier: "TableViewCellDays")
Replace your variable declaration as follows:
var CellDays:TableViewCellDays?
You will assign a value to it later in datasource methods.
Next in your tableView: UITableView, cellForRowAt get this cell and assign the instance variable CellDays to it:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if(indexPath.section == 0) {
let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCellDays") as! TableViewCellDays
self.CellDays = cell
return cell
} else {
return Bundle.main.loadNibNamed("TableViewCellPicker", owner: self, options: nil)?.first as! TableViewCellPicker
}
}
Only after that you can call your setSettingsButtons method.

How to make tableViewcell selection different in editing or not programming_swift3?

Image like this
I have an idea to do the two lines of red circle in the image.
Default cell selection is none,and switch to editing mode the selection is default.
If I dont set cell selection when editing mode, the checkmark isn't show up.
I dont want to use storyboard, I practice this for programming.
I try to do this, but it seems fail.
I dont know where I make the mistake.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var rightBtn: UIBarButtonItem!
#IBOutlet weak var leftBtn: UIBarButtonItem!
var items:[String] = ["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","A1","B1"]
var selectedIndexs = [Int]()
override func loadView() {
super.loadView()
}
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
self.tableView?.allowsMultipleSelectionDuringEditing = true
rightBtn.target = self
leftBtn.target = self
rightBtn.action = #selector(btnClick(_:))
leftBtn.action = #selector(leftBtnClick(_:))
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func leftBtnClick(_ sender: AnyObject) {
if(self.tableView!.isEditing == false) {
self.tableView!.setEditing(true, animated:true)
}
else {
self.tableView!.setEditing(false, animated:true)
}
}
func btnClick(_ sender: AnyObject) {
var selectedIndexs = [Int]()
if let selectedItems = tableView!.indexPathsForSelectedRows {
let sortedArray = selectedItems.sorted()
print("哈:\(sortedArray)")
for indexPath in selectedItems {
selectedIndexs.append(indexPath.row)
}
}
items.removeAt(indexes:selectedIndexs)
self.tableView?.reloadData()
self.tableView!.setEditing(false, animated:true)
}
}
extension ViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = self.items[indexPath.row]
if tableView.isEditing == true {
cell.selectionStyle = .default
}else{
cell.selectionStyle = .none
}
return cell
}
}
Add two line in leftBtnClick function.
func leftBtnClick(_ sender: AnyObject) {
if self.tableView!.isEditing == false {
self.tableView!.setEditing(true, animated:true)
self.tableView.allowsSelection = true
} else {
self.tableView!.setEditing(false, animated:true)
self.tableView.allowsSelection = false
}
}
And remove below code from cellForRawAtIndexPath.
if tableView.isEditing == true {
cell.selectionStyle = .default
} else{
cell.selectionStyle = .none
}

Getting a UITableView to refresh users after one begins a live stream

The landing page for an app I am working on has a place holder avatar named after the application that is present when no user is actively live streaming (using the Red5 Pro streaming framework for this).
However when someone does begin to stream, I want it to automatically refresh the tableview and display the new user's avatar. What I've written so far kind of works, but not entirely. When someone begins livestreaming the placeholder avatar does disappear, but the user that's streaming's avatar doesn't appear.
If I close the app and reopen it, then it is displayed correctly. Here's my code in Swift 3, what am I doing wrong? Should the call to refresh be moved out of ViewDidLoad? Am I using Dispatch Queue incorrectly? Thanks
import UIKit
import Firebase
class HomeController: UIViewController, UITableViewDataSource, UITableViewDelegate, cellDelegate {
#IBOutlet var tableView: UITableView!
var stream: String!
var top: [SSStream] = []
var recent: [SSStream] = [SSStream()]
var trending: [SSStream] = [SSStream()]
var ref: FIRDatabaseReference!
override func viewDidLoad() {
navigationItem.title = "Swiffshot"
navigationController?.navigationBar.isTranslucent = false
let settings = UIButton(frame: CGRect(x: 0, y: 0, width: 20, height: 20))
settings.setImage(#imageLiteral(resourceName: "Settings"), for: .normal)
settings.addTarget(self, action: #selector(settingsPressed), for: .touchUpInside)
navigationItem.leftBarButtonItem = UIBarButtonItem(customView: settings)
let friends = UIButton(frame: CGRect(x: 0, y: 0, width: 23, height: 20))
friends.setImage(#imageLiteral(resourceName: "AllFriends"), for: .normal)
friends.addTarget(self, action: #selector(friendsPressed), for: .touchUpInside)
navigationItem.rightBarButtonItem = UIBarButtonItem(customView: friends)
let nib = UINib(nibName: "MainHeader", bundle: Bundle.main)
tableView.register(nib, forHeaderFooterViewReuseIdentifier: "MainHeader")
ref = FIRDatabase.database().reference()
if !SSContact.shared.active {
performSegue(withIdentifier: "fromMainToAuth", sender: self)
}
SSContact.shared.load() { SSContact.shared.propertyCheck(self) { } }
// SSContact.shared.subscribeToTop(pulse: { (streams) in
// self.top.removeAll()
// self.top.append(contentsOf: streams)
// self.tableView.reloadSections(IndexSet(integer: 0), with: .automatic)
// })
ref.child("streams").observe(.value, with: { (snapshot) in
print("I ran")
self.top.removeAll()
if let userData = snapshot.value as? NSDictionary {
for stream in userData {
let newStream = SSStream()
newStream.username = stream.key as! String
print("Found stream \(stream.key as! String)")
newStream.isPrivate = !((stream.value as! NSDictionary)["public"] as! Bool)
newStream.views = (stream.value as! NSDictionary)["views"] as! Int
newStream.isEnabled = true
self.top.append(newStream)
}
}
if self.top.isEmpty {
print("No Streams Found")
self.top.append(SSStream())
}
DispatchQueue.main.async {
self.tableView.reloadSections(IndexSet(integer: 0), with: .automatic)
self.tableView.reloadData()
}
})
}
func cellGotPressed(_ stream: String) {
self.stream = stream
performSegue(withIdentifier: "toPlayer", sender: self)
}
func settingsPressed() {
performSegue(withIdentifier: "toSettings", sender: self)
}
func friendsPressed() {
performSegue(withIdentifier: "fromMainToExpandable", sender: self)
}
func cameraTapped() {
performSegue(withIdentifier: "toRed", sender: self)
}
func cellTapped() {
print("Cell Tapped")
}
// MARK: Segue
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "toPlayer" {
let player = segue.destination as! VideoPlayerViewController
player.isSubscribing = true
player.stream = stream
}
}
// MARK: Table View Functions
func numberOfSections(in tableView: UITableView) -> Int {
return 3
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.section == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "big") as! CategoryRow
cell.section = indexPath.section
cell.top = top
cell.delegate = self
cell.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(cameraTapped)))
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "small") as! CategoryRow
cell.section = indexPath.section
cell.recent = recent
cell.trending = trending
cell.delegate = self
return cell
}
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
if section == 0 {
return nil
} else {
let cell = self.tableView.dequeueReusableHeaderFooterView(withIdentifier: "MainHeader")
let header = cell as! MainHeader
if section == 1 {
header.fillHeader("RECENT")
} else if section == 2 {
header.fillHeader("Trending + Now")
} else {
print("Unknown Section")
}
return header
}
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath.section == 0 {
return 300
} else {
return 100
}
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
if section == 0 {
return 0
} else {
return 50
}
}
func tableView(_ tableView: UITableView, estimatedHeightForHeaderInSection section: Int) -> CGFloat {
return 50
}
}
I realized what I needed to do. I needed to set the
ref.child("streams").observe
in the call to Dispatch.Queue. By setting the reference before dispatch was called, the program wasn't syncing properly. It should be like this:
DispatchQueue.main.async {
ref.child("streams").observe(.value, with: { (snapshot) in
self.tableView.reloadSections(IndexSet(integer: 0), with: .automatic)
self.tableView.reloadData()
}
})

Resources