Sorry about the lengthy post!
I have a UICollectionView with 2 cells (newJobCellGeneral & newJobCellTechnical).
I am writing data to Firebase from each cell using notifications. The data isn't written until a button is pressed on the second page of my UICollectionView.
When the button is pressed the notification activates the action in newJobCellGeneral that the writes data to Firebase using autoId.
What I want to do is reference the autoId created in newJobCellGeneral into my newJobCellTechnical action so when I reference the data later on I can group it all together as they will all have the same Id.
I am not sure if this is the best way to do this but it's the only way I can think it can be done as I am still new to Xcode & Firebase
Here the desired JSON setup I would like to create:
{
"jobInfo" : {
"-L61ATfMsTg1RH2T90tV" : {
"agencyName" : "Ben",
"directorName" : "Ben",
"jobBrand" : "Ben",
"jobName" : "Ben",
"prodCoName" : "Ben"
}
},
"jobTechnical" : {
"-L61ATfTZojWmlkB7paU" : {
"jobId" : "L61ATfMsTg1RH2T90tV"
"FPS" : "34",
"resolutionHeight" : "1080",
"resolutionWidth" : "1920"
}
},
"shots" : {
"-L61ATfTZojWmlkB7paV" : {
"frame_001" : {
"jobId" : "L61ATfMsTg1RH2T90tV"
"FPS" : "34",
"resolutionHeight" : "1080",
"resolutionWidth" : "1920"
},
"frame_002" : {
"jobId" : "L61ATfMsTg1RH2T90tV"
"FPS" : "34",
"resolutionHeight" : "1080",
"resolutionWidth" : "1920"
},
"frame_003" : {
"jobId" : "L61ATfMsTg1RH2T90tV"
"FPS" : "34",
"resolutionHeight" : "1080",
"resolutionWidth" : "1920"
}
}
}
}
Here is the UICollectionView:
class page_newJobSwipingController : UICollectionViewController, UICollectionViewDelegateFlowLayout, UIImagePickerControllerDelegate, UINavigationControllerDelegate, UIPickerViewDelegate, UITextFieldDelegate {
// VARIABLES
var ref:DatabaseReference?
// BOTTOM BUTTONS
private let previousButton: UIButton = {
let button = UIButton(type: .system)
button.setTitle("Previous", for: .normal)
button.titleLabel?.font = UIFont.boldSystemFont(ofSize: 14)
button.setTitleColor(.gray, for: .normal)
button.addTarget(self, action: #selector(handlePrev), for: .touchUpInside)
button.translatesAutoresizingMaskIntoConstraints = false
return button
}()
private let nextButton: UIButton = {
let button = UIButton(type: .system)
button.setTitle("Next", for: .normal)
button.titleLabel?.font = UIFont.boldSystemFont(ofSize: 14)
let pinkColour = UIColor(red: 232/255, green: 68/266, blue: 133/255, alpha: 1)
button.setTitleColor(.mainPink, for: .normal)
button.translatesAutoresizingMaskIntoConstraints = false
button.addTarget(self, action: #selector(handleNext), for: .touchUpInside)
return button
}()
// SET UP NEXT AND PREVIOUS BUTTONS TO HAVE A FUNCTION
#IBAction func handlePrev(sender : UIButton) {
let prevIndex = max(pageControl.currentPage - 1, 0)
pageControl.currentPage = prevIndex
let indexPath = IndexPath(item: prevIndex, section: 0)
collectionView?.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true)
}
#IBAction func handleNext(sender : UIButton) {
let nextIndex = pageControl.currentPage + 1
pageControl.currentPage = nextIndex
if nextIndex == 1 {
print ("move to page 2")
} else {
print ("send alert message")
NotificationCenter.default.post(name: Notification.Name("handleNewJobGeneral"), object: nil)
NotificationCenter.default.post(name: Notification.Name("handleNewJobTechnical"), object: nil)
storyboardAlert()
}
let indexPath = IndexPath(item: 1, section: 0)
collectionView?.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true)
}
// HANDLE UPLOAD STORYBOARD OPTIONS
#IBAction func storyboardAlert() {
let imagePickerController = UIImagePickerController()
imagePickerController.delegate = self
// ACTION SHEET FOR ADDING NEW ATTACHMENT
let alert = UIAlertController(title: "New job is about to be created", message: "Do you want to upload storyboard cells now?", preferredStyle: .actionSheet)
alert.addAction(UIAlertAction(title: "Now", style: .default, handler: { (action:UIAlertAction) in
let storyboardUpload = page_newJobStoryboardUpload()
self.show(storyboardUpload, sender: self)
}))
alert.addAction(UIAlertAction(title: "Later", style: .default, handler: { (action:UIAlertAction) in
let jobList = page_jobList()
self.show(jobList, sender: self)
}))
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
self.present(alert, animated: true, completion: nil)
}
// PAGE CONTROL
private let pageControl: UIPageControl = {
let pc = UIPageControl()
pc.numberOfPages = 2
pc.currentPageIndicatorTintColor = .mainPink
pc.pageIndicatorTintColor = UIColor(red: 249/255, green: 207/266, blue: 224/255, alpha: 1)
return pc
}()
override func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
let x = targetContentOffset.pointee.x
pageControl.currentPage = Int(x / view.frame.width)
}
// CONSTRAINTS OF BOTTOM CONTROLS
fileprivate func setupBottomControls(){
let bottomControlsStackView = UIStackView(arrangedSubviews: [previousButton, pageControl, nextButton])
bottomControlsStackView.translatesAutoresizingMaskIntoConstraints = false
bottomControlsStackView.distribution = .fillEqually
view.addSubview(bottomControlsStackView)
NSLayoutConstraint.activate([
bottomControlsStackView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
bottomControlsStackView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
bottomControlsStackView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
bottomControlsStackView.heightAnchor.constraint(equalToConstant: 50)
])
}
// SUPER VIEW DID LOAD
override func viewDidLoad() {
super.viewDidLoad()
collectionView?.backgroundColor = .white
collectionView?.register(newJobCellGeneral.self, forCellWithReuseIdentifier: "newJobCellGeneral")
collectionView?.register(newJobCellTechnical.self, forCellWithReuseIdentifier: "newJobCellTechnical")
collectionView?.isPagingEnabled = true
setupBottomControls()
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 2
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if indexPath.item == 0 {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "newJobCellGeneral", for: indexPath) as! newJobCellGeneral
navigationItem.title = "General Info"
return cell
} else {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "newJobCellTechnical", for: indexPath) as! newJobCellTechnical
navigationItem.title = "Technical Specs"
return cell
}
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: view.frame.width, height: view.frame.height)
}
}
Here is the newJobCellGeneral init (there are other things in here, just wanted to show the notification) and action (I wont upload the rest of the code because it's quite long - unless its required):
override init(frame: CGRect) {
super.init(frame: frame)
NotificationCenter.default.addObserver(self, selector: #selector(handleNewJobGeneral), name: NSNotification.Name(rawValue: "handleNewJobGeneral"), object: nil)
}
// HANDLE NEW JOB GENERAL
#objc func handleNewJobGeneral(){
let newJobBrand = jobBrand.text!
let newJobName = jobName.text!
let newDirectorName = directorName.text!
let newAgencyName = agencyName.text!
let newProdCoName = prodCoName.text!
// WHERE TO PUT IN DATABASE
let jobInfoReference = Database.database().reference().child("jobInfo")
let jobInfoChildRef = jobInfoReference.childByAutoId()
// REFERENCING DICTIONARY
let jobBrandValue = ["jobBrand": newJobBrand]
let jobNameValue = ["jobName": newJobName]
let jobDirectorValue = ["directorName": newDirectorName]
let jobAgencyNameValue = ["agencyName": newAgencyName]
let jobProdCoValue = ["prodCoName": newProdCoName]
// WRITE TO DATABASE
jobInfoChildRef.updateChildValues(jobBrandValue)
jobInfoChildRef.updateChildValues(jobNameValue)
jobInfoChildRef.updateChildValues(jobDirectorValue)
jobInfoChildRef.updateChildValues(jobAgencyNameValue)
jobInfoChildRef.updateChildValues(jobProdCoValue)
}
Here is the newJobCellGeneral init (there are other things in here, just wanted to show the notification) and action (I wont upload the rest of the code because it's quite long - unless its required):
override init(frame: CGRect) {
super.init(frame: frame)
NotificationCenter.default.addObserver(self, selector: #selector(handleNewJobTechnical), name: NSNotification.Name(rawValue: "handleNewJobTechnical"), object: nil)
}
#IBAction func handleNewJobTechnical(){
//let numberOfShootDaysAmount = numberOfShootDays.text!
let FPS = FPSamount.text!
let resolutionWidth = resolutionWidthAmount.text!
let resolutionHeight = resolutionHeightAmount.text!
let shotCount = shotAmount.text!
// WHERE TO PUT IN DATABASE
let jobTechnicalReference = Database.database().reference().child("jobTechnical")
let jobTechnicalChildRef = jobTechnicalReference.childByAutoId()
let shotListReference = Database.database().reference().child("shots")
let shotListChildRef = shotListReference.childByAutoId()
// REFERENCING DICTIONARY
let FPSValue = ["FPS": FPS]
let resolutionWidthValue = ["resolutionWidth": resolutionWidth]
let resolutionHeightValue = ["resolutionHeight": resolutionHeight]
// ADD TO TECHNICAL JOB
jobTechnicalChildRef.updateChildValues(FPSValue)
jobTechnicalChildRef.updateChildValues(resolutionWidthValue)
jobTechnicalChildRef.updateChildValues(resolutionHeightValue)
// FOR LOOP VARIABLES
let shotCountTotal:Int = (Int(shotCount)! + 1)
for i in 1 ..< shotCountTotal {
let frame = "frame_"
let number = (String(format: "%03d", (i)))
let frameNumber = (frame + number)
let frameListChildRef = shotListChildRef.child(frameNumber)
// WRITE TO FRAMES DATABASE
frameListChildRef.updateChildValues(FPSValue)
frameListChildRef.updateChildValues(resolutionWidthValue)
frameListChildRef.updateChildValues(resolutionHeightValue)
}
}
Thank you for anyone who can help me get my head around this!
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)
}
I have a uiCollectionViewCell which loads image from an api. I want to display another image/icon on the cell when a user clicks on it. In my custom cell I have two images one which display the image from the URL and the second one is the one I would like to show if the user has clicked on it. I'm doing this to alert the user that they have selected that cell. Below is my sample code
protocol ModalDelegate {
func changeValue(userChoice: String, rateMovieID: String, rateImageUrl: String, title: String)
}
class GuestRateMovieView: UIViewController, ModalDelegate {
func changeValue(userChoice: String, rateMovieID: String, rateImageUrl: String, title: String) {
self.userChoice = userChoice
totalRated = totalRated + 1
lblRated.text = "\(totalRated) rated"
if totalRated > 0 {
ratedView.backgroundColor = .ratedGoldColour
}else{
ratedView.backgroundColor = .white
}
if totalRated >= 5 {
btnFloatNext.alpha = 1
}
if totalRated > 5 {
userChoiceMovieImage.sd_setImage(with: URL(string: rateImageUrl), placeholderImage: UIImage(named: "ImagePlaceholder"))
lblUserChoice.text = "Great taste. We love the \(title) too."
}
var rating = 1
if userChoice == "Hate it"{
rating = 1
}else if userChoice == "Good" {
rating = 3
}else{
rating = 5
}
let guestRatingValues = GuestUserRate(id: rateMovieID, imageUrl: rateImageUrl, userRate: rating)
GuestRateMovieView.createUserRating(guestRatingValues) =>", rateImageUrl)
print("Received on movie", totalRated)
}
func getMovieDetails(){
activityLoader.displayActivityLoader(image: activityLoader, view: activityLoaderView)
let urlString = "https://t2fmmm2hfg.execute-api.eu-west-2.amazonaws.com/mobile/media/onboarding-items"
let url = URL(string: urlString)!
var request = URLRequest(url: url)
request.setValue("application/json", forHTTPHeaderField: "Accept")
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
let postParameters: Dictionary<String, Any> = [
"category": "tv"
]
if let postData = (try? JSONSerialization.data(withJSONObject: postParameters, options: JSONSerialization.WritingOptions.prettyPrinted)){
request.httpBody = postData
let task = URLSession.shared.dataTask(with: request) { [weak self] data, response, error in
guard let data = data, error == nil else {
return
}
do {
print("got data")
let jsonResult = try JSONDecoder().decode([Responder].self, from: data)
DispatchQueue.main.async {
self?.movieObj = jsonResult
self?.moviesCollectionView.reloadData()
self?.activityLoader.removeActivityLoader(image: self!.activityLoader, view: self!.activityLoaderView)
}
// jsonResult.forEach { course in print(course.type) }
}catch {
print(error)
}
}
task.resume()
}
}
var movieObj: [Responder] = []
override func viewDidLoad() {
super.viewDidLoad()
getMovieDetails()
}
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return movieObj.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reUseMoviesCellID, for: indexPath) as! MoviesCollectionCell
cell.movieImage.image = nil
cell.configure(with: movieObj[indexPath.row].packShot?.thumbnail ?? ""
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let modalVC = RateSingleMovieView()
modalVC.movieID = movieObj[indexPath.row].id
modalVC.movieTitle = movieObj[indexPath.row].title
modalVC.movieImageURL = movieObj[indexPath.row].packShot?.thumbnail ?? ""
modalVC.delegate = self
modalVC.modalPresentationStyle = .overCurrentContext
modalVC.modalTransitionStyle = .crossDissolve
present(modalVC, animated: true, completion: nil)
}
class MoviesCollectionCell: UICollectionViewCell {
private var movieImages = NSCache<NSString, NSData>()
weak var textLabel: UILabel!
let movieImage: UIImageView = {
let image = UIImageView()
image.translatesAutoresizingMaskIntoConstraints = false
image.clipsToBounds = true
image.contentMode = .scaleAspectFill
image.layer.cornerRadius = 10
return image
}()
let btnRate: UIImageView = {
let image = UIImageView()
image.translatesAutoresizingMaskIntoConstraints = false
image.clipsToBounds = true
image.contentMode = .scaleAspectFit
image.alpha = 0
return image
}()
override init(frame: CGRect) {
super.init(frame: frame)
contentView.addSubview(movieImage)
movieImage.addSubview(btnRate)
NSLayoutConstraint.activate([
movieImage.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 10),
movieImage.topAnchor.constraint(equalTo: contentView.topAnchor),
movieImage.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -10),
movieImage.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
btnRate.centerXAnchor.constraint(equalTo: movieImage.centerXAnchor),
btnRate.centerYAnchor.constraint(equalTo: movieImage.centerYAnchor),
btnRate.widthAnchor.constraint(equalToConstant: 30),
btnRate.heightAnchor.constraint(equalToConstant: 30)
])
btnRate.tintColor = .white
btnRate.layer.shadowColor = UIColor.black.cgColor
btnRate.layer.shadowOffset = CGSize(width: 1.0, height: 2.0)
btnRate.layer.shadowRadius = 2
btnRate.layer.shadowOpacity = 0.8
btnRate.layer.masksToBounds = false
}
override func prepareForReuse() {
super.prepareForReuse()
movieImage.image = nil
btnRate.image = nil
}
func configure(with urlString: String, ratingObj: MovieRating){
movieImage.sd_setImage(with: URL(string: urlString), placeholderImage: UIImage(named: "ImagePlaceholder"))
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Now in my modal where the user will rate. In my case I'm using swipe gesture for the rating
class RateSingleMovieView: UIViewController, ModalDelegate, ModalSearchDelegate {
func changeValue(userChoice: String, rateMovieID: String, rateImageUrl: String, title: String) {
self.userChoice = userChoice
totalRated = totalRated + 1
}
var delegate: ModalDelegate?
override func viewDidLoad() {
super.viewDidLoad()
redBottomView.addGestureRecognizer(createSwipeGestureRecognizer(for: .up))redBottomView.addGestureRecognizer(createSwipeGestureRecognizer(for: .left))redBottomView.addGestureRecognizer(createSwipeGestureRecognizer(for: .right))
}
#objc private func didSwipe(_ sender: UISwipeGestureRecognizer) {
switch sender.direction {
case .up:
showUserRatingSelection(userChoice: "Good")
case .left:
showUserRatingSelection(userChoice: "Hate it")
case .right:
showUserRatingSelection(userChoice: "Love it")
default:
break
}
}
#objc private func removeModal(){
dismiss(animated: true, completion: nil)
}
private func showUserRatingSelection(userChoice: String){
self.hateItView.alpha = 1
if userChoice == "Hate it"{
userChoiceEmoji.image = UIImage(named: "HateIt")
lblRate.text = "Hate it"
}else if userChoice == "Good" {
userChoiceEmoji.image = UIImage(named: "goodRate")
lblRate.text = "Good"
}else{
userChoiceEmoji.image = UIImage(named: "LoveIt")
lblRate.text = "Love it"
}
userChoiceEmoji.alpha = 1
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
print("hello ", userChoice)
self.delegate?.changeValue(userChoice: userChoice, rateMovieID: self.movieID!, rateImageUrl: self.movieImageURL!, title: self.movieTitle!)
self.removeModal()
}
}
}
I am able to use the delegate here to send info back to GuestRateMovieView Controller and update a label there. Now my only problem is displaying the icon on the selected cell with the user choice.
First note... setup your constraints in init -- Absolutely NOT in layoutSubviews().
Edit -- forget everything else previously here, because it had nothing to do with what you're actually trying to accomplish.
New Answer
To clarify your goal:
display a collection view of objects - in this case, movies
when the user selects a cell, show a "Rate This Movie" view
when the user selects a Rating (hate, good, love), save that rating and update the cell with a "Rating Image"
So, the first thing you need is a data structure that includes a "rating" value. Let's use an enum for the rating itself:
enum MovieRating: Int {
case none, hate, good, love
}
Then we might have a "Movie Object" like this:
struct MovieObject {
var title: String = ""
var urlString: String = ""
var rating: MovieRating = .none
// maybe some other properties
}
For our data, we'll have an Array of MovieObject. When we configure each cell (in cellForItemAt), we need to set the Movie Image and the Rating Image.
So, your cell class may have this:
func configure(with movieObj: MovieObject) {
movieImage.sd_setImage(with: URL(string: movieObj.urlString), placeholderImage: UIImage(named: "ImagePlaceholder"))
switch movieObj.rating {
case .hate:
if let img = UIImage(systemName: "hand.thumbsdown") {
btnRate.image = img
}
case .good:
if let img = UIImage(systemName: "face.smiling") {
btnRate.image = img
}
case .love:
if let img = UIImage(systemName: "hand.thumbsup") {
btnRate.image = img
}
default:
btnRate.image = nil
}
}
and your cellForItemAt would look like this:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let c = collectionView.dequeueReusableCell(withReuseIdentifier: "c", for: indexPath) as! MoviesCollectionCell
c.configure(with: moviesArray[indexPath.row])
return c
}
When the user selects a cell, we can present a "Rate This Movie" view controller - which will have buttons for Hate / Good / Love.
If the user taps one of those buttons, we can use a closure to update the data and reload that cell:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let vc = RateTheMovieVC()
vc.movieObj = moviesArray[indexPath.item]
vc.callback = { [weak self] rating in
guard let self = self else { return }
// update the data
self.moviesArray[indexPath.item].rating = rating
// reload the cell
self.collectionView.reloadItems(at: [indexPath])
// dismiss the RateTheMovie view controller
self.dismiss(animated: true)
}
// present the RateTheMovie view controller
present(vc, animated: true)
}
Here's a complete example... I don't have your data (movie names, images, etc), so we'll use an array of "Movie Titles" from A to Z, and the cells will look like this:
and so on.
enum and struct
enum MovieRating: Int {
case none, hate, good, love
}
struct MovieObject {
var title: String = ""
var urlString: String = ""
var rating: MovieRating = .none
}
collection view cell
class MoviesCollectionCell: UICollectionViewCell {
let movieImage: UIImageView = {
let image = UIImageView()
image.translatesAutoresizingMaskIntoConstraints = false
image.clipsToBounds = true
image.contentMode = .scaleAspectFill
image.layer.cornerRadius = 10
image.backgroundColor = .blue
return image
}()
let btnRate: UIImageView = {
let image = UIImageView()
image.translatesAutoresizingMaskIntoConstraints = false
image.clipsToBounds = true
image.contentMode = .scaleAspectFit
return image
}()
// we don't have Movie Images for this example, so
// we'll use some labels for the Movie Title
var labels: [UILabel] = []
override init(frame: CGRect) {
super.init(frame: frame)
contentView.addSubview(movieImage)
for _ in 0..<4 {
let v = UILabel()
v.translatesAutoresizingMaskIntoConstraints = false
v.textAlignment = .center
v.textColor = .cyan
if let f = UIFont(name: "TimesNewRomanPS-BoldMT", size: 60) {
v.font = f
}
contentView.addSubview(v)
labels.append(v)
}
// stack views for the labels
let stTop = UIStackView()
stTop.axis = .horizontal
stTop.distribution = .fillEqually
stTop.addArrangedSubview(labels[0])
stTop.addArrangedSubview(labels[1])
let stBot = UIStackView()
stBot.axis = .horizontal
stBot.distribution = .fillEqually
stBot.addArrangedSubview(labels[2])
stBot.addArrangedSubview(labels[3])
let st = UIStackView()
st.axis = .vertical
st.distribution = .fillEqually
st.addArrangedSubview(stTop)
st.addArrangedSubview(stBot)
st.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(st)
contentView.addSubview(btnRate)
// setup constriaints here
NSLayoutConstraint.activate([
movieImage.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 10),
movieImage.topAnchor.constraint(equalTo: contentView.topAnchor),
movieImage.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -10),
movieImage.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
st.topAnchor.constraint(equalTo: movieImage.topAnchor),
st.leadingAnchor.constraint(equalTo: movieImage.leadingAnchor),
st.trailingAnchor.constraint(equalTo: movieImage.trailingAnchor),
st.bottomAnchor.constraint(equalTo: movieImage.bottomAnchor),
btnRate.centerXAnchor.constraint(equalTo: movieImage.centerXAnchor),
btnRate.centerYAnchor.constraint(equalTo: movieImage.centerYAnchor),
btnRate.widthAnchor.constraint(equalToConstant: 40),
btnRate.heightAnchor.constraint(equalToConstant: 40)
])
btnRate.tintColor = .white
btnRate.layer.shadowColor = UIColor.black.cgColor
btnRate.layer.shadowOffset = CGSize(width: 1.0, height: 2.0)
btnRate.layer.shadowRadius = 2
btnRate.layer.shadowOpacity = 0.8
btnRate.layer.masksToBounds = false
}
override func prepareForReuse() {
super.prepareForReuse()
movieImage.image = nil
}
func configure(with movieObj: MovieObject) {
// I don't have your cell images, or the "sd_setImage" function
// un-comment the next line to set your images
// movieImage.sd_setImage(with: URL(string: movieObj.urlString), placeholderImage: UIImage(named: "ImagePlaceholder"))
labels.forEach { v in
v.text = movieObj.title
}
switch movieObj.rating {
case .hate:
if let img = UIImage(systemName: "hand.thumbsdown") {
btnRate.image = img
}
case .good:
if let img = UIImage(systemName: "face.smiling") {
btnRate.image = img
}
case .love:
if let img = UIImage(systemName: "hand.thumbsup") {
btnRate.image = img
}
default:
btnRate.image = nil
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
example view controller
class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
var collectionView: UICollectionView!
var moviesArray: [MovieObject] = []
override func viewDidLoad() {
super.viewDidLoad()
let fl = UICollectionViewFlowLayout()
fl.itemSize = CGSize(width: 100.0, height: 200.0)
fl.scrollDirection = .vertical
fl.minimumLineSpacing = 8
fl.minimumInteritemSpacing = 8
collectionView = UICollectionView(frame: .zero, collectionViewLayout: fl)
collectionView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(collectionView)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
collectionView.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
collectionView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
collectionView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
collectionView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -20.0),
])
collectionView.register(MoviesCollectionCell.self, forCellWithReuseIdentifier: "c")
collectionView.dataSource = self
collectionView.delegate = self
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// let's change the collection view cell size to fit
// two "columns"
if let fl = collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
fl.itemSize = CGSize(width: (collectionView.frame.width - fl.minimumInteritemSpacing) * 0.5, height: 200.0)
}
simulateGettingData()
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return moviesArray.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let c = collectionView.dequeueReusableCell(withReuseIdentifier: "c", for: indexPath) as! MoviesCollectionCell
c.configure(with: moviesArray[indexPath.row])
return c
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let vc = RateTheMovieVC()
vc.movieObj = moviesArray[indexPath.item]
vc.callback = { [weak self] rating in
guard let self = self else { return }
// update the data
self.moviesArray[indexPath.item].rating = rating
// reload the cell
self.collectionView.reloadItems(at: [indexPath])
// dismiss the RateTheMovie view controller
self.dismiss(animated: true)
}
// present the RateTheMovie view controller
present(vc, animated: true)
}
func simulateGettingData() {
// let's just create an array of MovieObject
// where each Title will be a letter from A to Z
"ABCDEFGHIJKLMNOPQRSTUVWXYZ".forEach { c in
let m = MovieObject(title: String(c), urlString: "", rating: .none)
moviesArray.append(m)
}
collectionView.reloadData()
}
}
example "Rate The Movie" view controller
class RateTheMovieVC: UIViewController {
// this will be used to tell the presenting controller
// that a rating button was selected
var callback: ((MovieRating) -> ())?
var movieObj: MovieObject!
let movieImage: UIImageView = {
let image = UIImageView()
image.translatesAutoresizingMaskIntoConstraints = false
image.clipsToBounds = true
image.contentMode = .scaleAspectFill
image.layer.cornerRadius = 10
image.backgroundColor = .systemBlue
return image
}()
// we don't have Movie Images for this example, so
// we'll use a label for the "Movie Title"
let titleLabel: UILabel = {
let v = UILabel()
v.translatesAutoresizingMaskIntoConstraints = false
v.numberOfLines = 0
v.textAlignment = .center
v.textColor = .yellow
v.font = .systemFont(ofSize: 240, weight: .bold)
return v
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .lightGray
// let's add 3 "rate" buttons near the bottom
let btnHate = UIButton()
let btnGood = UIButton()
let btnLove = UIButton()
let btns: [UIButton] = [btnHate, btnGood, btnLove]
let names: [String] = ["hand.thumbsdown", "face.smiling", "hand.thumbsup"]
for (b, s) in zip(btns, names) {
b.backgroundColor = .systemRed
b.layer.cornerRadius = 8
b.layer.masksToBounds = true
if let img = UIImage(systemName: s, withConfiguration: UIImage.SymbolConfiguration(pointSize: 32)) {
b.setImage(img, for: [])
}
b.tintColor = .white
b.heightAnchor.constraint(equalToConstant: 60.0).isActive = true
}
let btnStack = UIStackView()
btnStack.spacing = 20
btnStack.distribution = .fillEqually
btns.forEach { b in
btnStack.addArrangedSubview(b)
}
view.addSubview(movieImage)
view.addSubview(titleLabel)
btnStack.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(btnStack)
// setup constriaints here
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
btnStack.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
btnStack.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
btnStack.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -20.0),
titleLabel.topAnchor.constraint(equalTo: movieImage.topAnchor, constant: 8.0),
titleLabel.leadingAnchor.constraint(equalTo: movieImage.leadingAnchor, constant: 12.0),
titleLabel.trailingAnchor.constraint(equalTo: movieImage.trailingAnchor, constant: -12.0),
titleLabel.bottomAnchor.constraint(equalTo: movieImage.bottomAnchor, constant: -8.0),
movieImage.topAnchor.constraint(equalTo: g.topAnchor, constant: 40.0),
movieImage.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40.0),
movieImage.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -40.0),
movieImage.bottomAnchor.constraint(equalTo: btnStack.topAnchor, constant: -20.0),
])
// here we would set the movie image
// set the title label text, since we don't have images right now
titleLabel.text = movieObj.title
btnHate.addTarget(self, action: #selector(hateTap(_:)), for: .touchUpInside)
btnGood.addTarget(self, action: #selector(goodTap(_:)), for: .touchUpInside)
btnLove.addTarget(self, action: #selector(loveTap(_:)), for: .touchUpInside)
}
#objc func hateTap(_ sender: UIButton) {
callback?(.hate)
}
#objc func goodTap(_ sender: UIButton) {
callback?(.good)
}
#objc func loveTap(_ sender: UIButton) {
callback?(.love)
}
}
It will look like this on launch:
Then we select the first cell and we see this:
We select the "Thumbs Up" button, and we see this:
Then scroll down and select-and-rate a few other cells:
You mention in a comment a "cache DB" ... assuming that will be persistent data, it's up to you to store the user-selected Rating.
in your MoviesCollectionCell file put function like this
func loadImageAfterClickingCell() {
// TODO: check this cell is already clicked and already download the image like if yourImageView == nil or not nil so you can add guard like guard yourImageView.image == nil else { return } similar to this
guard let preparedUrl = URL(string: urlString) else { return }
yourImageView.alpha = 1
yourImageView.sd_setImage(with: preparedUrl, placeholderImage: UIImage(named: "ImagePlaceholder"))
}
And after that in didSelectItemAt
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reUseMoviesCellID, for: indexPath) as! MoviesCollectionCell
cell.loadImageAfterClickingCell()
}
A simple method injection like this must save you as you want.
I have been at this for some time now. I can not get my tableView to appear. I think it has something to do with the fact that it is being presented as didMove(toParent)
I am trying to create a page that allows you to add a new Card to the profile. Every time I write it programmatically or use storyboard it crashes as it Unexpectedly found nil while implicitly unwrapping an Optional value.
Here is the view Controller that is presenting the Side Menu
import Foundation
import SideMenu
import FirebaseAuth
import UIKit
import CoreLocation
import SwiftUI
class BeginViewController: UIViewController, MenuControllerDelegate, CLLocationManagerDelegate {
private var sideMenu: SideMenuNavigationController?
struct customData {
var title: String
var image: UIImage
}
let data = [
customData(title: "NottingHill", image: #imageLiteral(resourceName: "norali-nayla-SAhImiWmFaw-unsplash")),
customData(title: "Southall", image: #imageLiteral(resourceName: "alistair-macrobert-8wMflrTLm2g-unsplash")),
customData(title: "Tower Hill", image: #imageLiteral(resourceName: "peregrine-communications-0OLnnZWg860-unsplash")),
customData(title: "Mansion House", image: #imageLiteral(resourceName: "adam-birkett-cndNklOnHO4-unsplash")),
customData(title: "Westminster", image: #imageLiteral(resourceName: "simon-mumenthaler-NykjYbCW6Z0-unsplash")),
customData(title: "London Bridge", image: #imageLiteral(resourceName: "hert-niks-CjouXgWrTRk-unsplash"))
]
struct Constants {
static let cornerRadius: CGFloat = 15.0 }
let manager = CLLocationManager()
private let ProfileController = ProfileViewController()
private let MyBookingsController = MyBookingsViewController()
private let WalletController = WalletViewController()
private let FAQController = FAQViewController()
private let SettingsController = SettingsViewController()
#IBOutlet weak var StoreButton: UIButton!
#IBOutlet weak var DeliverButton: UIButton!
#IBOutlet weak var AirportButton: UIButton!
#IBOutlet weak var HotelsButton: UIButton!
fileprivate let collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.translatesAutoresizingMaskIntoConstraints = false
cv.register(customCell.self, forCellWithReuseIdentifier: "cell")
return cv
}()
override func viewDidLoad() {
super.viewDidLoad()
// buttons
StoreButton.layer.cornerRadius = Constants.cornerRadius
StoreButton.layer.shadowOffset = .zero
StoreButton.layer.shadowOpacity = 0.3
StoreButton.layer.shadowColor = UIColor.black.cgColor
StoreButton.layer.shadowRadius = 5
DeliverButton.layer.cornerRadius = Constants.cornerRadius
DeliverButton.layer.shadowOffset = .zero
DeliverButton.layer.shadowOpacity = 0.3
DeliverButton.layer.shadowColor = UIColor.black.cgColor
DeliverButton.layer.shadowRadius = 5
AirportButton.layer.cornerRadius = Constants.cornerRadius
AirportButton.layer.shadowOffset = .zero
AirportButton.layer.shadowOpacity = 0.3
AirportButton.layer.shadowColor = UIColor.black.cgColor
AirportButton.layer.shadowRadius = 5
HotelsButton.layer.cornerRadius = Constants.cornerRadius
HotelsButton.layer.shadowOffset = .zero
HotelsButton.layer.shadowOpacity = 0.3
HotelsButton.layer.shadowColor = UIColor.black.cgColor
HotelsButton.layer.shadowRadius = 5
//CollectionViewNearbyPlaces
view.addSubview(collectionView)
collectionView.backgroundColor = .clear
collectionView.topAnchor.constraint(equalTo: view.topAnchor, constant: 450).isActive = true
collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 25).isActive = true
collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 0).isActive = true
collectionView.heightAnchor.constraint(equalTo: collectionView.widthAnchor, multiplier: 0.5).isActive = true
collectionView.delegate = self
collectionView.dataSource = self
// title
title = "handl"
//background
let menu = MenuController(with: [ "Home", "Profile", "My Bookings",
"Wallet",
"FAQ","Settings"])
menu.delegate = self
sideMenu = SideMenuNavigationController(rootViewController: menu)
sideMenu?.leftSide = true
sideMenu?.setNavigationBarHidden(true, animated: false)
SideMenuManager.default.leftMenuNavigationController = sideMenu
SideMenuManager.default.addPanGestureToPresent(toView: view)
addChildControllers()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.delegate = self
manager.requestWhenInUseAuthorization()
manager.startUpdatingLocation()
handleNotAuthenticated()
}
private func addChildControllers() {
addChild(ProfileController)
addChild(MyBookingsController)
addChild(WalletController)
addChild(FAQController)
addChild(SettingsController)
view.addSubview(ProfileController.view)
view.addSubview(MyBookingsController.view)
view.addSubview(WalletController.view)
view.addSubview(FAQController.view)
view.addSubview(SettingsController.view)
ProfileController.view.frame = view.bounds
MyBookingsController.view.frame = view.bounds
WalletController.view.frame = view.bounds
FAQController.view.frame = view.bounds
SettingsController.view.frame = view.bounds
ProfileController.didMove(toParent: self)
MyBookingsController.didMove(toParent: self)
WalletController.didMove(toParent: self)
FAQController.didMove(toParent: self)
SettingsController.didMove(toParent: self)
ProfileController.view.isHidden = true
MyBookingsController.view.isHidden = true
WalletController.view.isHidden = true
FAQController.view.isHidden = true
SettingsController.view.isHidden = true
}
#IBAction func SideMenuButton(_ sender: Any) {
present(sideMenu!, animated: true)
}
func didSelectMenuItem(named: String) {
sideMenu?.dismiss(animated: true, completion: { [weak self] in
self?.title = named
if named == "Home" {
self?.ProfileController.view.isHidden = true
self?.MyBookingsController.view.isHidden = true
self?.WalletController.view.isHidden = true
self?.FAQController.view.isHidden = true
self?.SettingsController.view.isHidden = true
}
if named == "Profile" {
self?.ProfileController.view.isHidden = false
self?.MyBookingsController.view.isHidden = true
self?.WalletController.view.isHidden = true
self?.FAQController.view.isHidden = true
self?.SettingsController.view.isHidden = true
}
if named == "My Bookings" {
self?.ProfileController.view.isHidden = true
self?.MyBookingsController.view.isHidden = false
self?.WalletController.view.isHidden = true
self?.FAQController.view.isHidden = true
self?.SettingsController.view.isHidden = true
}
else if named == "Wallet" {
self?.ProfileController.view.isHidden = true
self?.MyBookingsController.view.isHidden = true
self?.WalletController.view.isHidden = false
self?.FAQController.view.isHidden = true
self?.SettingsController.view.isHidden = true
}
else if named == "FAQ" {
self?.ProfileController.view.isHidden = true
self?.MyBookingsController.view.isHidden = true
self?.WalletController.view.isHidden = true
self?.FAQController.view.isHidden = false
self?.SettingsController.view.isHidden = true
}
else if named == "Settings" {
self?.ProfileController.view.isHidden = true
self?.MyBookingsController.view.isHidden = true
self?.WalletController.view.isHidden = true
self?.FAQController.view.isHidden = true
self?.SettingsController.view.isHidden = false
}
})
}
}
extension BeginViewController: UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collectionView.frame.width/2.5, height: collectionView.frame.width/2)
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return data.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! customCell
cell.data = self.data[indexPath.row]
return cell
}
class customCell: UICollectionViewCell {
var data: customData? {
didSet {
guard let data = data else { return }
bg.image = data.image
}
}
fileprivate let bg: UIImageView = {
let iv = UIImageView()
iv.image = #imageLiteral(resourceName: "adam-birkett-cndNklOnHO4-unsplash")
iv.translatesAutoresizingMaskIntoConstraints = false
iv.contentMode = .scaleAspectFill
iv.layer.shadowColor = UIColor.black.cgColor
iv.layer.shadowOpacity = 1
iv.layer.shadowOffset = CGSize.zero
iv.layer.shadowRadius = 10
iv.layer.shadowPath = UIBezierPath(rect: iv.bounds).cgPath
iv.layer.shouldRasterize = false
iv.layer.cornerRadius = 10
iv.clipsToBounds = true
return iv
}()
override init(frame: CGRect) {
super.init(frame: frame)
contentView.addSubview(bg)
bg.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true
bg.leadingAnchor.constraint(equalTo: contentView.leadingAnchor).isActive = true
bg.trailingAnchor.constraint(equalTo: contentView.trailingAnchor).isActive = true
bg.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
//if user is not logged in show login
private func handleNotAuthenticated() {
//check auth status
if Auth.auth().currentUser == nil {
//show log in screen
let loginVC = LoginViewController()
loginVC.modalPresentationStyle = .fullScreen
present(loginVC, animated: false)
}
}
}
and Here is the viewController I am trying to present a tableView on. It comes up with a white screen, but no tableView. Nor are my navigation items showing. Even when written programmatically.
import UIKit
class WalletViewController: UIViewController {
var addNewCard = [String]()
let button = UIButton()
let tableView = UITableView()
// MARK: - Properties
override func viewDidLoad() {
super.viewDidLoad()
addTable()
view.backgroundColor = UIColor(named: "RED")
button.setTitle("Add New Card", for: .normal)
view.addSubview(button)
button.backgroundColor = UIColor(named: "yellow-2")
button.setTitleColor(UIColor(named: "RED"), for: .normal)
button.frame = CGRect(x: 25, y: 700, width: 350, height: 50)
button.layer.cornerRadius = 15
button.addTarget(self, action: #selector(didTapAddButton), for: .touchUpInside)
if !UserDefaults().bool(forKey: "setup") {
UserDefaults().set(true, forKey: "setup")
UserDefaults().set(0, forKey: "count")
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Add New Card", style: .plain, target: self,
action: #selector(didTapAdd))
}
}
func updateCard() {
guard let count = UserDefaults().value(forKey: "count") as? Int else {
return
}
for x in 0..<count {
if let addCard = UserDefaults().value(forKey: "addCard\(x+1)") as? String {
addNewCard.append(addCard)
}
}
}
func addTable() {
tableView.frame = view.bounds
tableView.delegate = self
tableView.dataSource = self
tableView.separatorStyle = .singleLine
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "addCard")
self.view.addSubview(tableView)
}
#IBAction func didTapAdd() {
let vc = storyboard?.instantiateViewController(withIdentifier: "addCard") as! addCardViewController
vc.update = {
DispatchQueue.main.async {
self.updateCard()
}
}
navigationController?.pushViewController(vc, animated: true)
}
#objc private func didTapAddButton() {
let rootVC = addCardViewController()
let navVC = UINavigationController(rootViewController: rootVC)
navVC.modalPresentationStyle = .fullScreen
present(navVC, animated: true)
}
}
extension WalletViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
}
}
extension WalletViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let add = tableView.dequeueReusableCell(withIdentifier: "addCard", for: indexPath)
add.textLabel?.text = addNewCard[indexPath.row]
return add
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return addNewCard.count
}
}
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 )
}
}
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
}