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) }
}
Related
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()
}
}
I need to create a very similar class of the RatingControl class that you make when you create the FoodTracker tutorial. Only difference with mine is that I need that class to be able to create different instances of the rating control, that vary with the types of images. So instead of just having my positivePointRatingButtons UIButton array, I need it to be able to instantiate either the negativePointRatingButtons or superPointRatingButtons as-well. I just dont want to make another whole class just for this purpose, and I am new so I figure Id get some help.
import UIKit
#IBDesignable class PointRatingControl: UIStackView {
//MARK: Properties
private var positivePointRatingButtons = [UIButton]()
private var negativePointRatingButtons = [UIButton]()
private var superPointRatingButtons = [UIButton]()
var rating = 0 {
didSet {
updatePointButtonSelectionStates()
}
}
#IBInspectable var circleType: Int = 1 {
didSet {
setupButtons()
}
}
#IBInspectable var circleSize: CGSize = CGSize(width: 30.0, height: 30.0) {
didSet {
setupButtons()
}
}
#IBInspectable var circleCount: Int = 10 {
didSet {
setupButtons()
}
}
//MARK: Initialization
override init(frame: CGRect) {
super.init(frame: frame)
setupButtons()
}
required init(coder: NSCoder) {
super.init(coder: coder)
setupButtons()
}
//MARK: Button Action
#objc func ratingButtonTapped(button: UIButton) {
guard let index = positivePointRatingButtons.index(of: button) else {
fatalError("The button, \(button), is not in the positiveRatingButtons array: \(positivePointRatingButtons)")
}
// 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
}
}
//MARK: Private Methods
private func setupButtons() {
// Clear any existing buttons
for button in positivePointRatingButtons {
removeArrangedSubview(button)
button.removeFromSuperview()
}
positivePointRatingButtons.removeAll()
// Load button images, since its #IDDesignable in order to show in the interface builder you have to specifyexplicitly the catalog's bundle, as opposed to just using UIImage(named:) method
let bundle = Bundle(for: type(of: self))
let emptyCircle = UIImage(named: "greenCirclePhoto", in: bundle, compatibleWith: self.traitCollection)
let selectedCircle = UIImage(named: "greenFilledCirclePhoto", in: bundle, compatibleWith: self.traitCollection)
let highlightedCircle = UIImage(named: "greenSelectedCirclePhoto", in: bundle, compatibleWith: self.traitCollection)
for _ in 0..<circleCount {
let button = UIButton()
button.setImage(emptyCircle, for: .normal)
button.setImage(selectedCircle, for: .selected)
button.setImage(highlightedCircle, for: .highlighted)
button.setImage(highlightedCircle, for: [.highlighted, .selected])
button.translatesAutoresizingMaskIntoConstraints = false
button.heightAnchor.constraint(equalToConstant: circleSize.height).isActive = true
button.widthAnchor.constraint(equalToConstant: circleSize.width).isActive = true
button.addTarget(self, action: #selector(PointRatingControl.ratingButtonTapped(button:)), for: .touchUpInside)
addArrangedSubview(button)
positivePointRatingButtons.append(button)
}
updatePointButtonSelectionStates()
}
private func updatePointButtonSelectionStates() {
for (index, button) in positivePointRatingButtons.enumerated() {
// If the index of a button is less than the rating, that button should be selected.
button.isSelected = index < rating
}
}
}
Id like to be able to use #IBInspectable aswell using the circleType property I defined so that I can use like 1, 2, 3 Integers as representations for each case.
I figured out how to do it. I just made a switch case to load up different images based on the ratingType variable
I'm trying to create a separate class for a group of buttons to handle a logic of only one button get selected at one time (Radio button)
The logic is not important here. I just cannot receive the touchUpIndside event when tapping on one of these buttons.
I set the target to self (the custom RadionButtonsController class) but this class cannot receive the event.
I tried to add UIResponder as a superclass. But still cannot receive the event in this class.
Here is my code:
import UIKit
class RadioButtonsController: UIResponder
{
var buttons = [UIButton]()
var selectedValue: String?
init(numberOfButtons: Int, titles: [String], values: [String])
{
guard titles.count == numberOfButtons && values.count == numberOfButtons else
{
fatalError("number of items in `titles` and `values` must equal to the `numberOfButtons`")
}
super.init()
for i in 0..<numberOfButtons
{
let button = UIButton(type: .system)
button.setTitle(titles[i], for: .normal)
button.backgroundColor = [UIColor.red, UIColor.blue, UIColor.gray, UIColor.yellow].randomElement()
button.addTarget(self, action: #selector(radioButtonSelected(sender:)), for: .touchUpInside)
buttons.append(button)
}
}
#objc private func radioButtonSelected(sender: UIButton)
{
print("Selected Button: \(sender)") // this is will never get printed
}
}
And I use this buttons controller in a table view cell. In cellForRowAtIndexPath:
let itemOptionsCell = tableView.dequeueReusableCell(withIdentifier: "ItemOptionsCell", for: indexPath) as! ItemOptionsTableViewCell
let itemOption = logicController.item.options[indexPath.row]
itemOptionsCell.optionsTitleLabel.text = itemOption.name
let radioButtonsController = RadioButtonsController(numberOfButtons: itemOption.values.count,
titles: itemOption.values.map({ $0.name }),
values: itemOption.values.map({ $0.valueID }))
for button in radioButtonsController.buttons
{
itemOptionsCell.buttonsStackView.addArrangedSubview(button)
}
return itemOptionsCell
The buttons appear to be clicked, but the method radionButtonSelected(snder:) never get called.
The problem is that your code creates a RadioButtonsController object and then throws it away. Your buttons thus have no one to send their action to.
if you want to create a RadioButton without using any external pods
you can use this code :
import Foundation
import UIKit
class HRadioButton: UIButton {
#IBOutlet var otherButtons: [HRadioButton]!
var hSelected = false {
didSet {
if hSelected {
if imageView != nil{
self.setImage(checkedImage, for: .normal)
}else{
self.setImage(checkedImage, for: .normal)
print("image null")
}
if self.otherButtons != nil {
for button in self.otherButtons {
button.hSelected = false
button.imageView?.image = self.unCheckedImage
}
} else {
print("Button is null ")
}
}
}
}
var checkedImage: UIImage!, unCheckedImage: UIImage!
override func awakeFromNib() {
super.awakeFromNib()
checkedImage = #imageLiteral(resourceName: "ic_radio_button_checked").maskWithColor(color: tintColor)
unCheckedImage = #imageLiteral(resourceName: "ic_radio_button_unchecked").maskWithColor(color: tintColor)
setImage(unCheckedImage, for: .normal)
alignImageRight()
self.onTap {
self.imageView?.image = self.checkedImage
self.hSelected = true
if self.otherButtons != nil {
for button in self.otherButtons {
button.hSelected = false
button.imageView?.image = self.unCheckedImage
}
}
}
}
}
extension UIImage {
func maskWithColor(color: UIColor) -> UIImage? {
let maskImage = cgImage!
let width = size.width
let height = size.height
let bounds = CGRect(x: 0, y: 0, width: width, height: height)
let colorSpace = CGColorSpaceCreateDeviceRGB()
let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue)
let context = CGContext(data: nil, width: Int(width), height: Int(height), bitsPerComponent: 8, bytesPerRow: 0, space: colorSpace, bitmapInfo: bitmapInfo.rawValue)!
context.clip(to: bounds, mask: maskImage)
context.setFillColor(color.cgColor)
context.fill(bounds)
if let cgImage = context.makeImage() {
let coloredImage = UIImage(cgImage: cgImage)
return coloredImage
} else {
return nil
}
}
}
extension UIView {
// In order to create computed properties for extensions, we need a key to
// store and access the stored property
fileprivate struct AssociatedObjectKeys {
static var tapGestureRecognizer = "MediaViewerAssociatedObjectKey_mediaViewer"
}
fileprivate typealias Action = (() -> Void)?
// Set our computed property type to a closure
fileprivate var tapGestureRecognizerAction: Action? {
set {
if let newValue = newValue {
// Computed properties get stored as associated objects
objc_setAssociatedObject(self, &AssociatedObjectKeys.tapGestureRecognizer, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN)
}
}
get {
let tapGestureRecognizerActionInstance = objc_getAssociatedObject(self, &AssociatedObjectKeys.tapGestureRecognizer) as? Action
return tapGestureRecognizerActionInstance
}
}
// This is the meat of the sauce, here we create the tap gesture recognizer and
// store the closure the user passed to us in the associated object we declared above
public func onTap(action: (() -> Void)?) {
self.isUserInteractionEnabled = true
self.tapGestureRecognizerAction = action
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTapGesture))
self.addGestureRecognizer(tapGestureRecognizer)
}
// Every time the user taps on the UIImageView, this function gets called,
// which triggers the closure we stored
#objc fileprivate func handleTapGesture(sender: UITapGestureRecognizer) {
if let action = self.tapGestureRecognizerAction {
action?()
} else {
print("no action")
}
}
}
from xcode storyboard ui assign the class HRadioButton as the class of Button then you will find the otherButtons in Connection Inspector image of Connection Inspector Icon in Xcode
in xcode you will find otherButtons Appear there so drag and drop it to other buttons
and repeat this process on all buttons you want make it as Radio Group
for example
i have A Button,B Button,C Button
I will assign class to A Button then drag and drop otherbuttons from Connection Inspector to B and C Buttons
then Assign The Class To B button and make otherButtons refere to A,C Buttons and so on.
Later you can get the selected Btn from hSelected Property
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
}
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.