Swift 4 Split View Controller Detail Replaces Master - ios

I just started building an app and right now I am adding 2 Split View Controllers, in my Main.storyboard it looks like this image
I added the following code to my Master:
import UIKit
class ContactsMaster: UITableViewController {
var ContactsDetailController: ContactsDetail? = nil
var objects = [Any]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
navigationItem.leftBarButtonItem = editButtonItem
let addButton = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(insertNewObject(_:)))
navigationItem.rightBarButtonItem = addButton
if let split = splitViewController {
let controllers = split.viewControllers
ContactsDetailController = (controllers[controllers.count-1] as! UINavigationController).topViewController as? ContactsDetail
}
}
override func viewWillAppear(_ animated: Bool) {
clearsSelectionOnViewWillAppear = splitViewController!.isCollapsed
super.viewWillAppear(animated)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#objc
func insertNewObject(_ sender: Any) {
objects.insert(NSDate(), at: 0)
let indexPath = IndexPath(row: 0, section: 0)
tableView.insertRows(at: [indexPath], with: .automatic)
}
// MARK: - Segues
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showContactDetail" {
if let indexPath = tableView.indexPathForSelectedRow {
let object = objects[indexPath.row] as! NSDate
let controller = (segue.destination as! UINavigationController).topViewController as! ContactsDetail
controller.detailItem = object
controller.navigationItem.leftBarButtonItem = 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 objects.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let object = objects[indexPath.row] as! NSDate
cell.textLabel!.text = object.description
return cell
}
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.
}
}
}
And here is my Detail:
import UIKit
class ContactsDetail: UIViewController {
#IBOutlet weak var detailDescriptionLabel: UILabel!
func configureView() {
// Update the user interface for the detail item.
if let detail = detailItem {
if let label = detailDescriptionLabel {
label.text = detail.description
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
configureView()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
var detailItem: NSDate? {
didSet {
// Update the view.
configureView()
}
}
}
My problem is when I run my app and goto the Split View Controller and select an item in the Master, it does not goto the Detail, but instead replaces the master.
I have a sample app that is just the Split View Controller and I noticed in the App Delegate file of the sample app there is this code in the application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool method:
let splitViewController = window!.rootViewController as! UISplitViewController
let navigationController = splitViewController.viewControllers[splitViewController.viewControllers.count-1] as! UINavigationController
navigationController.topViewController!.navigationItem.leftBarButtonItem = splitViewController.displayModeButtonItem
splitViewController.delegate = self
And there is also this method:
func splitViewController(_ splitViewController: UISplitViewController, collapseSecondary secondaryViewController:UIViewController, onto primaryViewController:UIViewController) -> Bool {
guard let secondaryAsNavController = secondaryViewController as? UINavigationController else { return false }
guard let topAsDetailController = secondaryAsNavController.topViewController as? DetailViewController else { return false }
if topAsDetailController.detailItem == nil {
// Return true to indicate that we have handled the collapse by doing nothing; the secondary controller will be discarded.
return true
}
return false
}
My problem with this code is that my Split View Controller is not the inital controller and my problem with the splitViewController method, I have 2 split view controllers, I can only specificity 1 of them. How do I get this split view controller without making it the inital controller?

your master class must implement UISplitViewControllerDelegate.
so first thing you need to do :
class ContactsMaster: UITableViewController,UISplitViewControllerDelegate {
and override this function in your master:
func splitViewController(splitViewController: UISplitViewController, collapseSecondaryViewController secondaryViewController: UIViewController, ontoPrimaryViewController primaryViewController: UIViewController) -> Bool {
return true
}
then in viewDidLoad(master class) add following cods:
self.splitViewController!.delegate = self;
self.splitViewController!.preferredDisplayMode = UISplitViewControllerDisplayMode.AllVisible
self.extendedLayoutIncludesOpaqueBars = true
I think you skip many steps for configuring splitviewcontroller, if you want to understand all the way you can read one of many tutorials written for it, like:
http://nshipster.com/uisplitviewcontroller/

Did you by chance forget to set the ContactsDetail class for your Detail VC in your Storyboard?

Related

Segue not working in Swift

I'm trying to pass a certain part of an array to another view controller based on which tableview cell you click on. So when I click on the third cell for example, it'll send the array data from the third cell to the next screen. This data is an array called "todos". When I try to receive the data in the next screen, there is no data.
Code:
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "EditTodo" {
print("it is")
var vc = segue.destination as! ViewController
// var indexPath = tableView.indexPathForCell(sender as UITableViewCell)
var indexPath = tableView.indexPathForSelectedRow
if let index = indexPath {
vc.todo = todos[index.row]
}
}
}
I'm not sure if it's being called at all, or what. The identifier is correct, and I'm not sure what else to do. (When I run it, the print function is not called, but I'm not even sure if it's supposed to be).
Here is the whole page of code from the 'sending data' page with the table:
import UIKit
var todos: [TodoModel] = []
var filteredTodos: [TodoModel] = []
class HomeViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, UISearchDisplayDelegate {
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func viewDidAppear(_ animated: Bool) {
print("i think it worked...")
let defaults = UserDefaults.standard
if todos.count > 0 {
// Save what we have
let data = NSKeyedArchiver.archivedData(withRootObject: todos)
defaults.set(data, forKey: "TDDATA")
defaults.synchronize()
print("saved \(todos.count)")
} else if let storedTodoData = defaults.data(forKey: "TDDATA"),
let storedTodos = NSKeyedUnarchiver.unarchiveObject(with: storedTodoData) as? [TodoModel] {
// There was stored data! Use it!
todos = storedTodos
print("Used \(todos.count) stored todos")
tableView.reloadData()
self.tableView.tableFooterView = UIView()
}
//print([todos.first])
print("Here?: \(todos.first?.title)")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override var prefersStatusBarHidden: Bool {
return true
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if tableView == searchDisplayController?.searchResultsTableView {
return filteredTodos.count
}
else {
return todos.count
}
}
// Display the cell
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Must use 'self' here because searchResultsTableView needs to reuse the same cell in self.tableView
let cell = self.tableView.dequeueReusableCell(withIdentifier: "todoCell")! as UITableViewCell
var todo : TodoModel
if tableView == searchDisplayController?.searchResultsTableView {
todo = filteredTodos[indexPath.row] as TodoModel
}
else {
todo = todos[indexPath.row] as TodoModel
}
//var image = cell.viewWithTag(101) as! UIImageView
var title = cell.viewWithTag(102) as! UILabel
var date = cell.viewWithTag(103) as! UILabel
//image.image = todo.image
// image = UIImageView(image: newImage)
// if image.image == nil{
// print("nilish")
// image = UIImageView(image: UIImage(named: "EmptyProfile.png"))
// }
// image.image = todo.image
// if image.image == nil{
// print("pic is nil")
// image.image = UIImage(named: "CopyEmptyProfilePic.jpg")
// }
title.text = todo.title
date.text = "\(NSDate())"
let locale = NSLocale.current
let dateFormat = DateFormatter.dateFormat(fromTemplate: "yyyy-MM-dd", options:0, locale:locale)
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = dateFormat
return cell
}
// MARK - UITableViewDelegate
// Delete the cell
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == UITableViewCellEditingStyle.delete {
todos.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath as IndexPath], with: UITableViewRowAnimation.automatic)
let defaults = UserDefaults.standard
if todos.count >= 0 {
// Save what we have
let data = NSKeyedArchiver.archivedData(withRootObject: todos)
defaults.set(data, forKey: "TDDATA")
defaults.synchronize()
print("saved \(todos.count)")
} else if let storedTodoData = defaults.data(forKey: "TDDATA"),
let storedTodos = NSKeyedUnarchiver.unarchiveObject(with: storedTodoData) as? [TodoModel] {
// There was stored data! Use it!
todos = storedTodos
print("Used \(todos.count) stored todos")
}
tableView.reloadData()
}
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 80
}
// Edit mode
override func setEditing(_ editing: Bool, animated: Bool) {
super.setEditing(editing, animated: animated)
tableView.setEditing(editing, animated: true)
}
// Move the cell
func tableView(tableView: UITableView, canMoveRowAtIndexPath indexPath: NSIndexPath) -> Bool {
return self.isEditing
}
// func tableView(tableView: UITableView, moveRowAtIndexPath sourceIndexPath: NSIndexPath, toIndexPath destinationIndexPath: NSIndexPath) {
// let todo = todos.removeAtIndex(sourceIndexPath.row)
//todos.insert(todo, atIndex: destinationIndexPath.row)
// }
// MARK - UISearchDisplayDelegate
// Search the Cell
func searchDisplayController(controller: UISearchDisplayController, shouldReloadTableForSearchString searchString: String?) -> Bool {
//filteredTodos = todos.filter({( todo: TodoModel) -> Bool in
// let stringMatch = todo.title.rangeOfString(searchString)
// return stringMatch != nil
//})
// Same as below
filteredTodos = todos.filter(){$0.title.range(of: searchString!)
!= nil}
return true
}
// MARK - Storyboard stuff
// Unwind
#IBAction func close(segue: UIStoryboardSegue) {
print("closed!")
tableView.reloadData()
}
// Segue
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "EditTodo" {
print("it is")
var vc = segue.destination as! ViewController
// var indexPath = tableView.indexPathForCell(sender as UITableViewCell)
var indexPath = tableView.indexPathForSelectedRow
if let index = indexPath {
vc.todo = todos[index.row]
}
}
}
}
How I am receiving the data in viewDidLoad of receiving screen:
nameOfDocTF.delegate = self
nameOfDocTF.text = todo?.title
outputTextView.attributedText = todo?.desc
print(todo?.title)
//prints "nil"
As I suspected you´re missing the didSelectRowAt function which will be called when you click on a cell and from there you need to call your prepareForSegue. Implement the following function and try:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.performSegue(withIdentifier: "EditTodo", sender: indexPath.row)
}
And then replace your prepareForSegue function with the following:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "EditTodo" {
let vc = segue.destination as! ViewController
if let index = sender as? Int {
vc.todo = todos[index]
}
}
}

in iPad screen it is not displaying split view

In this split view was not displaying on the iPad screen if I drag it was displaying and if I select an index it is not displaying on the label
class ListTableViewController: UITableViewController {
let names = ["One","Two","Three","Four","Five","Six","Seven","Eight","Nine","Ten"]
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return names.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CellIdentifier", for: indexPath)
cell.isSelected = true
cell.textLabel?.text = names[indexPath.row]
return cell
}
// MARK:- Storyboard segue
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "ShowDetailIdentifier") {
var detail: DetailsViewController
if let navigationController = segue.destination as? UINavigationController {
detail = navigationController.topViewController as! DetailsViewController
} else {
detail = segue.destination as! DetailsViewController
}
if let path = tableView.indexPathForSelectedRow {
detail.selectedIndex = path.row + 1
}
}
}
the code in master view controller
#IBOutlet weak var numberLabel: UILabel!
var selectedIndex:Int = 1
override func viewDidLoad() {
super.viewDidLoad()
numberLabel?.text = "\(selectedIndex)"
print(selectedIndex)
if splitViewController?.responds(to: #selector(getter: UISplitViewController.displayModeButtonItem)) == true {
navigationItem.leftBarButtonItem = splitViewController?.displayModeButtonItem
navigationItem.leftItemsSupplementBackButton = true
}
the code in details view controller
class SplitViewController: UISplitViewController,UISplitViewControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
splitViewController?.preferredDisplayMode = .primaryOverlay
splitViewController?.delegate = self
// Do any additional setup after loading the view.
}
func splitViewController(_ splitViewController: UISplitViewController,
collapseSecondary secondaryViewController: UIViewController,
onto primaryViewController: UIViewController) -> Bool {
return true
}
the code for split view controller
you are missing thisatableView.reloadData()on yourviewDidLoadorviewDidAppear
I can't see where are you initializing the splitviewcontroller you need to pass the TableViewController, and detailViewController.
You need to pass it in the viewDidLoad of the class inheriting form UISplitViewController
self.viewControllers = [masterNav, detail]
and to always show splitviewcontroller you need this
self.displayMode = .allVisible

Swift 3 UITableView not updating

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.

Segue from tableview to detail doesn't work

I'm building a table view for shops in our town, and I'd like to be able to click on the name of the shop to see some more details. You can find some pictures of the main.storyboard and the files here. I also added a video of the problem with the clicking.
The code of the masterViewController is mentioned below:
import UIKit
class MasterTableViewController: UITableViewController {
// MARK: - Properties
var detailViewController: DetailViewController? = nil
var winkels = [Winkel]()
// MARK: - View Setup
override func viewDidLoad() {
super.viewDidLoad()
winkels = [
Winkel(category:"Literature", name:"Standaard"),
Winkel(category:"Literature", name:"Acco"),
Winkel(category:"Clothing", name:"H&M"),
Winkel(category:"Clothing", name:"C&A"),
Winkel(category:"Clothing", name:"Patio"),
Winkel(category:"Restaurants", name:"De 46"),
Winkel(category:"Restaurants", name:"Het hoekske"),
Winkel(category:"Supermarkets", name:"Carrefour"),
Winkel(category:"Supermarkets", name:"Colruyt")
]
winkels.sortInPlace({ $0.name < $1.name })
if let splitViewController = splitViewController {
let controllers = splitViewController.viewControllers
detailViewController = (controllers[controllers.count - 1] as! UINavigationController).topViewController as? DetailViewController
}
self.tableView.reloadData()
}
override func viewWillAppear(animated: Bool) {
clearsSelectionOnViewWillAppear = splitViewController!.collapsed
super.viewWillAppear(animated)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// MARK: - Table View
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return winkels.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
let winkel = winkels[indexPath.row]
cell.textLabel!.text = winkel.name
cell.detailTextLabel?.text = winkel.category
return cell
}
// MARK: - Segues
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showDetail" {
if let indexPath = tableView.indexPathForSelectedRow {
let winkel = winkels[indexPath.row]
let controller = (segue.destinationViewController as! UINavigationController).topViewController as! DetailViewController
controller.detailWinkel = winkel
controller.navigationItem.leftBarButtonItem = splitViewController?.displayModeButtonItem()
controller.navigationItem.leftItemsSupplementBackButton = true
}
}
}
}
The last part shows the segue to the next navigation controller (named showDetail, and is of kind "show Detail (e.g. replace)".
Below is the code of the detailViewController:
import UIKit
class DetailViewController: UIViewController {
#IBOutlet weak var detailDescriptionLabel: UILabel!
#IBOutlet weak var WinkelImageView: UIImageView!
var detailWinkel: Winkel? {
didSet {
configureView()
}
}
func configureView() {
if let detailWinkel = detailWinkel {
if let detailDescriptionLabel = detailDescriptionLabel, WinkelImageView = WinkelImageView {
detailDescriptionLabel.text = detailWinkel.name
WinkelImageView.image = UIImage(named: detailWinkel.name)
title = detailWinkel.category
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
configureView()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
I'm not sure what i'm doing wrong so that the clicking won't work.. Thanks in advance for teaching me how to fix this!
You need to implement the method didSelectRowAtIndexPath for the tableview in the first class. In this, you will perform the segue that actually moves from one ViewController to another.

How to perform a different segue when in Edit mode (in UITableView)?

I am creating a simple iPhone app using UITableView. I am using the default MasterDetail application template. Right now (in Edit mode) when I press any of the table cells nothing happens. However, when I am in normal mode the detail segue is initiated. How to override the Edit mode so that I initiate a custom segue to go to a different UIViewController.
P.S.: I still want to preserve the inherit delete functionality.
This is my code in my MasterViewController:
class MasterViewController: UITableViewController {
let kFileName: String = "/resolutionData.plist"
var resolutions = [Dictionary<String,String>]()
var achievedResolutions = [Dictionary<String,String>]()
// TO DO create a class to get this array
let iconArray = ["Fish","Fly","Heart","HelpingHand","Melon","Star","Tentacles","Volunteering"]
override func awakeFromNib() {
super.awakeFromNib()
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.navigationItem.leftBarButtonItem = self.editButtonItem()
let addButton = UIBarButtonItem(barButtonSystemItem: .Add, target: self, action: "insertNewObject:")
self.navigationItem.rightBarButtonItem = addButton
//extract the path
NSLog(dataFilePath())
/**
* Check where is the sandbox of the application
* and if there is read from the data file and story it to "objects" array
*/
if NSFileManager.defaultManager().fileExistsAtPath(dataFilePath()){
var temp = NSArray(contentsOfFile: dataFilePath()) as! [Dictionary<String,String>]
for res in temp{
if res["isAchieved"] == "Y"{
achievedResolutions.append(res)
}else{
resolutions.append(res)
}
}
//... if there is not - create it
} else {
let data = [["name":"Resolution name test","startingDate":"24-11-15","achievingDate":"01-01-2016","icon":iconArray[0],"isAchieved":"N"] as NSDictionary] as NSArray
//if the file does not exist...
if !NSFileManager.defaultManager().fileExistsAtPath(dataFilePath()){
//... create it
NSFileManager.defaultManager().createFileAtPath(dataFilePath(), contents: nil, attributes: nil)
}
//write to it
data.writeToFile(dataFilePath(), atomically: true)
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func insertNewObject(sender: AnyObject) {
performSegueWithIdentifier("editDetails", sender: sender)
}
func saveDateToFile(){
let data = resolutions as NSArray
data.writeToFile(dataFilePath(), atomically: true)
}
func notifyTableViewForNewInsertion(){
let indexPath = NSIndexPath(forRow: 0, inSection: 0)
self.tableView.insertRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
}
// MARK: - Segues
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showDetail" {
if let indexPath = self.tableView.indexPathForSelectedRow() {
let object = resolutions[indexPath.row] as Dictionary
(segue.destinationViewController as! DetailViewController).detailItem = object
}
}
}
// MARK: - Table View
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 2
}
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if section == 0 {
return "Active"
}else{
return "Achieved"
}
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
return resolutions.count
}else{
// TODO replace that with an actual array count
return achievedResolutions.count
}
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! UITableViewCell
if indexPath.section == 0{
let object: AnyObject? = resolutions[indexPath.row] ["name"]
cell.textLabel!.text = object as? String
cell.detailTextLabel!.text = resolutions[indexPath.row]["achievingDate"]
cell.imageView!.image = UIImage(named: resolutions[indexPath.row]["icon"]!)
} else {
let object: AnyObject? = achievedResolutions[indexPath.row] ["name"]
cell.textLabel!.text = object as? String
cell.detailTextLabel!.text = resolutions[indexPath.row]["achievingDate"]
cell.imageView!.image = UIImage(named: resolutions[indexPath.row]["icon"]!)
}
return cell
}
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
// Return false if you do not want the specified item to be editable.
return true
}
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
resolutions.removeAtIndex(indexPath.row)
saveDateToFile()
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .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.
}
}
func dataFilePath() -> String{
let paths = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)
//get the first path and convert it to str
let docDicrectyory: String = paths[0] as! String
return "\(docDicrectyory)\(kFileName)"
}
}
Instead of performing your segue directly from the storyboard, add a UITableViewDelegate method:
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if tableView.editing {
performSegueWithIdentifier("id_Segue_Editing_VC", sender: tableView.cellForRowAtIndexPath(indexPath))
} else {
performSegueWithIdentifier("id_Segue_Standard_VC", sender: tableView.cellForRowAtIndexPath(indexPath))
}
}
Set the sender to the selected cell -- this matches the default UIKit behavior.
Then in your prepareForSegue method you can add custom value to your view controllers according to the segue identifier.
Override willBeginEditingRowAtIndexPath: function. this willbe call before start editing. there you can initialize a global variable.
#property(nonatomic, String) BOOL editing
and in the
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if (editing) {
if([[segue identifier] isEqualToString:#"identifierOne"]){
}else if([[segue identifier] isEqualToString:#"identifierTwo"]){
}
}
}
There is a table view property for that:
self.tableView.allowsSelectionDuringEditing = YES;

Resources