Text Overlapping Text That Should've deleted - swift

HI, so this is my vc for a view where I save info about a plane (the type and rego). It saves the plane and deletes and you can close the app and come back and it saves it there in the core data model but I have a weird problem. After you make a plane and it shows in the table view and add a few more planes (table view cell which has info) then the labels which display the info start to overlap with what should be deleted information. The pictures bellow show what I mean. Any help would be greatly appreciated.
var context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
var typeField: UITextField?
var regoField: UITextField?
#IBAction func addPlaneButton(_ sender: Any) {
let alertController = UIAlertController(title: "New Plane", message: "Please Input The Type And Registration", preferredStyle: .alert)
alertController.addTextField(configurationHandler: typeField)
alertController.addTextField(configurationHandler: regoField)
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
let saveAction = UIAlertAction(title: "Save", style: .default, handler: self.savePlane)
alertController.addAction(cancelAction)
alertController.addAction(saveAction)
self.present(alertController, animated: true)
print("Add Plane Pressed")
}
func typeField(textField: UITextField!) {
typeField = textField
typeField?.placeholder = "Aircraft Type"
}
func regoField(textField: UITextField!) {
regoField = textField
regoField?.placeholder = "Aircraft Registration"
}
#IBOutlet weak var tableView: UITableView!
var timer = Timer()
let utcItem = UIBarButtonItem()
let utcLbl = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
//Table View
tableView.delegate = self
tableView.dataSource = self
self.tableView.rowHeight = 88
setupView()
}
//////Functions////
func setupView() {
//UTC Time Formatter
let dateFormatter = DateFormatter()
dateFormatter.timeZone = TimeZone(abbreviation: "UTC")
dateFormatter.dateFormat = "HH:mm"
_ = Timer.scheduledTimer(timeInterval: 0.05, target: self, selector: #selector(updateTime), userInfo: nil, repeats: true)
//UTC Time
utcLbl.frame = CGRect(x: 0, y: 0, width: 100, height: 20)
utcLbl.text = "\(dateFormatter.string(from: Date())) UTC"
utcItem.customView = utcLbl
utcLbl.backgroundColor = UIColor.init(fromHexCode: "4FB7F1")
utcLbl.layer.cornerRadius = 10
utcLbl.textAlignment = .center
utcLbl.layer.masksToBounds = true // Or utcLbl.clipsToBounds = true
self.navigationItem.setLeftBarButtonItems([utcItem], animated: true)
// Large Title
self.title = "Planes"
self.navigationController?.navigationBar.largeTitleTextAttributes = [NSAttributedString.Key.font: UIFont(name: "Avenir-Black", size: 35)!]
self.navigationController?.navigationBar.prefersLargeTitles = true
let customBlue = UIColor(red:0.08, green:0.38, blue:0.75, alpha:1.0)
navigationController?.navigationBar.barTintColor = customBlue
}
//Constant UTC Time Lbl
#objc func updateTime() {
let formatter = DateFormatter()
formatter.timeZone = TimeZone(abbreviation: "UTC")
formatter.dateFormat = "HH:mm"
utcLbl.text = formatter.string(from: Date()) + " UTC"
}
//Save the Plane Info
func savePlane(alert: UIAlertAction) {
if typeField?.text != "" || regoField?.text != "" {
let newLog = NSEntityDescription.insertNewObject(forEntityName: "Planes", into: context)
newLog.setValue(self.typeField?.text, forKey: "type")
newLog.setValue(self.regoField?.text, forKey: "rego")
do{
try context.save()
}
catch {
print(error)
}
//Making the table update itself when user logs the plane
self.fetchData()
self.tableView.reloadData()
}
print("Plane Saved")
}
func fetchData() {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
do{
planeArray = try context.fetch(Planes.fetchRequest())
}
catch{
print(error)
}
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
if editingStyle == .delete {
let save = planeArray[indexPath.row]
context.delete(save)
(UIApplication.shared.delegate as! AppDelegate).saveContext()
do {
planeArray = try context.fetch(Planes.fetchRequest())
}
catch {
print(error)
}
tableView.reloadData()
}
}
//Table View Functions
public func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return (planeArray.count)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
//Plane Pic
let planeView = UIImageView()
planeView.frame = CGRect(x: 0, y: 0, width: 69, height: 67)
//planeView.center = CGPoint(x: cell.center.x - 150, y: cell.center.y)
planeView.center = CGPoint(x: cell.center.x - 145, y: cell.center.y)
let planeImage: UIImage = UIImage(named: "plane")!
planeView.image = planeImage
cell.addSubview(planeView)
//Type Label
let type = UILabel()
type.frame = CGRect(x: 0, y: 0, width: 45, height: 21)
type.center = CGPoint(x: cell.center.x - 80, y: cell.center.y - 22.5)
type.text = "Type:"
type.font = UIFont(name: "Montserrat-Medium", size: 17)
cell.addSubview(type)
//Type Answer
let typeAnswer = UILabel()
typeAnswer.frame = CGRect(x: 0, y: 0, width: 220, height: 21)
typeAnswer.center = CGPoint(x: cell.center.x + 62.5, y: cell.center.y - 22.5)
typeAnswer.text = ""
typeAnswer.font = UIFont(name: "Montserrat-Light", size: 17)
typeAnswer.textAlignment = .right
cell.addSubview(typeAnswer)
//Rego Label
let rego = UILabel()
rego.frame = CGRect(x: 0, y: 0, width: 110, height: 21)
rego.center = CGPoint(x: cell.center.x - 47.5, y: cell.center.y + 18.5)
rego.text = "Registration:"
rego.font = UIFont(name: "Montserrat-Medium", size: 17)
cell.addSubview(rego)
//rego answer
let regoAnswer = UILabel()
regoAnswer.frame = CGRect(x: 0, y: 0, width: 160, height: 21)
regoAnswer.center = CGPoint(x: cell.center.x + 92.5, y: cell.center.y + 18.5)
regoAnswer.text = ""
regoAnswer.font = UIFont(name: "Montserrat-Light", size: 17)
regoAnswer.textAlignment = .right
cell.addSubview(regoAnswer)
let save = planeArray[indexPath.row]
typeAnswer.text = save.type
regoAnswer.text = save.rego
return cell
}
override func viewWillAppear(_ animated: Bool) {
//Making the table update itself when user logs the plane
fetchData()
tableView.reloadData()
}
}

UITableView reuses the cells. In your case, when the cells are created the first time, you add a UILabel to it. The next time this cell is loaded, UITableView reuses the existing cell and cellForRowAt adds another UILabel to this cell. The proper implementation is to create a custom UITableViewCell and reset the value of all the attributes in cellForRowAt method.
You can try something like below (please note that this is just a rough implementation and it is assumed that you know the basics of ios programming. If that is not the case, I'd recommend researching it a bit):
Add a custom tableview cell
class CustomTableViewCell: UITableViewCell {
#IBOutlet weak var imgViewPlane: UIImageView!
#IBOutlet weak var lblType: UILabel!
#IBOutlet weak var lblTypeAnswer: UILabel!
#IBOutlet weak var lblRego: UILabel!
#IBOutlet weak var lblRegoAnswer: UILabel!
}
Create a dynamic prototype cell in storyboard tableview and link the IBOutlets. Then in your cellForRowAt, do something like this
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! CustomTableViewCell
cell.imgViewPlane.image = planeImage
cell.lblType.text = "Type:"
cell.lblTypeAnswer.text = planeArray[indexPath.row].type
cell.lblRego.text = "Registration:"
cell.lblRegoAnswer.text = planeArray[indexPath.row].rego
return cell
}

You’re adding new subviews to your cell every time that cell is dequeued.
I would recommend you to create these views, labels, etc. as lazy variables inside UITableViewCell subclass and add them as subviews inside awakeFromNib()
class YourCell: UITableViewCell {
lazy var label: UILabel = {
var label = UILabel(...)
...
return label
}()
...
override func awakeFromNib() {
addSubview(label)
...
}
}
.... or use IBOutlet if you’re using storyboard
Then in cellForRowAt just change properties of you downcasted cell subclass’s views
let cell = ... as! YourCell
cell.label.text = ""
cell.anotherLabel.isHidden = true
...

Related

What is the reason of an index to be out of range and how to fix this issue in my code?

This is audio player, It worked fine before I started to try stream audios from firebase. Now data is downloaded from the firebase database, the image, title and subtitle are being displayed correctly, but when user taps on the cell the audio doesn't play and it throws a fatal error, Thread 1: Fatal error: Index out of range
This is a table view controller which lists the audio tracks from firebase database.
import UIKit
import AVKit
import AVFoundation
import FirebaseFirestore
import Combine
import SDWebImage
class ListOfAudioLessonsTableViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet var table: UITableView!
let placeHolderImage = UIImage(named: "placeHolderImage")
private var cancellable: AnyCancellable?
private var viewModel = AudiosViewModel()
var paragraphs = [Audio]()
override func viewDidLoad() {
super.viewDidLoad()
self.viewModel.fetchData()
self.title = "Audio Lessons"
table.delegate = self
table.dataSource = self
cancellable = viewModel.$audios.sink { _ in
DispatchQueue.main.async{
self.table.reloadData()
}
} // Do any additional setup after loading the view.
}
// Table
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print("audios count = ", viewModel.audios.count)
return viewModel.audios.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let audio = viewModel.audios[indexPath.row]
tableView.tableFooterView = UIView()
cell.textLabel?.text = audio.albumName
cell.detailTextLabel?.text = audio.name
cell.accessoryType = .disclosureIndicator
let imageURL = audio.audioImageName
cell.imageView?.sd_imageIndicator = SDWebImageActivityIndicator.gray
cell.imageView?.sd_setImage(with: URL(string: imageURL),
placeholderImage: placeHolderImage,
options: SDWebImageOptions.highPriority,
context: nil,
progress: nil,
completed: { downloadedImage, downloadException, cacheType, downloadURL in
if let downloadException = downloadException {
print("error downloading the image: \(downloadException.localizedDescription)")
} else {
print("successfuly downloaded the image: \(String(describing: downloadURL?.absoluteString))")
}
})
cell.textLabel?.numberOfLines = 0
cell.detailTextLabel?.numberOfLines = 0
let backgroundView = UIView()
backgroundView.backgroundColor = UIColor(named: "AudioLessonsCellHighlighted")
cell.selectedBackgroundView = backgroundView
cell.textLabel?.font = UIFont(name: "Helvetica-Bold", size: 14)
cell.detailTextLabel?.font = UIFont(name: "Helvetica", size: 12)
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 100
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
// present the player
let position = indexPath.row
//lessons
guard let vc = storyboard?.instantiateViewController(identifier: "AudioPlayer") as? AudioPlayerViewController else {
return
}
vc.paragraphs = paragraphs
vc.position = position
present(vc, animated: true)
}
func tableView(_ tableView: UITableView, didHighlightRowAt indexPath: IndexPath) {
if let cell = tableView.cellForRow(at: indexPath) {
cell.contentView.backgroundColor = UIColor(named: "AudioLessonsHighlighted")
cell.textLabel?.highlightedTextColor = UIColor(named: "textHighlighted")
cell.detailTextLabel?.highlightedTextColor = UIColor(named: "textHighlighted")
}
}
func tableView(_ tableView: UITableView, didUnhighlightRowAt indexPath: IndexPath) {
if let cell = tableView.cellForRow(at: indexPath) {
cell.contentView.backgroundColor = nil
}
}
}
import UIKit
import AVFoundation
import MediaPlayer
import AVKit
class AudioPlayerViewController: UIViewController {
public var position: Int = 0
public var paragraphs: [Audio] = []
#IBOutlet var holder: UIView!
var player: AVPlayer?
var playerItem: AVPlayerItem?
var isSeekInProgress = false
var chaseTime = CMTime.zero
fileprivate let seekDuration: Float64 = 15
var playerCurrentItemStatus: AVPlayerItem.Status = .unknown
// User Interface elements
private let albumImageView: UIImageView = {
let imageView = UIImageView()
imageView.contentMode = .scaleAspectFill
return imageView
}()
private let paragraphNumberLabel: UILabel = {
let label = UILabel()
label.textAlignment = .left
label.font = .systemFont(ofSize: 16, weight: .light)
label.numberOfLines = 0 // allow line wrap
label.textColor = UIColor(named: "PlayerColors")
return label
}()
private let albumNameLabel: UILabel = {
let label = UILabel()
label.textAlignment = .left
label.font = .systemFont(ofSize: 18, weight: .bold)
label.numberOfLines = 0 // allow line wrap
label.textColor = UIColor(named: "PlayerColors")
return label
}()
private let songNameLabel: UILabel = {
let label = UILabel()
label.textAlignment = .left
label.font = .systemFont(ofSize: 16, weight: .ultraLight)
label.numberOfLines = 0 // allow line wrap
label.textColor = UIColor(named: "PlayerColors")
return label
}()
private let elapsedTimeLabel: UILabel = {
let label = UILabel()
label.textAlignment = .left
label.font = .systemFont(ofSize: 12, weight: .light)
label.textColor = UIColor(named: "PlayerColors")
label.text = "00:00"
label.numberOfLines = 0
return label
}()
private let remainingTimeLabel: UILabel = {
let label = UILabel()
label.textAlignment = .left
label.font = .systemFont(ofSize: 12, weight: .light)
label.textColor = UIColor(named: "PlayerColors")
label.text = "00:00"
label.numberOfLines = 0
return label
}()
private let playbackSlider: UISlider = {
let v = UISlider()
v.addTarget(AudioPlayerViewController.self, action: #selector(progressScrubbed(_:)), for: .valueChanged)
v.minimumTrackTintColor = UIColor.lightGray
v.maximumTrackTintColor = UIColor.darkGray
v.thumbTintColor = UIColor(named: "PlayerColors")
v.minimumValue = 0
v.isContinuous = true
return v
}()
let playPauseButton = UIButton()
override func viewDidLoad() {
super.viewDidLoad()
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(panGesture(gesture:)))
self.playbackSlider.addGestureRecognizer(panGesture)
let audioSession = AVAudioSession.sharedInstance()
do {
try audioSession.setCategory(AVAudioSession.Category.playback)
}
catch{
print(error)
}
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if holder.subviews.count == 0 {
configure()
}
}
func configure() {
// set up player
let song = paragraphs[position]
let url = URL(string: song.trackURL)
let playerItem: AVPlayerItem = AVPlayerItem(url: url!)
do {
try AVAudioSession.sharedInstance().setMode(.default)
try AVAudioSession.sharedInstance().setActive(true, options: .notifyOthersOnDeactivation)
guard url != nil else {
print("urls string is nil")
return
}
player = AVPlayer(playerItem: playerItem)
let duration : CMTime = playerItem.asset.duration
let seconds : Float64 = CMTimeGetSeconds(duration)
remainingTimeLabel.text = self.stringFromTimeInterval(interval: seconds)
let currentDuration : CMTime = playerItem.currentTime()
let currentSeconds : Float64 = CMTimeGetSeconds(currentDuration)
elapsedTimeLabel.text = self.stringFromTimeInterval(interval: currentSeconds)
playbackSlider.maximumValue = Float(seconds)
player!.addPeriodicTimeObserver(forInterval: CMTimeMakeWithSeconds(1, preferredTimescale: 1), queue: DispatchQueue.main) { (CMTime) -> Void in
if self.player!.currentItem?.status == .readyToPlay {
let time : Float64 = CMTimeGetSeconds(self.player!.currentTime());
self.playbackSlider.value = Float(time)
self.elapsedTimeLabel.text = self.stringFromTimeInterval(interval: time)
}
let playbackLikelyToKeepUp = self.player?.currentItem?.isPlaybackLikelyToKeepUp
if playbackLikelyToKeepUp == false{
print("IsBuffering")
self.playPauseButton.isHidden = true
} else {
// stop the activity indicator
print("Buffering completed")
self.playPauseButton.isHidden = false
}
}
playbackSlider.addTarget(self, action: #selector(AudioPlayerViewController.progressScrubbed(_:)), for: .valueChanged)
self.view.addSubview(playbackSlider)
//subroutine used to keep track of current location of time in audio file
guard let player = player else {
print("player is nil")
return
}
player.play()
}
catch {
print("error accured")
}
// set up user interface elements
//album cover
albumImageView.frame = CGRect(x: 20,
y: 20,
width: holder.frame.size.width - 40,
height: holder.frame.size.width - 40)
albumImageView.image = UIImage(named: song.audioImageName)
holder.addSubview(albumImageView)
//Labels Song name, album, artist
albumNameLabel.frame = CGRect(x: 20,
y: holder.frame.size.height - 300,
width: holder.frame.size.width - 40,
height: 20)
paragraphNumberLabel.frame = CGRect(x: 20,
y: holder.frame.size.height - 280,
width: holder.frame.size.width-40,
height: 20)
songNameLabel.frame = CGRect(x: 20,
y: holder.frame.size.height - 260,
width: holder.frame.size.width-40,
height: 20)
playbackSlider.frame = CGRect(x: 20,
y: holder.frame.size.height - 235,
width: holder.frame.size.width-40,
height: 40)
elapsedTimeLabel.frame = CGRect(x: 25,
y: holder.frame.size.height - 200,
width: holder.frame.size.width-40,
height: 15)
remainingTimeLabel.frame = CGRect(x: holder.frame.size.width-60,
y: holder.frame.size.height - 200,
width: holder.frame.size.width-20,
height: 15)
songNameLabel.text = song.name
albumNameLabel.text = song.albumName
paragraphNumberLabel.text = song.paragraphNumber
holder.addSubview(songNameLabel)
holder.addSubview(albumNameLabel)
holder.addSubview(paragraphNumberLabel)
holder.addSubview(elapsedTimeLabel)
holder.addSubview(remainingTimeLabel)
//Player controls
let nextButton = UIButton()
let backButton = UIButton()
let seekForwardButton = UIButton()
let seekBackwardButton = UIButton()
//frames of buttons
playPauseButton.frame = CGRect(x: (holder.frame.size.width - 40) / 2.0,
y: holder.frame.size.height - 172.5,
width: 40,
height: 40)
nextButton.frame = CGRect(x: holder.frame.size.width - 70,
y: holder.frame.size.height - 162.5,
width: 30,
height: 20)
backButton.frame = CGRect(x: 70 - 30,
y: holder.frame.size.height - 162.5,
width: 30,
height: 20)
seekForwardButton.frame = CGRect(x: holder.frame.size.width - 140,
y: holder.frame.size.height - 167.5,
width: 30,
height: 30)
seekBackwardButton.frame = CGRect(x: 110,
y: holder.frame.size.height - 167.5,
width: 30,
height: 30)
let volumeView = MPVolumeView(frame: CGRect(x: 20,
y: holder.frame.size.height - 80,
width: holder.frame.size.width-40,
height: 30))
holder.addSubview(volumeView)
//actions of buttons
playPauseButton.addTarget(self, action: #selector(didTapPlayPauseButton), for: .touchUpInside)
backButton.addTarget(self, action: #selector(didTapBackButton), for: .touchUpInside)
nextButton.addTarget(self, action: #selector(didTapNextButton), for: .touchUpInside)
seekForwardButton.addTarget(self, action: #selector(seekForwardButtonTapped), for: .touchUpInside)
seekBackwardButton.addTarget(self, action: #selector(seekBackwardButtonTapped), for: .touchUpInside)
//styling of buttons
playPauseButton.setBackgroundImage(UIImage(systemName: "pause.fill"), for: .normal)
nextButton.setBackgroundImage(UIImage(systemName: "forward.fill"), for: .normal)
backButton.setBackgroundImage(UIImage(systemName: "backward.fill"), for: .normal)
seekForwardButton.setBackgroundImage(UIImage(systemName: "goforward.15"), for: .normal)
seekBackwardButton.setBackgroundImage(UIImage(systemName: "gobackward.15"), for: .normal)
playPauseButton.tintColor = UIColor(named: "PlayerColors")
nextButton.tintColor = UIColor(named: "PlayerColors")
backButton.tintColor = UIColor(named: "PlayerColors")
seekForwardButton.tintColor = UIColor(named: "PlayerColors")
seekBackwardButton.tintColor = UIColor(named: "PlayerColors")
holder.addSubview(playPauseButton)
holder.addSubview(nextButton)
holder.addSubview(backButton)
holder.addSubview(seekForwardButton)
holder.addSubview(seekBackwardButton)
}
#objc func panGesture(gesture: UIPanGestureRecognizer) {
let currentPoint = gesture.location(in: playbackSlider)
let percentage = currentPoint.x/playbackSlider.bounds.size.width;
let delta = Float(percentage) * (playbackSlider.maximumValue - playbackSlider.minimumValue)
let value = playbackSlider.minimumValue + delta
playbackSlider.setValue(value, animated: true)
}
#objc func progressScrubbed(_ playbackSlider: UISlider!) {
let seconds : Int64 = Int64(playbackSlider.value)
let targetTime:CMTime = CMTimeMake(value: seconds, timescale: 1)
player!.seek(to: targetTime)
if player!.rate == 0
{
player?.play()
}
}
func setupNowPlaying() {
// Define Now Playing Info
var nowPlayingInfo = [String : Any]()
nowPlayingInfo[MPMediaItemPropertyTitle] = "Unstoppable"
if let image = UIImage(named: "artist") {
nowPlayingInfo[MPMediaItemPropertyArtwork] = MPMediaItemArtwork(boundsSize: image.size) { size in
return image
}
}
nowPlayingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = player?.currentTime
nowPlayingInfo[MPMediaItemPropertyPlaybackDuration] = playerItem?.duration
nowPlayingInfo[MPNowPlayingInfoPropertyPlaybackRate] = player?.rate
// Set the metadata
MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo
}
func updateNowPlaying(isPause: Bool) {
// Define Now Playing Info
var nowPlayingInfo = MPNowPlayingInfoCenter.default().nowPlayingInfo!
nowPlayingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = player?.currentTime
nowPlayingInfo[MPNowPlayingInfoPropertyPlaybackRate] = isPause ? 0 : 1
// Set the metadata
MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo
}
func setupNotifications() {
let notificationCenter = NotificationCenter.default
notificationCenter.addObserver(self,
selector: #selector(handleInterruption),
name: AVAudioSession.interruptionNotification,
object: nil)
notificationCenter.addObserver(self,
selector: #selector(handleRouteChange),
name: AVAudioSession.routeChangeNotification,
object: nil)
}
#objc func handleRouteChange(notification: Notification) {
guard let userInfo = notification.userInfo,
let reasonValue = userInfo[AVAudioSessionRouteChangeReasonKey] as? UInt,
let reason = AVAudioSession.RouteChangeReason(rawValue:reasonValue) else {
return
}
switch reason {
case .newDeviceAvailable:
let session = AVAudioSession.sharedInstance()
for output in session.currentRoute.outputs where output.portType == AVAudioSession.Port.headphones {
print("headphones connected")
DispatchQueue.main.sync {
player?.play()
}
break
}
case .oldDeviceUnavailable:
if let previousRoute =
userInfo[AVAudioSessionRouteChangePreviousRouteKey] as? AVAudioSessionRouteDescription {
for output in previousRoute.outputs where output.portType == AVAudioSession.Port.headphones {
print("headphones disconnected")
DispatchQueue.main.sync {
player?.pause()
}
break
}
}
default: ()
}
}
#objc func handleInterruption(notification: Notification) {
guard let userInfo = notification.userInfo,
let typeValue = userInfo[AVAudioSessionInterruptionTypeKey] as? UInt,
let type = AVAudioSession.InterruptionType(rawValue: typeValue) else {
return
}
if type == .began {
print("Interruption began")
// Interruption began, take appropriate actions
}
else if type == .ended {
if let optionsValue = userInfo[AVAudioSessionInterruptionOptionKey] as? UInt {
let options = AVAudioSession.InterruptionOptions(rawValue: optionsValue)
if options.contains(.shouldResume) {
// Interruption Ended - playback should resume
print("Interruption Ended - playback should resume")
player?.play()
} else {
// Interruption Ended - playback should NOT resume
print("Interruption Ended - playback should NOT resume")
}
}
}
}
#objc func didTapPlayPauseButton() {
if player?.timeControlStatus == .playing {
//pause
player?.pause()
//show play button
playPauseButton.setBackgroundImage(UIImage(systemName: "play.fill"), for: .normal)
//shrink image
UIView.animate(withDuration: 0.2, animations: {
self.albumImageView.frame = CGRect(x: 50,
y: 50,
width: self.holder.frame.size.width - 100,
height: self.holder.frame.size.width - 100)
})
}
else {
//play
player?.play()
//show pause button
playPauseButton.setBackgroundImage(UIImage(systemName: "pause.fill"), for: .normal)
//increase image size
UIView.animate(withDuration: 0.4, animations: {
self.albumImageView.frame = CGRect(x: 20,
y: 20,
width: self.holder.frame.size.width - 40,
height: self.holder.frame.size.width - 40)
})
}
}
private func setupView() {
setupConstraints()
}
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
player?.play()
UIApplication.shared.isIdleTimerDisabled = true
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
player?.pause()
UIApplication.shared.isIdleTimerDisabled = false
}
}
AudioViewModel
import Foundation
import FirebaseFirestore
class AudiosViewModel: ObservableObject {
#Published var audios = [Audio]()
private var db = Firestore.firestore()
func fetchData() {
db.collection("audios").addSnapshotListener { [self] (querySnapshot, error) in
guard let documents = querySnapshot?.documents else {
print("No Documents")
return
}
self.audios = documents.map { (queryDocumentSnapshot) -> Audio in
let data = queryDocumentSnapshot.data()
let name = data["name"] as? String ?? ""
let albumName = data["albumName"] as? String ?? ""
let audioImageName = data["audioImageName"] as? String ?? ""
let paragraphNumber = data["paragraphNumber"] as? String ?? ""
let trackURL = data["trackURL"] as? String ?? ""
print(data)
return Audio(name: name, albumName: albumName, paragraphNumber: paragraphNumber, audioImageName: audioImageName, trackURL: trackURL)
}
}
}
}
Audio Structure
import Foundation
struct Audio {
let name: String
let albumName: String
let paragraphNumber: String
let audioImageName: String
let trackURL: String
}
Assign/append value to your paragraphs and also check your position if there will be no values/wrong position it'll throw index out of range

How to update progressview in uitableview cell by urlsession (download/upload file)

In Objective c i wrote download/upload with progressview in uitableviewcell (multiple upload/download) by AFNETWORKING. and work find it's can be update progressview/file/cell.
and Now im noob first time change to SWIFT programming and use urlsession.
code
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, URLSessionDelegate, URLSessionDataDelegate, URLSessionTaskDelegate {
//var dataArr:Dictionary<String,String> = [:]
var dataArr : NSMutableArray = NSMutableArray.init()
var myTableview:UITableView = UITableView.init()
let color = UIColor(red: 69/255, green: 57/255, blue: 169/255, alpha: 1.0)
let cellID: String = "customCell"
var progressBar : UIProgressView = UIProgressView.init()
let progressView : UIView = UIView.init(frame: CGRect(x: 100, y: 10, width: 100, height: 20))
func urlSession(_ session: URLSession, task: URLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64)
{
let uploadProgress:Float = Float(totalBytesSent) / Float(totalBytesExpectedToSend)
print(uploadProgress)
DispatchQueue.main.async {
self.progressBar.progress = uploadProgress
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataArr.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellID, for: indexPath)
let textName: UILabel = UILabel.init(frame: CGRect(x: 0, y: 0, width: 100, height: 50))
textName.textColor=UIColor.black
textName.backgroundColor=UIColor.green
textName.text=dataArr[indexPath.row] as? String
print("\(dataArr[indexPath.row])");
textName.font=UIFont.systemFont(ofSize: 14)
cell.addSubview(textName)
progressView.backgroundColor = UIColor.red
progressView.tag=indexPath.row
let customKeys=["type","Facebook","Google","Twitter"];
let customsValues=["uploadFile","Mark","Lary","Goo"];
let customDatas=Dictionary(uniqueKeysWithValues: zip(customKeys,customsValues))
progressBar = UIProgressView.init(frame: CGRect(x: 0, y: 5, width: 100, height: 20))
progressBar.tag=indexPath.row
progressView.addSubview(progressBar)
cell.addSubview(progressView)
uploadImage(data_dict: customDatas, uploadImg: dataArr[indexPath.row] as! String)
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
let hCell:CGFloat = 50.0
return hCell
}
// override func viewWillAppear(_ animated: Bool) {
// super.viewWillAppear(animated)
// setNeedsStatusBarAppearanceUpdate()
// }
override var preferredStatusBarStyle: UIStatusBarStyle {
// Change font of status bar is white.
.lightContent
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
dataArr=["1.jpg","2.jpg","3.jpg"]
//print(dataArr)
let myScreen = UIScreen.main.bounds
let statusHieght = UIApplication.shared.statusBarFrame.height
if #available(iOS 13.0, *) {
let app = UIApplication.shared
let statusBarHeight: CGFloat = app.statusBarFrame.size.height
let statusbarView = UIView()
statusbarView.backgroundColor = color
statusbarView.tintColor = .white
view.addSubview(statusbarView)
statusbarView.translatesAutoresizingMaskIntoConstraints = false
statusbarView.heightAnchor
.constraint(equalToConstant: statusBarHeight).isActive = true
statusbarView.widthAnchor
.constraint(equalTo: view.widthAnchor, multiplier: 1.0).isActive = true
statusbarView.topAnchor
.constraint(equalTo: view.topAnchor).isActive = true
statusbarView.centerXAnchor
.constraint(equalTo: view.centerXAnchor).isActive = true
} else {
let statusBar = UIApplication.shared.value(forKeyPath: "statusBarWindow.statusBar") as? UIView
statusBar?.backgroundColor = color
}
UINavigationBar.appearance().barTintColor = color
UINavigationBar.appearance().tintColor = .white
UINavigationBar.appearance().titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.white]
UINavigationBar.appearance().isTranslucent = false
let navBar = UINavigationBar(frame: CGRect(x: 0, y: statusHieght, width: myScreen.size.width, height: 44))
//navBar.isTranslucent=true
//navBar.backgroundColor = .red
let navItem = UINavigationItem(title: "SomeTitle")
let doneItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.done, target:nil , action:#selector(ClickDone))
navItem.rightBarButtonItem = doneItem
navBar.setItems([navItem], animated: false)
self.view.addSubview(navBar)
let AllTopDistance=statusHieght+navBar.frame.size.height
let myView:UIView = UIView.init(frame: CGRect(x: 0, y: AllTopDistance, width: myScreen.size.width, height: myScreen.size.height-AllTopDistance))
myView.backgroundColor = .lightGray
myTableview=UITableView.init(frame: CGRect(x: 0, y: 0, width: myScreen.size.width, height: myScreen.size.height-AllTopDistance))
myTableview.register(UITableViewCell.self, forCellReuseIdentifier: cellID)
print("\(statusHieght) \(myScreen.size.width) \(AllTopDistance)")
myTableview.delegate=self
myTableview.dataSource=self
myTableview.backgroundColor=UIColor.red
myView.addSubview(myTableview)
self.view.addSubview(myView)
}
#objc func ClickDone(){
print("Done")
}
func calculateTopDistance() -> CGFloat{
/// Create view for misure
let misureView : UIView = UIView()
misureView.backgroundColor = .clear
view.addSubview(misureView)
/// Add needed constraint
misureView.translatesAutoresizingMaskIntoConstraints = false
misureView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
misureView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
misureView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
if let nav = navigationController {
misureView.topAnchor.constraint(equalTo: nav.navigationBar.bottomAnchor).isActive = true
}else{
misureView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
}
/// Force layout
view.layoutIfNeeded()
/// Calculate distance
let distance = view.frame.size.height - misureView.frame.size.height
/// Remove from superview
misureView.removeFromSuperview()
return distance
}
#objc func uploadImage(data_dict : Dictionary<String,String>, uploadImg : String)
{
print("click \(data_dict)")
let image = UIImage(named: uploadImg)
// generate boundary string using a unique per-app string
let boundary = UUID().uuidString
let config = URLSessionConfiguration.default
//let session = URLSession(configuration: config)
let session = URLSession(configuration: config, delegate: self, delegateQueue: OperationQueue.main)
// Set the URLRequest to POST and to the specified URL
var urlRequest = URLRequest(url: URL(string: "http://x.x.x.x/xxx/Labs.php")!)
urlRequest.httpMethod = "POST"
// Set Content-Type Header to multipart/form-data, this is equivalent to submitting form data with file upload in a web browser
// And the boundary is also set here
urlRequest.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
var data = Data()
for (key, value) in data_dict {
print(key, value)
let fieldName = key
let fieldValue = value
data.append("\r\n--\(boundary)\r\n".data(using: .utf8)!)
data.append("Content-Disposition: form-data; name=\"\(fieldName)\"\r\n\r\n".data(using: .utf8)!)
data.append("\(fieldValue)".data(using: .utf8)!)
}
// Add the image data to the raw http request data
data.append("\r\n--\(boundary)\r\n".data(using: .utf8)!)
data.append("Content-Disposition: form-data; name=\"fileToUpload\"; filename=\"\(uploadImg)\"\r\n".data(using: .utf8)!)
data.append("Content-Type: image/png\r\n\r\n".data(using: .utf8)!)
data.append((image?.jpegData(compressionQuality: 1.0))!)
// End the raw http request data, note that there is 2 extra dash ("-") at the end, this is to indicate the end of the data
// According to the HTTP 1.1 specification https://tools.ietf.org/html/rfc7230
data.append("\r\n--\(boundary)--\r\n".data(using: .utf8)!)
// Send a POST request to the URL, with the data we created earlier
session.uploadTask(with: urlRequest, from: data, completionHandler: { responseData, response, error in
if(error != nil){
print("\(error!.localizedDescription)")
}
guard let responseData = responseData else {
print("no response data")
return
}
if let responseString = String(data: responseData, encoding: .utf8) {
print("Response data : \(responseString)")
}
}).resume()
}
}
?? How define URLSESSION update progressview in uitableview by cell/file
Thank you.
I'm curious how you did it in Objective-C with AFNetworking. At least conceptional, there shouldn't be a big difference to implementation in Swift with URLSession.
Imho your main problem about updating your progress is, that you share the progressView variable with a single UIView instance for all of your cells.
you do not initialize a new progressView for each cell, but share one view for all cells
because of 1, cell.addSubview(progressView) doesn't only add your progressView to that cell, it removes your progressView from the other cells as well, because a view can only have one parent view.
your progressView will have multiple UIProgressBars as subview. One for each time tableView(_:cellForRowAt indexPath:) is called
with self.progressBar.progress = uploadProgress you will always update the progressBar which was last initialized, because you don't have a reference to the other ones.
To get this to work in a clean way, I'd recommend you do some research to MVVM architecture.
create a UITableViewCell subclass for your cell
create a ViewModel class for that cell
store a viewModel instance for each of your cells in your viewController (or better in a separate TableViewDataSource object)
implement URLSessionDelegate protocol in your ViewModel and set the appropriate viewModel instance as delegate for your uploadTasks
For a quick and dirty fix:
Remove these lines:
var progressBar : UIProgressView = UIProgressView.init()
let progressView : UIView = UIView.init(frame: CGRect(x: 100, y: 10, width: 100, height: 20))
Add variable:
var uploadTasks: [URLSessionDataTask: IndexPath] = [:]
Add a helper function to calculate viewTag:
func viewTag(for indexPath: IndexPath) -> Int {
return indexPath.row + 1000
}
Change your tableView(_:cellForRowAt indexPath:) to:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellID, for: indexPath)
let textName: UILabel = UILabel.init(frame: CGRect(x: 0, y: 0, width: 100, height: 50))
textName.textColor=UIColor.black
textName.backgroundColor=UIColor.green
textName.text=dataArr[indexPath.row] as? String
print("\(dataArr[indexPath.row])");
textName.font=UIFont.systemFont(ofSize: 14)
cell.addSubview(textName)
let progressView = UIView(frame: CGRect(x: 100, y: 10, width: 100, height: 20))
progressView.backgroundColor = UIColor.red
let customKeys=["type","Facebook","Google","Twitter"];
let customsValues=["uploadFile","Mark","Lary","Goo"];
let customDatas=Dictionary(uniqueKeysWithValues: zip(customKeys,customsValues))
let progressBar = UIProgressView.init(frame: CGRect(x: 0, y: 5, width: 100, height: 20))
progressBar.tag = viewTag(for: indexPath)
progressView.addSubview(progressBar)
cell.addSubview(progressView)
uploadImage(data_dict: customDatas, indexPath: indexPath)
return cell
}
Change your uploadImage method to:
#objc func uploadImage(data_dict : Dictionary<String,String>, indexPath : IndexPath) {
print("click \(data_dict)")
let uploadImg = dataArr[indexPath.row] as! String
let image = UIImage(named: uploadImg)
...
let task = session.uploadTask(with: urlRequest, from: data, completionHandler: { responseData, response, error in
...
})
uploadTasks[task] = indexPath
task.resume()
}
Change your urlSession delegate method to:
func urlSession(_ session: URLSession, task: URLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) {
let uploadProgress:Float = Float(totalBytesSent) / Float(totalBytesExpectedToSend)
print(uploadProgress)
guard let indexPath = uploadTasks[task] else { return }
let viewTag = viewTag(for: indexPath)
guard let progressBar = self.view.viewWithTag(viewTag) as? UIProgressView else { return }
DispatchQueue.main.async {
progressBar.progress = uploadProgress
}
}

Transferring Image (Downloaded from Firebase) into collection View Cell on home Page

Im trying to transfer an image between two view controllers. It is a profile image of the current logged in User, if there is one. The image is successful in downloading from Firebase. It downloads on a launch screen along with the username. If there is no user logged in, view controller changes to a login screen, if there is a logged in user it transfers to a home screen.
This is the launch screen. Both the username label and the image view are clear so user cannot see.
import UIKit
import Firebase
class LaunchScreen: UIViewController {
#IBOutlet weak var usernameLabel: UILabel!
#IBOutlet weak var userImage: UIImageView!
var ref : DatabaseReference!
var storage: StorageReference!
override func viewDidLoad() {
super.viewDidLoad()
ref = Database.database().reference()
storage = Storage.storage().reference()
checkUser()
// Do any additional setup after loading the view.
}
func checkUser() {
Auth.auth().addStateDidChangeListener { (auth, user) in
if user != nil {
let uid = Auth.auth().currentUser?.uid
self.ref.database.reference().child("users/profile").child(uid!).observe(.value, with: { (snapshot: DataSnapshot) in
if let dictionary = snapshot.value as? [String: AnyObject] {
let currentUser = dictionary["username"] as! String
let url = dictionary["photoURL"] as! String
let storageRef = self.storage.storage.reference(forURL: url)
storageRef.getData(maxSize: 1 * 1024 * 1024, completion: { (data, error) in
if let error = error {
print(error)
} else {
self.userImage.image = UIImage(data: data!)
self.reloadInputViews()
}
})
guard let image = self.userImage.image else {return}
self.usernameLabel.text = currentUser
let imageJPG = image.jpegData(compressionQuality: 1)
print(imageJPG?.count)
let defaults = UserDefaults.standard
defaults.set(self.usernameLabel.text, forKey: "user")
defaults.synchronize()
}
})
_ = Timer.scheduledTimer(withTimeInterval: 3.0, repeats: false, block: { (timer) in
let vc = self.storyboard?.instantiateViewController(withIdentifier: "homeVC")
self.present(vc!, animated: true, completion: nil)
})
print("loggedIn")
} else {
_ = Timer.scheduledTimer(withTimeInterval: 3.0, repeats: false, block: { (timer) in
let loginvc = self.storyboard?.instantiateViewController(withIdentifier: "loginVC")
self.present( loginvc!,animated: true, completion: nil)
})
print("loggedOut")
}
}
}
}
This is my home page. where there is a side menu. the side menu pops open from the left hand side of the screen and on it are four collection view cells. the top one is the profile cell which will display the username and the user profile image. that image is where I want the downloaded Firebase userImage in launchScreenVC to be transferred too.
class home VC: UIViewController {
#IBOutlet weak var profileMenuButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
collectionView.delegate = self
collectionView.dataSource = self
collectionView.reloadData()
}
let blackView = UIView()
let collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
let cv = UICollectionView(frame: .zero , collectionViewLayout: layout )
cv.backgroundColor = UIColor.init(displayP3Red: 50/255, green: 51/255, blue: 51/255, alpha: 1)
return cv
}()
#IBAction func profileMenu() {
let width = view.frame.width - 60
collectionView.frame = CGRect(x: 0, y: 0, width: width, height: view.frame.height)
blackView.backgroundColor = UIColor(white: 0, alpha: 0.5)
blackView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleDismiss)))
blackView.frame = view.frame
blackView.alpha = 0
view.addSubview(blackView)
view.addSubview(collectionView)
UIView.animate(withDuration: 1.5, delay: 0, options: .curveEaseOut, animations: {
self.blackView.alpha = 1
self.collectionView.frame = CGRect(x: 0 , y: 0, width: width, height: self.view.frame.height)
}, completion: nil)
handleSearchDismiss(searchView)
}
#objc func handleDismiss() {
UIView.animate(withDuration: 0.5){
if let window = UIApplication.shared.keyWindow {
self.collectionView.frame = CGRect(x: 0 - (window.frame.width), y: 0, width: window.frame.width, height: self.collectionView.frame.height)
}
self.blackView.alpha = 0
}
}
}
extension homeVC: UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout, {
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if indexPath.item == 0 {
let cell1 = collectionView.dequeueReusableCell(withReuseIdentifier: "ProfileCellID", for: indexPath) as! profileCell
let defaults = UserDefaults.standard
cell1.usernameLabel.text = (defaults.object(forKey: "user") as! String)
cell1.usernameLabel.textColor = UIColor.lightGray
cell1.usernameLabel.font = UIFont.boldSystemFont(ofSize: 25)
// print(imageData)
// cell1.profileImage.image = (defaults.object(forKey: "userImage") as! Data)
return cell1
} else if indexPath.item == 1{
let cell2 = collectionView.dequeueReusableCell(withReuseIdentifier: "Settings", for: indexPath) as! settingsCell
return cell2
} else if indexPath.item == 2 {
let cell3 = collectionView.dequeueReusableCell(withReuseIdentifier: "Favourites", for: indexPath) as! favouritesCell
return cell3
} else {
let cell4 = collectionView.dequeueReusableCell(withReuseIdentifier: "Logout", for: indexPath) as! logoutCell
return cell4
}
}
}
this is the profile cell class
class profileCell: UICollectionViewCell {
override init(frame: CGRect) {
super.init(frame: frame)
setUpViews()
}
let profileImage: UIImageView = {
let imageView = UIImageView()
return imageView
}()
let usernameLabel: UILabel = {
let label = UILabel()
return label
}()
func setUpViews() {
addSubview(profileImage)
addSubview(usernameLabel)
profileImage.frame = CGRect(x: (center.x)-60, y: 30, width: 120, height: 120)
profileImage.layer.cornerRadius = (profileImage.frame.width/2)
usernameLabel.frame = CGRect(x: (center.x)-60, y: 125, width: 120, height: 120)
usernameLabel.textAlignment = NSTextAlignment.center
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Any Help or tips would be greatly appreciated! Thanks Guys!

swift 3 table view with slide out menu

I got problem with making table view on the same view with slide menu, could you help me how to do that, I created view controller and embeded navigation controller, now I just want add table vie behind the slide menu and show table view with two section and show data with one custom cell.
#Egle Matutyte, I am supposing that you have done all work except SlideBar.
I am providing code for SlideBar. Just add and try.
First Add the SideBar class i.e. below without any change
import UIKit
#objc protocol SideBarDelegate{
func sideBarDidSelectButtonAtIndex(_ index:Int)
#objc optional func sideBarWillClose()
#objc optional func sideBarWillOpen()
}
class SideBar: NSObject, SideBarTableViewControllerDelegate {
let barWidth:CGFloat = 150.0
let sideBarTableViewTopInset:CGFloat = 64.0
let sideBarContainerView:UIView = UIView()
let sideBarTableViewController:SideBarTableViewController = SideBarTableViewController()
var originView:UIView = UIView()
var animator:UIDynamicAnimator!
var delegate:SideBarDelegate?
var isSideBarOpen:Bool = false
override init() {
super.init()
}
init(sourceView:UIView, menuItems:Array<String>){
super.init()
originView = sourceView
sideBarTableViewController.tableData = menuItems
setupSideBar()
animator = UIDynamicAnimator(referenceView: originView)
let showGestureRecognizer:UISwipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: #selector(SideBar.handleSwipe(_:)))
showGestureRecognizer.direction = UISwipeGestureRecognizerDirection.right
originView.addGestureRecognizer(showGestureRecognizer)
let hideGestureRecognizer:UISwipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: #selector(SideBar.handleSwipe(_:)))
hideGestureRecognizer.direction = UISwipeGestureRecognizerDirection.left
originView.addGestureRecognizer(hideGestureRecognizer)
}
func setupSideBar(){
//sideBarContainerView.frame = CGRect(x: -barWidth - 1, y: originView.frame.origin.y, width: barWidth, height: originView.frame.size.height)
sideBarContainerView.frame = CGRect(x: -barWidth - 1, y: sideBarTableViewTopInset, width: barWidth, height: originView.frame.size.height)
sideBarContainerView.backgroundColor = UIColor.clear
sideBarContainerView.clipsToBounds = false
originView.addSubview(sideBarContainerView)
let blurView:UIVisualEffectView = UIVisualEffectView(effect: UIBlurEffect(style: UIBlurEffectStyle.light))
blurView.frame = sideBarContainerView.bounds
sideBarContainerView.addSubview(blurView)
sideBarTableViewController.delegate = self
sideBarTableViewController.tableView.frame = sideBarContainerView.bounds
sideBarTableViewController.tableView.clipsToBounds = false
sideBarTableViewController.tableView.separatorStyle = UITableViewCellSeparatorStyle.none
sideBarTableViewController.tableView.backgroundColor = UIColor.clear
sideBarTableViewController.tableView.scrollsToTop = false
//sideBarTableViewController.tableView.contentInset = UIEdgeInsetsMake(sideBarTableViewTopInset, 0, 0, 0)
sideBarTableViewController.tableView.contentInset = UIEdgeInsetsMake(1, 0, 0, 0)
sideBarTableViewController.tableView.reloadData()
sideBarContainerView.addSubview(sideBarTableViewController.tableView)
}
func handleSwipe(_ recognizer:UISwipeGestureRecognizer){
if recognizer.direction == UISwipeGestureRecognizerDirection.left{
showSideBar(false)
delegate?.sideBarWillClose?()
}else{
showSideBar(true)
delegate?.sideBarWillOpen?()
}
}
func showSideBar(_ shouldOpen:Bool){
animator.removeAllBehaviors()
isSideBarOpen = shouldOpen
let gravityX:CGFloat = (shouldOpen) ? 0.5 : -0.5
let magnitude:CGFloat = (shouldOpen) ? 20 : -20
let boundaryX:CGFloat = (shouldOpen) ? barWidth : -barWidth - 1
let gravityBehavior:UIGravityBehavior = UIGravityBehavior(items: [sideBarContainerView])
gravityBehavior.gravityDirection = CGVector(dx: gravityX, dy: 0)
animator.addBehavior(gravityBehavior)
let collisionBehavior:UICollisionBehavior = UICollisionBehavior(items: [sideBarContainerView])
collisionBehavior.addBoundary(withIdentifier: "sideBarBoundary" as NSCopying, from: CGPoint(x: boundaryX, y: 20), to: CGPoint(x: boundaryX, y: originView.frame.size.height))
animator.addBehavior(collisionBehavior)
let pushBehavior:UIPushBehavior = UIPushBehavior(items: [sideBarContainerView], mode: UIPushBehaviorMode.instantaneous)
pushBehavior.magnitude = magnitude
animator.addBehavior(pushBehavior)
let sideBarBehavior:UIDynamicItemBehavior = UIDynamicItemBehavior(items: [sideBarContainerView])
sideBarBehavior.elasticity = 0.3
animator.addBehavior(sideBarBehavior)
}
func sideBarControlDidSelectRow(_ indexPath: IndexPath) {
delegate?.sideBarDidSelectButtonAtIndex(indexPath.row)
showSideBar(false)
}
}
After this, add a new swift class
SideBarTableViewController
which inherit UITableViewController
import UIKit
protocol SideBarTableViewControllerDelegate{
func sideBarControlDidSelectRow(_ indexPath:IndexPath)
}
class SideBarTableViewController: UITableViewController {
var delegate:SideBarTableViewControllerDelegate?
var tableData:Array<String> = []
override func viewDidLoad() {
super.viewDidLoad()
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return tableData.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell:UITableViewCell? = tableView.dequeueReusableCell(withIdentifier: "Cell")
if cell == nil {
cell = UITableViewCell(style: .default, reuseIdentifier: "Cell")
cell!.backgroundColor = UIColor.clear
cell!.textLabel?.textColor = UIColor.darkText
let selectedView:UIView = UIView(frame: CGRect(x: 0, y: 0, width: cell!.frame.size.width, height: cell!.frame.size.height))
selectedView.backgroundColor = UIColor.black.withAlphaComponent(0.3)
cell!.selectedBackgroundView = selectedView
}
cell!.textLabel?.text = tableData[indexPath.row]
return cell!
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 45
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
delegate?.sideBarControlDidSelectRow(indexPath)
}
}
Now paste the code below in your TableViewController or the page on which you want to add SlideBar.
let the name of your page is Dashboard, add the delegate as below
class Dashboard: UIViewController, SideBarDelegate
{
//declare sidebar
var sideBar:SideBar = SideBar()
override func viewWillAppear(_ animated: Bool)
{
//Initialise the content of SlideBar Row
sideBar = SideBar(sourceView: self.view, menuItems: ["Home", "Dashboard"])
sideBar.delegate = self
}
func sideBarControlDidSelectRow(_ indexPath: IndexPath) {
print("Selected from SlideBar")
}
func sideBarDidSelectButtonAtIndex(_ index: Int)
{
//
}
//Add a button(Optional)in ViewDidLoad()
override func viewDidLoad()
{
super.viewDidLoad()
let btn_Hamburger = UIButton(frame: CGRect(x: 10, y: 70, width: 100, height: 25))
btn_Hamburger.addTarget(self, action: #selector(btn_hamburger(sender:)), for: .touchUpInside)
//add Image on Hamburger button
btn_Hamburger.setImage(#imageLiteral(resourceName: "img_hemberger"), for: .normal)
view.addSubview(btn_Hamburger)
}
//To in and out SideBar menu
var flag:Bool = true
func btn_hamburger(sender: UIButton!)
{
if flag == true {
sideBar.showSideBar(true)
flag = false
}
else{
sideBar.showSideBar(false)
flag = true
}
}
}

UITableView cellForRowAtIndexPath function not updating after reload function

I am trying to reload data in my tableview after getting the data from a delegate method. But the issue is that all the data is not coming through to the tableView function cellForRowAtIndexPath. I have set the variable self.restNames to hold the values from the delegate method which comes around 4 values but not all of them show up in the function cellForRowAtIndexPath. When I change the tabs and go back to the tableView, some data does come through but not all of it.Will appreciate any help on this. Apologies if I have missed something, I am new to Swift and haven't raised much questions in StackOverflow.
Regards,
Saurabh
Below is my the code for the tableViewController
class RestTableViewController: UITableViewController,getDistanceTime {
var RestTable = [Restaurant]()
let label = UILabel()
let menuUrl = "menu url"
let listProjectUrl = "url"
override func viewDidLoad() {
super.viewDidLoad()
let headerView = self.tableView
headerView.tableHeaderView?.frame = CGRectMake(0, 30, self.view.frame.width, 40)
headerView.tableHeaderView?.backgroundColor = UIColor.redColor()
self.tableView.tableHeaderView = self.tableView.tableHeaderView
self.tableView.tableHeaderView?.frame = CGRectMake(0, 30, self.view.frame.width, 40)
let token = keychain[""]
let menuData = Alamofire.request(Method.GET, self.menuUrl, headers: ["Authorization":"JWT \(token!)"])
menuData.responseJSON{ response in
let data = JSON(response.result.value!)
var localmenu = [menu]()
for (_,item) in data{
let menuOne = menu(place: item["place"].string!, types: item["types"].string!, name: item["name"].string!, price: item["price"].string!)
localmenu.append(menuOne)
}
self.menus = localmenu
}
}
// Delegate method
func loadWithDisTime(distance: String,name: String)
{
dispatch_async(dispatch_get_main_queue())
{ () -> Void in
self.userDistanceFromLocation = distance
//This will hold 4 values
self.restNames = name
self.tableView.reloadData()
}
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(true)
self.getDistance = getDistanceTimeVC()
self.getDistance.delegate = self
let restCount = self.RestTable.count
if restCount == 0
{
let alertView = UIAlertController(title: "There are no restaurants near your location", message: "Press Okay to go back", preferredStyle: UIAlertControllerStyle.Alert)
let alertAction = UIAlertAction(title: "Okay", style: UIAlertActionStyle.Cancel, handler: nil)
alertView.addAction(alertAction)
self.presentViewController(alertView, animated: true, completion: nil)
}
else
{
for item in 0...restCount - 1
{
self.restLat = self.RestTable[item].lat
self.restLng = self.RestTable[item].lng
self.getDistance.getDistance(self.userLat, userLng: self.userLng, restLat: self.restLat, restLng: self.restLng,restName: self.RestTable[item].name)
}
}
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.RestTable.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! RestTableViewCell
//Not all values coming through here
print(self.restNames)
if self.RestTable[indexPath.row].name == self.restNames
{
cell.textLabel?.text = "\(indexPath.row + 1) - \(self.RestTable[indexPath.row].name) - \(self.userDistanceFromLocation)"
cell.backgroundColor = UIColor.whiteColor()
cell.textLabel?.textColor = UIColor.blackColor()
cell.textLabel?.font = UIFont(name: "Avenir-Heavy", size: 15)
}
return cell
}
In this block leave only reload table view
dispatch_async(dispatch_get_main_queue())
{ () -> Void in
self.tableView.reloadData()
}