//---------- 3.1 Variable ------------------------ var age: Int - Unlike most of OO, for exampe, in Jave like public int age = 3 var age: Int do { print(age) } catch { print("never reach here") } - the above code will not run - compiling error: constant 'age' used before being initialized - modify it by var age:Int = 3 //---------- 3.2 func ------------------------ // --- syntax-1 --- // declare a func func div1(a:Int, b: Int) -> Int { return a /b } // call it let result1 = div1(a:10, b:2) print(result1) // --- syntax-2 using underscore --- // declare a func func div2(_ a:Int, _ b: Int) -> Int { return a /b } // call it let result2 = div2(10,2) // classic look print(result2) //---------- 3.3.1 class basic----------------- // lab in Xcode, add a label and button, connect to the view controller // label name is msg // outside class ViewController, define a class and init the properties class Dog{ var name: String = "" var weight: Int = 0 } // in the handler for the button, add the code as below let dog1 = Dog() // create an instance dog1.name = "Tairo" // set properties dog1.weight = 32 // output the data self.msg.text = " \(dog1.name) , \(String(dog1.weight))" //---------- 3.3.2 class inheritance ----------------- In the following apple link, you'll find the demo class MediaItem { var name: String init(name: String) { self.name = name } } class Movie: MediaItem { var director: String init(name: String, director: String) { self.director = director super.init(name: name) } } class Song: MediaItem { var artist: String init(name: String, artist: String) { self.artist = artist super.init(name: name) } } let library = [ Movie(name: "Casablanca", director: "Michael Curtiz"), Song(name: "Blue Suede Shoes", artist: "Elvis Presley"), Movie(name: "Citizen Kane", director: "Orson Welles"), Song(name: "The One And Only", artist: "Chesney Hawkes"), Song(name: "Never Gonna Give You Up", artist: "Rick Astley") ] notes: * class Movie and Song are inherited from MediaItem. * All the classes use method init. * When creating a instance, key word new is not used. * and, its init method is used. *The type of library is [AnyObject]. comments: 1. There is no lab here. 2. Lab will be demo in 3.5.4 Optional for type cast. 3. Here, it shows a model style in swift. 4. The swift model class code can be in a different file. 5. iOS uses MVC design pattern. It is a dialect.//---------- 3.4 Any ----------------- class Product{} // define a class let p1 = new Product() // create an instance let a: [Any] = [1, 0.99, "hello", true, p1] //---------- 3.5 Optional -------------- - To being able to understand some code is important. - Sometimes, those code are provided, and you have to modify them. - In Swift, optional concept is quite unique. - There are two main types - variable with nil value, variable type convertion. - The related understanding helps. - Most times, in the Xcode editor, some warning or error icons show up, you can click the fix icons to modify the code - 3.5.1 unwrap and test nil * When some variable can not be empty, use optional. * Unwrap it, if the assignment is good, use the unwrapped data. * if it is bad, nothing happens. * test in Xcode - one label and one button let theUrl: String? = "https://xcloud.com/yz" //let theUrl: String? = nil if let temp = theUrl { // unwrap and test nil print(theUrl) print(temp) self.result.text = theUrl } else { print(theUrl) print("because of nil, no update the label") } test result with data - wrapped data: Optional("http://xcloud/yz") - unwrapped data: http://xcloud/yz - the unwrapped data is used. test result without data - wrapped data: nil - Nothing happens. - 3.5.2 Unconditional Unwrapping, ! operator When you’re certain that an instance of Optional contains a value, you can unconditionally unwrap the value by using the forced unwrap operator (postfix !). let name: String! = "John Doe" self.result.text = name You can use it directly. - 3.5.3 Optional chain class Residence{ var numOfRooms = 1 } class Person { // Class Person uses class Residence var residence: Residence? } class ViewController: UIViewController { @IBOutlet weak var result: UILabel! @IBAction func test(_ sender: Any) { let johnResidence = Residence() johnResidence.numOfRooms = 4 let john = Person() john.residence = johnResidence // comment out this to test nil if let roomCount = john.residence?.numOfRooms { // optional chain to test nil for residence self.result.text = String(roomCount) // if success, retrieve data } else { print("Unable to retrieve the number of rooms") // if fail, no data retrival } } ... } - 3.5.4 Optional for type cast as? as! In 3.3.2 class inheritance, there are 3 classes - super class: MediaItem - child class: Movie, song then, create a constant library: [MediaItem] The following code is to use the above setup to demo optioanl cast for item in library { if let movie = item as? Movie { print("Movie: \(movie.name), dir. \(movie.director)") } else if let song = item as? Song { print("Song: \(song.name), by \(song.artist)") } } * iterate each item in the array * downcast item from MediaItem to Movie * if succeed, assign it * if fail, do not assign * the same for Type song lab: * create a iOS project, in the storyboard, add a label, and a button * connect them to the files. * In ViewController.swift, ouside class ViewController, add code for class MeiaItem, Movie, Song * in the button's event handler method, add code as below: class MediaItem{...} class Movie: MediaItem{...} class Song: MediaItem{...} class ViewControler...{ ... @IBAction func test(_ sender: Any){ let library = [ Movie(name: "...", director: "..."), Song(name: "...", artist: "...), ...] for item in library { if let movie = item as? Movie { print("Movie: \(movie.name), dir. \(movie.director)") } else if let song = item as? Song { print("Song: \(song.name), by \(song.artist)") } } * the result is in the debug area. * for-in loop is used to use test this scenario for querying data with different child types. * to see the part in the middle of a chain can be casted or not.
class ViewController: UIViewController, UITableViewDataSource {
func tableView(_ tableView: UITableView,
numberOfRowsInSection section: Int) -> Int {
return 15
}
func tableView(_ tableView: UITableView,
cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
cell.textLabel?.text = "row \(indexPath.row)"
return cell
}
-- step 4 contents in a cell
-- step 4.1 imageView object * prepare one image in 3 different sizes myImage.png 50 * 50 myImage@2x.png 100 * 100 myImage@3x.png 150 * 150 * add them to the project - in the project navigator, open Assets.xcassets - click plus icon, click New Image Set - Drag these 3 images from the finder onto the target locations - name the data set to myImage * render it by code no work in storyboard - in ViewController, method cellForRowAt - after a cell is created, add code as below: cell.imageView?.image = UIImage(named: "myImage") * test For the cell in each row, the image is on the left. -- step 4.2 accessoryType object cell.accessoryType = .disclosureIndicator //go to its detail screen -- step 4.3 detailTextLabel object cell.detailTextLabel?.text = "data for detailTextLabel" // below the line for textLabel -- test: you can each cell display as below image icon text navigation icon detail text
-- step 5 section, indexPath's property
-- step 5.1 numberOfSections * implementing a table view data source method(not required) * if it is not implemented, the default is 1. * in ViewController.swift, inside the class, type the method name, the code intelliscence will complete the coding. * modify as: return 3 * test it to see 3 sections. -- step 5.2 numberOfRowsInSection * implementing a table view data source method(required) * modify as below: switch section { case 0: return 5 case 1: return 2 case 3: return 3 default: return 0 *test -- step 5.3 cellForRowAt * implementing a table view data source method(required) * modify as below: func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "cell1", for: indexPath) switch indexPath.section { case 0: cell.imageView?.image = UIImage(named: "myImage") cell.textLabel?.text = "row \(indexPath.row)" cell.accessoryType = .disclosureIndicator cell.detailTextLabel?.text = "detailTextLabel for section 1" case 1: .... case 2: .... default: cell.textLabel?.text = "error xyz" } return cell } * test -- step 5.4 titleForHeaderInSection * implementing a table view data source method(required) * modify as below: switch section { case 0: return "Section 1" case 1: return "Section 2" case 3: return "section 3" default: return nil *test
class ViewController.... var breakfastMenu = [ ["name": "oatmeal, eggs, coffee", "price": 12.50], ["name": "toast, fruits, tea", "price": 10.50], ["name": "French toast, scramlbe eggs, coffee", "price": 14.50], ["name": "pancake, porch eggs, coffee", "price": 13.50], ["name": "oatmeal, coffee", "price": 9.50], ] var lunchMenu = [ ["name": "Salad, coffee", "price": 10.50], ["name": "burger", "price": 11.50], ["name": "French chicken", "price": 15.50] ] var dinnerMenu = [ ["name": "Salmon", "price": 15.50], ["name": "clam chowder", "price": 12.50], ["name": "Onion soup", "price": 8.50] ]
-- method numberOfRowsInSection, using data as below: switch section { case 0: return breakfastMenu.count case 1: return lunchMenu.count case 2: return dinnerMenu.count .... -- method cellForRowAt, using data as below switch indexPath.section { case 0: let name1 = breakfastMenu[indexPath.row]["name"] as! String row is for specific table view section for the view row is also for the corresponding data array. cell.textLabel?.text = name1 ... let price1 = breakfastMenu[indexPath.row]["price"] as! Double cell.detailTextLabel?.text = String(price1) case 1: // lunchMenu case 2: // dinnerMenu
print("You select section \(indexPath.section), row \(indexPath.row)")
import UIKit class ViewController: UIViewController,UITableViewDataSource { // 1. data --------------------------------------- let amTasks = ["check weather", "plan todo", "walk dogs"] let pmTasks = ["do yard work", "study"] let eveningTasks = ["watch TV"] // 2. data source methods ------------------------- func numberOfSections(in tableView: UITableView) -> Int { return 3 } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "cell1", for: indexPath) switch indexPath.section { case 0: cell.textLabel?.text = amTasks[indexPath.row] case 1: cell.textLabel?.text = pmTasks[indexPath.row] case 2: cell.textLabel?.text = eveningTasks[indexPath.row] default: cell.textLabel?.text = "error in cellForRowAt" } return cell } // not required func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { switch section { case 0: return "Morning Task" case 1: return "Afternoon Tasks" case 2: return "Evening Tasks" default: return nil } } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { switch section { case 0: return amTasks.count case 1: return pmTasks.count case 2: return eveningTasks.count default: return 0 } } // 3. UIViewController methods -------------------- 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. } }
The above is the setup for ALL the following labs.
// --------------- code is as below: ------------
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
guard editingStyle == .delete else { return }
print("-----delete ------")
switch indexPath.section {
case 0:
amTasks.remove(at:indexPath.row)
case 1:
pmTasks.remove(at:indexPath.row)
case 2:
eveningTasks.remove(at:indexPath.row)
default:
print("error in remove a row")
}
tableView.deleteRows(at: [indexPath], with: .automatic)
}
// code as below override func viewDidLoad() { super.viewDidLoad() self.navigationController?.navigationBar.barTintColor = UIColor.green self.title = "My Todolist" let addbutton = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(addRow)) self.navigationItem.rightBarButtonItem = addbutton } @objc func addRow(){ // 1 get a new row from a user's input // 1.1 create an alert let alert = UIAlertController(title: "enter a new task info, 0-based", message: "task§ion_no&row_no", preferredStyle: .alert) // 1.2 add a UITextField alert.addTextField { (taskField: UITextField) in taskField.placeholder = "ig. cook&1&1 for pm and 2nd row" } // 1.3 add a UIAlertAction alert.addAction(UIAlertAction(title: "OK", style: .default, handler: {(action:UIAlertAction) in if let taskField = alert.textFields?.first { if taskField.text == "" { print("You have to enter a task") } else { print("task is \(taskField.text ?? "new task")") // step 2: process the input data - task description, insert location within the same scope self.insertOneRow(inputRowData: taskField.text!) } } }) ) self.present(alert, animated: true, completion: nil) } func insertOneRow(inputRowData: String){ // step 2.1: parse the input data print("inputRowData = \(inputRowData)") let inArray = inputRowData.split(separator: "&") let task = inArray[0] let sectionNo = Int(inArray[1]) let rowNo = Int(inArray[2]) print("task = \(task)") print("sectionNo = \(sectionNo ?? 0)") print("rowNo = \(rowNo ?? 0)") // step 2.2: updata data switch sectionNo { case 0: amTasks.insert(String(task), at: rowNo!) print(amTasks) case 1: pmTasks.insert(String(task), at: rowNo!) print(pmTasks) case 2: eveningTasks.insert(String(task), at: rowNo!) print(eveningTasks) default: print("error in updata data") } // step 2.3: refresh view tableView.reloadData() }
// call save for adding a row alert.addAction(... .... self.insertOneRow(....) self.save() ) // call save for deleting a row // in the method for commit // after remove method self.save() // call load // add at the end. self.load()
func save(){ UserDefaults.standard.set(eveningTasks, forKey: "eveTasks") UserDefaults.standard.synchronize() } func load(){ if let loadData = UserDefaults.standard.value(forKey: "eveTasks") as? [String] { eveningTasks = loadData tableView.reloadData() } }
// code for defining the full path override func viewDidLoad(){ ... let docsDir = NSSearchPathForDirectoriesInDomains(.documentDirectory, .allDomainsMask, true) fileWithFullPath = docsDir[0].appending("eveningTasks.txt") load() } // code for save func save() { let newData:NSArray = NSArray(array: eveningTasks) newData.write(toFile: fileWithFullPath, atomically: true) } // code for load func load() { print("path = \(fileWithFullPath)") if let loadedData = NSArray(contentsOfFile:fileWithFullPath) as? [String] { evenTasks = loadedData tableView.reloadData() } }