Tracking upload progress in UICollectionView is duplicating cells - ios

I have a following setup:
UploadedCell
DisplayUploadsVC
UploadHelper with delegate that tracks
progress (this is singleton)
In my controller I have a timer in cellForItemAt that gets the progress for uploadId that is currently uploading and update the cell upload item.
In my cell I use prepareForReuse and set my upload to nil.
But again when I scroll and cells reuse I see duplicate cells. I also see when I pullToRefress or go to the end of results to get more results from server.
Just not sure what I'm missing or if I can use this kind of implementation with timer in collection view cell to get the progress.
UploadedCell
class UploadedCell: UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
// MARK: Variables declaration
let uploadBadge = UIImageView(image: #imageLiteral(resourceName: "uploads") , contentMode: .scaleAspectFit)
let uploadName = UILabel(font: UIFont.openSansSemiboldFontOfSize(18))
let statusName = UILabel(font: UIFont.openSansSemiboldFontOfSize(18))
#objc lazy var moreButton: UIButton = {
let button = UIButton()
button.setImage(UIImage(named: "dots-menu")?.withRenderingMode(.alwaysTemplate), for: .normal)
button.tintColor = .lightGray
button.addTarget(self, action: #selector(handleMore), for: .touchDown)
return button
}()
// MARK - didSet
var upload: UploadModel.UploadItem? {
didSet {
uploadName.text = upload?.title
statusName.text = upload?.status
}
}
// MARK: - Main Init
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .white
setupViews()
}
override func prepareForReuse() {
self.upload = nil
super.prepareForReuse()
}
}
DisplayUploadsVC
class DisplayUploadsVC: UICollectionViewController, UICollectionViewDelegateFlowLayout {
// MARK: - Properties
/// various object init
var uploadCell: UploadCell = UploadCell()
var uploadProgress = (progress: Float(0), uploadId: "")
var progressTimer = Timer()
override func viewDidLoad() {
super.viewDidLoad()
UploadHelper.shared.uploadHelperDelegate = self
setupViews()
setupEmptyBackgroundView()
fetchUploads()
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! UploadCell
cell.upload = nil
var upload = isSearching ? filteredResults[indexPath.item] : results?[indexPath.item]
cell.upload = upload
// get uploads with paging if we are paginating
if (self.uploadProgress.uploadId == upload?.id && self.uploadProgress.progress < 99) {
progressTimer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { timer in
upload?.status = String(format: NSLocalizedString("Uploading %.0f%%", comment: ""), self.uploadProgress.progress)
cell.upload = upload
if self.uploadProgress.progress > 99.9 {
self.progressTimer.invalidate()
upload?.status = NSLocalizedString("Upload DONE", comment: "")
cell.upload = upload
}
}
}
return cell
}
extension DisplayUploadsVC : UploadHelperProgress {
func showProgress(progress: Float, uploadId: String) {
self.uploadProgress.progress = progress
self.uploadProgress.uploadId = uploadId
}
}
UploadHelper
protocol UploadHelperProgress : class {
func showProgress(progress: Float, uploadId: String)
}
private var id: String?
private let progressBlock = { bytesWritten, bytesTotal in
var progress = Float(bytesWritten) / Float(bytesTotal)
UploadHelper.shared.uploadHelperDelegate?.showProgress(progress: progress * 100, uploadId: id ?? "")
} as UploadProgressBlock
class UploadHelper: NSObject {
/// delegate
weak var uploadHelperDelegate: UploadHelperProgress?
/// singleton
static let shared = UploadHelper()
func upload(fileUrl: URL, fileUploadUrl: String, uploadId: String) {
id = uploadId
//
// upload logic
//
upload?.progressBlock = progressBlock
upload?.resume()
}
}

Related

Autoadapt height of View

I m building a chat View in xCode with swift language.
I m create two custom cell one to sender message and another one to received message. The problem is when the text of message is greater than the width of the screen of iPhone. Then I need to configure the two views to increase the height if the text is too big.
So this is my view:
I configured the height of View Lbl Message >= 30 but if you see xCode set to RED that constraint and for me the error is in that part.
Anyway this is the result of my iPhone when application stil running:
If you check with more attention after the text "mio" in the blue cell you can see that there are other text but it is not displayed.
EDIT
I added ChatVC controller where is the UITableView:
//
// ChatVC.swift
// appUser
//
// Created by mac on 11/10/21.
// Copyright © 2021 Michele Castriotta. All rights reserved.
//
import UIKit
import SDWebImage
import SwiftyJSON
import AVFoundation
class ChatVC: UIViewController, UITableViewDelegate, UITableViewDataSource {
var aggiorna:Bool = false
var arrMsgs:[ChatModel] = []
var receiverId = ""
var storeName = ""
var userName = ""
var userId = ""
var strReason = ""
var strReasonID = ""
var strType = "User"
var strRighTitle = ""
var strPrname = ""
var daNotifica:Bool = false
#IBOutlet weak var viewWriteMessage: UIView!
#IBOutlet weak var lblTitleChat: UILabel!
#IBOutlet weak var txtMessage: UITextField!
#IBOutlet weak var tableViewChat: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
self.showProgressBar()
self.tableViewChat.separatorColor = UIColor.clear
self.viewWriteMessage.setCornerRadius(cornerRadius: 22, borderColor: nil, borderWidth: nil)
userId = kUserDefault.value(forKey: USERID) as! String
self.lblTitleChat.text = self.storeName
wsGetChatAgain()
}
func wsGetChatAgain() {
// showProgressBar()
var paramDict : [String:AnyObject] = [:]
paramDict["receiver_id"] = receiverId as AnyObject
paramDict["sender_id"] = userId as AnyObject
CommunicationManeger.callPostServiceReturnJson(apiUrl: RouterProd.get_chat.url(), parameters: paramDict, parentViewController: self, successBlock: { (responseData, message) in
DispatchQueue.main.async {
do {
let chats = try JSONDecoder().decode(ResponseChatModel.self, from: responseData as! Data)
if(chats.status == "1") {
self.arrMsgs = chats.result
DispatchQueue.main.async {
// Main thread, called after the previous code:
// hide your progress bar here
self.tableViewChat.reloadData()
self.hideProgressBar()
}
//self.scrollToBottom()
//self.lbl_ChatReason.text = self.strReason
}
}catch{
print("errore durante la decodifica dei dati: \(error)")
self.hideProgressBar()
//Utility.noDataFound("Errore", tableViewOt: self.tableViewChat, parentViewController: self)
}
}
}, failureBlock: { (error : Error) in
Utility.showAlertMessage(withTitle: EMPTY_STRING, message: (error.localizedDescription), delegate: nil,parentViewController: self)
self.hideProgressBar()
})
self.aggiorna = true
self.aggiornaChat()
}
func aggiornaChat(){
if(aggiorna){
DispatchQueue.main.asyncAfter(deadline: .now() + 15.0, execute: wsGetChatAgain)
}
}
#IBAction func btnBack(_ sender: Any) {
self.aggiorna = false
if(self.daNotifica == false){
self.navigationController?.popViewController(animated: true)
}else{
Switcher.updateRootVC()
}
}
//MARK: - Table View Methods
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.arrMsgs.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let message = self.arrMsgs[indexPath.row]
var messaggio = message.chatMessage
var orario = Utility.getDateStringFromString(dateString: message.date, inputFormate: "yyyy-MM-dd HH:mm:ss", outputFormate: "HH:mm")
if(message.senderID == userId){
//messaggio inviato dal utente
let cell = tableView.dequeueReusableCell(withIdentifier: "ChatCell", for: indexPath) as! ChatCell
cell.lblMessage.text = messaggio
cell.lblTime.text = orario
var maximumLabelSize: CGSize = CGSize(width: 280, height: 9999)
var expectedLabelSize: CGSize = cell.lblMessage.sizeThatFits(maximumLabelSize)
// create a frame that is filled with the UILabel frame data
var newFrame: CGRect = cell.lblMessage.frame
// resizing the frame to calculated size
newFrame.size.height = expectedLabelSize.height
// put calculated frame into UILabel frame
cell.lblMessage.frame = newFrame
return cell
}else{
//messaggio inviato dallo o runner
let cell = tableView.dequeueReusableCell(withIdentifier: "ChatCell2", for: indexPath) as! ChatCell2
cell.lblMessage.text = messaggio
var maximumLabelSize: CGSize = CGSize(width: 280, height: 9999)
var expectedLabelSize: CGSize = cell.lblMessage.sizeThatFits(maximumLabelSize)
// create a frame that is filled with the UILabel frame data
var newFrame: CGRect = cell.lblMessage.frame
// resizing the frame to calculated size
newFrame.size.height = expectedLabelSize.height
newFrame.size.width = expectedLabelSize.width
// put calculated frame into UILabel frame
cell.lblMessage.frame = newFrame
cell.lblTime.text = orario
return cell
}
}
#IBAction func sendMessage(_ sender: Any) {
if txtMessage.text == "Scrivi qui..." || txtMessage.text!.count == 0 {
GlobalConstant.showAlertMessage(withOkButtonAndTitle: APPNAME, andMessage: "Per favore scrivi del testo...", on: self)
} else {
wsSendMessage()
}
}
func wsSendMessage() {
self.showProgressBar()
var localTimeZoneIdentifier: String { return TimeZone.current.identifier }
var paramDict : [String:AnyObject] = [:]
paramDict["receiver_id"] = receiverId as AnyObject
paramDict["sender_id"] = userId as AnyObject
paramDict["chat_message"] = self.txtMessage.text! as AnyObject
paramDict["timezone"] = localTimeZoneIdentifier as AnyObject
paramDict["request_id"] = strReasonID as AnyObject
paramDict["type"] = strType as AnyObject
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
var dataOra = formatter.string(from:Date())
paramDict["date_time"] = "\(dataOra)" as AnyObject
CommunicationManeger.callPostService(apiUrl: RouterProd.insert_chat.url(), parameters: paramDict, parentViewController: self, successBlock: { (responseData, message) in
DispatchQueue.main.async {
let swiftyJsonVar = JSON(responseData)
print(swiftyJsonVar)
if(swiftyJsonVar["result"].stringValue == "successful") {
self.txtMessage.text = ""
self.view.endEditing(true)
self.wsGetChatAgain()
}
self.hideProgressBar()
}
}, failureBlock: { (error : Error) in
Utility.showAlertMessage(withTitle: EMPTY_STRING, message: (error.localizedDescription), delegate: nil,parentViewController: self)
self.hideProgressBar()
})
}
}
This is chatCell class
import UIKit
class ChatCell: UITableViewCell {
#IBOutlet weak var lblMessage: UILabel!
#IBOutlet weak var viewLblMessage: UIView!
#IBOutlet weak var lblTime: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
self.viewLblMessage.setCornerRadius(cornerRadius: 10, borderColor: nil, borderWidth: nil)
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
If you don't know the size of the text, don't add height to the view. You should add your constraints without adding height

Printing Contents of TableView in Swift IOS Application

I am making an app with a scene that contains a tableview. Each cell in the table view contains a rating control (made up of 5 stars) and a label. At the click of a button I would like to print all of the labels as well as the number of stars that user has clicked from the rating controls from the entire table view. to the console.
How can I do this?
Here is my tableview(_:cellForRowAt:) method
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Configure the cell
// Table view cells are reused and should be dequeued using a cell identifier
let cellId = "cell"
guard let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as? MatchingTableViewCell else {
fatalError("The dequeued cell is not an instacne of MatchingTableViewCell")
}
// Fetches the appropriate match for the data source layout.
let match = matching[indexPath.row]
cell.nameLabel.text = match.name
cell.photoImagView.image = match.photo
cell.ratingControl.rating = match.rating
return cell
}
Data model object is an array of structs of Match objects:
import Foundation
import UIKit
import os.log
class Match: NSObject, NSCoding {
// MARK: Properties
var name: String
var photo: UIImage?
var rating: Int
// MARK: Archiving Paths
static let DocumentsDirectory = FileManager().urls(for: .documentDirectory, in: .userDomainMask).first
static let ArchiveURL = DocumentsDirectory?.appendingPathComponent("matching")
// MARK: Types
struct PropertyKey {
static let name = "name"
static let photo = "photo"
static let rating = "rating"
}
init?(name: String, photo: UIImage?, rating: Int) {
// The name must not be empty
guard !name.isEmpty else{
return nil
}
// The rating must be between 0 and 5 inclusively
guard (rating >= 0) && (rating <= 5) else {
return nil
}
// Initialize stored properties
self.name = name
self.photo = photo
self.rating = rating
}
override var description : String {
return "rating \(self.rating) \n"
}
// MARK: NSCoding
func encode(with aCoder: NSCoder) {
aCoder.encode(name, forKey: PropertyKey.name)
aCoder.encode(photo, forKey: PropertyKey.photo)
aCoder.encode(rating, forKey: PropertyKey.rating)
}
required convenience init?(coder aDecoder: NSCoder) {
// The name is required if we cannot decode a name string, the init should fail
guard let name = aDecoder.decodeObject(forKey: PropertyKey.name) as? String else{
os_log("Unable to decode the name for a Match object", log: OSLog.default, type: .debug)
return nil
}
// Because the photo is an optional property of Match, just use conditional cast.
let photo = aDecoder.decodeObject(forKey: PropertyKey.photo) as? UIImage
let rating = aDecoder.decodeObject(forKey: PropertyKey.rating)
// Must call designated init
self.init(name: name, photo: photo, rating: rating as! Int)
}
}
RatingControl.swiift:
import UIKit
#IBDesignable class RatingControl: UIStackView {
// MARK: Properties
private var ratingButtons = [UIButton]()
var rating = 0 {
didSet {
updateButtonSelectionStates()
}
}
#IBInspectable var starSize: CGSize = CGSize(width: 44.0, height: 44.0) {// Defines size of buttons/
didSet{
setupButtons()
}
}
#IBInspectable var starCount: Int = 5 {// Defines number of buttons
didSet{
setupButtons()
}
}
// MARK: Initialization
override init(frame: CGRect) {
super.init(frame: frame)
setupButtons()
}
required init(coder: NSCoder) {
super.init(coder: coder)
setupButtons()
}
// MARK: Private Methods
private func setupButtons(){
// Clear any existing buttons
for button in ratingButtons{
removeArrangedSubview(button)
button.removeFromSuperview()
}
ratingButtons.removeAll()
// Load Button Images
let bundle = Bundle(for: type(of: self))
let filledStar = UIImage(named: "filledStar", in: bundle, compatibleWith: self.traitCollection)
let emptyStar = UIImage(named: "emptyStar", in: bundle, compatibleWith: self.traitCollection)
let highligtedStar = UIImage(named: "highlightedStar", in: bundle, compatibleWith: self.traitCollection)
for _ in 0..<starCount {
// Create the button
let button = UIButton()
// Set the button images
button.setImage(emptyStar, for: .normal)
button.setImage(filledStar, for: .selected)
button.setImage(highligtedStar, for: .highlighted)
button.setImage(highligtedStar, for: [.highlighted, .selected])
// Adding constraints
button.translatesAutoresizingMaskIntoConstraints = false // disables buttons automatically generated constraints
button.heightAnchor.constraint(equalToConstant: starSize.height).isActive = true // defines height
button.widthAnchor.constraint(equalToConstant: starSize.width).isActive = true // defines width
//Setup the button action
button.addTarget(self, action: #selector(RatingControl.ratingButtonTapped(button:)), for: .touchUpInside)
// Add button to stack
addArrangedSubview(button)
// Add the new button to the rating button Array
ratingButtons.append(button)
}
updateButtonSelectionStates()
}
// MARK: Button Action
#objc func ratingButtonTapped(button:UIButton){
guard let index = ratingButtons.index(of: button) else {
fatalError("The button, \(button), is not in the ratingButtons array: \(ratingButtons)")
}
// Calculate the rating of the selected button
let selectedRating = index + 1
if selectedRating == rating { // If the selected star represents the current rating, reset the rating to 0
rating = 0
} else{
// Otherwise set the rating to the selected star
rating = selectedRating
}
}
private func updateButtonSelectionStates() { // Update buttons appearance
for (index, button) in ratingButtons.enumerated() {
// If the index of a button is less than the rating, that button should be selected
button.isSelected = index < rating
}
}
}
You've got this wrong. Your table view does not save data, it displays it. You want to have a data model that holds the values you display from your table view. That's what feeds your tableView dataSource methods. Often it will be an array of structs.
You want to print the contents of your table view's data model.
Edit:
Now that you've provided that info we can help you.
Add CustomStringConvertible conformance to your data model:
class Match: NSObject, NSCoding, CustomStringConvertible {
The only requirement for CustomStringConvertible is that you provide a computed property description that's a String:
var description: String {
return "Match(name:\(name),rating:\(rating))"
}
Then in your button action
#IBAction func logInfo(sender: UIButton) {
matching.forEach { print($0) }
}
Since your Match class now conforms to CustomStringConvertible, you can print Match objects directly.
Or if you want indexes:
#IBAction func logInfo(sender: UIButton) {
matching.enumerated()forEach { print(String(format:"%02l", $0.0) + ": " + $0.1) }
}

Issue with Activity Indicator While loading data in UITableView

I'm getting record from server using php
As i'm getting 11 record currently so i want in starting i will show just 6 records and remaining next 5 record will show when user reached at last cell while scrolling. So this process is in working form, but the problem is while running, it working so fast that before reaching last row all records are already showing while scrolling and the activity indicator is just animating at the bottom of tableView.
I don't know what is the problem.
Also i want when user reached at last cell, activity indicator start animating while loading data .
Here is my code
import UIKit
import AlamofireImage
struct property{
let property_Id : String
let propertyTitle : String
let imageURL : String
let user_Id : String
let area : String
let bed : String
let unit : String
let bath : String
let price : String
}
class searchRecordsViewController: UIViewController, UITableViewDelegate, UITableViewDataSource,favouriteButtonTableViewCellDelegate {
var y = 6
var m = 12
#IBOutlet weak var tableView : UITableView!
var RESULT : [NSDictionary] = []
var myProperty = [property]()
var myPropertyCopy = [property]()
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
//MARK: Getting dictionary data from previous controller
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationItem.title = "Results"
self.navigationController?.navigationBar.tintColor = UIColor.black
//MARK: Getting dictionary data from previous controller
for item in RESULT {
let propertyInfo = property(property_Id: String(item["propertyId"]! as! Int), propertyTitle: item["propertyTitle"]! as! String, imageURL: item["imagePath"]! as! String, user_Id: String(item["userId"]! as! Int), area: item["area"]! as! String, bed: item["bed"]! as! String, unit: item["unit"]! as! String, bath: item["bath"]! as! String, price: item["price"]! as! String )
myProperty.append(propertyInfo)
}
//MARK: Inserting first 6 records in Array
for i in 0 ..< 6 {
if !(myProperty.indices.contains(i)) {
break
}
myPropertyCopy.append(myProperty[i])
}
}
func downloadImage(imagePath : String, theIMAGEVIEW : UIImageView) {
let myUrl = URL(string: URL_IP+imagePath);
//MARK: AlamofireImage to download the image
theIMAGEVIEW.af_setImage(withURL: myUrl!, placeholderImage: #imageLiteral(resourceName: "addProperty"), filter: nil, progress: nil, runImageTransitionIfCached: true, completion: nil)
}
func tableView(_ tableView:UITableView, numberOfRowsInSection section:Int) -> Int
{
return myPropertyCopy.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "RecordCell") as! searchrecordsTableViewCell
let property = myPropertyCopy[indexPath.row]
cell.area.text = property.area+" "+property.unit
if property.bath == ""{
cell.bath.text = property.bath
}
else{
cell.bath.text = property.bath+" Baths"
}
if property.bed == ""{
cell.bed.text = property.bed
}
else{
cell.bed.text = property.bed+" Baths"
}
cell.propertyTitle.text = property.propertyTitle
cell.price.text = convertAMOUNT(price : property.price)
downloadImage(imagePath: property.imageURL, theIMAGEVIEW: cell.myImageView)
//----
cell.delegate = self
return cell
}
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
if myPropertyCopy.count != myProperty.count{
let lastRow = myPropertyCopy.count - 1
if indexPath.row == lastRow {
let spinner = UIActivityIndicatorView(activityIndicatorStyle: .gray)
spinner.startAnimating()
spinner.frame = CGRect(x: CGFloat(0), y: CGFloat(0), width: tableView.bounds.width, height: CGFloat(44))
self.tableView.tableFooterView = spinner
self.tableView.tableFooterView?.isHidden = false
moreData()
}
}
}
func moreData(){
for i in y ..< m {
if !(myProperty.indices.contains(i)) {
break
}
myPropertyCopy.append(myProperty[i])
}
y = y + 10
m = m + 10
self.tableView.reloadData()
}
}
currently my TableView looks like
See output here
Thanks in Advance.
After a long practice i have done my problem, i have just added UIScrollView delegate function for checking if the user reached at last cell then start activityIndicator for 3 seconds and then load data and that's it.
As i have just very small amount of data that's why i'm start ing UIactivityIndicator for 3 seconds before getting data.
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
// UITableView only moves in one direction, y axis
let currentOffset = scrollView.contentOffset.y
let maximumOffset = scrollView.contentSize.height - scrollView.frame.size.height
if maximumOffset - currentOffset <= 10.0 {
let spinner = UIActivityIndicatorView(activityIndicatorStyle: .gray)
if myPropertyCopy.count != myProperty.count {
//print("this is the last cell")
spinner.startAnimating()
spinner.frame = CGRect(x: CGFloat(0), y: CGFloat(0), width: tableView.bounds.width, height: CGFloat(44))
spinner.hidesWhenStopped = true
self.tableView.tableFooterView = spinner
self.tableView.tableFooterView?.isHidden = false
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
//MARK: Loading more data
self.moreData()
}
}
else{
self.tableView.tableFooterView?.isHidden = true
spinner.stopAnimating()
}
}
}

how to call rating view from json and show the rating in my table view

I have one custom cell, in that i have " name", "address" "rating view". Rating view is one class separately library file for rating view it will have some 3 images ( full star, half star, empty star ). Now from my json data i have some rating values for each cell. like below json structure :
This is my custom cell :
class customCell: UITableViewCell {
#IBOutlet weak var vendorName: UILabel! // vendor label name
#IBOutlet weak var vendorAddress: UILabel! // vendor address aname
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
My table view controller :
i have 2 more custom cell.But if i try to add for one cell i will make and understand code and i will do for all custom cell.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// if cell tap condition
if(isTapped == true && indexPath == selectedIndex)
{
if (premiumUserCheck && indexPath == selectedIndex ) {
let cell1:premiumUsercell = self.tableView.dequeueReusableCellWithIdentifier("cell3") as! premiumUsercell
cell1.phoneNumber = (arrDict[indexPath.section] .valueForKey("phone") as? String)!
cell1.vendorName3.text=arrDict[indexPath.section] .valueForKey("name") as? String
cell1.vendorAdddress3.text=arrDict[indexPath.section] .valueForKey("address") as? String
print("premium user")
return cell1
}
else {
let cell1:ExpandCell = self.tableView.dequeueReusableCellWithIdentifier("cell2") as! ExpandCell
cell1.VendorName.text=arrDict[indexPath.section] .valueForKey("name") as? String
cell1.vendorAdress.text=arrDict[indexPath.section] .valueForKey("address") as? String
//cell1.externalView.hidden = true
print("non premium user")
return cell1
}
}
// show default cutsom cell
let cell:customCell = self.tableView.dequeueReusableCellWithIdentifier("cell") as! customCell
cell.vendorName.text=arrDict[indexPath.section] .valueForKey("name") as? String
cell.vendorAddress.text=arrDict[indexPath.section] .valueForKey("address") as? String
print("norml user")
return cell
}
Here below code is rating view is some library files , which i am using for rating view. i place custom view under one view inside my custom cell.I have to select my view in my custom cell. And i have to assign that particular class as RatingView classes..Then if i click my rating view in my custom cell , i can see like below image to set `rating star, number of star, off, empty half image:
My ratingviewclasses:
import UIKit
#objc public protocol RatingViewDelegate {
/**
Called when user's touch ends
- parameter ratingView: Rating view, which calls this method
- parameter didChangeRating newRating: New rating
*/
func ratingView(ratingView: RatingView, didChangeRating newRating: Float)
}
/**
Rating bar, fully customisable from Interface builder
*/
#IBDesignable
public class RatingView: UIView {
/// Total number of stars
#IBInspectable public var starCount: Int = 5
/// Image of unlit star, if nil "starryStars_off" is used
#IBInspectable public var offImage: UIImage?
/// Image of fully lit star, if nil "starryStars_on" is used
#IBInspectable public var onImage: UIImage?
/// Image of half-lit star, if nil "starryStars_half" is used
#IBInspectable public var halfImage: UIImage?
/// Current rating, updates star images after setting
#IBInspectable public var rating: Float = Float(0) {
didSet {
// If rating is more than starCount simply set it to starCount
rating = min(Float(starCount), rating)
updateRating()
}
}
/// If set to "false" only full stars will be lit
#IBInspectable public var halfStarsAllowed: Bool = true
/// If set to "false" user will not be able to edit the rating
#IBInspectable public var editable: Bool = true
/// Delegate, must confrom to *RatingViewDelegate* protocol
public weak var delegate: RatingViewDelegate?
var stars = [UIImageView]()
override init(frame: CGRect) {
super.init(frame: frame)
customInit()
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override public func awakeFromNib() {
super.awakeFromNib()
customInit()
}
override public func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
customInit()
}
func customInit() {
let bundle = NSBundle(forClass: RatingView.self)
if offImage == nil {
offImage = UIImage(named: "star_empty", inBundle: bundle, compatibleWithTraitCollection: self.traitCollection)
}
if onImage == nil {
onImage = UIImage(named: "star_full", inBundle: bundle, compatibleWithTraitCollection: self.traitCollection)
}
if halfImage == nil {
halfImage = UIImage(named: "star_half_full", inBundle: bundle, compatibleWithTraitCollection: self.traitCollection)
}
guard let offImage = offImage else {
assert(false, "offImage is not set")
return
}
for var i = 1; i <= starCount; i++ {
let iv = UIImageView(image: offImage)
addSubview(iv)
stars.append(iv)
}
layoutStars()
updateRating()
}
override public func layoutSubviews() {
super.layoutSubviews()
layoutStars()
}
func layoutStars() {
if stars.count != 0,
let offImage = stars.first?.image {
let halfWidth = offImage.size.width/2
let distance = (bounds.size.width - (offImage.size.width * CGFloat(starCount))) / CGFloat(starCount + 1) + halfWidth
var i = 1
for iv in stars {
iv.frame = CGRectMake(0, 0, offImage.size.width, offImage.size.height)
iv.center = CGPointMake(CGFloat(i) * distance + halfWidth * CGFloat(i - 1),
self.frame.size.height/2)
i++
}
}
}
/**
Compute and adjust rating when user touches begin/move/end
*/
func handleTouches(touches: Set<UITouch>) {
let touch = touches.first!
let touchLocation = touch.locationInView(self)
for var i = starCount - 1; i >= 0; i-- {
let imageView = stars[i]
let x = touchLocation.x;
if x >= imageView.center.x {
rating = Float(i) + 1
return
} else if x >= CGRectGetMinX(imageView.frame) && halfStarsAllowed {
rating = Float(i) + 0.5
return
}
}
rating = 0
}
/**
Adjust images on image views to represent new rating
*/
func updateRating() {
// To avoid crash when using IB
if stars.count == 0 {
return
}
// Set every full star
var i = 1
for ; i <= Int(rating); i++ {
let star = stars[i-1]
star.image = onImage
}
if i > starCount {
return
}
// Now add a half star
if rating - Float(i) + 1 >= 0.5 {
let star = stars[i-1]
star.image = halfImage
i++
}
for ; i <= starCount; i++ {
let star = stars[i-1]
star.image = offImage
}
}
}
// MARK: Override UIResponder methods
extension RatingView {
override public func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
guard editable else { return }
handleTouches(touches)
}
override public func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
guard editable else { return }
handleTouches(touches)
}
override public func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
guard editable else { return }
handleTouches(touches)
guard let delegate = delegate else { return }
delegate.ratingView(self, didChangeRating: rating)
}
}
Now i need to get the rating number from my json and i have to assign to my uiview in my custom cell, and i need to show the respective rating in my all table view cell.
Please help me out. I am strugling to do with getting json data dynamically??
Thnaks !
UPDATED :
customcell.swift
#IBOutlet weak var ratingView: RatingView!
#IBOutlet weak var vendorName: UILabel! // vendor label name
#IBOutlet weak var vendorAddress: UILabel! // vendor address aname
override func awakeFromNib() {
super.awakeFromNib()
super.awakeFromNib()
//ratingView = RatingView(frame:CGRectMake(0, 0, cellWidth, cellHeight))
// Initialization code
}
Viewcontroller.swift
let cell:customCell = self.tableView.dequeueReusableCellWithIdentifier("cell") as! customCell
let ratingString = "\(arrDict[indexPath.section].valueForKey("rating"))"
cell.ratingView?.rating = Float(ratingString)!
cell.vendorName.text=arrDict[indexPath.section] .valueForKey("name") as? String
cell.vendorAddress.text=arrDict[indexPath.section] .valueForKey("address") as? String
Add RatingView as a subview in TableViewCell's awakeNib method and make it as global variable say ratingView. then
var ratingView:RatingView? = nil
override func awakeFromNib() {
super.awakeFromNib()
ratingView = RatingView(frame:CGRectMake(0, 0, cellWidth, cellHeight)) // your requiredFrame
}
in cellForRowAtIndexPath
let ratingString = "\(arrDict[indexPath.section].valueForKey("rating"))"
if let ratingValue = Float(ratingString) {
cell1.ratingView?.rating = ratingValue
}
else {
cell1.ratingView?.rating = 0
}

Extend UIView with necessary dataSource and delegate

I want in UITableViewCell add customized UIView (will be analog segmented control)
I wrote subclass `protocol ITISegmentedViewDelegate: class {
func segmentedViewButtonChanged(index: Int)
}
public protocol ITISegmentedViewDataSource : NSObjectProtocol {
#available(iOS 2.0, *)
func segmentedView(itemsInSegmentedView: ITISegmentedView) -> [String]
}
public class ITISegmentedView: UIView {
var delegate: ITISegmentedViewDelegate?
var dataSource: ITISegmentedViewDataSource?
var selectedItem = -1
override init(frame: CGRect) {
super.init(frame: frame)
}
required public init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
self.addButtons()
}
private func addButtons(){
if delegate == nil || dataSource == nil{
return
}
let height = frame.height
let width = frame.width
let array = dataSource!.segmentedView(self)
let totalItem = array.count
var startX = CGFloat(0)
for var index = 0; index < totalItem; ++index{
let button = UIButton(frame: CGRectMake(startX, 0, width/CGFloat(totalItem), height))
button.setTitle(array[index], forState: UIControlState.Normal)
button.tag = index
button.addTarget(button, action: "onButtonPressed", forControlEvents: .TouchUpInside)
startX += width/CGFloat(totalItem)
addSubview(button)
}
if totalItem>0{
selectedItem = 0
delegate?.segmentedViewButtonChanged(0)
}
}
func onButtonPressed(button: UIButton){
if selectedItem != button.tag{
delegate?.segmentedViewButtonChanged(button.tag)
selectedItem = button.tag
}
}
}`
In storyboard added UIView and set class ITISegmentedView
in my ViewController:
let cell = tableView.dequeueReusableCellWithIdentifier( cellName, forIndexPath: indexPath)
let seg = (cell.viewWithTag(1) as! ITISegmentedView)
seg.dataSource = self
seg.delegate = self
PROBLEM:
init(coder aDecoder: NSCoder) calls on dequeueReusableCell and at this moment data source and delegate is not set, so ITISegmentedView doesn't work.
Fall back to an empty dataSource when you encounter nil.
Also, try not to use !, but rather ? and ?? to always take into account that an Optional is... well. Optional.

Resources