Using UITableView in detail, how to display string in TextView (Master-Detail project, Swift, iOS) - ios

I want to use a TableView for the detail side of a Master Detail app. I have started with the standard Master Detail project in Xcode, deleted the standard app that comes with it, deleted the standard UIView detail controller, added a TableView controller, added a TextView to the prototype cell for testing, and created a new segue to the new TableView. I subclassed UITableViewCell and created an outlet (detailTextView) from the TextView to the subclass (TableViewCell). Changed the class in DetailViewController.swift from UIViewController to UITableViewController. I am successfully passing a string stringForTextView = "String for TextView" from master to the detail. But I can't figure out how to display that string in the TextView. I tried to reference the TextView text in the detail view through the outlet (detailTextView.text) but got "Use of unresolved identifier detailTextView"
Any help will be greatly appreciated.
Relevant code is shown below.
You can also download the whole project here if that would be helpful:
http://greendept.com/MasterDetailTwoTableViews/
TableViewCell.swift (subclass for prototype cell in detail)
import UIKit
class TableViewCell: UITableViewCell {
#IBOutlet weak var detailTextView: UITextView!
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
}
}
DetailViewController.swift
import UIKit
class DetailViewController: UITableViewController {
var stringForTextView : String?
var detailItem: AnyObject? {
didSet {
// Update the view.
self.configureView()
}
}
func configureView() {
// THE NEXT TWO LINES WORK: PASSED IN STRING PRINTS TO CONSOLE
let printThis = stringForTextView! as String
print("\(printThis)")
// BUT THE REFERENCE TO THE OUTLET BELOW DOES NOT WORK, GIVES
// "Use of unresolved identifier detailTextView"
detailTextView.text = printThis
}
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.
}
}
MasterViewController.swift
import UIKit
class MasterViewController: UITableViewController {
var detailViewController: DetailViewController? = nil
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
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!.collapsed
super.viewWillAppear(animated)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Segues
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showDetail" {
if let indexPath = self.tableView.indexPathForSelectedRow {
let controller = (segue.destinationViewController as! UINavigationController).topViewController as! DetailViewController
controller.stringForTextView = "String for TextView"
controller.navigationItem.leftBarButtonItem = self.splitViewController?.displayModeButtonItem()
controller.navigationItem.leftItemsSupplementBackButton = true
}
}
}
// MARK: - Table View
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
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
}
}

DetailViewController is a UITableViewController, and you can't access the detailTextView in the tableView controller. You defined the outlet in the cell, and that is where you can access and configure the detailTextView.
It doesn't make any sense to have the DetailViewController as a UITableViewController, if what you really want is to configure the text view there. Then you should set it back to a UIViewController, and add the text view as a single UITextView to the view controllers view.

This link below shows how you can change text in a cell label even though the outlet to the textview is in the cell subclass. It shows this with a single TableView.
creating custom tableview cells in swift
In adapting the above approach for my test project, I didn't have to change the Master at all. In the Detail view, the configureView() doesn't do the main job of updating the TextView. That happens in cellForRowAtIndexPath -- second to the last function in detail view. Another difference is I could not, and did not need to, implement #IBOutlet var tableView: UITableView! -- because tableView was already available as a stored property. I also had to add overrride in a couple of places. Finally, in the TableViewCell class, I added an outlet linked to the content view of the TextView. The result is that the TextView text is getting updated.
TableViewCell.swift:
import UIKit
class TableViewCell: UITableViewCell {
#IBOutlet weak var detailTextView: UITextView!
#IBOutlet weak var detailContentView: UIView!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
print ("awakeFromNib")
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
print("test")
}
}
DetailViewController.swift:
import UIKit
class DetailViewController: UITableViewController {
// #IBOutlet var tableView: UITableView! -- cannot override a stored property
var stringForTextView : String?
// Don't forget to enter this in IB also
let cellReuseIdentifier = "reuseIdentifier"
var detailItem: AnyObject? {
didSet {
// Update the view.
self.configureView()
}
}
func configureView() {
// Update the user interface for the detail item.
// stringForTextView
let printThis = stringForTextView! as String
print("\(printThis)")
// detailTextView.text = printThis
}
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
self.configureView()
}
// needed "override" here
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
// create a cell for each table view row
// needed "override" here
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:TableViewCell = self.tableView.dequeueReusableCellWithIdentifier(cellReuseIdentifier) as! TableViewCell
cell.detailTextView.text = stringForTextView
print("cell.detailTextView.text: \(cell.detailTextView.text)")
print("row : \(indexPath.row)")
return cell
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

Related

How to bring UIImageView to foreground upon tapping a tableViewCell?

currently thumbnails are being shown in UITableViewCell and upon tapping a cell, I want the image to be shown in foreground with a cross/X button on right top to dismiss the image and show tableView. I have the following code in didSelectRow:
let hoverImage = UIImageView()
hoverImage.image = UIImage(named: "splashpt")
hoverImage.contentMode = .scaleAspectFit
self.view.addSubview(hoverImage)
hoverImage.center = self.view.center
hoverImage.layer.zPosition = 5
self.view.bringSubview(toFront: hoverImage)
Still the image doesn't show up. The computation is hitting this section of the code because I'm able to debug and step through this part of the code. But nothing shows up on screen. I'm using the zPosition AND bringSubview(toFront:) and neither of the seem to work for my requirement. Any help would be greatly appreciated. Thank you.
This is a Demo table view.On tapping the table view cells a view is poped up with close button. And on pressing the close button the pop up view is closed.
import UIKit
class ViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:UITableViewCell = self.tableView.dequeueReusableCellWithIdentifier("cell")! as UITableViewCell
cell.textLabel?.text = "Happy"
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
print("Cell\(indexPath.row) is selected")
let storyBoard = UIStoryboard(name: "Main", bundle: nil)
let popoverVC = storyBoard.instantiateViewControllerWithIdentifier("PopViewController") as! PopViewController
popoverVC.delegate = parentViewController as? InfoViewDelegate
let nav = UINavigationController(rootViewController: popoverVC)
nav.modalPresentationStyle = UIModalPresentationStyle.Popover
nav.navigationBar.hidden = true
self.presentViewController(nav, animated: true)
{
}
popoverVC.passingViewController = self
}
}
This is a PopUpViewController:
import UIKit
protocol InfoViewDelegate: class
{
func infoViewClicked(tag: Int)
}
class PopViewController :UIViewController
{
#IBOutlet weak var btnClose: UIButton!
var delegate = InfoViewDelegate?()
var passingViewController: UIViewController!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewWillAppear(animated: Bool) {
}
override func viewDidDisappear(animated: Bool) {
self.presentingViewController?.dismissViewControllerAnimated(true
, completion: {
})
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
#IBAction func btnClicked(sender: UIButton) {
if(sender == self.btnClose)
{
self.delegate?.infoViewClicked(1)
self.presentingViewController?.dismissViewControllerAnimated(true
, completion: {
})
}
}
}

Storyboard initial viewcontroller doesn't appear

My app is in swift using story board. After launch file my app start the initial view controller the one I have already set the entry point. But suddenly my app doesn't appear the initial viewcontroller after the Launching screen.but if I set the entry point to another view controller its appear. The first viewcontroller viewdidload and all other UITableView delegates and datasource are running when Idebug but the screen doesnt come it just keep staying on my launch file.
What would be the reason for this? Please help me.
Thanks
UPDATE
import UIKit
class SubscriptionViewController: UIViewController,UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tblSettings: UITableView!
#IBOutlet weak var vwTitle: UIView!
var mutableDictionary :[String]=["Business","News","Sports","Entertainment","Crime","Politics"]
var imageView = UIImageView.init(frame: CGRectMake(0, 0, 20, 20))
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
tblSettings.backgroundColor=UIColor.clearColor()
tblSettings.delegate=self
tblSettings.dataSource=self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return mutableDictionary.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("SettingsCell") as! SettingsTableViewCell!
if cell == nil
{
//tableView.registerNib(UINib(nibName: "UICustomTableViewCell", bundle: nil), forCellReuseIdentifier: "UICustomTableViewCell")
tableView.registerClass(SettingsTableViewCell.classForCoder(), forCellReuseIdentifier: "SettingsCell")
cell = SettingsTableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "SettingsCell")
}
cell.backgroundColor=UIColor.clearColor()
// Custom view options
let image=UIImage.init(named: "SettingsUnChecked")
imageView.image=image
cell.accessoryType = UITableViewCellAccessoryType.None
cell.accessoryView = imageView//UIButton(frame: CGRectMake(0, 0, 20, 20))
cell.accessoryView!.backgroundColor = UIColor.clearColor()
if let label = cell.lblName{
label.text = mutableDictionary[indexPath.row]
label.textColor=UIColor.whiteColor()
}
return cell
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}

Swift 2 Nil Delegate

So I want to create a IOS application that generates a group of students, adds them to a course and then shows students. I can show students in a list in a table view but now I want to let the user touch a student's name and be taken to a page with information about that student (name highest grade etc). The student class is flawless, the course works and the only problem I have is that I can't get a student from one view to the other.
Here's what I have so far:
//
// DataTableViewController.swift
// assignment8
//
import Foundation
import UIKit
class DataTableViewController: UITableViewController {
var delegate:StudentSelectionDelegate! = nil
var students = [Student]();
var course = Course();
// MARK: - UITableViewDataSource
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
}
func didSelectStudent(controller:UITableViewController, student:Student!) {
controller.navigationController?.popViewControllerAnimated(true)
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
self.course = courseStorage.getCourse();
self.students = course.getArrayOfStudentSortedByLastName();
return course.count;
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let row = indexPath.row
let currentStudent = students[row];
if (delegate != nil) {
delegate.didSelectStudent(self,student:currentStudent)
}
else {
print ("delegate is nil :(");
}
tableView.deselectRowAtIndexPath(indexPath, animated: true)
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("studentCell", forIndexPath: indexPath)
cell.textLabel?.text = students[indexPath.row].lastName + ", " +
students[indexPath.row].firstName;
return cell
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
print("ping");
if segue.identifier == "studentSegue" {
let nextScene = segue.destinationViewController as! studentViewController
// Pass the selected object to the new view controller.
if let indexPath = self.tableView.indexPathForSelectedRow {
let selectedStudent = students[indexPath.row]
print (selectedStudent.firstName);
nextScene.student = selectedStudent;
}
}
}
}
and
//
// DataViewController.swift
// assignment8
//
import UIKit
class DataViewController: UIViewController {
#IBOutlet weak var dataLabel: UILabel!
var dataObject: String = ""
let tableData = ["One","Two","Three"];
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.dataLabel!.text = dataObject
}
func tableView(tableView: UITableView!, numberOfRowsInSection section: Int)
-> Int {
return self.tableData.count;
}
}
and
//
// studentViewController.swift
// assignment8
//
import UIKit
protocol StudentSelectionDelegate {
func didSelectStudent(controller: UITableViewController, student:Student)
}
class studentViewController: UIViewController {
var delegate = StudentSelectionDelegate.self;
var name = String();
var student = Student();
#IBOutlet weak var StudentName: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
func didSelectStudent(controller:UITableViewController, student:Student!) {
student.description;
print ("pong")
StudentName.text = student.firstName + " " + student.lastName;
controller.navigationController?.popViewControllerAnimated(true);
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// override func viewWillAppear(animated: Bool) {
// StudentName.text = name
// }
}
This is my storyboard so far.
So, any time I try clicking a student it will print the message that I've decided to use if the delegate is nil. So far I've tried looking at all the other answers on SO but none of them have fixed my issue.
To be able to send information from one view controller to another you should use segues. It seems like that's what you're doing according to the image. If you don't know how to use a segue, you can find a good answer here: Sending data with Segue with Swift
With segues you'll be able to set the delegate of the next view controller:
protocol MyDelegate {
func myFunction()
}
class FirstViewController: UIViewController, MyDelegate {
func myFunction() {
// do what the function does
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let secondVC = segue.destinationViewController as? SecondViewController {
secondVC.delegate = self
}
}
}
class SecondViewController: UIViewController {
var delegate: MyDelegate!
}
Before you segue to the second view controller (you're preparing for the segue), you set the delegate variable of SecondViewController to self, because FirstViewController conforms to MyDelegate protocol so it can be used there. Now, in SecondViewController you can use delegate.myFunction() and it will do whatever is written inside the FirstVC's function, because the FirstVC is SecondVC's delegate.

Swift tableview in popover doesn't show data

I am trying to setup a popover view containing a textfield and a tableview, but I couldn't make the tableview to show the data. It would be much appreciated if you could help me on this.
On the main ViewController, I put a label to trigger the segue of popover,
import UIKit
class ViewController: UIViewController, UIPopoverPresentationControllerDelegate {
#IBOutlet weak var textField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func popover(sender: AnyObject) {
self.performSegueWithIdentifier("ShowDetails", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "ShowDetails" {
if let controller = segue.destinationViewController as? UIViewController {
controller.popoverPresentationController!.delegate = self
controller.preferredContentSize = CGSize(width: 320, height: 50)
}
}
}
func adaptivePresentationStyleForPresentationController(controller: UIPresentationController) -> UIModalPresentationStyle {
return .None
}
}
A PopoverCell is setup for the prototype cell,
import UIKit
class PopoverCellTableViewCell: UITableViewCell {
#IBOutlet weak var AreaCellLabel: UILabel!
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
}
}
A PopoverControllerView is set for the popover itself.
import UIKit
class PopoverViewController: UIViewController, UITextFieldDelegate, UITableViewDataSource, UITableViewDelegate {
var areas = [Area]()
// #IBOutlet weak var NewArea: UITextField!
// #IBOutlet weak var SaveNewArea: UIButton!
#IBOutlet weak var subtableView: UITableView!
override func viewDidLoad() {
subtableView.dataSource = self
subtableView.delegate = self
super.viewDidLoad()
LoadsampleArea()
// Do any additional setup after loading the view.
}
func LoadsampleArea () {
let area1 = Area(AreaName:"Moountain")!
let area2 = Area(AreaName:"ByHill")!
let area3 = Area(AreaName:"Yard")!
areas += [area1, area2, area3]
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return areas.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// Table view cells are reused and should be dequeued using a cell identifier.
let cellIdentifier = "AreaCell"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! PopoverCellTableViewCell
let area = areas[indexPath.row]
cell.AreaCellLabel.text = area.AreaName
dismissViewControllerAnimated(true, completion:nil)
return cell
}
}
and a simple data file to put the data.
import UIKit
class Area {
var AreaName: String
init? (AreaName: String) {
self.AreaName = AreaName
if AreaName.isEmpty {
return nil
}
}
}
Where is your UITableView object in your PopoverViewController class ? I can't see any reference to it.
Maybe your didn't copy-paste it since the textfield is commented too, in this case I'll suggest to check if the delegate an datasource are set property.

Swift - TableView of steppers, click one stepper in a cell and other steppers get activated?

I am new to IOS and basically I have a tableView and whenever it has 40 cells and each cell has a stepper and a label. the label displays the stepper's value. The tableview generates the cells fine, but the problem is that whenever I click the stepper in one cell, some other random cells also have their steppers activated.This is swift by the way. Here is the code for the cell:
import UIKit
class StudentTableViewCell: UITableViewCell {
#IBOutlet weak var studentNameAndValue: UILabel!
#IBOutlet weak var studentValueChanger: UIStepper!
let name:String?
let value:Int?
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
}
#IBAction func stepperValueChanged(sender: AnyObject) {
studentNameAndValue.text = "\(name): \(Int(studentValueChanger.value))"
}
}
Here is the code for the viewcontroller:
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
tableView.delegate = self
tableView.dataSource = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 40
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("studentCell") as StudentTableViewCell
return cell
}
}
The problem is in your table view controller. It would be better to put the value-changed method there. Alternatively, review your cellForRowAtIndexPath method. You are not updating the cells correctly when they are recycled.
You have to set the value of stepper and label explicitly in cellForRowAtIndexPath. You cannot read these values from the cell - they should be in your datasource (i.e. the table view controller should know what to display for a given index path).
Connect the stepper handler to the method in the view controller, then identify the proper index path via the sender argument.
#IBAction func stepperChanged(sender: UIStepper) {
let point = sender.convertPoint(CGPointZero, toView: tableView)
let indexPath = self.tableView.indexPathForRowAtPoint(point)!
let myData = dataArray[indexPath.row] // or whatever your datasource
// if you need to update the cell
let cell = self.tableView.cellForRowAtIndexPath(indexPath)
}

Resources