So this is how my Realm classes looks like:
class Workout: Object {
#objc dynamic var date: Date? // Date I did the exercise
#objc dynamic var exercise: String? // Name of exercise, example; Bench Press, Squat, Deadlift etc..
// List of sets (to-many relationship)
var sets = List<Set>()
}
class Set: Object {
#objc dynamic var reps: Int = 0 // Number of reps for the each set
#objc dynamic var kg: Double = 0.0 // Amount of weight/kg for each set
#objc dynamic var notes: String? // Notes for each set
// Define an inverse relationship to be able to access your parent workout for a particular set (if needed)
var parentWorkout = LinkingObjects(fromType: Workout.self, property: "sets")
convenience init(numReps: Int, weight: Double, aNote: String) {
self.init()
self.reps = numReps
self.kg = weight
self.notes = aNote
}
}
I want to get the weight(kg) and number of reps for each set, from the last time I did this exercise. So I am trying something like this. To query a workout with exercise "Text Exercise2", I do this:
func queryFromRealm() {
let realm = try! Realm()
let getExercises = realm.objects(Workout.self).filter("exercise = 'Text Exercise2'").sorted(byKeyPath: "date",ascending: false)
print(getExercises.last!)
myTableView.reloadData()
}
That gives me an output of this:
Workout {
date = 2019-11-24 00:33:21 +0000;
exercise = Text Exercise2;
sets = List<Set> <0x600003fcdb90> (
[0] Set {
reps = 12;
kg = 7;
notes = light workout;
},
[1] Set {
reps = 12;
kg = 8;
notes = medium workout;
},
[2] Set {
reps = 12;
kg = 9;
notes = heavy workout;
}
);
}
This is how I am passing data from mainVC to ExerciseCreatorViewController:
var selectedWorkout = Workout()
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "toEditExerciseCreatorSegue" {
if let nextVC = segue.destination as? ExerciseCreatorViewController {
nextVC.selectedWorkout = self.selectedWorkout
}
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
selectedWorkout = resultFromDate[indexPath.row]
performSegue(withIdentifier: "toEditExerciseCreatorSegue", sender: self)
}
From now on, I want to add these into at UITableView, where each row contains the kg and number of reps. How can I do this? Should I change my setup somehow? Looking forward for your help.
EDIT - Uploaded the whole ExerciseCreatorViewController.swift file:
import UIKit
import RealmSwift
class ExerciseCreatorViewController: UIViewController, UITextFieldDelegate, UITableViewDelegate, UITableViewDataSource {
let menuButtonSize: CGSize = CGSize(width: 44, height: 44)
let actionButtonSize: CGSize = CGSize(width: 44, height: 66)
let screenSize: CGRect = UIScreen.main.bounds
var resultFromDate:[Workout] = []
var selectedWorkout = Workout()
#IBOutlet var exerciseNameTextField: UITextField!
let exerciseName = UILabel()
#IBOutlet var navView: UIView!
let navLabel = UILabel()
#IBOutlet var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
exerciseNameTextField.delegate = self
// Get main screen bounds
let screenSize: CGRect = UIScreen.main.bounds
let screenWidth = screenSize.width
let screenHeight = screenSize.height
//self.view.backgroundColor = UIColor(red: 32/255, green: 32/255, blue: 32/255, alpha: 1.0)
self.view.backgroundColor = UIColor.white
// Navigation View
navView.frame = CGRect(x: 0, y: 0, width: screenSize.width, height: 66)
navView.frame.origin.y = 28
//navLabel.backgroundColor = UIColor(red: 32/255, green: 32/255, blue: 32/255, alpha: 1.0)
navLabel.backgroundColor = UIColor.white
//navView.backgroundColor = UIColor.red.withAlphaComponent(0.5)
self.view.addSubview(navView)
// Navigation Label
self.navView.addSubview(navLabel)
navLabel.frame = CGRect(x: 0, y: 0, width: screenSize.width, height: 66)
navLabel.textAlignment = .center
navLabel.font = UIFont.boldSystemFont(ofSize: 20)
navLabel.textColor = UIColor.black
//navLabel.text = "Workout"
navLabel.text = "Log workout"
navLabel.adjustsFontSizeToFitWidth = true
navLabel.isUserInteractionEnabled = true
self.view.backgroundColor = UIColor.white
configureButtons()
setupFooter()
self.hideKeyboard()
print("Printing ... ... ...")
print(selectedWorkout)
}
func setupFooter() {
let customView = UIView(frame: CGRect(x: 0, y: 0, width: screenSize.width, height: 50))
customView.backgroundColor = UIColor.lightGray
let button = UIButton(frame: CGRect(x: 0, y: 0, width: screenSize.width, height: 50))
button.setTitle("Add Set", for: .normal)
button.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
customView.addSubview(button)
tableView.tableFooterView = customView
}
#objc func buttonAction(_ sender: UIButton!) { // Add new set/row in tableView
print("Button tapped")
let a = Array(stride(from: 1, through: 50, by: 1)) // Haven't tested yet. Add new set in array
setsArray.append("a")
// Update Table Data
tableView.beginUpdates()
tableView.insertRows(at: [
(NSIndexPath(row: setsArray.count-1, section: 0) as IndexPath)
], with: .automatic)
tableView.endUpdates()
}
func configureButtons() {
confiureCloseButton()
confiureSaveButton()
confiureClearButton()
}
func confiureCloseButton() {
//let button = UIButton(frame: CGRect(x: 100, y: 100, width: 100, height: 50))
let button = UIButton(frame: CGRect(origin: CGPoint.zero, size: menuButtonSize))
button.setImage(UIImage(named: "icon_close"), for: UIControl.State.normal)
button.setImage(UIImage(named: "icon_close")?.alpha(0.5), for: UIControl.State.highlighted)
button.center = CGPoint(x: 50, y: 61)
button.layer.cornerRadius = button.frame.size.width/2
button.layer.zPosition = 1
button.addTarget(self, action: #selector(closeButtonAction), for: .touchUpInside)
button.setTitleColor(.black, for: UIControl.State.normal)
button.backgroundColor = UIColor.red
button.isUserInteractionEnabled = true
self.view.addSubview(button)
}
#objc func closeButtonAction(sender: UIButton!) {
self.dismiss(animated: true, completion: nil)
}
func confiureSaveButton() {
let button = UIButton(frame: CGRect(origin: CGPoint(x: screenSize.width-44-8, y: 28), size: actionButtonSize))
button.setTitle("Save", for: UIControl.State.normal)
//button.backgroundColor = UIColor.blue
button.layer.zPosition = 1
button.addTarget(self, action: #selector(saveButtonAction), for: .touchUpInside)
button.setTitleColor(.black, for: UIControl.State.normal)
button.isUserInteractionEnabled = true
self.view.addSubview(button)
}
#objc func saveButtonAction(sender: UIButton!) {
saveWorkout()
}
func confiureClearButton() {
let button = UIButton(frame: CGRect(origin: CGPoint(x: screenSize.width-44-8-44-8, y: 28), size: actionButtonSize))
button.setTitle("Clear", for: UIControl.State.normal)
//button.backgroundColor = UIColor.red
button.layer.zPosition = 1
button.addTarget(self, action: #selector(clearButtonAction), for: .touchUpInside)
button.isUserInteractionEnabled = true
button.setTitleColor(.black, for: UIControl.State.normal)
self.view.addSubview(button)
}
#objc func clearButtonAction(sender: UIButton!) {
clearWorkout()
}
func clearWorkout() { // Clear workout exercise
print("clear")
}
let nowDate = Date()
var setsArray = [String]()
var previousArray = [String]()
var kgArray = [String]()
var repsArray = [String]()
var notesArray = [String]()
func saveWorkout() { // Save workout exercise
if setsArray.isEmpty {//if !setsArray.isEmpty {
print("Saving workout...")
/*let realm = try! Realm()
let myWorkout = Workout()
myWorkout.date = nowDate
myWorkout.exercise = "Text Exercise"
let aSet0 = Set(numReps: 12, weight: 11.5, aNote: "light workout")
let aSet1 = Set(numReps: 12, weight: 12.5, aNote: "medium workout")
let aSet2 = Set(numReps: 12, weight: 21.0, aNote: "heavy workout")
myWorkout.sets.append(objectsIn: [aSet0, aSet1, aSet2] )
try! realm.write {
realm.add(myWorkout)
print("Saved!")
}*/
} else {
// create the alert
let alert = UIAlertController(title: nil, message: "Please add any exercise before saving!", preferredStyle: UIAlertController.Style.alert)
// add an action (button)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertAction.Style.default, handler: nil))
// show the alert
self.present(alert, animated: true, completion: nil)
}
}
// number of rows in table view
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return setsArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! ExerciseCreatorCell
let realm = try! Realm()
let getExercises = realm.objects(Workout.self).filter("exercise = 'Text Exercise2'").sorted(byKeyPath: "date",ascending: false)
print(getExercises.last!)
let myWorkout = Workout()
// Find the Workout instance "Bench Press"
let benchPressWorkout = realm.objects(Workout.self).filter("exercise = 'Text Exercise2'").sorted(byKeyPath: "date",ascending: false)
// Access the sets for that instance
let sets = benchPressWorkout[0].sets
for i in sets.indices {
let set0 = sets[i]
// Access reps and kg for set 0
let reps0 = set0.reps
let kg0 = set0.kg
// and so on ...
print([kg0]) // number of KG
print([reps0]) // number of reps
cell.previousTextField.text = "\(kg0) x \(reps0)"
}
cell.setLabel.text = "\(indexPath.row + 1)"
return cell
}
// method to run when table view cell is tapped
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("tapped")
}
}
EDIT: I have three objects as #rs7 suggested earlier:
Workout.swift:
#objc dynamic var id = 0
#objc dynamic var date: Date?
// List of exercises (to-many relationship)
var exercises = List<Exercise>()
override static func primaryKey() -> String? {
return "id"
}
Exercise.swift
class Exercise: Object {
#objc dynamic var name: String?
// List of sets (to-many relationship)
var sets = List<Set>()
var parentWorkout = LinkingObjects(fromType: Workout.self, property: "exercises")
}
Set.swift
class Set: Object {
#objc dynamic var reps: Int = 0
#objc dynamic var kg: Double = 0.0
#objc dynamic var notes: String?
// Define an inverse relationship to be able to access your parent workout for a particular set (if needed)
var parentExercise = LinkingObjects(fromType: Exercise.self, property: "sets")
convenience init(numReps: Int, weight: Double, aNote: String) {
self.init()
self.reps = numReps
self.kg = weight
self.notes = aNote
}
}
I'll give you the answer anyway, but don't forget to fix your code.
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// number of rows = number of sets (not counting the header row)
return selectedExercise.sets.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! ExerciseCreatorCell
let currentSet = selectedExercise.sets[indexPath.row]
cell.setNumberLabel.text = "\(indexPath.row)"
cell.kgLabel.text = "\(currentSet.kg)"
cell.repsLabel.text = "\(currentSet.reps)"
return cell
}
Related
**Aim: ** To show latest apple calendar type of date picker(inline) in app when user wants to select the date.How it should look
Scenario:**
The user can input multiple entries (maximum 3) the exercise(suryanamaskar). These multiple entries has two functions
1.date-picker and
2.number entry(number of 'suryanamaskar')
When user clicks on the date-picker button. the date picker shows up (.inline). Each entry fetches the post API call that saves the 'suryanamaskar' count entry on server.
What is the issue: I tried to implement the date-picker using and few UI frames : .inline
I replaced the date-picker completely and implemented ".wheels" that works fine.
The datepicker is show up correctly first time. But when the user tried to add other entry or press the date entry buttton . The datepicker gets cut and is not properly visible. This is how it looks when user clicks on datepicker button second time.
What my code looks like:
import UIKit
import SwiftyJSON
import Alamofire
struct surynamaskarStruct {
var date: String
var count: String
}
class AddSuryaNamskarCountVC: UIViewController, UITextFieldDelegate {
var surynamaskarData: [surynamaskarStruct] = [
surynamaskarStruct(date: "Date", count: "0"),
surynamaskarStruct(date: "Date", count: "0"),
surynamaskarStruct(date: "Date", count: "0")
]
#IBOutlet var tableView: UITableView!
#IBOutlet weak var lblMember: UILabel!
#IBOutlet weak var btnMember: UIButton!
#IBOutlet weak var familyMemberHeightConstraint: NSLayoutConstraint!
var viewPicker : UIView!
let datePicker = UIDatePicker()
var btnIndex : Int!
var strMemberId : String!
var dicMember = [[String:Any]]()
lazy var loader : UIActivityIndicatorView = {
let indicator = UIActivityIndicatorView(style: .large)
indicator.hidesWhenStopped = true
return indicator
}()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
if self.dicMember.count > 0 {
self.strMemberId = self.dicMember[0]["id"] as? String
self.lblMember.text = self.dicMember[0]["name"] as? String
self.familyMemberHeightConstraint.constant = 90
} else {
self.familyMemberHeightConstraint.constant = 0
}
view.addSubview(loader)
loader.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
loader.centerXAnchor.constraint(equalTo: view.centerXAnchor),
loader.centerYAnchor.constraint(equalTo: view.centerYAnchor)
])
}
override func viewWillAppear(_ animated: Bool) {
navigationBarDesign(txt_title: "Record Suryanamskar", showbtn: "back")
}
#IBAction func onMemberClick(_ sender: UIButton) {
let vc = self.storyboard?.instantiateViewController(withIdentifier: "PickerTableViewWithSearchViewController") as! PickerTableViewWithSearchViewController
vc.selectedItemCompletion = {dict in
self.lblMember.text = dict["name"] as? String
self.strMemberId = dict["id"] as? String
}
vc.dataSource = dicMember
vc.strNavigationTitle = "Family Member"
vc.modalPresentationStyle = UIModalPresentationStyle.fullScreen
self.present(vc, animated: true, completion: nil)
}
#IBAction func onCancelClick(_ sender: UIButton) {
self.dismiss(animated: true)
}
#IBAction func onSaveClick(_ sender: UIButton) {
var arrData = [[String:Any]]()
for arr in surynamaskarData {
if arr.date != "Date" && arr.count != "0" {
var dict = [String : Any]()
dict["date"] = arr.date
dict["count"] = arr.count
arrData.append(dict)
}
}
if arrData.count > 0 {
self.saveSuryaNamskarCountDataAPI(arrData)
////static controller to directly update the suryanamaskar chart instead of going back and then getting updated suryanamaskar
let alertController = UIAlertController(title:APP.title, message: "Suryanamaskar has been saved successfully!", preferredStyle:.alert)
let Action = UIAlertAction.init(title: "Ok", style: .default) { (UIAlertAction) in
// Write Your code Here
let vc = self.storyboard?.instantiateViewController(withIdentifier: "SuryaNamskarVC") as! SuryaNamskarVC
vc.modalPresentationStyle = UIModalPresentationStyle.fullScreen
// vc.modalTransitionStyle = UIModalTransitionStyle.crossDissolve
self.present(vc, animated: true, completion: nil)
}
alertController.addAction(Action)
self.present(alertController, animated: true, completion: nil)
//// Till here for alert controller. if not implemented, updated suryanamaskar will happen only once user goes back and comes back to suryanamaskarVC
}
// else {
// showAlert(title: APP.title, message: "Please select date and count before Save")
// }
}
// MARK: - DatePicker functions
func showDatePicker(){
viewPicker = UIView(frame: CGRect(x: 0.0, y: self.view.frame.height - 380, width: self.view.frame.width, height: 380))
viewPicker.backgroundColor = UIColor.white
viewPicker.clipsToBounds = true
// Posiiton date picket within a view
datePicker.frame = CGRect(x: 10, y: 50, width: self.view.frame.width, height: 200)
datePicker.datePickerMode = .date
datePicker.minimumDate = Calendar.current.date(byAdding: .month, value: -2, to: Date())
datePicker.maximumDate = Calendar.current.date(byAdding: .year, value: 0, to: Date())
datePicker.backgroundColor = UIColor.white
if #available(iOS 14.0, *) {
datePicker.preferredDatePickerStyle = .inline
} else {
if #available(iOS 13.4, *) {
datePicker.preferredDatePickerStyle = .wheels
} else {
// Fallback on earlier versions
}
// Fallback on earlier versions
}
// Add an event to call onDidChangeDate function when value is changed.
datePicker.addTarget(self, action: #selector(AddMemberStep1VC.datePickerValueChanged(_:)), for: .valueChanged)
datePicker.center.x = self.view.center.x
//ToolBar
var toolbar = UIToolbar();
toolbar.sizeToFit()
toolbar = UIToolbar(frame: CGRect(x: 0.0, y: 0.0, width: self.view.frame.width, height: 50))
let doneButton = UIBarButtonItem(title: "done".localized, style: .plain, target: self, action: #selector(donedatePicker));
let spaceButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.flexibleSpace, target: nil, action: nil)
let cancelButton = UIBarButtonItem(title: "cancel".localized, style: .plain, target: self, action: #selector(cancelDatePicker));
toolbar.setItems([doneButton,spaceButton,cancelButton], animated: false)
// txtDate.inputAccessoryView = toolbar
// txtDate.inputView = datePicker
self.viewPicker.addSubview(toolbar)
self.viewPicker.addSubview(datePicker)
self.viewPicker.clipsToBounds = true
self.view.addSubview(self.viewPicker)
}
#objc func datePickerValueChanged(_ sender: UIDatePicker){
// Create date formatter
let dateFormatter: DateFormatter = DateFormatter()
// Set date format
dateFormatter.dateFormat = "dd/MM/yyyy"
// Apply date format
let _: String = dateFormatter.string(from: sender.date)
// print("Selected value \(selectedDate)")
}
#objc func donedatePicker(){
let formatter = DateFormatter()
formatter.dateFormat = "dd/MM/yyyy"
let strDate = formatter.string(from: datePicker.date)
surynamaskarData[btnIndex].date = strDate
DispatchQueue.main.async {
self.tableView.reloadData()
}
self.viewPicker.isHidden = true
}
#objc func cancelDatePicker(){
self.viewPicker.isHidden = true
}
#objc func onAddMoreClicked() {
print("AddMore Button Pressed")
for i in surynamaskarData {
if i.date == "Date" || i.count == "0" || i.count == "" {
showAlert(title: APP.title, message: "Please select date and count before AddMore")
return
}
}
surynamaskarData.append(surynamaskarStruct(date: "Date", count: "0"))
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
#objc func onDateClick(_ sender: UIButton) {
btnIndex = sender.tag
view.endEditing(true)
if self.viewPicker == nil {
self.showDatePicker()
} else {
if self.viewPicker.isHidden == true {
self.showDatePicker()
} else {
self.viewPicker.isHidden = true
}
}
}
#objc func onDeleteClick(_ sender: UIButton) {
surynamaskarData.remove(at: sender.tag)
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
func textFieldDidBeginEditing(_ textField: UITextField) {
if self.viewPicker != nil {
if self.viewPicker.isHidden == false {
self.viewPicker.isHidden = false
}
}
}
func textFieldDidEndEditing(_ textField: UITextField) {
if textField.text == "" {
textField.text = "0"
}
surynamaskarData[textField.tag].count = textField.text ?? "0"
}
func json(from object:Any) -> String? {
guard let data = try? JSONSerialization.data(withJSONObject: object, options: []) else {
return nil
}
return String(data: data, encoding: String.Encoding.utf8)
}
func saveSuryaNamskarCountDataAPI(_ data : [[String:Any]]) {
var parameters: [String: Any] = [:]
parameters["surynamaskar"] = self.json(from: data)
parameters["member_id"] = self.strMemberId // _appDelegator.dicDataProfile![0]["member_id"] as? String
print(parameters)
// APIUrl.save_suryanamaskar
//live API only
//"https://myhss.org.uk/api/v1/suryanamaskar/save_suryanamaskar_count"
loader.startAnimating()
let url = URL(string: APIUrl.save_suryanamaskar)!
AF.request(url, method: .post, parameters: parameters, encoding: JSONEncoding.default)
.validate()
.responseJSON { response in
self.loader.stopAnimating()
switch response.result {
case .success(let response):
print(response)
let jsonData = JSON(response)
if let status = jsonData["status"].int
{
if status == 1
{
let strMessage : String = jsonData["message"].rawValue as! String
print(strMessage)
// create the alert
let alert = UIAlertController(title: APP.title, message: strMessage, preferredStyle: UIAlertController.Style.alert)
// add an action (button)
let ok = UIAlertAction(title: "Ok".localized, style: .default, handler: { action in
DispatchQueue.main.async {
self.dismiss(animated: true)
}
})
alert.addAction(ok)
// show the alert
self.present(alert, animated: true, completion: nil)
} else {
if let strError = jsonData["message"].string {
showAlert(title: APP.title, message: strError)
}
}
}
case .failure(let error):
print(error.localizedDescription)
showAlert(title: APP.title, message: error.localizedDescription)
}
}
}
}
extension AddSuryaNamskarCountVC : UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return surynamaskarData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: AddSuryaNamskarCountTVCell.cellIdentifier) as! AddSuryaNamskarCountTVCell
cell.btnDate.tag = indexPath.row
cell.btnDate.addTarget(self, action: #selector(self.onDateClick(_:)), for: .touchUpInside)
cell.btnCancel.tag = indexPath.row
cell.btnCancel.addTarget(self, action: #selector(self.onDeleteClick(_:)), for: .touchUpInside)
cell.lblDate.text = surynamaskarData[indexPath.row].date
cell.txtCount.text = surynamaskarData[indexPath.row].count
cell.txtCount.tag = indexPath.row
cell.txtCount.delegate = self
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 60.0
}
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
let viewFooter = UIView()
viewFooter.backgroundColor = UIColor.systemGray5
let size = tableView.frame.size
let addMoreButton = UIButton()
addMoreButton.setTitle("+ Add More", for: .normal)
addMoreButton.setTitleColor(Colors.txtAppDarkColor, for: .normal)
addMoreButton.frame = CGRect(x: 0, y: 0, width: size.width, height: 50)
addMoreButton.addTarget(self, action: #selector(onAddMoreClicked), for: .touchUpInside)
viewFooter.addSubview(addMoreButton)
return viewFooter
}
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 50
}
}
Changing the Co-ordinates works:
Following co-orinates fixes the calender view for this issue.
func showDatePicker(){
viewPicker = UIView(frame: CGRect(x: 0.0, y: self.view.frame.height - 500, width: self.view.frame.width, height: 500))
viewPicker.backgroundColor = UIColor.white
viewPicker.clipsToBounds = true
// Posiiton date picket within a view
datePicker.frame = CGRect(x: 10, y: 50, width: self.view.frame.width, height: 500)
datePicker.datePickerMode = .date
datePicker.minimumDate = Calendar.current.date(byAdding: .month, value: -2, to: Date())
datePicker.maximumDate = Calendar.current.date(byAdding: .year, value: 0, to: Date())
datePicker.backgroundColor = UIColor.white
if #available(iOS 14.0, *) {
datePicker.preferredDatePickerStyle = .inline
} else {
if #available(iOS 13.4, *) {
datePicker.preferredDatePickerStyle = .wheels
} else {
// Fallback on earlier versions
}
// Fallback on earlier versions
}
// Add an event to call onDidChangeDate function when value is changed.
datePicker.addTarget(self, action: #selector(AddMemberStep1VC.datePickerValueChanged(_:)), for: .valueChanged)
datePicker.center.x = self.view.center.x
//ToolBar
var toolbar = UIToolbar();
toolbar.sizeToFit()
toolbar = UIToolbar(frame: CGRect(x: 0.0, y: 0.0, width: self.view.frame.width, height: 50))
let doneButton = UIBarButtonItem(title: "done".localized, style: .plain, target: self, action: #selector(donedatePicker));
let spaceButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.flexibleSpace, target: nil, action: nil)
let cancelButton = UIBarButtonItem(title: "cancel".localized, style: .plain, target: self, action: #selector(cancelDatePicker));
toolbar.setItems([doneButton,spaceButton,cancelButton], animated: false)
self.viewPicker.addSubview(toolbar)
self.viewPicker.addSubview(datePicker)
self.viewPicker.clipsToBounds = true
self.view.addSubview(self.viewPicker)
}
My app downloads images from firebase and displays them in the tableViewController cells, also I need the AudioPlayerViewController to display exactly same image of the selected row/cell. For now in anyway it displays only the last downloaded image. I tried to save downloaded images into array but it always threw errors like cannot assign UIImage to [UIImage] or something like this. Hope to your help friends.
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 viewModel = AudiosViewModel()
private var cancellable: AnyCancellable?
override func viewDidLoad() {
super.viewDidLoad()
self.viewModel.fetchData()
self.title = "Audio Lessons"
let nib = UINib(nibName: "AudioCustomTableViewCell", bundle: nil)
table.register(nib, forCellReuseIdentifier: "audioCustomCell")
table.delegate = self
table.dataSource = self
cancellable = viewModel.$audios.sink { _ in
DispatchQueue.main.async{
self.table.reloadData()
}
}
}
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: "audioCustomCell", for: indexPath) as? AudioCustomTableViewCell
let song = viewModel.audios[indexPath.row]
let imageURL = song.audioImageName
cell?.audioImage.sd_imageIndicator = SDWebImageActivityIndicator.gray
cell?.audioImage.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))")
}
self.viewModel.image = cell?.audioImage.image
})
tableView.tableFooterView = UIView()
cell?.textLabel?.font = UIFont(name: "Helvetica-Bold", size: 14)
cell?.detailTextLabel?.font = UIFont(name: "Helvetica", size: 12)
cell?.commonInit(song.albumName, song.name, viewModel.image)
return cell!
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 120
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
let position = indexPath.row
guard let vc = storyboard?.instantiateViewController(identifier: "AudioPlayer") as? AudioPlayerViewController else {
return
}
vc.mainImage = viewModel.image
vc.paragraphs = viewModel.audios
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
}
}
}
This is the viewModel
import Foundation
import FirebaseFirestore
import SDWebImage
class AudiosViewModel: ObservableObject {
#Published var audios = [Audio]()
private var db = Firestore.firestore()
var image: UIImage?
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 image = image
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 ?? ""
return Audio(image: image, name: name, albumName: albumName, paragraphNumber: paragraphNumber, audioImageName: audioImageName, trackURL: trackURL)
}
}
}
}
this is the AudioPlayerViewController
import UIKit
import AVFoundation
import MediaPlayer
import AVKit
import Combine
class AudioPlayerViewController: UIViewController {
// private var viewModel = AudiosViewModel()
public var position: Int = 0
public var paragraphs: [Audio] = []
public var mainImage = UIImage(named: "placeHolderImage")
#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.layer.shadowOpacity = 0.3
imageView.layer.shadowRadius = 3
imageView.layer.shadowOffset = CGSize(width: 6, height: 6)
imageView.layer.shadowRadius = 8
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 = false
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 = mainImage
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)
})
}
}
This is Audio Struct
import Foundation
import UIKit
struct Audio {
let image: UIImage?
let name: String
let albumName: String
let paragraphNumber: String
let audioImageName: String
let trackURL: String
}
This is customTableViewCell
import UIKit
class AudioCustomTableViewCell: UITableViewCell {
#IBOutlet weak var cellView: UIView!
#IBOutlet weak var audioImage: UIImageView!
#IBOutlet weak var mainTitle: UILabel!
#IBOutlet weak var detailTitle: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
setCardView(toView: audioImage)
setCardView(toView: cellView)
self.audioImage.layer.cornerRadius = 6
audioImage.clipsToBounds = true
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
func commonInit(_ title: String, _ subtitle: String, _ image: UIImage?){
mainTitle.text = title
detailTitle.text = subtitle
audioImage.image = image
}
func setCardView(toView: UIView) {
toView.layer.shadowColor = UIColor.black.cgColor
toView.layer.shadowOpacity = 0.3
toView.layer.shadowRadius = 3
toView.layer.shadowOffset = CGSize(width: 6, height: 6)
toView.layer.shadowRadius = 8
toView.layer.cornerRadius = 6
}
}
when you are navigating from didSelectRowAt Indexpath please pass all the values to your detailViewController when you navigate to that view all value will be there to use.
use let song = viewModel.audios[indexPath.row]
let imageURL = song.audioImageName
Get values and pass it to your detailViewController
do the same thing in didSelectRowAt Indexpath what you did in cellForRowAt indexPath because there you are just passing position and taking values from model and passing it that's why this might be happening.
if you are getting only this error cannot assign UIImage to [UIImage]
then you can just convert your UIImage to [UIImage] and assign it to your [UIImage]
This line causes the bug, viewModel.image will always be the last download one.
self.viewModel.image = cell?.audioImage.image
you can simply pass the image URL to the next VC
vc.mainImage = viewModel.audios[indexPath.row].audioImageName
then use SDWebImage to load this image from the url
I tried the other method of SDWebImage and in didselectRowAt according to suggestion of Rahul Nimje. Thanks to him it worked. Dear Rahul, how will it affect on weight of the app, is it the correct way?
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
let position = indexPath.row
let song = viewModel.audios[indexPath.row]
let imageURL = song.audioImageName
SDWebImageManager.shared.loadImage(
with: URL(string: imageURL),
options: .continueInBackground, // or .highPriority
progress: nil,
completed: { [weak self] (image, data, error, cacheType, finished, url) in
guard let self = self else { return }
if let err = error {
// Do something with the error
return
}
guard let img = image else {
// No image handle this error
return
}
// Do something with image
self.viewModel.image = image
})
I have a design in which the sign-out button is placed at the bottom and should not scroll, side menu has a tableview controller in which we can add rows but my requirement is to add the sign-out button at the bottom. I have tried by adding the sign-out button in the footer view to side menu tableview controller, but showing just bellow the rows which I don't want.
import UIKit
import SideMenu
class ProfileViewController: UIViewController {
var menu:SideMenuNavigationController?
override func viewDidLoad() {
super.viewDidLoad()
configureSideMenu()
}
func configureSideMenu() {
menu = SideMenuNavigationController(rootViewController: MenuListController())
menu?.navigationBar.setBackgroundImage(UIImage(named: "top_navbrBG"), for: .default)
let firstFrame = CGRect(x: 20, y: 0, width: menu?.navigationBar.frame.width ?? 0/2, height: menu?.navigationBar.frame.height ?? 0)
let firstLabel = UILabel(frame: firstFrame)
firstLabel.text = "Settings"
menu?.navigationBar.addSubview(firstLabel)
SideMenuManager.default.addPanGestureToPresent(toView: self.view)
let screenSize = UIScreen.main.bounds
let screenHeight = screenSize.height + 40
let leftBorderView = UIView(frame: CGRect(x: 1, y: -40, width: 1, height: screenHeight))
leftBorderView.backgroundColor = UIColor.init(hexString: "#cfcfcf")
menu?.navigationBar.addSubview(leftBorderView)
}
#IBAction func menuButtonAction(_ sender: UIButton) {
if let menu = menu {
present(menu, animated: true)
}
}
}
// functions
extension ProfileViewController {
#objc func signOutButtonTapped(_ sender: AnyObject?) {
print("sigin out")
}
func getSignOutButton()->UIButton {
let button = UIButton()
button.setTitle("Sign out", for: .normal)
let color = UIColor.init(hexString: "#1A73E9")
button.setTitleColor(color, for: .normal)
button.addTarget(self, action: #selector(signOutButtonTapped), for: .touchUpInside)
return button
}
func setConstraintsForSignOutButton(button: UIButton) {
let screenSize = UIScreen.main.bounds
let screenHeight = screenSize.height
guard let menuTopAnchor = menu?.navigationBar.topAnchor else { return }
guard let menuLeadingAnchor = menu?.navigationBar.leadingAnchor else { return }
guard let menuTrailingAnchor = menu?.navigationBar.trailingAnchor else { return }
guard let menuWidth = menu?.menuWidth else { return }
button.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
button.leadingAnchor.constraint(equalTo: menuLeadingAnchor, constant: 0),
button.trailingAnchor.constraint(equalTo: menuTrailingAnchor, constant: 0),
button.widthAnchor.constraint(equalToConstant: menuWidth),
button.heightAnchor.constraint(equalToConstant: 40),
button.bottomAnchor.constraint(equalTo: menuTopAnchor, constant: 300)
])
}
}
// Menu Items
class MenuListController: UITableViewController {
var menuItems = [[String: String]]()
override func viewDidLoad() {
super.viewDidLoad()
menuItems.append(["name": "Privacy", "img": "privacy", "key" : "privacy"])
menuItems.append(["name": "Report issue", "img": "report_issue", "key": "report_issue"])
tableView.dataSource = self
tableView.delegate = self
tableView.separatorStyle = .none
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
tableView.tableFooterView = getSignOutButton()
}
#objc func signOutButtonTapped(_ sender: AnyObject?) {
print("sigin out")
}
func getSignOutButton()->UIButton {
let button = UIButton()
button.height = 20
button.width = 100
button.setTitle("Sign out", for: .normal)
let color = UIColor.init(hexString: "#1A73E9")
button.setTitleColor(color, for: .normal)
button.addTarget(self, action: #selector(signOutButtonTapped), for: .touchUpInside)
return button
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
menuItems.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
if menuItems.indices.contains(indexPath.row) {
let dict = menuItems[indexPath.row]
cell.textLabel?.text = dict["name"]
if let img = dict["img"] {
cell.imageView?.image = UIImage(named: img)
}
}
return cell
}
}
desired design
Replace this "tableView.tableFooterView = getSignOutButton()" with
self.getSignOutButton() in viewDidLoad.
Next replace your function "getSignOutButton()->UIButton {}" with below code.
func getSignOutButton() {
let button = UIButton()
button.setTitle("Sign out", for: .normal)
button.setTitleColor(.blue, for: .normal)
button.addTarget(self, action: #selector(signOutButtonTapped), for: .touchUpInside)
view.addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
button.leadingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.leadingAnchor),
button.trailingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.trailingAnchor),
button.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor,constant: -20),
button.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor),
])
}
I have a simple gym workout App that consists of 2 view controllers (vc1 and vc2).
Users put some information into VC2, this is then this is passed to and displayed in VC1.
VC1 is a UITableView. The information in VC2 is stored in a struct and passed to VC1 via protocol delegate method.
The user inputs data in VC2 to fill the struct. Then VC1 populates itself based on the struct.
VC1 is an extendible UITableView. Users put in the title of the workout and then when the row is clicked the users can see the exercises that they entered along with their sets and reps.
my plan is to have 10 exercises sections that the users can enter.
My problem is that if the user doesn't want a workout to consist of 10 exercises, say they want the workout to consist of only 3 exercisers. So they will fill in the fields for 3 exercises only. I am left with 3 cells in the UITableView that are populated with information and 7 that are occupied by black strings.
How can I get rid of the 7 blank exercises so they will not show up in the UITableView? In this code below I have only added one exercise element not 10 yet. I just want to solve this issue first while the code isn't too big.
VC1 is called ContactsController this is the UITableView:
import Foundation
import UIKit
private let reuseidentifier = "Cell"
struct cellData {
var opened = Bool()
var title = String()
var sectionData = [String]()
}
//here
struct Contact {
var fullname: String
var excerciseOne: String
var excerciseOneReps: String
var excerciseOneSets: String
}
class ContactController: UITableViewController {
//new
var tableViewData = [cellData]()
var contacts = [Contact]()
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.navigationBar.prefersLargeTitles = true
self.navigationItem.title = "Workouts"
// self.navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(handleAddContact))
view.backgroundColor = .white
tableView.register(UITableViewCell.self, forCellReuseIdentifier: reuseidentifier)
}
#IBAction func handleAddContact(_ sender: Any) {
let controller = AddContactController()
controller.delegate = self
self.present(UINavigationController(rootViewController: controller), animated: true, completion: nil)
}
//UITABLEVIEW
//all new
override func numberOfSections(in tableView: UITableView) -> Int {
//new
return tableViewData.count
}
override func tableView(_ tableView: UITableView,
numberOfRowsInSection section: Int) -> Int {
//old needed return contacts.count
//new
if tableViewData[section].opened == true {
return tableViewData[section].sectionData.count + 1
}else {
return 1
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//old needed let cell = tableView.dequeueReusableCell(withIdentifier: reuseidentifier, for: indexPath)
// cell.textLabel?.text = contacts[indexPath.row].fullname
// return cell
if indexPath.row == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: reuseidentifier, for: indexPath)
cell.textLabel?.text = tableViewData[indexPath.section].title
return cell
}else {
//use a different cell identifier if needed
let cell = tableView.dequeueReusableCell(withIdentifier: reuseidentifier, for: indexPath)
// cell.textLabel?.text = tableViewData[indexPath.section].sectionData[indexPath.row]
cell.textLabel?.text = tableViewData[indexPath.section].sectionData[indexPath.row - 1]
return cell
}
}
//did select row new
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if tableViewData[indexPath.section].opened == true {
tableViewData[indexPath.section].opened = false
let sections = IndexSet.init(integer: indexPath.section)
tableView.reloadSections(sections, with: .none) //play around with animation
}else {
tableViewData[indexPath.section].opened = true
let sections = IndexSet.init(integer: indexPath.section)
tableView.reloadSections(sections, with: .none) //play around with animation
}
}
}
//this is an extention to addContactController. this is what happens whent he done button is clicked in addcontactcontroller
extension ContactController: AddContactDelegate {
func addContact(contact: Contact) {
self.dismiss(animated: true) {
self.contacts.append(contact)
self.tableViewData.append(cellData.init(opened: false, title: contact.fullname, sectionData: [contact.excerciseOne + " Reps: " + contact.excerciseOneReps + " Sets: " + contact.excerciseOneSets, "cell2", "cell3"]))
self.tableView.reloadData()
}
}
}
The Following is my VC2 where the user inputs the data to create a workout and its associated exercises:
import Foundation
import UIKit
//here
protocol AddContactDelegate {
func addContact(contact: Contact)
}
class AddContactController: UIViewController {
//delegate
var delegate: AddContactDelegate?
//Title TextField
let titleTextField: UITextField = {
let tf = UITextField()
tf.placeholder = "What you you like to call your workout?"
tf.textAlignment = .center
tf.translatesAutoresizingMaskIntoConstraints = false
return tf
}()
//Exercise 1 title TextField
let excercise1TextField: UITextField = {
let ex1 = UITextField()
ex1.placeholder = "What excercise are you doing?"
ex1.textAlignment = .center
ex1.translatesAutoresizingMaskIntoConstraints = false
return ex1
}()
//Exercise 1 title TextField
let excercise1RepsTextField: UITextField = {
let ex1Reps = UITextField()
ex1Reps.placeholder = "Reps"
ex1Reps.textAlignment = .center
ex1Reps.translatesAutoresizingMaskIntoConstraints = false
return ex1Reps
}()
//Exercise 1 title TextField
let excercise1SetsTextField: UITextField = {
let ex1Sets = UITextField()
ex1Sets.placeholder = "Sets"
ex1Sets.textAlignment = .center
ex1Sets.translatesAutoresizingMaskIntoConstraints = false
return ex1Sets
}()
override func viewDidLoad() {
super.viewDidLoad()
//making scroll view
let screensize: CGRect = UIScreen.main.bounds
let screenWidth = screensize.width
let screenHeight = screensize.height
var scrollView: UIScrollView!
scrollView = UIScrollView(frame: CGRect(x: 0, y: 120, width: screenWidth, height: screenHeight))
scrollView.contentSize = CGSize(width: screenWidth, height: 2000)
view.addSubview(scrollView)
//setting up how view looks-----
view.backgroundColor = .white
//top buttons
self.navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(handleDone))
self.navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(handleCancel))
//view elements in scrollview
//Workout Title label
let workoutTitleLabel = UILabel(frame: CGRect(x: 0, y: 0, width: 200, height: 25))
workoutTitleLabel.center = CGPoint(x: view.frame.width / 2 , y: view.frame.height / 20)
workoutTitleLabel.textAlignment = .center
workoutTitleLabel.text = "Workout Title"
workoutTitleLabel.font = UIFont.boldSystemFont(ofSize: 20)
scrollView.addSubview(workoutTitleLabel)
//workout title textfield
scrollView.addSubview(titleTextField)
titleTextField.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
titleTextField.centerXAnchor.constraint(equalTo: scrollView.centerXAnchor),
// textField.centerYAnchor.constraint(equalTo: scrollView.centerYAnchor),
titleTextField.centerYAnchor.constraint(equalTo: scrollView.centerYAnchor ,constant: -350),
titleTextField.widthAnchor.constraint(equalToConstant: view.frame.width - 64)
])
titleTextField.becomeFirstResponder()
//excercise 1 the locked in title Label
let excersice1label = UILabel(frame: CGRect(x: 0, y: 0, width: 200, height: 21))
excersice1label.center = CGPoint(x: view.frame.width / 2 , y: view.frame.height / 5)
excersice1label.textAlignment = .center
excersice1label.text = "Excercise 1"
excersice1label.font = UIFont.boldSystemFont(ofSize: 20)
scrollView.addSubview(excersice1label)
//excercise 1 title textField
scrollView.addSubview(excercise1TextField)
excercise1TextField.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
excercise1TextField.centerXAnchor.constraint(equalTo: scrollView.centerXAnchor),
// textField.centerYAnchor.constraint(equalTo: scrollView.centerYAnchor),
excercise1TextField.centerYAnchor.constraint(equalTo: scrollView.centerYAnchor ,constant: -220),
excercise1TextField.widthAnchor.constraint(equalToConstant: view.frame.width - 64)
])
excercise1TextField.becomeFirstResponder()
//excercise 1 Reps textField
scrollView.addSubview(excercise1RepsTextField)
excercise1RepsTextField.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
excercise1RepsTextField.centerXAnchor.constraint(equalTo: scrollView.centerXAnchor),
// textField.centerYAnchor.constraint(equalTo: scrollView.centerYAnchor),
excercise1RepsTextField.centerYAnchor.constraint(equalTo: scrollView.centerYAnchor ,constant: -190),
excercise1RepsTextField.widthAnchor.constraint(equalToConstant: view.frame.width - 64)
])
excercise1RepsTextField.becomeFirstResponder()
//excercise 1 Sets textField
scrollView.addSubview(excercise1SetsTextField)
excercise1SetsTextField.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
excercise1SetsTextField.centerXAnchor.constraint(equalTo: scrollView.centerXAnchor),
// textField.centerYAnchor.constraint(equalTo: scrollView.centerYAnchor),
excercise1SetsTextField.centerYAnchor.constraint(equalTo: scrollView.centerYAnchor ,constant: -160),
excercise1SetsTextField.widthAnchor.constraint(equalToConstant: view.frame.width - 64)
])
excercise1SetsTextField.becomeFirstResponder()
}
//done button
#objc func handleDone(){
print("done")
//setting the uitextfields to the the contact
guard let fullname = titleTextField.text, titleTextField.hasText else {
print("handle error here")
return
}
/* guard might be needed if a certain element is left empty
guard let excerciseOne = excercise1TextField.text, excercise1TextField.hasText else {
print("handle error here")
let excerciseOne = "empty"
return
}
*/
//setting the user input elements to to the contact so it can be passed to vc1 and presented
let excerciseOne = excercise1TextField.text
let excerciseOneReps = excercise1RepsTextField.text
let excerciseOneSets = excercise1SetsTextField.text
let contact = Contact(fullname: fullname, excerciseOne: excerciseOne!, excerciseOneReps: excerciseOneReps!, excerciseOneSets: excerciseOneSets!)
delegate?.addContact(contact: contact)
print(contact.fullname)
}
//cancel button
#objc func handleCancel(){
self.dismiss(animated: true, completion: nil )
}
}
I want to send data to my adapter view where it should put correct data to it.I send the data over the "exerciseName" variable.In the print its correct but when it reaches the destination the second view it becomes nil.Please help
var workoutInfoData : WorkoutInfo!
var RoundLabels = ["Round 1","Round 2","Round 3","Round 4","Round 5","Round 6","Round 7","Round 8","Round 9","Round 10","Round 11","Round 12"]
var exerciseName:String = ""
var roundPosition = [Int]()
let numberOfExercises = 1
var scrollerSize = 0
override func viewDidLoad() {
super.viewDidLoad()
//self.workoutTitle.title! = workoutInfoData.workout_name
navigationItem.title = workoutInfoData.workout_name
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func workoutBack(sender: AnyObject) {
self.dismissViewControllerAnimated(true, completion: {});
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Potentially incomplete method implementation.
// Return the number of sections.
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete method implementation.
// Return the number of rows in the section.
return RoundLabels.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
var cell : RoundWorkoutCell! = tableView.dequeueReusableCellWithIdentifier("Cell") as! RoundWorkoutCell
if(cell == nil)
{
cell = NSBundle.mainBundle().loadNibNamed("Cell", owner: self, options: nil)[0] as! RoundWorkoutCell;
}
let exerviseName = RoundLabels[indexPath.row]
if(indexPath.row == 1){
}
for countMe in 0..<self.numberOfExercises {
if(countMe<1){
roundPosition.append(5)
}else{
roundPosition.append(115+roundPosition[countMe-1])
}
scrollerSize = 115+roundPosition[countMe]
}
cell.RoundExercise_Cell_ScrollView.contentSize = CGSizeMake(CGFloat(scrollerSize),115)
cell.RoundExercise_Cell_ScrollView.showsHorizontalScrollIndicator = true
cell.RoundExercise_Cell_ScrollView.indicatorStyle = .Default
for index in 0..<self.numberOfExercises {
var imageView : UIImageView
imageView = UIImageView(frame:CGRect(x:roundPosition[index],y: 5, width:110, height: 110 ))
imageView.backgroundColor = UIColor.whiteColor()
cell.RoundExercise_Cell_ScrollView.addSubview(imageView)
let label1: UILabel = UILabel()
label1.frame = CGRect(x:roundPosition[index],y: 5, width:110, height: 20 )
label1.textColor = UIColor(red:17/255.0, green: 22/255.0, blue: 40/255.0, alpha:1.0)
label1.textAlignment = NSTextAlignment.Center
label1.font = UIFont(name: "OpenSans-CondensedLight", size: 14)
label1.text = "Pushups"
cell.RoundExercise_Cell_ScrollView.addSubview(label1)
let frame1 = CGRect(x:roundPosition[index]+10,y:25, width:90, height: 90 )
let button = UIButton(frame: frame1)
button.backgroundColor = UIColor.redColor()
button.setBackgroundImage(UIImage(named: "sit_thrust") as UIImage?, forState: .Normal)
button.setTitleColor(UIColor.blackColor(), forState: .Normal)
button.setTitle("air_sit_front_raise", forState: .Normal)
button.setTitleColor(UIColor(red:0/255,green:0/255,blue:0/255,alpha:0.0), forState: .Normal)
button.addTarget(self, action: "buttonClick:", forControlEvents: .TouchUpInside)
cell.RoundExercise_Cell_ScrollView.addSubview(button)
let label: UILabel = UILabel()
label.frame = CGRect(x:roundPosition[index],y: 115, width:110, height: 20 )
label.font = UIFont(name: "OpenSans", size: 14)
label.textColor = UIColor.whiteColor()
label.textAlignment = NSTextAlignment.Center
label.text = "30sec"
cell.RoundExercise_Cell_ScrollView.addSubview(label)
}
cell.RoundExercise_Cell_Label.text = exerviseName
return cell as RoundWorkoutCell
}
func buttonClick(sender:AnyObject){
exerciseName = String(sender.currentTitle)
//print(sender.currentTitle)
self.performSegueWithIdentifier("showExercise", sender:exerciseName)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showExercise" {
let vc = segue.destinationViewController as! ViewExerciseFromMenu
//vc.exerciseGet = UIImage(named: "sit_thrust")!
//vc.title = "SIT THRUSTS"
print(exerciseName)
vc.exerciseGet = exerciseName
navigationItem.title = "easy"
}
}
The adapter view
#IBOutlet var imageView: UIImageView!
#IBOutlet var musInvImageHolder: UIImageView!
var exerciseGet: String = ""
var image = UIImage()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
//var exerciseCurrent: String
print(exerciseGet)
switch(String(exerciseGet)){
case "air_sit_front_raise":
self.title = "AIR SIT FRONT RAISE"
musInvImageHolder.image = UIImage(named: "mus_inv_20")
let url = NSBundle.mainBundle().URLForResource("air_sit_front_raise", withExtension: "gif")!
let imageData = NSData(contentsOfURL: url)
imageView.image = UIImage.animatedImageWithData(imageData!)
break;
Here is the fix for it
#IBOutlet var roundExercisesTable: UITableView!
var workoutInfoData : WorkoutInfo!
var RoundLabels = ["Round 1","Round 2","Round 3","Round 4","Round 5","Round 6","Round 7","Round 8","Round 9","Round 10","Round 11","Round 12"]
var exerciseName:String? = ""
var myButtons: [Int : UIButton] = [:]
var roundPosition = [Int]()
let numberOfExercises = 1
var scrollerSize = 0
let exerciseInfo=ExercisesInfo()
override func viewDidLoad()
{
super.viewDidLoad()
//self.workoutTitle.title! = workoutInfoData.workout_name
navigationItem.title = workoutInfoData.workout_name
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func workoutBack(sender: AnyObject) {
self.dismissViewControllerAnimated(true, completion: {});
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Potentially incomplete method implementation.
// Return the number of sections.
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete method implementation.
// Return the number of rows in the section.
return RoundLabels.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
var cell : RoundWorkoutCell! = tableView.dequeueReusableCellWithIdentifier("Cell") as! RoundWorkoutCell
if(cell == nil)
{
cell = NSBundle.mainBundle().loadNibNamed("Cell", owner: self, options: nil)[0] as! RoundWorkoutCell;
}
let exerviseName = RoundLabels[indexPath.row]
if(indexPath.row == 1){
}
for countMe in 0..<self.numberOfExercises {
if(countMe<1){
roundPosition.append(5)
}else{
roundPosition.append(115+roundPosition[countMe-1])
}
scrollerSize = 115+roundPosition[countMe]
}
cell.RoundExercise_Cell_ScrollView.contentSize = CGSizeMake(CGFloat(scrollerSize),115)
cell.RoundExercise_Cell_ScrollView.showsHorizontalScrollIndicator = true
cell.RoundExercise_Cell_ScrollView.indicatorStyle = .Default
for index in 0..<self.numberOfExercises {
var imageView : UIImageView
imageView = UIImageView(frame:CGRect(x:roundPosition[index],y: 5, width:110, height: 110 ))
imageView.backgroundColor = UIColor.whiteColor()
cell.RoundExercise_Cell_ScrollView.addSubview(imageView)
let label1: UILabel = UILabel()
label1.frame = CGRect(x:roundPosition[index],y: 5, width:110, height: 20 )
label1.textColor = UIColor(red:17/255.0, green: 22/255.0, blue: 40/255.0, alpha:1.0)
label1.textAlignment = NSTextAlignment.Center
label1.font = UIFont(name: "OpenSans-CondensedLight", size: 14)
label1.text = "Pushups"
cell.RoundExercise_Cell_ScrollView.addSubview(label1)
let frame1 = CGRect(x:roundPosition[index]+10,y:25, width:90, height: 90 )
let button = UIButton(frame: frame1)
button.backgroundColor = UIColor.redColor()
button.setBackgroundImage(UIImage(named: "sit_thrust") as UIImage?, forState: .Normal)
button.setTitleColor(UIColor.blackColor(), forState: .Normal)
button.setTitle("air_sit_front_raise", forState: .Normal)
button.setTitleColor(UIColor(red:0/255,green:0/255,blue:0/255,alpha:0.0), forState: .Normal)
button.addTarget(self, action: "buttonClick:", forControlEvents: .TouchUpInside)
cell.RoundExercise_Cell_ScrollView.addSubview(button)
let label: UILabel = UILabel()
label.frame = CGRect(x:roundPosition[index],y: 115, width:110, height: 20 )
label.font = UIFont(name: "OpenSans", size: 14)
label.textColor = UIColor.whiteColor()
label.textAlignment = NSTextAlignment.Center
label.text = "30sec"
cell.RoundExercise_Cell_ScrollView.addSubview(label)
}
cell.RoundExercise_Cell_Label.text = exerviseName
return cell as RoundWorkoutCell
}
func buttonClick(sender:AnyObject){
exerciseName = sender.currentTitle
//print(sender.currentTitle)
self.performSegueWithIdentifier("showExercise", sender:exerciseName)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showExercise" {
let vc = segue.destinationViewController as! ViewExerciseFromMenu
//vc.exerciseGet = UIImage(named: "sit_thrust")!
//vc.title = "SIT THRUSTS"
print(exerciseName)
vc.exerciseGet = exerciseName!
navigationItem.title = exerciseInfo.hradnesExerciseDeterminer(exerciseName!)
}
}