PerformSegue from long press on a a custom UiTableViewCell - swift

maybe the question has been asked several times, but can't find it.
I'm a newbie.
I have a UITableView with a custom UITableViewCell.
in the cell there are 3 different labels.
I added the gesture recognizer on the custom cell, so that if long press is done on a label :
- label 1 should show another UiViewController
- label 2 should do a call
- label 3 should create a mail
for labels 2 and 3 i had no problem
but how to perform the segue to open the view controller ?
This is the storyboard
This is the custom tableviewcell
import UIKit
import MessageUI
class OfficeCell: UITableViewCell, MFMailComposeViewControllerDelegate {
#IBOutlet weak var lbOffice: UILabel!
#IBOutlet weak var lbAddress: UILabel!
#IBOutlet weak var lbPhone: UILabel!
#IBOutlet weak var lbMail: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
self.setupLabelTap()
// Configure the view for the selected state
}
func setupLabelTap() {
let lbAddressTap = UILongPressGestureRecognizer(target: self, action: #selector(self.longPressReconizer(_:)))
self.lbAddress.isUserInteractionEnabled = true
self.lbAddress.addGestureRecognizer(lbAddressTap)
let lbAddressTap2 = UILongPressGestureRecognizer(target: self, action: #selector(self.longPressReconizer(_:)))
self.lbMail.isUserInteractionEnabled = true
self.lbMail.addGestureRecognizer(lbAddressTap2)
let lbAddressTap3 = UILongPressGestureRecognizer(target: self, action: #selector(self.longPressReconizer(_:)))
self.lbPhone.isUserInteractionEnabled = true
self.lbPhone.addGestureRecognizer(lbAddressTap3)
}
#objc func longPressReconizer(_ sender: UITapGestureRecognizer) {
print("labelTapped")
let etichetta :UILabel = sender.view as! UILabel
print (etichetta.text!)
switch etichetta.tag {
case 0:
self.performSegue(withIdentifier: "moveToMaps", sender: self)
case 1:
let telNumber = etichetta.text!.replacingOccurrences(of: " ", with: "")
if let phoneCallURL = URL(string: "telprompt://\(telNumber)") {
let application:UIApplication = UIApplication.shared
if (application.canOpenURL(phoneCallURL)) {
application.open(phoneCallURL, options: [:], completionHandler: nil)
}
}
case 2:
if MFMailComposeViewController.canSendMail() {
let mail = MFMailComposeViewController()
mail.mailComposeDelegate = self
mail.setToRecipients([etichetta.text!])
mail.setSubject("Informazioni")
self.window?.rootViewController?.present(mail, animated: true)
} else {
// show failure alert
}
default:
print("default")
}
}
func mailComposeController(_ controller:MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
controller.dismiss(animated: true)
}
}
but Xcode give me this error
Value of type 'OfficeCell' has no member 'performSegue'
on
self.performSegue(withIdentifier: "moveToMaps", sender: self)
how to achieve what i need ?
Thanks in advance
Fabrizio

you need to implement delegate and perform segue from main class because cell class can't perform segue ... in storyBoard attach "moveToMaps" segue from main controller
protocol CellDelegate {
func didTapAddressButton()
}
class OfficeCell: UITableViewCell, MFMailComposeViewControllerDelegate {
var delegate: CellDelegate?
}
In your main View controller class
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "OfficeCell", for: indexPath) as! OfficeCell
cell.delegate = self
return cell
}
extension MainController: CellDelegate {
func didTapAddressButton(){
self.performSegue(withIdentifier: "moveToMaps", sender: self)
}
}

Related

Why the property does not pass to another view Controller?

extension ReservationViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let vc = storyboard?.instantiateViewController(withIdentifier: "restaurantDetail") as? RestaurantViewController {
vc.restaurantNametxt = searchRestaurant[indexPath.row].restaurantName
print(searchRestaurant[indexPath.row].restaurantName)
}
print(indexPath.row)
print(searchRestaurant[indexPath.row].restaurantID ?? "")
restaurantsTableView.deselectRow(at: indexPath, animated: true)
performSegue(withIdentifier: "restaurantDetail", sender: self)
}
}
class RestaurantViewController: UIViewController {
#IBOutlet weak var restaurantName: UILabel!
#IBOutlet weak var reserverButton: UIButton!
var restaurantNametxt: String = ""
override func viewDidLoad() {
super.viewDidLoad()
restaurantName.text = restaurantNametxt
print(restaurantNametxt)
}
#IBAction func reserverButtonPressed(_ sender: UIButton) {
performSegue(withIdentifier: "FinalStep", sender: self)
self.navigationItem.hidesBackButton = true
}
}
When the user performs didSelectRow at I want to pass searchRestaurant[indexPath.row].restaurantName to another vc which is RestaurantViewController but on UILabel is still "".
vc.restaurantNametxt contains the real restaurantName because it is printing the value.
Your approach fails because you do not use the vc you instantiated. With performSegue you create a new ViewController that gets presented. And its property is empty as you already discovered.
Try:
self.presentViewController(vc, animated: true, completion: nil)
and remove:
performSegue(withIdentifier: "restaurantDetail", sender: self)

Table Content disappears on Scroll in TableView with Custom Cell using Subview - Swift

I have a ViewController which uses multiple Subviews (HomeViewController, etc.) which can be selected via a Custom Tab Bar at the bottom of my app. Inside the HomeViewController there is a UIView containing a UITableView containing a Prototype Custom Cell with name and image.
import UIKit
class HomeViewController: UIViewController {
#IBOutlet weak var friendView: UITableView!
let friends = ["batman", "harsh", "ava", "sasha", "fatima", "alfred"]
override func viewDidLoad() {
super.viewDidLoad()
friendView.delegate = self
friendView.dataSource = self
friendView.allowsSelection = false
}
}
extension HomeViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 120
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return friends.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = friendView.dequeueReusableCell(withIdentifier: "customCell") as! CustomCell
let friend = friends[indexPath.row]
cell.avatarImg.image = UIImage(named: friend)
cell.nameLbl.text = friend
return cell
}
}
Custom cell:
import UIKit
class CustomCell: UITableViewCell {
#IBOutlet weak var friendView: UIView!
#IBOutlet weak var nameLbl: UILabel!
#IBOutlet weak var avatarImg: UIImageView!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
When I start the app, everything looks just fine. However, when I start scrolling inside the table, all data suddenly disappears. All relations between storyboard and code should be just fine. I think it might have got something to do with my need of using a Subview.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var tabBarView: UIView!
#IBOutlet weak var contentView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
Design.makeCornersRound(view: tabBarView, radius: 10.0)
Timer.scheduledTimer(withTimeInterval: 0.1, repeats: false) { (timer) in
self.switchToHomeViewController()
}
}
#IBAction func onClickTabBar(_ sender: UIButton) {
let tag = sender.tag
if tag == 1 {
switchToIncomingsViewController()
}
else if tag == 2 {
switchToSpendingsViewController()
}
else if tag == 3 {
switchToHomeViewController()
}
else if tag == 4 {
switchToSavingsViewController()
}
else if tag == 5 {
switchToSettingsViewController()
}
}
func switchToHomeViewController() {
guard let Home = self.storyboard?.instantiateViewController(withIdentifier: "HomeViewController") as? HomeViewController else { return }
contentView.addSubview(Home.view)
Home.didMove(toParent: self)
}
...
}
Reference to the tutorial I have been trying to implement: https://www.youtube.com/watch?v=ON3Z0PXSoVk
In this function:
func switchToHomeViewController() {
// 1
guard let Home = self.storyboard?.instantiateViewController(withIdentifier: "HomeViewController") as? HomeViewController else { return }
// 2
contentView.addSubview(Home.view)
// 3
Home.didMove(toParent: self)
// 4
}
At 1 you create an instance of HomeViewController
at 2 you add its view to cotentView
at 3 you call didMove() ... but that doesn't do anything because you haven't added the controller to your hierarchy
at 4 your Home instance goes away, so the code in that controller no longer exists
You need to add the controller as a child controller.
As a side note, use lowerCase for variable names:
func switchToHomeViewController() {
// create an instance of HomeViewController
guard let homeVC = self.storyboard?.instantiateViewController(withIdentifier: "HomeViewController") as? HomeViewController else { return }
// add it as a child view controller
self.addChild(homeVC)
// add its view
contentView.addSubview(homeVC.view)
// here you should either set the view's frame or add constraints
// such as:
homeVC.view.frame = contentView.bounds
// inform the controller that it moved to a parent controller
homeVC.didMove(toParent: self)
}

How to add an action button to a custom XIB cell?

I want to create a custom XIB Table View Cell with an action button that can segue to a new view controller.
So far, I created a custom XIB Cell (TaskListCell) with a button (timeButton), and registered the TaskListCell to TaskListViewController. For tableView(_:cellForRowAt:), I added tag for timeButton and addTarget(_:action:for:) to respond when timeButton is tapped. However, I get this error message: Thread 1: "-[Sprints.TaskListCell pressedTimeButton:]: unrecognized selector sent to instance 0x105311560"
Here is the custom XIB cell file:
class TaskListCell: UITableViewCell {
#IBOutlet weak var nameField: UITextField!
#IBOutlet weak var timeButton: UIButton!
#IBAction func pressedTimeButton(_ sender: UIButton) {
}
override func awakeFromNib() {
}
override func setSelected(_ selected: Bool, animated: Bool) {
}
}
Here is the ViewController that displays the custom cells in a Table View:
class TaskListViewController: UIViewController {
// MARK: - Outlet Variables
#IBOutlet weak var taskList: SelfSizedTableView!
...
// MARK: - Instance Variables
var taskData = [TaskData]()
var taskCount: Int = 1
...
// MARK: - View Controller Methods
override func viewDidLoad() {
super.viewDidLoad()
// Register TaskListCell.xib file
let taskCellNib = UINib(nibName: "TaskCell", bundle: nil)
taskList.register(taskCellNib, forCellReuseIdentifier: "taskCell")
...
// Connect table view's dataSource and delegate to current view controller
taskList.delegate = self
taskList.dataSource = self
}
// MARK: - Action Methods
// Adds new cell in Table View
#IBAction func pressedAddTask(_ sender: UIButton) {
taskCount += 1
taskList.reloadData()
taskList.scrollToRow(at: IndexPath(row: taskCount-1, section: 0), at: .bottom, animated: true)
}
// MARK: - Methods
// Segues to SelectTime screen when timeButton is pressed
#objc func pressedTimeButton(_ sender: UIButton) {
let destination = SelectTimeViewController()
navigationController?.pushViewController(destination, animated: true)
}
}
extension TaskListViewController: UITableViewDataSource {
// Return the number of rows in table view
func tableView(_ tableView: UITableView,
numberOfRowsInSection section: Int) -> Int {
return taskCount
}
// Return the cell to insert in table view
func tableView(_ tableView: UITableView,
cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "taskCell", for: indexPath) as! TaskListCell
// Fatal error for next line: "Unexpectedly found nil while implicitly unwrapping an Optional value"
cell.timeButton.tag = indexPath.row
cell.timeButton.addTarget(self, action: #selector(pressedTimeButton(_:)), for: .touchUpInside)
return cell
}
}
extension TaskListViewController: UITableViewDelegate {
}
Check instance of IBAction from your cell may be it is being called. Remove this function and it will work
#IBAction func pressedTimeButton(_ sender: UIButton) {
}

Beginner question on passing data between view controllers

I am trying to recreate the Notes app in iOS. I have created an initial View Controller which is just a table view. A user can go to a Detail View Controller to compose a new note with a Title and Body section. When they click Done, I want to manipulate the tableView with note's details.
I am struggling saving the details of what the user entered to use on my initial view controller.
Here's my Notes class which defines the notes data:
class Notes: Codable {
var titleText: String?
var bodyText: String?
}
Here is the Detail View controller where a user can input Note details:
class DetailViewController: UIViewController {
#IBOutlet var noteTitle: UITextField!
#IBOutlet var noteBody: UITextView!
var noteDetails: Notes?
var noteArray = [Notes]()
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(updateNote))
noteTitle.borderStyle = .none
}
#objc func updateNote() {
noteDetails?.titleText = noteTitle.text
noteDetails?.bodyText = noteBody.text
noteArray.append(noteDetails!) // This is nil
// not sure if this is the right way to send the details over
// let vc = ViewController()
// vc.noteArray.append(noteDetails!)
if let vc = storyboard?.instantiateViewController(identifier: "Main") {
navigationController?.pushViewController(vc, animated: true)
}
}
}
I also have an array on my initial view controller as well. I think I need this one to store note data to display in the tableView (and maybe don't need the one on my Detail View controller?). The tableView is obviously not completely implemented yet.
class ViewController: UITableViewController {
var noteArray = [Notes]()
override func viewDidLoad() {
super.viewDidLoad()
print(noteArray)
self.navigationItem.setHidesBackButton(true, animated: true)
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .compose, target: self, action: #selector(composeNote))
}
#objc func composeNote() {
if let dvc = storyboard?.instantiateViewController(identifier: "Detail") as? DetailViewController {
navigationController?.pushViewController(dvc, animated: true)
}
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
noteArray.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
return cell
}
Just using Delegate:
First create delegate protocol with a func to send back note to your viewController
protocol DetailViewControllerDelegate: AnyObject {
func newNoteDidAdded(_ newNote: Note)
}
Next add the delegate variable to DetailViewController, and call func noteDataDidUpdate to send data back to viewController
class DetailViewController: UIViewController {
weak var delegate: DetailViewControllerDelegate?
#objc func updateNote() {
....
delegate?.newNoteDidAdded(newNote)
}
}
finally, set delegate variable to viewController and implement this in ViewController
class ViewController: UIViewController {
....
#objc func composeNote() {
if let dvc = storyboard?.instantiateViewController(identifier: "Detail") as? DetailViewController {
dvc.delegate = self
navigationController?.pushViewController(dvc, animated: true)
}
}
}
extension ViewController: DetailViewControllerDelegate {
func newNoteDidAdded(_ newNote: Note) {
// do some thing with your new note
}
}

ContextMenuConfiguration for User Images?

I want the image of users to popup (without action option) and then be dismissed if touched outside.
Say table view consist of 2 UI elements (a button and a text) and one picture. How would you set the pop up context menu for only the picture - immy?
public override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! ViewControllerTableViewCell
let immy = cell.viewWithTag(1) as! UIImageView
let person: Userx = people[indexPath.row]
let button = cell.viewWithTag(3) as! UIButton
cell.lblName.text = person.Education
cell.postID = self.people[indexPath.row].postID
if let PhotoPosts = person.PhotoPosts {
let url = URL(string: PhotoPosts)
print(PhotoPosts, "sttdb")
immy.sd_setImage(with: url)
}
return cell
}
extension homepage {
func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration? {
let configuration = UIContextMenuConfiguration(identifier: nil, previewProvider: nil)
return configuration
}
}
Below is the ViewControlleTableViewCell, added after the first comment. My reply to the comment also explains how this looks in main.storyboard.
class ViewControllerTableViewCell: UITableViewCell {
#IBOutlet weak var button: UIButton!
#IBOutlet weak var immy: UIImageView!
#IBOutlet weak var lblgenre1: UILabel!
#IBOutlet weak var lblName: UILabel!
#IBOutlet weak var lblName1: UILabel!
#IBOutlet weak var likeLabel: UILabel!
var postID: String!
var caption1: String!
var keyToPost: String!
var latestLocation1: [String: Double]?
let databaseRef = Database.database().reference()
var refArtists: DatabaseReference!
let profileImageView: UIImageView = {
let imageView = UIImageView()
return imageView
} ()
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
#IBAction func button1(_ sender: Any) {
}
#IBAction func button2NBTHISISTORPREVENTSEGUE(_ sender: Any) {
}
}
In homepage:
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! ViewControllerTableViewCell
What you need is to enable user interaction in your UIImageView, make your ViewControllerTableViewCell conform to UIContextMenuInteractionDelegate, create an UIContextMenuInteraction and set your cell as the delegate. Then add the iteraction to your image view. Don't forget to remove the contextMenuInteraction method from your HomePage extension.
Your ViewControllerTableViewCell contextMenuInteraction interaction implementation should look like this:
import UIKit
class ViewControllerTableViewCell: UITableViewCell, UIContextMenuInteractionDelegate {
#IBOutlet weak var immy: UIImageView!
override func awakeFromNib() {
super.awakeFromNib()
immy.isUserInteractionEnabled = true
immy.addInteraction(UIContextMenuInteraction(delegate: self))
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration? {
UIContextMenuConfiguration(identifier: nil, previewProvider: nil) { _ in
let share = UIAction(title: "Share", image: UIImage(systemName: "square.and.arrow.up")) { _ in
// share code
}
return UIMenu(title: "Profile Picture Menu", children: [share])
}
}
}