project1(GoodAsOldPhones)
基于上次的思维导图,准备一个一个项目的记录,先上第一个项目的截图
这是xcode中Main.storybard的截图
图片中有一个tab bar controller, 一个navigation controller,这两个在Editor菜单中的Embed in都能找到,tab bar controller 目的就是在屏幕下方生成tab bar,navigation controller的目的就是屏幕上方生成navigation bar,还有一个用于显示"老电话列表"的view controller,可以看见上面有一个table view,和一个显示老电话详情的view controller,另外一个tab bar用于显示us的内容,一共五个页面
先看ProductsTableViewController的代码
import UIKit
class ProductsTableViewController: UITableViewController {
fileprivate var products: [Product]?
fileprivate let identifer = "productCell"
override func viewDidLoad() {
super.viewDidLoad()
products = [
Product(name: "1907 Wall Set", cellImageName: "image-cell1", fullscreenImageName: "phone-fullscreen1"),
Product(name: "1921 Dial Phone", cellImageName: "image-cell2", fullscreenImageName: "phone-fullscreen2"),
Product(name: "1937 Desk Set", cellImageName: "image-cell3", fullscreenImageName: "phone-fullscreen3"),
Product(name: "1984 Moto Portable", cellImageName: "image-cell4", fullscreenImageName: "phone-fullscreen4")
]
}
// MARK: - UITableViewDataSource
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return products?.count ?? 0
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: identifer, for: indexPath)
guard let products = products else { return cell }
cell.textLabel?.text = products[(indexPath as NSIndexPath).row].name
if let imageName = products[(indexPath as NSIndexPath).row].cellImageName {
cell.imageView?.image = UIImage(named: imageName)
}
return cell;
}
// MARK: - View Transfer
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showProduct" {
if let cell = sender as? UITableViewCell,
let indexPath = tableView.indexPath(for: cell),
let productVC = segue.destination as? ProductViewController {
productVC.product = products?[(indexPath as NSIndexPath).row]
}
}
}
}
该controller继承UITableViewController
需要实现两个方法
1、numberOfRowsInSection
2、cellForRowAt
numberOfRowsInSection
返回的是一个section中有多少row,是一个int类型
cellForRowAt
这个方法返回一个UITableViewCell的实例
首先要讲的是 tableView.dequeueReusableCell(withIdentifier: identifer, for: indexPath) 这个方法,这个是为了效率,重复利用UITableViewCell,这个identifer在Main.storyboard可以设置
guard let products = products else { return cell }
Swift规定,guard - else必须同时出现,而且,else中,必须进行return。
这是一个比较常见的swift的用法,意思是除非products有值,否则return cell,有值之后会继续执行下面的代码,接下的两个方法其实是设置cell的textLabel和imageView
prepare方法
这个方法其实和tableview没啥关系,只是用于转场(segue) 的方法,定义转场是在Main.storyboard里设置的,按住ctrl,鼠标左键点击tableViewCell然后拖拉到productViewController上,选择类型为push,然后输入identifier,如下图
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showProduct" {
if let cell = sender as? UITableViewCell,
let indexPath = tableView.indexPath(for: cell),
let productVC = segue.destination as? ProductViewController {
productVC.product = products?[(indexPath as NSIndexPath).row]
}
}
}
sender如果转型成UITableViewCell成功,则tableView.indexPath可以根据传入的cell获得相应的indexPath,为啥要获得indexPath呢?因为segue.destination as? ProductViewController这个含义是转场的目的地如果是ProductViewController
的话,则把products数组中下标为indexPath(前面得到的)的赋值给ProductViewController实例的product这个属性
ProductViewController 代码
import UIKit
class ProductViewController: UIViewController {
@IBOutlet var productImageView: UIImageView!
@IBOutlet var productNameLabel: UILabel!
var product: Product?
override func viewDidLoad() {
super.viewDidLoad()
productNameLabel.text = product?.name
if let imageName = product?.fullscreenImageName {
productImageView.image = UIImage(named: imageName)
}
}
@IBAction func addToCartButtonDidTap(_ sender: AnyObject) {
print("Add to cart successfully")
}
}
这个controller没啥好讲的,类属性product就是接受上个controller的传入的值
UIImage(named: imageName)这个方法可以直接获得image
ContactViewController
import UIKit
class ContactViewController: UIViewController {
@IBOutlet weak var scrollView: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(scrollView)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if #available(iOS 11.0, *) {
scrollView.frame = CGRect(x: 0, y: view.safeAreaInsets.top, width: view.frame.width, height: view.frame.height - view.safeAreaInsets.bottom - view.safeAreaInsets.top)
} else {
scrollView.frame = CGRect(x: 0, y: topLayoutGuide.length, width: view.frame.width, height: view.frame.height - topLayoutGuide.length - bottomLayoutGuide.length)
}
scrollView.contentSize = CGSize(width: self.view.frame.width, height: 800)
print(self.view.frame.width)
}
}
这个里面其实定义了一个scrollView, if #available(iOS 11.0, *) 这个是为了适配ios11,估计是ios11 多了一个safeArea的概念,
height:view.frame.height - view.safeAreaInsets.bottom - view.safeAreaInsets.top
height: view.frame.height - topLayoutGuide.length - bottomLayoutGuide.length
scrollView两个height 在不同系统中不同的计算方式,最后设定了scrollView的contentSize,scrollView的frame 和contentSize的概念在之后的文章中会讲到
import Foundation
class Product {
var name: String?
var cellImageName: String?
var fullscreenImageName: String?
init(name: String, cellImageName: String, fullscreenImageName: String) {
self.name = name
self.cellImageName = cellImageName
self.fullscreenImageName = fullscreenImageName
}
}
这个类,类似于java中的model类,没啥好说的
最后说一下一个注意点
这里需要按住ctrl+左键点击图片拖拽到本身上来设置一个aspect的ratio,不然会在iphone8以上的设备中不适配屏幕宽度,constraints设置好才能在不同的iphone设备之间达到好的显示效果。
那么这个p1先到这里,未完待续。。。