Autoadapt height of View - ios

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

Related

Swift CVCalendar not updating monthLabel

I recently started using CVCalendar to create a CalendarView with a Week presentation.
I created a UILabel called monthLabel which has an outlet to the view's controller
The ViewController for this particular view is this one:
import UIKit
import CVCalendar
class EventsViewController: UIViewController {
#IBOutlet weak var headerView: UIView!
#IBOutlet weak var menuView: CVCalendarMenuView!
#IBOutlet weak var calendarView: CVCalendarView!
#IBOutlet weak var eventsTable: UITableView!
#IBOutlet weak var monthLabel: UILabel!
private var currentCalendar: Calendar?
private var animationFinished: Bool = true
let mainAppViewHeader: MainAppViewHeader = UIView.fromNib()
override func viewDidLoad() {
super.viewDidLoad()
loadViewHeader()
setupTable()
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
setupDatePicker()
}
override func awakeFromNib() {
let timeZoneBias = 480 // (UTC+08:00)
currentCalendar = Calendar(identifier: .gregorian)
currentCalendar?.locale = Locale(identifier: "fr_FR")
if let timeZone = TimeZone(secondsFromGMT: -timeZoneBias * 60) {
currentCalendar?.timeZone = timeZone
}
}
func setupDatePicker() {
menuView.delegate = self
calendarView.delegate = self
menuView.backgroundColor = ColorManager.Gray5
calendarView.backgroundColor = ColorManager.Gray5
calendarView.appearance.dayLabelPresentWeekdaySelectedBackgroundColor = ColorManager.PrimaryColor
calendarView.appearance.dayLabelWeekdaySelectedBackgroundColor = ColorManager.PrimaryColor
if let currentCalendar = currentCalendar {
monthLabel.text = CVDate(date: Date(), calendar: currentCalendar).globalDescription
}
menuView.commitMenuViewUpdate()
calendarView.commitCalendarViewUpdate()
}
func loadViewHeader() {
mainAppViewHeader.setupHeader()
headerView.addSubview(mainAppViewHeader)
}
func setupTable() {
let rowNib = UINib(nibName: "EventTableCell", bundle: nil)
eventsTable.dataSource = self
eventsTable.delegate = self
eventsTable.register(rowNib, forCellReuseIdentifier: "eventCell")
eventsTable.separatorStyle = .none
}
}
extension EventsViewController: CVCalendarViewDelegate, CVCalendarMenuViewDelegate {
func presentationMode() -> CalendarMode {
return .weekView
}
func firstWeekday() -> Weekday {
return .sunday
}
func shouldAutoSelectDayOnWeekChange() -> Bool {
return true
}
func disableScrollingBeforeDate() -> Date {
let currentDate = Date()
return currentDate.addingTimeInterval(-8 * 24 * 60 * 60)
}
func disableScrollingBeyondDate() -> Date {
let currentDate = Date()
return currentDate.addingTimeInterval(180 * 24 * 60 * 60)
}
func maxSelectableRange() -> Int {
return 1
}
func shouldSelectRange() -> Bool {
false
}
func earliestSelectableDate() -> Date {
let currentDate = Date()
return currentDate.addingTimeInterval(-8 * 24 * 60 * 60)
}
func latestSelectableDate() -> Date {
let currentDate = Date()
return currentDate.addingTimeInterval(180 * 24 * 60 * 60)
}
func shouldShowWeekdaysOut() -> Bool {
return true
}
func didShowPreviousWeekView(from startDayView: DayView, to endDayView: DayView) {
print(startDayView, endDayView)
}
func presentedDateUpdated(_ date: CVDate) {
monthLabel.text = "asdf" //calendarView.presentedDate.globalDescription
print(date.globalDescription)
}
}
extension EventsViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let eventCell = tableView.dequeueReusableCell(withIdentifier: "eventCell") as? EventTableCell else {
return UITableViewCell()
}
eventCell.setupCell()
eventCell.updateConstraintsIfNeeded()
return eventCell
}
}
extension EventsViewController: UITableViewDelegate {
}
And the outlet reference:
The issue here is that even tho I'm telling the UILabel to change its text whenever the presented date is updated:
func presentedDateUpdated(_ date: CVDate) {
monthLabel.text = "asdf" //calendarView.presentedDate.globalDescription
print(date.globalDescription)
}
It's not updating its value, yet I get the date.globalDescription value printed in the console.
Is there something I'm missing when trying to update the label?
I tried using the code in the official example:
func presentedDateUpdated(_ date: CVDate) {
if monthLabel.text != date.globalDescription && self.animationFinished {
let updatedMonthLabel = UILabel()
updatedMonthLabel.textColor = monthLabel.textColor
updatedMonthLabel.font = monthLabel.font
updatedMonthLabel.textAlignment = .center
updatedMonthLabel.text = date.globalDescription
updatedMonthLabel.sizeToFit()
updatedMonthLabel.alpha = 0
updatedMonthLabel.center = self.monthLabel.center
let offset = CGFloat(48)
updatedMonthLabel.transform = CGAffineTransform(translationX: 0, y: offset)
updatedMonthLabel.transform = CGAffineTransform(scaleX: 1, y: 0.1)
UIView.animate(withDuration: 0.35, delay: 0, options: UIView.AnimationOptions.curveEaseIn, animations: {
self.animationFinished = false
self.monthLabel.transform = CGAffineTransform(translationX: 0, y: -offset)
self.monthLabel.transform = CGAffineTransform(scaleX: 1, y: 0.1)
self.monthLabel.alpha = 0
updatedMonthLabel.alpha = 1
updatedMonthLabel.transform = CGAffineTransform.identity
}) { _ in
self.animationFinished = true
self.monthLabel.frame = updatedMonthLabel.frame
self.monthLabel.text = updatedMonthLabel.text
self.monthLabel.transform = CGAffineTransform.identity
self.monthLabel.alpha = 1
updatedMonthLabel.removeFromSuperview()
}
self.view.insertSubview(updatedMonthLabel, aboveSubview: self.monthLabel)
}
}
But this approach is only pushing a new view with the updated month and then dismissing the view, and while it contains basically the same code that I was using:
self.monthLabel.text = updatedMonthLabel.text
The monthLabel is not being updated, this is what happens when using the above code, it reverts back to original text.
If I remove this line:
updatedMonthLabel.removeFromSuperview()
Both texts get overlapped, so it's ignoring this line:
self.monthLabel.text = updatedMonthLabel.text
I've done some extra testing, and checked that the label is correctly linked with an outlet to my view, I changed the text color:
func presentedDateUpdated(_ date: CVDate) {
print(Thread.current)
print("Wololo")
monthLabel.textColor = UIColor.green
monthLabel.text = "asdf"
// calendarView.contentController.refreshPresentedMonth()
// monthLabel.text = "asdf" //calendarView.presentedDate.globalDescription
// print(date.globalDescription)
}
It prints:
<NSThread: 0x600002b6c100>{number = 1, name = main}
Wololo
N number of times I change the day by clicking on the calendar, however the text remains unchanged:
For some reason the code inside:
override func awakeFromNib() {
let timeZoneBias = 480 // (UTC+08:00)
currentCalendar = Calendar(identifier: .gregorian)
currentCalendar?.locale = Locale(identifier: "fr_FR")
if let timeZone = TimeZone(secondsFromGMT: -timeZoneBias * 60) {
currentCalendar?.timeZone = timeZone
}
}
was in conflict with not updating the label, if we remove the above code and instead do this:
func setupDatePicker() {
menuView.delegate = self
calendarView.delegate = self
menuView.commitMenuViewUpdate()
calendarView.commitCalendarViewUpdate()
monthLabel.text = calendarView.presentedDate.globalDescription //This line at the bottom of this method
}
Everything works once again.
If someone knows the reason behind it, feel free to make a comment and I'll add it to the answer.
presentedDateUpdated is likely being called on a background thread making it so your UI changes aren't being displayed. you can confirm this by printing Thread.current inside presentedDateUpdated.
a simple solution would be this:
func presentedDateUpdated(_ date: CVDate) {
DispatchQueue.main.async { [weak self] in
self?.monthLabel.text = "asdf" //calendarView.presentedDate.globalDescription
print(date.globalDescription)
}
}

Tracking upload progress in UICollectionView is duplicating cells

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()
}
}

Passing a URL from a tableview controller to a view controller with an AVAudioPlayer

I am currently trying to play audio from my firebase database. I have two view controllers, the first is a tableviewcontroller and the second is a view controller with an AVAudioPlayer. I am trying to figure out how to sucessfully play the audio url that is grabbed from firebase that is then passed to the second view controller to play when the audio is selected in the tableview. Here is my current code which returns "Fatal error: Unexpectedly found nil while unwrapping an Optional value" on my second viewcontroller when the audio is selected from the tableview controller.
TableViewController:
import UIKit
import Firebase
class HeaderViewTableViewController: UITableViewController {
#IBOutlet weak var trackImage: UIImageView!
#IBOutlet weak var trackName: UILabel!
#IBOutlet weak var artistName: UILabel!
#IBOutlet weak var trackHeaderImage: UIImageView!
var image: UIImageView?
var image2: UIImageView?
var songs = [String]()
var urls = [URL]()
var song:SongPost?
private let tableHeaderViewHeight: CGFloat = 350.0
private let tableHeaderViewCutAway: CGFloat = 0.1
var headerView: HeaderView!
var headerMaskLayer: CAShapeLayer!
override func viewDidLoad() {
super.viewDidLoad()
print(song)
trackName.text = song?.title
ImageService.getImage(withURL: song!.coverImage) { image in
self.trackImage.image = image
self.trackHeaderImage.image = image
}
artistName.text = song?.author.fullname
tableView.tableFooterView = UIView()
headerView = tableView.tableHeaderView as! HeaderView
headerView.imageView = trackImage
tableView.tableHeaderView = nil
tableView.addSubview(headerView)
tableView.contentInset = UIEdgeInsets(top: tableHeaderViewHeight, left: 0, bottom: 0, right: 0)
tableView.contentOffset = CGPoint(x: 0, y: -tableHeaderViewHeight + 64)
//cut away header view
headerMaskLayer = CAShapeLayer()
headerMaskLayer.fillColor = UIColor.black.cgColor
headerView.layer.mask = headerMaskLayer
let effectiveHeight = tableHeaderViewHeight - tableHeaderViewCutAway/2
tableView.contentInset = UIEdgeInsets(top: effectiveHeight, left: 0, bottom: 0, right: 0)
tableView.contentOffset = CGPoint(x: 0, y: -effectiveHeight)
updateHeaderView()
}
func updateHeaderView() {
let effectiveHeight = tableHeaderViewHeight - tableHeaderViewCutAway/2
var headerRect = CGRect(x: 0, y: -effectiveHeight, width: tableView.bounds.width, height: tableHeaderViewHeight)
if tableView.contentOffset.y < -effectiveHeight {
headerRect.origin.y = tableView.contentOffset.y
headerRect.size.height = -tableView.contentOffset.y + tableHeaderViewCutAway/2
}
headerView.frame = headerRect
let path = UIBezierPath()
path.move(to: CGPoint(x: 0, y:0))
path.addLine(to: CGPoint(x: headerRect.width, y: 0))
path.addLine(to: CGPoint(x: headerRect.width, y: headerRect.height))
path.addLine(to: CGPoint(x: 0, y: headerRect.height - tableHeaderViewCutAway))
headerMaskLayer?.path = path.cgPath
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
self.tableView.decelerationRate = UIScrollView.DecelerationRate.fast
}
override func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return UITableView.automaticDimension
}
#IBAction func backButton(_ sender: Any) {
dismiss(animated: true, completion: nil)
}
}
extension HeaderViewTableViewController {
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return (song?.audioName.count)!
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "SongCell", for: indexPath) as! SongNameTableViewCell
cell.set(song: self.song!)
cell.songName.text = song?.audioName[indexPath.row]
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let vc = storyboard?.instantiateViewController(identifier: "ViewController2") as? ViewController2
vc?.song = song
vc?.audioName = (song?.audioName[indexPath.row])!
self.present(vc!, animated: true, completion: nil)
vc?.url = song?.audioUrl[indexPath.row]
}
}
extension HeaderViewTableViewController {
override func scrollViewDidScroll(_ scrollView: UIScrollView) {
updateHeaderView()
}
}
Second ViewController:
import UIKit
import AVFoundation
import MediaPlayer
import Firebase
class ViewController2: UIViewController, UITabBarControllerDelegate {
var song2 = [SongPost]()
var song: SongPost?
var audioName : String?
var url:URL?
#IBOutlet weak var progressBar: UISlider!
#IBOutlet weak var downChevron: UIButton!
var timer:Timer?
var player = AVAudioPlayer()
#IBOutlet weak var currentTime: UILabel!
#IBOutlet weak var durationTime: UILabel!
#IBOutlet weak var coverImage: UIImageView!
#IBOutlet weak var backgroundImage: UIImageView!
#IBOutlet weak var songName: UILabel!
#IBOutlet weak var artistName: UILabel!
var trackID: Int = 0
var toggleState = 1
var ref: DatabaseReference?
#IBOutlet weak var playButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
setUI()
tabBarController?.tabBar.barTintColor = UIColor.black
tabBarController?.tabBar.isTranslucent = false
tabBarController?.tabBar.tintColor = .white
tabBarController?.tabBar.unselectedItemTintColor = .gray
do {
let songURL = url
print(songURL)
try player = AVAudioPlayer(contentsOf: songURL!)
progressBar.maximumValue = Float(player.duration)
timer = Timer.scheduledTimer(timeInterval: 0.00001, target: self, selector: #selector(changeSliderValueFollowPlayerCurtime), userInfo: nil, repeats: true)
player.delegate = self
player.play()
MPNowPlayingInfoCenter.default().nowPlayingInfo = [
MPMediaItemPropertyTitle : "The Box",
MPMediaItemPropertyArtist: "Roddy Ricch",
MPMediaItemPropertyPlaybackDuration: player.duration,
]
UIApplication.shared.beginReceivingRemoteControlEvents()
becomeFirstResponder()
} catch {
}
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playback, options: .defaultToSpeaker)
} catch {
}
}
func setUI() {
ImageService.getImage(withURL: song!.coverImage) { image in
self.coverImage.image = image
self.backgroundImage.image = image
}
songName.text = song?.title
artistName.text = song?.author.fullname
}
override func remoteControlReceived(with event: UIEvent?) {
if let event = event {
if event.type == .remoteControl {
switch event.subtype {
case .remoteControlPlay:
player.play()
case .remoteControlPause:
player.pause()
case .remoteControlNextTrack:
print("next")
case .remoteControlPreviousTrack:
print("previous")
default:
print("error")
}
}
}
}
#objc func changeSliderValueFollowPlayerCurtime() {
let curValue = Float(player.currentTime)
progressBar.value = curValue
}
#IBAction func sliderUsed(_ sender: Any) {
player.pause()
let curTime = progressBar.value
player.currentTime = TimeInterval(curTime)
player.play()
}
#IBAction func playPauseButtonTapped(_ sender: Any) {
let playBtn = sender as! UIButton
if toggleState == 1 {
player.pause()
toggleState = 2
playBtn.setImage(UIImage(named:"play 5.png"),for:UIControl.State.normal)
} else {
player.play()
toggleState = 1
playBtn.setImage(UIImage(named:"pause 4.png"),for:UIControl.State.normal)
}
}
#IBAction func nextButton(_ sender: Any) {
}
#IBAction func backButton(_ sender: Any) {
if trackID != 0 || trackID > 0 {
trackID -= 1
}
do {
let audioPath = Bundle.main.path(forResource: "the box", ofType: "mp3")
try player = AVAudioPlayer(contentsOf: NSURL(fileURLWithPath: audioPath!) as URL)
} catch {
}
}
#IBAction func downChevronTapped(_ sender: Any) {
dismiss(animated: true, completion: nil)
}
}
extension ViewController2: AVAudioPlayerDelegate {
func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
if flag {
do {
let audioPath = Bundle.main.path(forResource: "the box", ofType: "mp3")
try self.player = AVAudioPlayer(contentsOf: NSURL(fileURLWithPath: audioPath!) as URL)
progressBar.maximumValue = Float(player.duration)
timer = Timer.scheduledTimer(timeInterval: 0.00001, target: self, selector: #selector(changeSliderValueFollowPlayerCurtime), userInfo: nil, repeats: true)
player.delegate = self
player.play()
} catch {
}
}
}
}
SongPost Class:
import Foundation
class SongPost {
var id: String
var author: UserProfile
var title: String
var coverImage: URL
var audioUrl: [URL]
var audioName : [String]
var createdAt:Date
init(id:String, author:UserProfile, title:String, coverImage:URL, audioUrl:[URL], audioName: [String] ,timestamp:Double) {
self.id = id
self.author = author
self.coverImage = coverImage
self.audioUrl = audioUrl
self.audioName = audioName
self.title = title
self.createdAt = Date(timeIntervalSince1970: timestamp / 1000)
}
}
UPDATED
Func Configure Code:
func configure(song: SongPost, audioName: String, url:URL) {
do {
let songURL = url
print(songURL)
// let audioPath = Bundle.main.path(forResource: "the box", ofType: "mp3")
try player = AVAudioPlayer(contentsOf: songURL)
progressBar.maximumValue = Float(player.duration)
timer = Timer.scheduledTimer(timeInterval: 0.00001, target: self, selector: #selector(changeSliderValueFollowPlayerCurtime), userInfo: nil, repeats: true)
player.delegate = self
player.play()
MPNowPlayingInfoCenter.default().nowPlayingInfo = [
MPMediaItemPropertyTitle : "The Box",
MPMediaItemPropertyArtist: "Roddy Ricch",
MPMediaItemPropertyPlaybackDuration: player.duration,
]
UIApplication.shared.beginReceivingRemoteControlEvents()
becomeFirstResponder()
} catch {
}
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playback, options: .defaultToSpeaker)
} catch {
}
}
Avoid using unwrapping so much in your code if you're not exactly sure optional is not nil.
At the time you call instantiateViewController, viewDidLoad method of ViewController2 may be called.
let vc = storyboard?.instantiateViewController(identifier: "ViewController2")
as? ViewController2
Which means you will get error on the line where you unwrap the songURL which is nil.
try player = AVAudioPlayer(contentsOf: songURL!)
So viewDidLoad is not a good place to setup your view in your case.
Create a configure function in ViewController2, which accept parameters from previous viewController, then you can setup your player when configure is called which means your parameters are not nil.
class ViewController2: UIViewController, UITabBarControllerDelegate {
func configure(song: SongPost, audioName : String, url:URL) {
//setup your view
}
//...... other methods, viewDidload etc.
}
//
extension HeaderViewTableViewController {
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let vc = storyboard?.instantiateViewController(identifier: "ViewController2")
as? ViewController2
self.present(vc!, animated: true, completion: nil)
vc?.configure(
song: song,
audioName: (song?.audioName[indexPath.row])!,
url: song?.audioUrl[indexPath.row])
}
}

How do I update text input position of the y depending on keyboard height in swift

I'm trying to implement the comment view controller.
I want using the auto layout via the storyboard.
My question is...
When I tap the input text..then keyboard will move up...but input text is not move up..
Keyboard is overlapping the text input..
Here is TableViewController.swift
import UIKit
import Parse
var commentUUID = [String]()
var commentOwner = [String]()
class CommentViewController: UIViewController, UITextViewDelegate, UITableViewDelegate, UITableViewDataSource{
//UI Objects
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var commentTextView: UITextView!
#IBOutlet weak var sendButton: UIButton!
var refresher = UIRefreshControl()
//values for reseting UI to default
var tableViewHeight : CGFloat = 0
var commentY : CGFloat = 0
var commentHeight : CGFloat = 0
//arryas to hold server data
var usernameArray = [String]()
var profileArray = [PFFile]()
var commentArray = [String]()
var dateArray = [NSDate?]()
//variable to hold keyboard frame
var keyboard = CGRect()
//page size
var page : Int32 = 15
override func viewDidLoad() {
super.viewDidLoad()
tableView.backgroundColor = .redColor()
//title at the top
self.navigationItem.title = "COMMENTS"
self.navigationItem.hidesBackButton = true
let backButton = UIBarButtonItem(title: "back", style: .Plain, target: self, action: #selector(CommentViewController.back(_:)))
self.navigationItem.leftBarButtonItem=backButton
//swipe to go back
let backSwipe = UISwipeGestureRecognizer(target: self, action: #selector(CommentViewController.back(_:)))
backSwipe.direction=UISwipeGestureRecognizerDirection.Right
self.view.addGestureRecognizer(backSwipe)
// catch notification if the keyboard is shown or hidden
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(CommentViewController.keyboardWillShow(_:)), name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(CommentViewController.keyboardWillHide(_:)), name: UIKeyboardWillHideNotification, object: nil)
//disable button from the beginning
sendButton.enabled = false
//call function
alignment()
loadComments()
}
//I think it is not affect on the layout...
func configureTableView() {
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 160.0
}
// preload func
override func viewWillAppear(animated: Bool) {
//hide bottom bar
self.tabBarController?.tabBar.hidden = true
}
// postload func
override func viewWillDisappear(animated: Bool) {
self.tabBarController?.tabBar.hidden = false
}
//func loading when keyboard is shown
func keyboardWillShow(notification : NSNotification){
//define keyboard frame size
keyboard = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey]!.CGRectValue)!
//move UI up
UIView.animateWithDuration(0.4){ () -> Void in
self.tableView.frame.size.height = self.tableViewHeight - self.keyboard.height - self.commentTextView.frame.size.height + self.commentHeight
print("keyboard show")
self.commentTextView.frame.origin.y = self.commentY - self.keyboard.height - self.commentTextView.frame.size.height + self.commentHeight
self.sendButton.frame.origin.y = self.commentTextView.frame.origin.y
self.commentTextView.frame.origin.y = 250
}
}
//func loading when keyboard is hidden
func keyboardWillHide(notification : NSNotification){
//move UI down
UIView.animateWithDuration(0.4){() -> Void in
self.tableView.frame.size.height = self.tableViewHeight
self.commentTextView.frame.origin.y = self.commentY
self.sendButton.frame.origin.y = self.commentY
}
}
//alignment function
func alignment(){
let width = self.view.frame.size.width
let height = self.view.frame.size.height
tableView.frame = CGRectMake(0, 0, width, height - self.navigationController!.navigationBar.frame.size.height)
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 160.0
commentTextView.layer.cornerRadius = commentTextView.frame.size.width / 50
//delegates
commentTextView.delegate = self
tableView.delegate = self
tableView.dataSource = self
//assign reseting values
tableViewHeight = tableView.frame.size.height
commentHeight = commentTextView.frame.size.height
commentY = commentTextView.frame.origin.y
}
//while writing something
func textViewDidChange(textView: UITextView) {
//disable button if entered no text
let spacing = NSCharacterSet.whitespaceAndNewlineCharacterSet()
//It shown when user entered type
if !commentTextView.text.stringByTrimmingCharactersInSet(spacing).isEmpty{
sendButton.enabled = true
}else {
sendButton.enabled = false
}
// + paragraph
if textView.contentSize.height > textView.frame.size.height && textView.frame.height < 130{
//find difference to add
let difference = textView.contentSize.height - textView.frame.size.height
//redefine frame of commentText
textView.frame.origin.y = textView.frame.origin.y - difference
textView.frame.size.height = textView.contentSize.height
//move up tableView
if textView.contentSize.height + keyboard.height + commentY >= tableView.frame.size.height {
tableView.frame.size.height = tableView.frame.size.height - difference
}
}
// - parapraph
else if textView.contentSize.height < textView.frame.size.height {
//find difference to deduct
let difference = textView.frame.size.height - textView.contentSize.height
//redefine frame of commentText
textView.frame.origin.y = textView.frame.origin.y + difference
textView.frame.size.height = textView.contentSize.height
//move down tableview
if textView.contentSize.height + keyboard.height + commentY > tableView.frame.size.height {
tableView.frame.size.height = tableView.frame.size.height + difference
}
}
}
//load comments function
func loadComments(){
//STEP 1. Count total comments in order to skip all except page size
let countQuery = PFQuery(className: "comments")
countQuery.whereKey("to", equalTo: commentUUID.last!)
countQuery.countObjectsInBackgroundWithBlock({(count:Int32, error:NSError?) -> Void in
//if comments on the server for current post are more than (page size 15) implement pull to refresh func
if self.page < count {
self.refresher.addTarget(self, action: #selector(CommentViewController.loadMore), forControlEvents: UIControlEvents.ValueChanged)
self.tableView.addSubview(self.refresher)
}
//STEP 2. Request last (page size 15) comments
let query = PFQuery(className: "comments")
query.whereKey("to", equalTo: commentUUID.last!)
query.skip = count - self.page
query.addAscendingOrder("createdAt")
query.findObjectsInBackgroundWithBlock({(objects:[PFObject]?, error:NSError?) -> Void in
if error == nil {
//clean up
self.usernameArray.removeAll(keepCapacity: false)
self.profileArray.removeAll(keepCapacity: false)
self.commentArray.removeAll(keepCapacity: false)
self.dateArray.removeAll(keepCapacity: false)
//find related object
for object in objects!{
self.usernameArray.append(object.objectForKey("username") as! String)
self.profileArray.append(object.objectForKey("profileImg") as! PFFile)
self.commentArray.append(object.objectForKey("comment") as! String)
self.dateArray.append(object.createdAt)
self.tableView.reloadData()
//scroll to bottom
self.tableView.scrollToRowAtIndexPath(NSIndexPath(forRow: self.commentArray.count - 1, inSection: 0), atScrollPosition: UITableViewScrollPosition.Bottom, animated: false)
}
}else {
print(error?.localizedDescription)
}
})
})
}
//Pagenation
func loadMore(){
//STEP 1. Count total comments in order to skip all except page size
let countQuery = PFQuery(className: "comments")
countQuery.whereKey("to", equalTo: commentUUID.last!)
countQuery.countObjectsInBackgroundWithBlock({(count:Int32, error:NSError?) -> Void in
//self refresher
self.refresher.endRefreshing()
//remove refresher if loaded all comments
if self.page >= count {
self.refresher.removeFromSuperview()
}
//STEP2. Load more comments
if self.page < count {
//increase page to laod 30 as first paging
self.page = self.page + 15
//request existing comments from the server
let query = PFQuery(className: "comments")
query.whereKey("to", equalTo: commentUUID.last!)
query.skip = count - self.page
query.addAscendingOrder("createdAt")
query.findObjectsInBackgroundWithBlock({(objects:[PFObject]?, error:NSError?) -> Void in
if error==nil{
//clean up
self.usernameArray.removeAll(keepCapacity: false)
self.profileArray.removeAll(keepCapacity: false)
self.commentArray.removeAll(keepCapacity: false)
self.dateArray.removeAll(keepCapacity: false)
//find related objects
for object in objects! {
self.usernameArray.append(object.objectForKey("username") as! String)
self.profileArray.append(object.objectForKey("profileImg") as! PFFile)
self.commentArray.append(object.objectForKey("comments") as! String)
self.dateArray.append(object.createdAt)
self.tableView.reloadData()
}
}else {
print(error?.localizedDescription)
}
})
}
})
}
//Send Button Tapped
#IBAction func sendButtonTapped(sender: AnyObject) {
print("send tapped")
//STEP1. Add row in tableView
usernameArray.append(PFUser.currentUser()!.username!)
profileArray.append(PFUser.currentUser()?.objectForKey("profileImg") as! PFFile)
dateArray.append(NSDate())
commentArray.append(commentTextView.text.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet()))
tableView.reloadData()
//STEP2. Send comment to server
let commentObj = PFObject(className: "comments")
commentObj["to"] = commentUUID.last
commentObj["username"] = PFUser.currentUser()?.username
commentObj["profileImg"] = PFUser.currentUser()?.valueForKey("profileImg")
commentObj["comment"] = commentTextView.text.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet())
commentObj.saveEventually()
//Scroll to bottom
self.tableView.scrollToRowAtIndexPath(NSIndexPath(forItem: commentArray.count - 1, inSection: 0), atScrollPosition: UITableViewScrollPosition.Bottom, animated: false)
//STEP 3. Reset UI
sendButton.enabled = false
commentTextView.text = ""
commentTextView.frame.size.height = commentHeight
commentTextView.frame.origin.y = sendButton.frame.origin.y
tableView.frame.size.height = self.tableViewHeight - self.keyboard.height - self.commentTextView.frame.size.height + self.commentHeight
}
//tableview
//cell numb
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return commentArray.count
}
//cell height
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
//Cell config
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
//declaire cell
let cell = tableView.dequeueReusableCellWithIdentifier("commentCell") as! CommentTableViewCell
cell.usernameButton.setTitle(usernameArray[indexPath.row], forState: .Normal)
cell.usernameButton.sizeToFit()
cell.commentLabel.text = commentArray[indexPath.row]
profileArray[indexPath.row].getDataInBackgroundWithBlock({(data:NSData?, error:NSError?) -> Void in
cell.profileImagevView.image = UIImage(data: data!)
})
//calculate date
let from = dateArray[indexPath.row]
let now = NSDate()
let components : NSCalendarUnit = [.Second, .Minute, .Hour, .Day, .WeekOfMonth]
let difference = NSCalendar.currentCalendar().components(components, fromDate: from!, toDate: now, options: [])
if difference.second <= 0 {
cell.dateLabel.text = "now"
}
if difference.second > 0 && difference.minute == 0 {
cell.dateLabel.text = "\(difference.second)s"
}
if difference.minute > 0 && difference.hour == 0 {
cell.dateLabel.text = "\(difference.minute)m"
}
if difference.hour > 0 && difference.day == 0 {
cell.dateLabel.text = "\(difference.hour)h"
}
if difference.day > 0 && difference.weekOfMonth == 0 {
cell.dateLabel.text = "\(difference.day)d."
}
if difference.weekOfMonth > 0 {
cell.dateLabel.text = "\(difference.weekOfMonth)w."
}
cell.usernameButton.layer.setValue(indexPath, forKey: "index")
return cell
}
//clicked username button
#IBAction func usernameButtonTapped(sender: AnyObject) {
//call index of current button
let i = sender.layer.valueForKey("index") as! NSIndexPath
//Call cell to call further cell data
let cell = tableView.cellForRowAtIndexPath(i) as! CommentTableViewCell
//if user tapped on his username go home, else go guest
if cell.usernameButton.titleLabel?.text == PFUser.currentUser()?.username {
let home = self.storyboard?.instantiateViewControllerWithIdentifier("HomeViewController") as! HomeViewController
self.navigationController?.pushViewController(home, animated: true)
}else {
guestname.append(cell.usernameButton.titleLabel!.text!)
let guest = self.storyboard?.instantiateViewControllerWithIdentifier("GuestHomeViewController") as! GuestHomeViewController
self.navigationController?.pushViewController(guest, animated: true)
}
}
//go back
func back(sender : UIBarButtonItem){
//push back
self.navigationController?.popViewControllerAnimated(true)
//clean comment uuid from holding information
if !commentUUID.isEmpty{
commentUUID.removeLast()
}
//clean comment owner from last holding information
if !commentOwner.isEmpty{
commentOwner.removeLast()
}
}
}
And It is my cell controller
import UIKit
class CommentTableViewCell: UITableViewCell {
#IBOutlet weak var profileImagevView: UIImageView!
#IBOutlet weak var usernameButton: UIButton!
#IBOutlet weak var commentLabel: UILabel!
#IBOutlet weak var dateLabel: UILabel!
//default func
override func awakeFromNib() {
super.awakeFromNib()
//round profile
profileImagevView.layer.cornerRadius = profileImagevView.frame.size.width/2
profileImagevView.clipsToBounds = true
}
}
Two comments:
Use UIKeyboardDidShowNotification instead of UIKeyboardWillShowNotification
Do not modify frame directly when you're using auto layout. Just link your bottom layout constraint to the controller and change a constant value when needed.
Try this:
self.tableView.frame.size.height = self.view.frame.height - keyboard.height - self.tableView.frame.size.height
print("keyboard show")
self.commentTextView.frame = CGRect(self.commentTextView.frame.minX, self.commentTextView.frame.minY - keyboard.height, self.commentTextView.frame.width, self.commentTextView.frame.height)
self.sendButton.frame.origin.y = self.commentTextView.frame.origin.y
Edit
#Arsen's Answer makes much more sense and probably much easier, by the way :P But this should work the same.

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
}

Resources