I am new to swift language and start to creating stub application having sliding menu. I used tutorial from http://www.appcoda.com/sidebar-menu-swift/ to create slide menu but want to create application which is dynamic not static as shown in example. I am facing problem in creating segue or doing navigation from the uitableviewcell to the respective uiviewcontrollers which is connected to respective uinavigationcontroller.
following is the code for sliding menu class:
MenuController.swift
import UIKit
class MenuController:UITableViewController
{
let menuControlList = ["Category 1", "Category 2", "Category 3", "Category 4"]
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// Return the number of sections.
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Return the number of rows in the section.
return menuControlList.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! MenuTableCell
let row = indexPath.row
cell.menuCellText.text = menuControlList[row]
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
print("row clicked :: ", indexPath.row)
switch indexPath.row
{
case 0:
var cat1View = Category1(nibName:"Category1", bundle:nil)
self.navigationController?.pushViewController(cat1View, animated: true)
print(indexPath.row)
break
case 1:
var cat2View = Category2(nibName:"Category2", bundle:nil)
self.navigationController?.pushViewController(cat2View, animated: true)
print(indexPath.row)
break
case 3:
var cat3View = Category3(nibName:"Category3", bundle:nil)
self.navigationController?.pushViewController(cat3View, animated: true)
print(indexPath.row)
break
case 4:
var cat4View = Category4(nibName:"Category4", bundle:nil)
self.navigationController?.pushViewController(cat4View, animated: true)
print(indexPath.row)
break
default:
return;
}
}
}
Following is the screenshot of my storyboard:
if i am doing any mistake in creating this please let me know and help me rectify it.
Following is the code of my category 1 class :
class Category1 :UIViewController
{
#IBOutlet var menuBtn: UIBarButtonItem!
override func viewDidLoad() {
super.viewDidLoad()
if self.revealViewController() != nil {
menuBtn.target = self.revealViewController()
menuBtn.action = "revealToggle:"
self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
EDIT:
Tried following solution:
let vc = storyboard.instantiateViewControllerWithIdentifier("Category1") as! Category1
presentViewController(vc, animated: true, completion: nil)
the above code directly opens up category 1 viewcontroller with slideup animation but it is not opening through the navigationcontroller attached with the respective viewcontroller.
if i use the following code:
let vc = storyboard.instantiateViewControllerWithIdentifier("Category1") as! Category1
self.revealViewController().setFrontViewController(vc, animated: true)
the above code also loads viewcontroller but the sliding menu doesnt slide back in?
First of all, give your Navigation View Controllers a storyboard ID, and not the actual view controllers. So for example, if Category1 is embedded in the Navigation Controller, then give the Navigation Controller a storyboard ID of Category1NavController for example. Then your code should be as follows:
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
print("row clicked :: ", indexPath.row)
switch indexPath.row
{
case 0:
let navController1 = storyboard.instantiateViewControllerWithIdentifier("Category1NavController") as! UINavigationController
self.revealViewController().setFrontViewController(navController1, animated: true)
print(indexPath.row)
break
..........
// Handle other cases similarly here
case 1:
.....
case 2:
.....
default:
return;
}
self.revealViewController().setFrontViewPosition(FrontViewPosition.Left, animated: true)
}
You should also reset the Top View position after your switch statement ends. Here's how it should be (sorry again if syntax is not correct, please fix accordingly):
self.revealViewController().setFrontViewPosition(FrontViewPosition.Left, animated: true)
There must be a similar method to be called in Swift.
Hope this helps. Please pardon my SWIFT syntax and correct it if needed.
Related
Im having an issue with my table view controller in swift 3. I have a very simple Cell configured as a prototype where an Image is on the left side of the cell, and a text label takes up 3/4 of the space after the image going towards the right side of the cell. Whenever I load the view, everything looks fine. However, when I either segue to a new view and then go back to that view from a back button on a navigation controller, or if I scroll up on the table to bring a cell out of the view and go back down the cells view totally screws up and moves the text label to the left and either gets rid of the image or overlays the image on top of the text label. I have seen one similar question on here, but the answers are in objective C or simply do not work. I have tried placing the loading logic in different methods such as viewWillAppear, viewDidLoad, etc. I have tried self.tableView.reload. I have also tried messing with the constraints manually and using tableViews estimatedHeight method. Can anyone help with this? Here is my class:
import UIKit
import Parse
var bikeList = [String]()
var customerBikeIDList = [String]()
var customerIndex: Int = 0;
class BikeListController: UITableViewController {
#IBAction func addBike(_ sender: Any) {
// let storyboard = UIStoryboard(name: "Main", bundle: nil)
//let vc = storyboard.instantiateViewController(withIdentifier: "addBikeVC")
//self.present(vc, animated: true, completion: nil)
self.performSegue(withIdentifier: "addBike", sender: self)
}
override func viewDidLoad() {
super.viewDidLoad()
//associate user to this device for notification usage
CommonUtils.addFCMTokenToParse()
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem()
}
//was func viewWillAppear
override func viewWillAppear(_ animated: Bool) {
let query = PFQuery(className: "Bike")
if let userID = PFUser.current()?.objectId {
print("user ID: "+userID)
query.whereKey("userID", equalTo: userID)
query.findObjectsInBackground(block: { (objects: [PFObject]?, error: Error?) in
if error == nil {
if let objects = objects {
for object in objects {
print("printing items from parse query")
print(object["userID"])
print(object["model"])
if(!(bikeList.contains(object["model"] as! String))){
bikeList.append(object["model"] as! String)
customerBikeIDList.append(object.objectId!)
}
}
self.tableView.reloadData()
}
}else {
//there was an error
print("There was an error...")
}
})
}
}
//no reason for having this function here other then idk
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return (bikeList.count)
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! BikeListCell
cell.theText.text = bikeList[indexPath.row]
// Configure the cell...
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
customerIndex = indexPath.row
self.performSegue(withIdentifier: "customerViewBike", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "addBike" {
let navVC = segue.destination as? UINavigationController
_ = navVC?.viewControllers.first as! AddBikeViewController
}
}
}
Here is the BikeListCell
import UIKit
class BikeListCell: UITableViewCell {
#IBOutlet var theText: UILabel!
#IBOutlet var bikePhoto: 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
}
}
Here is my constraints:
I was assigned with converting an older Objective C app to Swift.
I had another project come up but when I came back to it and to my somewhat working Swift 2 version, upon updating to Swift 3 the UITableView does not seem to update.
It is built in Interface Builder (IB). The data source and delegate functions are linked in IB.
I made a sample project where I want to reload a different array on a button press. On a button press the main array is set equal to a different array. then self.tableView.reloadData() is called. The array in debugging has a value of 4 and is not empty so numberOfRowsInSection returns a number greater than 0. The table height and width are what you would expect and are visible. The table just does not refresh. The cells populate the first time the array loads.
I have also tried downloading tutorials where they add a new cell to a table but it does not appear to work either. I have also tried manually assigning the app delegate and datasource in MasterViewController.swift. I also tried wrapping the reloadData() call in DispatchQueue.main.async but that did not seem to help either.
Hopefully I'm just missing something very basic here. Below is my MasterViewController file. Thanks for any help.
Current version of Xcode: 8.2.1
Version of swift: 3.0.2
OSX: Sierra 10.12.2
import UIKit
class MasterViewController: UITableViewController {
var detailViewController: DetailViewController? = nil
var objects = [Any]()
var list1 = ["Eggs", "Milk", "Bread", "Bacon"];
var list2 = ["France", "Italy", "England", "Spain"];
var currentArray = [String]();
var setVar = false;
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.navigationItem.leftBarButtonItem = self.editButtonItem
if(currentArray.count <= 0){
currentArray = list2;
}else{
currentArray = list1;
}
//self.tableView.dataSource = self;
// self.tableView.delegate = self;
//self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell");
//self.tableView.reloadData();
let addButton = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(insertNewObject(_:)))
self.navigationItem.rightBarButtonItem = addButton
if let split = self.splitViewController {
let controllers = split.viewControllers
self.detailViewController = (controllers[controllers.count-1] as! UINavigationController).topViewController as? DetailViewController
}
}
override func viewWillAppear(_ animated: Bool) {
self.clearsSelectionOnViewWillAppear = self.splitViewController!.isCollapsed
super.viewWillAppear(animated)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func insertNewObject(_ sender: Any) {
objects.insert(NSDate(), at: 0)
let indexPath = IndexPath(row: 0, section: 0)
self.tableView.insertRows(at: [indexPath], with: .automatic)
}
// MARK: - Segues
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showDetail" {
if let indexPath = self.tableView.indexPathForSelectedRow {
let object = objects[indexPath.row] as! NSDate
let controller = (segue.destination as! UINavigationController).topViewController as! DetailViewController
controller.detailItem = object
controller.navigationItem.leftBarButtonItem = self.splitViewController?.displayModeButtonItem
controller.navigationItem.leftItemsSupplementBackButton = true
}
}
}
// MARK: - Table View
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return currentArray.count;
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
cell.textLabel!.text = currentArray[indexPath.row];
return cell
}
func refreshUI(){
self.tableView.reloadData();
}
func changeVar(){
if(setVar){
currentArray.removeAll();
self.currentArray = self.list1;
setVar = false;
}else{
currentArray.removeAll();
self.tableView.reloadData();
list2.append("Italy");
self.currentArray = self.list2;
setVar = true;
}
self.refreshUI();
}
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
// Return false if you do not want the specified item to be editable.
return true
}
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
objects.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .fade)
} else if editingStyle == .insert {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view.
}
}
}
class DetailViewController: UIViewController {
#IBOutlet weak var detailDescriptionLabel: UILabel!
#IBOutlet weak var test2: UIButton!
#IBOutlet weak var test1: UIButton!
var mc = MasterViewController();
func configureView() {
// Update the user interface for the detail item.
if let detail = self.detailItem {
if let label = self.detailDescriptionLabel {
label.text = detail.description
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.configureView()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
var detailItem: NSDate? {
didSet {
// Update the view.
self.configureView()
}
}
#IBAction func button1(){
NSLog("here is the button1");
mc.changeVar();
}
#IBAction func button2(){
NSLog("here is the button2");
mc.changeVar();
}
}
Your tableView is using the data of currentArray in cellForRowAt function.
The only place that you assign to currentArray is in viewDidLoad.
Currently, in the code the add button will cause a new string of the current timestamp to the objects array, As the table is pulling data from currentArray and not objects, it will never change.
I do not see changeVar() function being called anywhere in the code.
Change the target of the add button to call changeVar function and see if that updates the data in the table. If not, you'll have to provide the exact code that your saying is not working, as the current code I wouldn't expect it to change anything.
EDIT:
In your detail view controller you are trying to update a value from the master view controller... But.. It is not the same instance.
var mc = MasterViewController();
^^ This code creates a NEW instance of MasterViewController so calling that code wont change anything on your tableView.
change this line to
weak var mc: MasterViewController?
Then when you create the detailviewcontroller you can do:
let controller = (segue.destination as! UINavigationController).topViewController as! DetailViewController
controller.detailItem = object
controller.mc = self
Then your DetailViewController has a reference to the master controller and can call the function as expected.
I'm going to make this as clear as possible. In my Xcode project, I am reusing a prototype cell and I want each cell to open a different view controller. How would you guys think I would be able to do that?
Here is a pic of the reusable cell in my Main.storyboard:
Here is the result of the cells being reused:
And here is the code of my controller class:
import UIKit
class NewsTableViewController: UITableViewController {
#IBOutlet var menuButton:UIBarButtonItem!
#IBOutlet var extraButton:UIBarButtonItem!
override func viewDidLoad() {
super.viewDidLoad()
tableView.estimatedRowHeight = 242.0
tableView.rowHeight = UITableViewAutomaticDimension
if revealViewController() != nil {
menuButton.target = revealViewController()
menuButton.action = #selector(SWRevealViewController.revealToggle(_:))
revealViewController().rightViewRevealWidth = 150
extraButton.target = revealViewController()
extraButton.action = #selector(SWRevealViewController.rightRevealToggle(_:))
view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
// Return the number of sections
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Return the number of rows
return 3
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! NewsTableViewCell
// Configure the cell...
if indexPath.row == 0 {
cell.postImageView.image = UIImage(named: "watchkit-intro")
cell.postTitleLabel.text = "WatchKit Introduction: Building a Simple Guess Game"
} else if indexPath.row == 1 {
cell.postImageView.image = UIImage(named: "custom-segue-featured-1024")
cell.postTitleLabel.text = "Building a Chat App in Swift Using Multipeer Connectivity Framework"
} else {
cell.postImageView.image = UIImage(named: "webkit-featured")
cell.postTitleLabel.text = "A Beginner’s Guide to Animated Custom Segues in iOS 8"
}
return cell
}
}
To be clear, I want each cell in pic 2 to open a different view controller. For example, I would want Cell 1 to open New View Controller 1, Cell 2 to open New View Controller 2, and Cell 3 to open New View Controller 3.
Thanks for reading!
I think you should implement tableview's delegate method
e.g.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
if indexPath.row == 0
{
// open VC1
}
else if indexPath.row == 1
{
// open VC2
}
else if indexPath.row == 2
{
// open VC3
}
}
i think it something like this
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
switch indexPath.row {
case 0:
// segue to view 1
case 1:
// segue to view 2
case 2:
// segue to view 3
case 3:
// segue to view 4
default:
// default go here
}
}
You would have to implement tableView(_:, didSelectRowAt:):
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.row == 0 {
let newViewController1 = ...
present(newViewController1, animated: true, completion: nil)
} else if indexPath.row == 1 {
let newViewController2 = ...
present(newViewController2, animated: true, completion: nil)
} else {
let newViewController3 = ...
present(newViewController3, animated: true, completion: nil)
}
}
Alternatively, you could add a gesture recognizer to your table view cells and implement a custom cell delegate. It doesn't seem like that would be necessary in your situation though, so I won't recommend it. It seems like it may be overkill. If you're interested, though:
class NewsTableViewCell: UITableViewCell {
var delegate: NewsTableViewCellDelegate?
override func awakeFromNib() {
let recognizer = UIGestureRecognizer(target: self, action: #selector(tapped))
contentView.addGestureRecognizer(recognizer)
}
func tapped() {
delegate?.cellTapped(self)
}
}
protocol NewsTableViewCellDelegate {
func cellTapped(_ cell: NewsTableViewCell)
}
extension MyViewController: NewsTableViewCellDelegate {
func cellTapped(_ cell: NewsTableViewCell) {
if cell == cell1 { }
else if cell == cell2 { }
else if cell == cell3 { }
//You would have to manually determine how to check if a cell is cell1, cell2, or cell3, since you wouldn't have an indexPath available.
}
}
For both the error,
Remove segue from main.storyboard because you are using segue from storyboard but in code work you presenting your ViewConroller.
In code work, replace your lines by below
**
`let storyboard = UIStoryboard(name: "main.Storyboard", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier:"myViewController") as! UIViewController
self.present(controller, animated: true, completion: nil)`
**
If you are pushing your VC, then use this below code:
let myViewController = self.storyboard?.instantiateViewController(withIdentifier: "myViewController") as! Conversation_VC
self.navigationController?.pushViewController(myViewController, animated: true)
Hi all. I'm troubling with tableView in swift. Actually i created the table view with two rows(About and Login) in main view controller. Problem at initial, when i click the about or Login , then the New View controller is not opened. But, I try second time to another one row, then the first clicked view Controller is opened at this time of Clicking. so, this cycles shown wrong view controller at every time of clicking. Please tell me as whats wrong with my Code?? Please refer screenshot given below.Thanks in advance!!!
import UIKit
class ViewController: UIViewController,UITableViewDataSource,UITableViewDelegate {
#IBOutlet var tableView: UITableView!
var titles = ["About","LogIn"]
override func viewDidLoad() {
super.viewDidLoad()
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return titles.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier("tableCell", forIndexPath: indexPath) as! TableCell
cell.label.text = titles[indexPath.row]
return cell
}
func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) {
switch indexPath.row
{
case 0: self.performSegueWithIdentifier("aboutSegue", sender: self)
case 1: self.performSegueWithIdentifier("loginSegue", sender: self)
default: break
}
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "aboutSegue"
{
let vc = segue.destinationViewController as! about
vc.title = "About"
}
else
{
let vc = segue.destinationViewController as! login
vc.title = "Login"
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
Below Screen shown, I clicked About Row. But, the Login ViewController is Opened.
This is my Story Board Connections.
Just update didSelectRowAtIndexPath method
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
switch indexPath.row
{
case 0:
self.performSegueWithIdentifier("aboutSegue", sender: self)
break;
case 1:
self.performSegueWithIdentifier("loginSegue", sender: self)
break;
default: break
}
}
i am trying to call the viewDidLoad of view controllers only once while using SWRevealViewController.I am following these tutorial
and want to do a little change loading the view controllers programmatically instead of using segues..
I tried to set the frontview controller in MenuController class as
class MenuController: UITableViewController {
var navcontroller: UINavigationController?
override func viewDidLoad() {
super.viewDidLoad()
navcontroller = self.storyboard?.instantiateViewControllerWithIdentifier("pager") as? UINavigationController
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewWillAppear(animated: Bool) {
println("ok this i get called")
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if indexPath.row == 2{
if let temp = navcontroller{
self.revealViewController().setFrontViewController(navcontroller!, animated: true)
}else{
println("navigation controller contains nil vaue")
}
}
}
}
The problem i get is i get black screen when i try to set the frontViewController once...Here is the work that i have done
Actually everything was working fine just need another method to call
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if indexPath.row == 2{
if let temp = navcontroller{
self.revealViewController().pushFrontViewController(temp, animated: true)
}else{
println("navigation controller contains nil vaue")
}
}
}