I started to learn Swift 2 weeks ago. With some courses and youtube videos, I learn basics of uitableview but I couldn't get how can I use specific table view cell like a button.
What I wanna do?
I am trying to use the sidebar menu(https://github.com/yannickl/FlowingMenu) for my project, everything is cool and working okay instead of using cells.
In every basic tutorial people showing how to use segue with uitableview but they only use 2 pages and independent from cell value, no matter what text in your cell it calls segue for the second controller.
I want to use the sidebar menu with cells and if my cell value is "main menu" I want to perform "toMainMenu" segue.
I only know using cells with arrays don't know is there any other way for work with cells, and my main goal is cell-specific segue.
I watched a lot of tutorials but can't find anything useful for me.
import UIKit
import FlowingMenu
class SideBarMenu: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var nameLabel: UILabel!
#IBOutlet weak var surnameLabel: UILabel!
#IBOutlet weak var topBar: UINavigationBar!
#IBOutlet weak var backButtonItem: UIBarButtonItem!
#IBOutlet weak var userTableView: UITableView!
#IBOutlet weak var profilePic: UIImageView!
let CellName = "Menu"
let mainColor = UIColor(hex: 0xC4ABAA)
var sections = ["Main Menu", "Settings", "Profile"]
override func viewDidLoad() {
super.viewDidLoad()
userTableView.delegate = self
userTableView.dataSource = self
nameLabel.text = Helper.name
surnameLabel.text = Helper.surname
topBar.tintColor = .black
topBar.barTintColor = mainColor
topBar.titleTextAttributes = [
NSAttributedString.Key.font: UIFont(name: "HelveticaNeue-Light", size: 22)!,
NSAttributedString.Key.foregroundColor: UIColor.black
]
userTableView.backgroundColor = mainColor
view.backgroundColor = mainColor
}
// MARK: - Managing the Status Bar
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
// MARK: - UITableView DataSource Methods
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return sections.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
cell.textLabel?.text = sections[indexPath.row]
cell.contentView.backgroundColor = mainColor
return cell
}
// MARK: - UITableView Delegate Methods
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("Select row at")
}
#IBAction func profileButton(_ sender: Any) {
performSegue(withIdentifier: "toProfile", sender: nil)
}
}
I want to perform "toSettings" segue when "Settings" cell clicked.
Implement the UITableViewDelegate protocol method tableView(_:didSelectRowAt:) as below
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
switch sections[indexPath.row] {
case "Main Menu":
performSegue(with: "toMainMenu", sender: nil)
case "Settings":
performSegue(with: "toSettings", sender: nil)
case "Profile":
performSegue(with: "toProfile", sender: nil)
}
}
If you need to configure or pass some data to the destination controller, you can override the controller's prepare(for:sender:) method to get a reference, for example:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "toMainMenu" {
let dvc = segue.destination as! MenuViewController
// now you have a reference
}
}
If I understand, your menu is a tableView, so you need to segue to the view you wanna open in the table didSelectAt
You can use override prepare for segue. so when you link your views with a segue you set an identifier for each one and can call whatever you need from there.
Related
I have a sequence where the user logs in and then they are brought to view showing a UITableView via a segue. I am trying to inject data from the login screen to the table after a successful login.
In the login view...
func transitionToHome() {
print("Hey you logged in!")=
performSegue(withIdentifier: "loginSuccess", sender: self)
self.navigationController!.setNavigationBarHidden(false, animated: false)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let vc = segue.destination as! ViewController
vc.models = [(title: "title", note: "Note")]
}
And in the Home Screen view
#IBOutlet var table: UITableView!
#IBOutlet var label: UILabel!
#IBOutlet weak var newNoteButton: UIButton!
var models: [(title: String, note: String)] = []
override func viewDidLoad() {
super.viewDidLoad()
table.delegate = self
table.dataSource = self
title = "Notes"
}
I have tried to call table.reloadData() in the the viewDidLoad, as well as in viewDidAppear and viewWillAppear. Neither has worked.
I also have printed out models in the viewDidLoad and I saw that the data is being correctly passed to the view controller. But,I cannot get the table to load this data when the view controller is loaded from the segue.
If you models data prints correctly in viewDidLoad() you should be able to configure a tableview cell with the data in the dataSource implementation. See sample extension for the dataSource implementation below. Make sure you provide a reuse identifier to your prototype cell in the storyboard.
extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
var content = cell.defaultContentConfiguration()
content.text = "This is a cell primary text"
cell.contentConfiguration = content
return cell
}
I'm creating an app, in which one of the functions is, that the user should be able to write a person's name and an answer to a question - and then when pressing the save-button he/she should be redirected to the previous controller again, which not have created a tableViewCell with this data as title. (Later on you can ofcourse click this cell and see the data in third viewcontroller.)
My way of tackling this was to let the "save" button save the name and the answer by using NSUserDefault. Then connecting a segue to the button at the same time to make it redirect the user to the previous controller - and finally to have the tableView in the previous controller refer to the newly created NSUserDefault-key in the cell.textfield.
I have two questions.
Why does this not work? My code from both viewControllers are underneeth. I don't get why it doesn't work.
If I do get this to work: How do I implement the effect, that every time you enter the "Creating viewController", in which you can write the name and the answer - the user gets the option of saving a NEW person and adding a NEW cell, instead of overriding the old one, which I'm afraid will happen if I get the current approach to work...
Code in the "Creating viewController", where you can write the name and the answer:
class CreateNewPerson: UIViewController {
let defaults = UserDefaults.standard
#IBOutlet weak var Question: UILabel!
#IBOutlet weak var ExtraIdentifier: UILabel!
#IBOutlet weak var PersonName: UITextField!
#IBOutlet weak var PersonAnswer: UITextField!
#IBOutlet weak var PersonExtraIdentifier: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
showDiaryIdentifiers () }
func showDiaryIdentifiers () {
let DiaryQuestion = self.defaults.string(forKey: "DiaryQuestionKey")
let ExtraIdentifer = self.defaults.string(forKey: "RandomIdentifierKey")
self.Question.text = DiaryQuestion
self.ExtraIdentifier.text = ExtraIdentifer
}
#IBAction func SavePerson () {
self.defaults.setValue(self.PersonName.text, forKey: "PersonNameKey")
self.defaults.setValue(self.PersonAnswer.text, forKey: "PersonAnswerKey")
self.defaults.setValue(self.PersonExtraIdentifier.text, forKey: "PersonExtraIdentiferKey")
} }
Code in the other viewController:
class AllPersonsInYourDiary: UIViewController, UITableViewDelegate, UITableViewDataSource {
let defaults = UserDefaults.standard
#IBOutlet weak var ShowingDiaryName: UILabel!
#IBOutlet weak var ShowingDiaryQuestion: UILabel!
#IBOutlet weak var ShowingExtraIdentifer: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
showDiaryIdentifiers()
self.navigationItem.rightBarButtonItem = self.editButtonItem
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func showDiaryIdentifiers () {
let DiaryName = self.defaults.string(forKey: "DiaryNameKey")
let DiaryQuestion = self.defaults.string(forKey: "DiaryQuestionKey")
let ExtraIdentifer = self.defaults.string(forKey: "RandomIdentifierKey")
self.ShowingDiaryName.text = DiaryName
self.ShowingDiaryQuestion.text = DiaryQuestion
self.ShowingExtraIdentifer.text = ExtraIdentifer
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Person1", for: indexPath)
cell.textLabel?.text = self.defaults.string(forKey: "PersonNameKey")
cell.textLabel?.numberOfLines = 0
cell.textLabel?.font = UIFont.preferredFont(forTextStyle: UIFontTextStyle.headline)
return cell
}
In this code, I guess what is not working is the cellForRowAt method. What am I getting wrong? Right now it's not creating any cells at all.
Also, I know I should notr1 return 1 row and 1 section. It's just for now. I know I should in the end return Something.count - but I haven't yet figured out what this something is...
Thanks!
You already created a table view with only one row.
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
When returning to previous controller just reload tableview like(Make sure before reloading datasource have contain new data.)
tableView.reloadData()
If I understand correctly that you need the user to enter a set of values and then use these values to populate a table view in another view controller, then what you wanna do is:
1- create 2 dictionaries, an optional dictionary in AllPersonsInYourDiary that would carry the new values and one in your CreateNewPerson something like this let dic = [[String: String]]().
2- Instantiate the view controller:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "#yourSegueIdentifier" {
let vc = segue.destination as! AllPersonsInYourDiary
vc.dic = self.dic
}
}
3- in your AllPersonsInYourDiary view controller, override the functions like this:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dic.count
}
and populate the cell
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Person1", for: indexPath)
cell.textLabel?.text = dic[indexPath.row]["#whateverKeyForValue"]
cell.textLabel?.numberOfLines = 0
cell.textLabel?.font = UIFont.preferredFont(forTextStyle: UIFontTextStyle.headline)
return cell
}
I make app with news feed which has to open on other ViewController. But can't pass data via segue.
Viewcontroller with newsfeed
class SecondViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var titlenews = ""
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "newsfeedCell", for: indexPath) as! NewsFeedCell
cell.newsfeed_title.text = self.news?[indexPath.item].headline
cell.newsfeed_topic.text = self.news?[indexPath.item].topic
cell.newsfeed_time.text = timetime(from: (self.news?[indexPath.item].time)!)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("tableview")
let vc = storyboard?.instantiateViewController(withIdentifier: "newsBody") as? NewsBody
vc?.labeltext = (self.news?[indexPath.item].headline)!
print((self.news?[indexPath.item].headline)!)
self.navigationController?.pushViewController(vc!, animated: true)
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.news!.count
} //number of rows
#IBOutlet weak var tableview: UITableView!
var news: [Newsfeed]? = []
override func viewDidLoad() {
super.viewDidLoad()
getJSON()
}
func getJSON(){
///Here all do right
}
}
Viewcontroller which has to receive data from news feed
class NewsBody: UIViewController {
#IBOutlet weak var testLabel: UILabel!
var labeltext = ""
override func viewDidLoad() {
super.viewDidLoad()
print(labeltext)
testLabel.text = labeltext
}
}
print(labeltext) shows that NewsBody receive empty value or nothing.
But print((self.news?[indexPath.item].headline)!) inside of SecondViewController shows that I try to push proper value.
What I do incorrect between this actions? What wrong with segue and pass of data?
It seems that instantiateViewController(withIdentifier: "newsBody") triggers view load under the hood. It should not (in theory) but it might do just that in your case.
This means that viewDidLoad() will be called before the vc?.labeltext = (self.news?[indexPath.item].headline)! is executed.
I'd recommend you to do the following.
class NewsBody: UIViewController {
#IBOutlet weak var testLabel: UILabel!
var labeltext: String? {
didSet { updateUI() }
}
override func viewDidLoad() {
super.viewDidLoad()
updateUI()
}
private func updateUI() {
testLabel.text = labeltext
}
}
This way if you set the labeltext property after the view is loaded, it will still trigger the UI update. And if you set the labeltext property before the view is loaded, as soon as viewDidLoad() is called.
BTW, you are not using segues here. But even if you do, you can easily use the same method as I proposed, because it allows you to stop thinking about whether property updates will update the UI.
Also please note that I made the property optional. It will allow you to avoid force casts and just do
vc?.labeltext = self.news?[indexPath.item].headline
UILabel.text is also an optional String property, so they will play well together.
what I did so far:
I create a tableView controller with empty rows, and a popup window on another ViewController, the purpose of the popup is to add two data(name - link) to the tableView on one ROW (passing Textfields).
what I want :
when I add a new raw (name - link) click save , I want the data to be stored , when I add other data, create another row .
my problem: when I tray to add more raws, it's always wright over the old one, so no matter how much data I input, the result always be 1 row!
popup VC
#IBOutlet weak var nameP: UITextField!
#IBOutlet weak var linkP: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let SEC: TEstVC = segue.destination as! TEstVC
SEC.add(name: nameP.text!, link: linkP.text!)
}
HomeScreen VC
class TEstVC: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
var names = [String]()
var links = [String]()
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.rowHeight = 100.0
tableView.dataSource = self
tableView.delegate = self
}
func add(name: String, link: String) {
names.append(name)
links.append(link)
}
override func viewWillAppear(_ animated: Bool) {
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return names.count //or links.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell
cell.cell4Name.text = names[indexPath.row]
cell.cell4Link.text = links[indexPath.row]
return cell
}
Move your array declaration to global and call reloadData in your tableView like this:
//Popup
#IBOutlet weak var nameP: UITextField!
#IBOutlet weak var linkP: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let SEC: TEstVC = segue.destination as! TEstVC
SEC.add(name: nameP.text!, link: linkP.text!)
self.tableView.reloadData()
}
Your VC
var names = [String]()
var links = [String]()
class TEstVC: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.rowHeight = 100.0
tableView.dataSource = self
tableView.delegate = self
}
func add(name: String, link: String) {
names.append(name)
links.append(link)
}
override func viewWillAppear(_ animated: Bool) {
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return names.count //or links.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell
cell.cell4Name.text = names[indexPath.row]
cell.cell4Link.text = links[indexPath.row]
return cell
}
Simple, after you add a new item to the datasource (names) you need to reload the tableview.
self.tableView.reloadData()
Well, it looks like you're going to a completely new instance of TestVC every single time. So, it's not overwriting the old line. It's putting a new line in a new instance of TestVC. I infer this because you're calling add(name:link:) in prepareForSegue, which gets called by the storyboard when a new viewController is being put on screen with a segue.
Also, your add(name:link:) method should call tableView.reloadData() if you want it to update the screen.
I have ViewController that contains a UITableView. Data is loaded into that table via the following code:
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UsernameSentDelegate {
#IBOutlet weak var scrollView: UIScrollView!
#IBOutlet weak var receiveUsername: UILabel!
#IBOutlet weak var userEmailText: UILabel!
var userEmail: String?
var communities = [String]() { didSet { communitiesTableView.reloadData()
}
}
var flag = false
#IBOutlet weak var communitiesTableView: UITableView!
#IBAction func unwindToHome(_ segue: UIStoryboardSegue) {
}
//recieves email address from delegate from LoginViewController
func userLoggedIn(data: String) {
userEmailText.text = data
}
override func viewDidLoad() {
super.viewDidLoad()
self.communitiesTableView.delegate = self
self.communitiesTableView.dataSource = self
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.communities.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let title = self.communities[indexPath.row]
let cell = UITableViewCell()
cell.textLabel?.text = title
return cell
}
I then set up 1 prototype cell within the UITableView so I could create a segue to my second view controller, ShowCommunitiesViewController and named this segue, "showCommunitySegue"
In ShowCommunitiesViewController I have a label set up and ready to use as the title of the cell name carried across, named communityName.
In ViewController I have set up the following function to deal with the segue, including the destination variable for the cell title that has been selected.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.performSegue(withIdentifier: "showCommunitySegue", sender: self)
showCommunityController.communityName = //what do I put here?
}
What do I need to put on that last line so showCommunityController.communityName displays the cell title?
Just declare selectedCellTitle as String in your viewController where your cells are.
var selectedCellTitle: String?
This will be the global variable keeping track of the selected cell's title.
Add the following in didSelectRowAt:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// Set your global variable to the title
self.selectedCellTitle = self.communities[indexPath.row]
// Trigger your segue
performSegue(withIdentifier: "showCommunitySegue", sender: self)
}
Override prepareforsegue method the following way:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showCommunitySegue" {
// Check if the segue's destination viewcontroller is your viewcontroller
if let showCommunityController = segue.destination as? ShowCommunityViewController {
// Assign the selected title to communityName
showCommunityController.communityName = self.selectedCellTitle
}
}
}