【4858.com】在不借助于三方库的景况下如何异步下载和缓存图片,创制表视图

By admin in 4858.com on 2019年9月17日

在可滚动视图(如UITableView)中异步加载大批量图形是三个很宽泛的职分。
可是,在图纸正在下载的还要又要保证应用程序流畅滚动,恐怕有一些挑衅。

连接Table Cell客户分界面到代码

在你能够在table view
cell中彰显动态数据以前,你须求创建outlet来连接storyboard中的属性和在MealTableViewCell.swift文件中象征table
view cell的代码。

连日这几个视图到MealTableViewCell.swift代码

  1. 在storyboard中,选择table view cell中的label。
  2. 开荒助编器。

    4858.com 1image:
    ../Art/assistant_editor_toggle_2x.png

  3. 如有须求,尽只怕扩大职业区空间。

    4858.com 2image:
    ../Art/navigator_utilities_toggle_on_2x.png

  4. 在编辑器选用器栏中,它显得在助编器的最上端,把助理视图从预览视图切换成切换成Automatic >
    MealTableViewCell.swift.

    4858.com 3image:
    ../Art/CTV_assistant_switchtocode_2x.png

    MealTableViewCell.swift展现在右臂的编辑器中。

  5. 在MealTableViewCell.swift中,找到class行:

class MealTableViewCell: UITableViewCell {
  1. 在class行的下边,增多下边注释:

//MARK: Properties
  1. 按住Control键,从画布中拖拽label到右臂编辑器显示的代码中,在刚才增多的疏解的底下释放。

    4858.com 4image:
    ../Art/CTV_label_dragoutlet_2x.png

  2. 在弹出的对话框中,Name字段键入nameLabel。让别的选拔保持原样。你的对话框看起来是那般的。

    4858.com 5image:
    ../Art/CTV_label_addoutlet_2x.png

  3. 点击Connect。

  4. 在storyboard中,选择table view cell的image view。
  5. 按住Control键,从画布中拖拽image
    view到左边手工编织辑器突显的代码中,在刚才加多的nameLabel属性上边释放。

    4858.com 6image:
    ../Art/CTV_imageview_dragoutlet_2x.png

  6. 在弹出的对话框中,Name字段键入photoImageView。让任何选项保持原样。并点击Connect。

    4858.com 7image:
    ../Art/CTV_imageview_addoutlet_2x.png

  7. 在storyboard中,选择table view cell的rating控件。

  8. 按住Control键,从画布中拖拽rating控件到左臂工编织辑器呈现的代码中,在刚才增多的photoImageView属性上面释放。

    4858.com 8image:
    ../Art/CTV_ratingcontrol_dragoutlet_2x.png

  9. 在弹出的对话框中,Name字段键入ratingControl。让其余选择保持原样。并点击Connect。

    4858.com 9image:
    ../Art/CTV_ratingcontrol_addoutlet_2x.png

在MealTableViewCell.swift中您的outlet看上去应该是那样的:

 @IBOutlet weak var nameLabel: UILabel! @IBOutlet weak var photoImageView: UIImageView! @IBOutlet weak var ratingControl: RatingControl!

此篇为针对ios中的UI基础知识,为了能让大家更清楚的理解,此整理中编写了采用知识点+案例的方式,配有非常详细的截图和代码注释,添加了许多大白话进行解释,如有错误之处,望指正,愿与您相互交流学习,共同进步!---"会飞的猴子_阿新"

非常多 iOS
应用程序向客户显示列表项,并允许顾客选择,删除或重新排列列表项。
不管是彰显客户地址簿中的职员列表的应用程序照旧 App Store
上销路广产品的列表,都接纳了 UITableView

重重开拓人士依附像Alamofire和SDWebImage那样的库来幸免背景图像加载最后的分神和缓存管理的分神。
可是,如若你想和煦用纯代码并不是借助第三方库, 那该怎么写吧?

加载起头数据

为了在您的table
cell中展现真实数据,你必要编写制定代码来加载这几个数据。在这一点上,你曾经有菜色的数据模型了:Meal类。你还供给保留这个菜的品性的三个列表。跟踪这些数据的最自然的地方是与菜的品性列表场景连接的自定义视图调整器子类。这些视图调控器将管理呈现菜的色调列表的视图,并有贰个援引指向在顾客分界面显示的从头到尾的经过背后的数据模型。

先是,创立三个自定义的table view controller子类来保管那个菜的品性列表场景。

创立二个UITableViewController子类

  1. 采用File > New > File (恐怕按下 Command-N)。
  2. 在产出的对话框中,接纳iOS,并且选用Cocoa Touch Class。
  3. 点击Next。
  4. 在Class字段,键入Meal。
  5. 在Subclass
    of字段,选取UITableViewController。把类标题改为MealTableViewController。
  6. 确定保障Also create XIB
    file选项未有被选中。XIB文件是三个旧的通过视图调整器设计视图处理的秘籍。它们早于storyboard出现,基本相当于代表storyboard上的十足视图。这么些视图调节器无需贰个XIB文件,因为您早已定义它连接使用的storyboard了。
  7. 担保语言是Swift。
  8. 点击Next。保存地点是项目目录。Group选项为暗许的FoodTracker。在对象区域,你的施用被增选,而利用的tests未有被挑选。
  9. 别的的挑选保持不改变,点击Create。Xcode创制了MealTableViewController.swift,三个概念自定义table
    view controller子类的源代码文件。
  10. 须求时,在Project
    navigator,拖拽MealTableViewController.swift到和别的的斯威夫特文件一同。

在这一个自定义类中,你能定义二个属性来囤积Meal对象的列表。Swift标准(斯维夫特standard library)满含叁个被称为Array的结构,它能够很好的追踪列表的项。

加载开头数据

  1. 重返规范编辑器。并尽可能的扩展工作区空间。

    4858.com 10image:
    ../Art/standard_toggle_2x.png

  2. 打开MealTableViewController.swift。

  3. 追随class行添加上面包车型客车代码:

//MARK: Properties var meals = [Meal]()

那代码在MealTableViewController声明了壹天性质,并用三个暗中同意值开始化它(贰个空的要素项为Meal对象数组)。注明meals为变量实际不是常量,意味着你能在最早化它之后还足以给它添新币素项。

  1. Table view
    controller模版包蕴众多银行卡方法以及对此那些措施的讲解。那个占位的贯彻格局你能够去除注释和扩展来定义表的外观和表现。在您设置完结模型数据后你将见到这个方法。未来,滚动到这几个办法的下面,在完工花括号的地点增添如下方法:

//MARK: Private Methods private func loadSampleMeals() { }

那是一个匡助方法,用来加载样本数量到应用。

  1. 在loadSampleMeals()方法中,首先加载上边八个菜色图样:

let photo1 = UIImage(named: "meal1") let photo2 = UIImage(named: "meal2") let photo3 = UIImage(named: "meal3")

保险图片在项目中的名字和在代码中的一致。

  1. 在加载完这几个图片后,成立八个菜的色调对象。

guard let meal1 = Meal(name: "Caprese Salad", photo: photo1, rating: 4) else { fatalError("Unable to instantiate meal1") } guard let meal2 = Meal(name: "Chicken and Potatoes", photo: photo2, rating: 5) else { fatalError("Unable to instantiate meal2") } guard let meal3 = Meal(name: "Pasta with Meatballs", photo: photo3, rating: 3) else { fatalError("Unable to instantiate meal2") }

因为菜色类的 init!(name:, photo:,
rating:)初叶化器是可难倒的,所以你供给检查伊始化器再次来到的结果。在本例中,你传递的是卓有作用的参数吗所以起首化器长久不会倒闭。若果最初化器战败,你的代码就有了错误。为了扶持你标志和修补错误,若是开首化器退步,fatalError()函数就能够在调节台打字与印刷四个错误音信,並且应用程序终止。

  1. 在创设Meal对象之后,使用如下方法增多它们到meals数组。

 meals += [meal1, meal2, meal3]
  1. 找到 viewDidLoad()方法。模版的贯彻看上去是如此的:

override func viewDidLoad() { super.viewDidLoad() // 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() }

模版达成的那些艺术富含注释,那些注释是在Xcode创设MealTableViewController.swift的时候插入的。像这样的代码注释在源代码文件中提供了提示和上下文新闻,可是在本课中您用不到它们。


  1. viewDidLoad()方法中,删除注释,并在super.viewDidLoad()前边加多如下方法来加载样本菜的色调数据。

// Load the sample data. loadSampleMeals()

当视图加载时,那几个代码调用你刚写的加载样本数量的扶植方法。你把它分离到和煦的办法中,为的是代码尤其模块化和易读。

您的viewDidLoad()方法看起来应该是那样的:

override func viewDidLoad() { super.viewDidLoad() // Load the sample data. loadSampleMeals() }]

你的loadSampleMeals()方法看起来应该是那般的:

private func loadSampleMeals() { let photo1 = UIImage(named: "meal1") let photo2 = UIImage(named: "meal2") let photo3 = UIImage(named: "meal3") guard let meal1 = Meal(name: "Caprese Salad", photo: photo1, rating: 4) else { fatalError("Unable to instantiate meal1") } guard let meal2 = Meal(name: "Chicken and Potatoes", photo: photo2, rating: 5) else { fatalError("Unable to instantiate meal2") } guard let meal3 = Meal(name: "Pasta with Meatballs", photo: photo3, rating: 3) else { fatalError("Unable to instantiate meal2") } meals += [meal1, meal2, meal3] }

检查点:通过 Product >
Build.选用创设项目。你的创设应该时不曾错误的。注意,那时候,你只怕看到一个有关在利用中无法达到View
Controller场景的Xcode警告。你将要下一课修复它。在本课剩下来的片段,先忽略它。

重要设若您运营出现难题,确定保证项目中的图片名字是还是不是真的和在代码中应用的名字一样,

本篇案例修改图为(后边会日趋优化细节难题)
4858.com 11
尤为重要内容从支付宝案例开端,器重在背后哦!

UITableView 显示全体可变数量行的单列数据。 图10.1来得了有的
UITableView 的例子。

嗯,一下就是本人所要讲的:

呈现数据

明天,你的自定义table view
controller子类,MealTableViewController,有了二个可变数组,它用部分样书数量预先做了填充。今后您须求在客户分界面上展示那么些数据。

为了显得动态数据,一个table view须求五个首要的助理:数据源(data
source)和寄托。贰个table view
数据源,仿佛它的名字暗暗表示的那样,提供那些table
view要显示的多少。二个table view委托帮忙table
view管理cell的精选、行高、以及与体现数据相关的别的地方。私下认可景况下,UITableViewController及其子类选拔须求的批评来让table
view
controller成为它关系的表视图的数据源(UITableViewDataSource公约)和嘱托(UITableViewDelegate合同)。你的干活正是在table
view controller子类中完成适当的合计章程,那样您的table
view就有了不利的作为。

Table view要求完毕多少个表视图数据源方法。

 func numberOfSections(in tableView: UITableView) -> Int func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell

率先个
numberOfSections方法,它非常的慢表视图某个许section要展现。section是表视图内部cell的可视化分组,它在表视图里有无尽数码的时候特意有用。对于简易的表视图,比如FoodTracker
应用,你只必要二个局地显得就可以了,所以完毕那么些艺术很简短。

在table view 中展现三个section


  1. MealTableViewController.swift中,找到numberOfSections数据源方法。模版完成看上去是这么的:

override func numberOfSections(in tableView: UITableView) -> Int { // #warning Incomplete implementation, return the number of sections return 0 }
  1. 把重临值改为1,删掉警告注释。

 override func numberOfSections(in tableView: UITableView) -> Int { return 1 }

以此代码让table view显示1个部分。你删掉的笺注说的是#warning Incomplete
implementation(警告 没有做达到成),但您早就落实了,所以就删掉了。

接下去的数据源方法,tableView(_:numberOfRowsInSection:),高速表视图二个加以的一对里面有个别许行。你的表视图唯有一个片段,何况每一个Meal对象都应该有投机的行。那就象征行数应该是meals数组里Meal对象的数额。

返回table view的行数

  1. 在MealTableViewController.swift中,找到tableView(_:numberOfRowsInSection:)数据源方法。它的模版完成看上去是这么的:

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { // #warning Incomplete implementation, return the number of rows return 0 }

你要回到您有的菜的色调树木。Array有一个本性称为count,它回到数组中项目标总额,所以行数就是meals.count。

  1. 改变tableView(_:numberOfRowsInSection:)数据源方法重临合适的行树,移除警告注释。

 override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return meals.count }

终极三个数据源方法,tableView(_:cellForRowAt:),为给定的行配置并提供八个cell用来呈现。表视图中的各样行都有三个cell,这几个cell决定行中展现的剧情以及那些内容怎么着布局。

对于唯有为数非常的少行的表视图,全体行或者都在显示器上,所以那么些艺术调用表中的每一行。可是有大多行的表视图,在给定的时刻里唯有小片段能够呈现在显示屏上。表视图借使只央求须要被出示的行的cell就能够大大升高了功能,那正是tableView(_:cellForRowAt:)允许表视图做的。

对于在table
view中任何给定的row,你通过获得在meals数组中的合适的Meal来配置cell,然后用Meal类的合适值来设置cell的属性。

在table view中布置和展现cell

  1. 在MealTableViewController.swift,找到tableView(_:cellForRowAt:)
    数据源方法并撤销注释。(要取消注释,只要删除围绕它的 /* 和
    */字符。)完结后这一个那几个措施看起来是这么的:

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath) // Configure the cell... return cell }

dequeueReusableCell(withIdentifier:for:)方法从那些table
view中呼吁多个cell。作为代替在顾客滚动cell的时候创制新cell并删除旧cell的格局,表格会尽可能的选择cell。若无可用的cell,
dequeueReusableCell(withIdentifier:for:)会实例化四个新的;不过如若cell滚动到显示器的外面,它们会被引用。标志符(identifier)会告诉dequeueReusableCell(withIdentifier:for:)哪个品种的cell要被成立或录取。为了这段代码能够专门的学业,你供给转移在storyboard中的cell(MealTableViewCell)标志符属性,然后增加代码来布局cell。

  1. 在这些办法一起首的地方增加上边包车型大巴代码:

// Table view cells are reused and should be dequeued using a cell identifier. let cellIdentifier = "MealTableViewCell"

这使用storyboard中设置的标志符创立了一个常量。

  1. 使用cellIdentifier变量更新方法中的标志符,像下边那样:

let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath)
  1. 因为你创制了一三个您想用的自定义cell类,将cell的花色降级到您自定义的cell的子类,MealTableViewCell。

 guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? MealTableViewCell else { fatalError("The dequeued cell is not an instance of MealTableViewCell.") }

这段代码有比比较多事要做:

  • as?
    MealTableViewCell表明式试图把重临对象从UITableViewCell类降级到MealTableViewCell类。那几个再次回到值是二个可选值。
  • guard let表明式会安全解包那些可选值。
  • 万一您的storyboard设置科学,並且cellIdentifier相配storyboard的标识符,那么降级管理将不会退步。假如降级退步,fatalError()函数就能够在调节台打字与印刷错误新闻,并甘休应用。
  1. 在guard语句前面,增多底下的代码:

// Fetches the appropriate meal for the data source layout. let meal = meals[indexPath.row]

本条代码从meals数组获取合适的菜色对象。

  1. 后天,使用meal对象来配置你的cell。用下边包车型大巴代码来替换// Configure the
    cell注释。

 cell.nameLabel.text = meal.name cell.photoImageView.image = meal.photo cell.ratingControl.rating = meal.rating

以此代码为各种在table view
cell中的视图设置合适的多寡,这个多少出自meal对象。

你的tableView(_:cellForRowAt:)方法看起来是如此的:

 override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { // Table view cells are reused and should be dequeued using a cell identifier. let cellIdentifier = "MealTableViewCell" guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? MealTableViewCell else { fatalError("The dequeued cell is not an instance of MealTableViewCell.") } // Fetches the appropriate meal for the data source layout. let meal = meals[indexPath.row] cell.nameLabel.text = meal.name cell.photoImageView.image = meal.photo cell.ratingControl.rating = meal.rating return cell }

在客户分界面展现数据的末梢一步是把MealTableViewController.swift中定义代码连接到菜的品性列表场景。

将Table View Controller指向MealTableViewController.swift

  1. 打开storyboard。
  2. 经过点击在气象dock直到整个场景有了二个紫蓝的大致来抉择table view
    controller。

    4858.com 12【4858.com】在不借助于三方库的景况下如何异步下载和缓存图片,创制表视图。image:
    ../Art/CTV_scenedock_table_2x.png

  3. 打开Identity inspector。

  4. 在Identity
    inspector中,找到Class字段,并选择MealTableViewController。

    4858.com 13image:
    ../Art/CTV_inspector_identity_tablevc_2x.png

检查点:运转应用。你在
viewDidLoad()方法中增多的档案的次类别表应该出示在table
view的cell上了。你只怕注意到在table view
cell和状态栏之间有贰个小的重合——你将要下一课修复它。

4858.com 14image:
../Art/CTV_sim_finalUI_2x.png

4858.com 15

图10.1 UITableView示例

在这篇小说中,您将应用你的技能营造多个水到渠成的应用程序,从iTunes
API中领到游戏标题和Logo的列表。 一路上,您将了然GCD(Grand Central
Dispatch)和NSCache是怎么协同工作以管理互连网图片并生成三个干净的缓存管理体系。

为导航妄图菜的色调详细情况场景

当您筹划在FoodTracker应用中落实导航,你要求删除一些占位的代码和你不再要求的的客商分界面。

扫除项目不行使的有的

  1. 开发storyboard并查阅菜的品性详细情况场景。你的菜的品性实际情况场景的顾客界面看起来是如此的:

    4858.com 16image:
    ../Art/CTV_mealsceneUI_old_2x.png

  2. 在那一个情况中,选拔Meal Name
    标签,并按下删除键删除它。在栈视图中的别的因素会自动重一直。

    4858.com 17image:
    ../Art/CTV_mealsceneUI_new_2x.png

  3. 打开ViewController.swift。

  4. 在ViewController.swift中,找到textFieldDidEndEditing方法。

 func textFieldDidEndEditing(_ textField: UITextField) { mealNameLabel.text = textField.text }
  1. 删除设置label的text属性的行。

 mealNameLabel.text = textField.text

您将火速使用新的完结来替换它。

  1. 在ViewController.swift,找到mealNameLabel的outlet,并剔除它。

@IBOutlet weak var mealNameLabel: UILabel!

因为现在你有五个视图调控器在品种中,供给给ViewController.swift多个尤为有含义的名字。

重命名ViewController.swift文件

  1. 在project navigator,点击
    ViewController.swift文件二遍,并按下回车键。Xcode会允许你为那些文件输入贰个新名字。
  2. 重命名叫MealViewController.swift,按下回车键。
  3. 在MealViewController.swift中,找到类申明行

 class ViewController: UIViewController, UITextFieldDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
  1. 变动类名叫MealViewController

 class MealViewController: UIViewController, UITextFieldDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
  1. 在文书顶上部分的评释中,也把名字从ViewController.swift
    改为MealViewController.swift。
  2. 打开storyboard。
  3. 经过点击它的地方dock选取视图调控器。

    4858.com 18image:
    ../Art/CTV_scenedock_mealscene_2x.png

  4. 当选这些视图调节器,张开Identity inspector。

  5. 在Identity
    inspector,在Class字段把ViewController改为MealViewController。

    4858.com 19image:
    ../Art/CTV_mealscenefinal_2x.png

检查点:营造或运转应用。一切应有健康。

应用UITableViewControler调控器达成支付宝口碑案例
UITableViewControler 破绽: 现阶段只适合全屏的..

开始支付 Homepwner 应用程序

在本章中,您将起动二个名称为 Homepwner
的应用程序,该应用程序保留了具备资金财产的清单。
在发生火灾或其余苦难的状态下,您将有担保公司的笔录。
(“Homepwner”)顺便说一下,不是打字错误,假诺您必要 “pwn”
一词的定义,请访问
www.wiktionary.org。)

到最近结束,您的 iOS 项目一贯十分的小,但是 Homepwner
将要八章中成长为四个很复杂的应用程序。 在本章结尾,Homepwner 将在
UITableView 中显示 Item 实例列表,如图10.2所示。

图10.2 Homepwner:阶段1

首先,请打开 Xcode 并创办三个新的 iOS Single View Application 项目。
配置如图10.3所示。

图10.3 配置Homepwner

好,让我们开头吧:

小结

在本课中,你创设了贰个自定义的table view cell。你把模型对象附加到了table
view
controller。你给模型添加了范本数量,并且你兑现利用模型数据动态的填写表格所需的表视图调整器代码。

下一课,你将加多在表视图和菜的品性视图之间导航的功用。

注意想看本课的一体化代码,下载这些文件并在Xcode中开荒。下载文件

01-UITableViewController体验

  • 也正是八个调控器自带tableView
  • viewController管理的是view
  • tableViewController管理的是tableView
  • 也正是在tableViewController下,self.view = self.tableView

UITableViewController

UITableView 是三个视图对象。 回顾一下,每一个 iOS 开采人士尽力遵从 MVC
设计形式,每一个类都属于以下项目之一:

  • model:保存数据,不驾驭UI
  • view:对客商可知,对模型对象一窍不通
  • controller:保持UI和模型对象同步并调节应用程序的流程

用作视图对象,UITableView 不管理应用程序逻辑或数量。 使用
UITableView 时,您必需考虑在应用程序中使表事业所需的别样职能:

  • UITableView 平时须求三个视图调节器来管理其在荧屏上的外观。
  • UITableView 需求一个数据源。 UITableView
    央求其数据源获得要浮现的行数,要展现在那么些行中的数据以及使
    UITableView 成为实用 UI 的别的东西。
    未有数据源,表视图只是三个空容器。 只要顺应
    UITableViewDataSource 协议,UITableViewdataSource
    能够是别的类型的靶子。
  • UITableView 日常须求三个方可通报任何对象关系到 UITableView
    事件的 委托(delegate)。 只要符合 UITableViewDelegate
    合同,委托能够是任何对象。

UITableViewController
类的实例能够知足全体多个剧中人物:视图调整器,数据源和委托。

UITableViewControllerUIViewController
的贰个子类,由此有所二个视图。 UITableViewController 的视图通常是
UITableView 的二个实例,UITableViewController 处理
UITableView 的预备和显现。

UITableViewController 创设其视图时,UITableView
dataSourcedelegate 属性将自动安装为指向
UITableViewController(图10.4)。

图10.4 UITableViewController-UITableView 关系

开采Xcode,从菜单中选取“File \ New \ Project”,选用Single
View应用程序模板并将项目命名称叫“ImagesDownloadAndCache”。

02-tableView的HeaderView和FooterView

  • tableView的header – (x, y, width) 能够任由钦定,(height) 实际数值
  • tableView的footer – (y, width) 能够不管钦赐,(x, height) 实际数值

成立三个demo体验一下:
1.预备:新建文件–>把暗中同意的的viewController删掉–>新建LJXTableViewController–>把Main.storyboard中默许的调节器删掉–>重写拖拽三个UITableViewController–>钦定初阶箭头,钦赐class.
2.增添底部视图

- (void)viewDidLoad {
    [super viewDidLoad];

    UIView *headerView = [[UIView alloc]init];
    headerView.backgroundColor =[UIColor redColor];
    //把某个控件设置到tableHeaderView上时,只有高度需要我们自己设置,前面 X , Y ,Width都可以直接给个0
    headerView.frame = CGRectMake(0, 0, 0, 100);

    //设置tableView的最顶部视图  
    self.tableView.tableHeaderView  = headerView;
    //-->这里只需要headerView赋值给tableHeaderView 这个属性,内部就在添加到父控件上我们就不用addSubview:

}

3.增加数据注脚一下头顶视图跟 “一组和多组” 未有提到

//
//  LJXTableViewController.m
//  LJX_tableView的头部和尾部视图

#import "LJXTableViewController.h"

@interface LJXTableViewController ()

@end

//重用标识
static NSString *ID = @"cell";

@implementation LJXTableViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    UIView *headerView = [[UIView alloc]init];
    headerView.backgroundColor =[UIColor redColor];
    //把某个控件设置到tableHeaderView上时,只有高度需要我们自己设置,前面 X , Y ,Width都可以直接给个0
    headerView.frame = CGRectMake(0, 0, 0, 100);


    //设置tableView的最顶部视图    注意:跟一组和多组没有关系
    self.tableView.tableHeaderView  = headerView;
    //-->这里只需要headerView赋值给tableHeaderView 这个属性,内部就在添加到父控件上我们就不用addSubview:

    //注册cell
    [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:ID];
}
//2组
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
    return  2;
}
//每组4行
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    return 4;
}

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID forIndexPath:indexPath];

    cell.textLabel.text =@(indexPath.row).description;//简单设置数据,每个cell显示一个行数

    return cell;
}
@end

4858.com 20

4.增多尾部视图

UIButton *footerBtn = [[UIButton alloc]init];
    footerBtn.backgroundColor = [UIColor purpleColor]; //紫色背景
    //把某个控件设置到tableHeaderView上时,只有高度需要我们自己设置,前面 X , Y ,Width都可以直接给个0,但是x是可以改的,改了会有影响但不建议去改
    footerBtn.frame = CGRectMake(0, 0, 0, 100);
    //设置tableView的最底部视图
    self.tableView.tableFooterView = footerBtn;

4858.com 21

子类化UITableViewController

您将在 Homepwner 中创建 UITableViewController 的子类。
创立一个名叫 ItemsViewController 的新的 Swift 文件。 在
ItemsViewController.swift 中,定义叁个名叫 ItemsViewController
UITableViewController 子类。

**`import Foundation`**
import UIKit

class ItemsViewController: UITableViewController {

}

打开 Main.storyboard。 您希望最初视图调整器是二个表视图调整器。
在画布上摘取现成的 View Controller,然后按 Delete 删除。 然后将
Table View Controller 从对象库拖动到画布上。 选取
Table View Controller 后,打开其地位检查器,将 class 更改为
ItemsViewController。 最后,打开 Items View Controller
的习性检查器,并选中 Is Initial View Controller

构建并运转您的应用程序。 您应该看到二个空的表视图,如图10.5所示。 作为
UIViewController 的子类,UITableViewController 继承了 view
属性。 当第一遍访谈此属性时,将调用 loadView()
方法,该方法成立并加载视图对象。 UITableViewController 的视图始终是
UITableView 的三个实例,由此得以用 UITableViewController
view 属性来取得明白,有光辉和空的表视图。

图10.5 空的UITableView

你不再要求模板为你创建的 ViewController.swift 文件。
在项目导航器中选拔此文件,然后
Delete 删除。

4858.com 22

03-星级评价-需要深入分析

4858.com 23

1.满星个数 "分数强转成整形,如4.5转成整形是4 那就表示有4颗满星"
2.半星个数 "分数 - 整星个数如果不为0那说明就有一个半星"
3.空心个数  "for(NSInteger i = 已添加星星个数; i < 5; i++)"

每一个商家的评分不一样,所以分值要动态传入,去动态设置星星样子

创建 Item 类

您的表视图要求出示一些行。 表格视图中的每一行将呈现叁个音信项,举个例子包含名称(name),种类号(serial number) 和 法郎值(value in dollars)。

创办两个名叫 Item 的新的Swift文件。 在 Item.swift 中,定义 Item
类并赋予它三个属性。

**`import Foundation`**
import UIKit

class Item: NSObject {
  var name: String
  var valueInDollars: Int
  var serialNumber: String?
  let dateCreated: Date
}

Item 继承自 NSObjectNSObject 是大好多 Objective-C
类承袭的基类。 您所利用的装有 UIKit 类——UIViewUITextField
UIViewController,仅举几例——直接或直接地从 NSObject 承接。
当要求与运作时系统开展交互时,您本人的类常常须求从 NSObject 继承。

请注意,serialNumber 是可选的字符串,因为 item 只怕未有系列号。
其余,请留意,未有贰个天性具有私下认可值。
您将索要在钦定的构造器中给它们赋值。

你将在营造的应用程序包蕴单个分界面,带有刷新控件的表视图,以加载内容。

04-星级评价

自定义构造器

你在第2章中领悟了结构体的构造器。在结构体上的构造器非常轻松,因为结构体不援助继承。
相反,类构造器扶助承接。

类能够有三种构造器:指派构造器(designated initializers)
便利构造器(convenience initializers)

派出构造器 是此类的严重性构造器。 每个类都至少有多少个 指派构造器。
指派构造器 可确认保障类中的全部属性都持有值。 一旦它确定保证了,指派初构造器
就能够在其父类(假设局地话)上调用 指派构造器。

Item 类上完结七个新的 指派构造器,它设置有着属性的初步值。

import UIKit

class Item: NSObject {
  var name: String
  var valueInDollars: Int
  var serialNumber: String?
  let dateCreated: Date

  init(name: String, serialNumber: String?, valueInDollars: Int) {
    self.name = name
    self.valueInDollars = valueInDollars
    self.serialNumber = serialNumber
    self.dateCreated = Date()

    super.init()
  }
}

该构造器接收 nameserialNumbervalueInDollars 参数。
由于参数名称和属性名称一致,因而你必需运用 self 来区分属性和参数。

既是你已经完毕了协调的自定义构造器,那么您将错过该类的暗中同意构造器
init()
当您的全数类的属性都存有默许值,而无需格外的行事来创立新实例时,默许的构造器很有用。
Item 类不相符此标准,由此你已经为此类申明了贰个自定义初步化器。

种种类必需至少有多少个 指派构造器,但 便利构造器 是可选的。 你能够将
便利构造器 视为助理。 二个 便利构造器
总是在同多个类上调用另贰个开头化器。 开头化器名称前面包车型地铁 convenience
关键字表示 便利构造器。

丰硕二个 便利构造器 到 Item 类中,用于随机生成 item。

convenience init(random: Bool = false) {
  if random {
    let adjectives = ["Fluffy", "Rusty", "Shiny"]
    let nouns = ["Bear", "Spork", "Mac"]

    var idx = arc4random_uniform(UInt32(adjectives.count))
    let randomAdjective = adjectives[Int(idx)]

    idx = arc4random_uniform(UInt32(nouns.count))
    let randomNoun = nouns[Int(idx)]

    let randomName = "\(randomAdjective) \(randomNoun)"
    let randomValue = Int(arc4random_uniform(100))
    let randomSerialNumber = UUID().uuidString.components(separatedBy: "-").first!

    self.init(name: randomName,serialNumber: randomSerialNumber, valueInDollars: randomValue)
  } else {
      self.init(name: "", serialNumber: nil, valueInDollars: 0)
  }
}

如果 randomtrue,则实例配置有私下名称,类别号和值。
arc4random_uniform 函数重返 0(满含0) 和
作为参数字传送递的值(不富含该值)之间的随机值。)请细心,在基准的多少个分支的最终,您正在调用到
Item 的 指派构造器。 便利构造器 必须调用同样类别的另四个构造器,而
指派构造器 必需在其父类上调用 指派构造器。

Item 类已安不忘忧妥善了。 在下有个别中,您将要表视图中显得三个 Item
的实例数组。

Xcode
storyboard文件带有四个暗中认可的UIViewController,让我们删除它并替换为贰个UITableViewController,暗许境况下UITableViewController有一个刷新控件属性。

4.1星级评价达成

创设实体文件夹,方便日后移植
为了便利最终结合代码时的移植,不直接在调控器中落到实处,而是单独创造承接UIView的类,
4858.com 24

//
//  ZFBLeverStar.h
//  LJX_星级评价

#import <UIKit/UIKit.h>

@interface ZFBLeverStar : UIView
///接收评分
@property (nonatomic, assign) CGFloat lever;

@end

4858.com 25

//
//  ZFBLeverStar.m
//  LJX_星级评价

#import "ZFBLeverStar.h"

@implementation ZFBLeverStar

//重写set方法在里面处理显示星星图片的细节
-(void)setLever:(CGFloat)lever{
    _lever = lever;

    //1.满星
    //把传过来的分数强转为整型,整数值就是满星的个数
    NSInteger fullStartCount = (NSInteger)lever;
    for(NSInteger i = 0 ; i < fullStartCount ; i++){
        [self makeLeverStarWithImageName:@"full_star" andPosition:i];
    }
    //2.半星
    // (取传过来的个数) - (强转后的整数),如果 > 0   就表示有半星
    if((lever - fullStartCount)>0){
        [self makeLeverStarWithImageName:@"half_star" andPosition:fullStartCount];
        //如果添加了一个半星那把满星个数的值做加1 用它当做 目前已经添加星星的总个数了
        fullStartCount++;
    }

    //3.空星 "如果到这来还不够5颗星 那剩下的就是空星的个数了"
    for (NSInteger i = fullStartCount; i < 5; i++) {
        [self makeLeverStarWithImageName:@"empty_star" andPosition:i];
    }
}

/**
 创建星星imageView

 @param imageName 星星图片
 @param position 星星位置 从0开始
 */
-(void)makeLeverStarWithImageName:(NSString *)imageName andPosition:(NSInteger)position{
    //1.创建imageView设置图片,那么imageView创建出来的就有尺寸
    UIImageView *imageView = [[UIImageView alloc]initWithImage:[UIImage imageNamed:imageName]];
    //2.设置图片的x
    CGFloat imageViewX = position * imageView.image.size.width;
    imageView.frame = CGRectMake(imageViewX, 0, imageView.image.size.width, imageView.image.size.height);
    [self addSubview:imageView];    
}

@end

4858.com 26

//
//  ViewController.m
//  LJX_星级评价


#import "ViewController.h"
#import "ZFBLeverStar.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    ZFBLeverStar *leverStar = [[ZFBLeverStar alloc]init];
    leverStar.frame = CGRectMake(100, 100, 60, 12);//素材中图片2X的是大小24*24,高24/2=12, 宽为5颗*12=60
    leverStar.backgroundColor = [UIColor purpleColor];

    leverStar.lever = 2.5;//先传一个2.5分验证一下
    [self.view addSubview:leverStar];
}

@end

UITableView 的数据源

在 Cocoa Touch(用于构建iOS应用程序的框架集合)中为 UITableView
提供数据行的经过与第一名的 进程编制程序 分歧。
在进度编制程序中,您能够告知表格视图应该显得怎么。 而在 Cocoa Touch
中,表视图会询问另一个对象——它的 dataSource——它应有出示怎么。
在当前景况下, ItemsViewController 是数据源,因而须求一种存款和储蓄 item
数据的法子。

您将利用一个数组来存款和储蓄 Item 实例,不过要小心。 保存 Item
实例的数组将被架空为另多少个目的 – 四个 ItemStore(图10.6)。

图10.6 Homepwner 对象图

假诺叁个目的想要查看全部的 item,它会询问包涵它们的数组的
ItemStore。 在以后的章节中,store
将承担对数组实践操作,如重复排序,增加和删除项目。
它还将承受从磁盘保存和加载 item。

始建一个名字为 ItemStore 的新的 Swift 文件。 在 ItemStore.swift
中,定义 ItemStore 类并声澳优(Ausnutria Hyproca)特品质来囤积 Item 列表。

**`import Foundation`**
import UIKit

class ItemStore {
  var allItems = [Item]()
}

ItemStore 是一个 Swift 基类(base class) – 它不会三回九转任何别的类。
与事先定义的 Item 类不同,ItemStore 不需要 NSObject
提供的别的表现。

ItemViewController 将要创制三个新的 Item 时 调用 ItemStore
的方法。 ItemStore 将威吓创立对象并将其增添到 Item
的实例数组中。

ItemStore.swift 中,实现 createItem() 来制造并重回三个新的
Item

@discardableResult func createItem() -> Item {
  let newItem = Item(random: true)

  allItems.append(newItem)

  return newItem
}

@discardableResult
注释意味着此函数的调用者能够随意地忽视调用此函数的结果。
看看上边包车型地铁代码列表来验证那一个效能。

// This is OK
let newItem = itemStore.createItem()

// This is also OK; the result is not assigned to a variable
itemStore.createItem()

从类型导航器视图中甄选Main.storyboard,将UITableViewController对象从指标库拖动到画布。
接下来,选用起来的View Controller,然后从键盘中式茶食击delete。

4.2缓和轻便重复成立难点

小编们只开创加多,未有管过移除啥的,顾导致传贰回分,创制一回….
4858.com 27
涸泽而渔重复创立的主题素材
重写最早化方法(起初化方法它只会调一次),那样一创设leverStar这一个控件,里面就自带5颗星星imageView,
4858.com 28
4858.com 29

即对ZFBLeverStar.m做了修改

//
//  ZFBLeverStar.m
//  LJX_星级评价

#import "ZFBLeverStar.h"

@implementation ZFBLeverStar

- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        //来了 直接把5个imageView创建添加好
        for (NSInteger i = 0; i < 5; i++) {
            UIImageView *imageView =[[UIImageView alloc]initWithImage:[UIImage imageNamed:@"full_star"]];//这个目的不是拿图片,目的是随便拿一张图片,图片就有了大小了
        //设置图片的x
         CGFloat imageViewX = i * imageView.image.size.width;
            imageView.frame = CGRectMake(imageViewX, 0, imageView.image.size.width, imageView.image.size.height);

             [self addSubview:imageView];

        }
    }
    return self;
}

//重写set方法在里面处理显示星星图片的细节
-(void)setLever:(CGFloat)lever{
    _lever = lever;

    //1.满星
    //把传过来的分数强转为整型,整数值就是满星的个数
    NSInteger fullStartCount = (NSInteger)lever;
    for(NSInteger i = 0 ; i < fullStartCount ; i++){
        [self makeLeverStarWithImageName:@"full_star" andPosition:i];
    }
    //2.半星
    // (取传过来的个数) - (强转后的整数),如果 > 0   就表示有半星
    if((lever - fullStartCount)>0){
        [self makeLeverStarWithImageName:@"half_star" andPosition:fullStartCount];
        //如果添加了一个半星那把满星个数的值做加1 用它当做 目前已经添加星星的总个数了
        fullStartCount++;
    }

    //3.空星 "如果到这来还不够5颗星 那剩下的就是空星的个数了"
    for (NSInteger i = fullStartCount; i < 5; i++) {
        [self makeLeverStarWithImageName:@"empty_star" andPosition:i];
    }
}

/**
 创建星星imageView

 @param imageName 星星图片
 @param position 星星位置 从0开始
 */
-(void)makeLeverStarWithImageName:(NSString *)imageName andPosition:(NSInteger)position{
    //获取相应位置的子控件
    UIImageView *imageView = self.subviews[position];
    //设置图片
    imageView.image = [UIImage imageNamed:imageName];
}

@end

让调整器访谈 ItemStore

在 ItemsViewController.swift 中, 增添一个 ItemStore 属性。

class ItemsViewController: UITableViewController {

  var itemStore: ItemStore!
}

现行反革命,您应该在 ItemsViewController 实例哪个地点设置此属性呢?
应用程序第一遍运行时,AppDelegate
application(_:didFinishLaunchingWithOptions 🙂 方法被调用。
AppDelegateAppDelegate.swift
中宣称,看名就能够知道意思,该应用程序用作应用程序本身的嘱托。
它承受管理应用程序所经历的事态的改变。 您将在第16章中精通
AppDelegate 和应用程序所经历的越来越多音讯。

打开 AppDelegate.swift。 访问 ItemsViewController (它将是窗口的
根视图调整器(rootViewController)),并将其 ItemStore 属性设置为
ItemStore 的新实例。

func application(_ application: UIApplication, didFinishLaunchingWithOptions
    launchOptions: [UIApplicationLaunchOptionsKey : Any]?) -> Bool {
  // Override point for customization after application launch.

  // Create an ItemStore
  let itemStore = ItemStore()

  // Access the ItemsViewController and set its item store
  let itemsController = window!.rootViewController as! ItemsViewController
  itemsController.itemStore = itemStore

  return true
}

最后,在 ItemStore.swift 中,达成 指派构造器 来增加四个随机 item。

init() {
  for _ in 0..<5 {
    createItem()
  }
}

如果 createItem() 没有用 @discardableResult
注释,那么那些函数的调用就必需像这么:

// Call the function, but ignore the result
let _ = createItem()

这时候您可能会想清楚为啥 itemStore 要在 ItemsViewController
外部设置。 为啥 ItemsViewController 实例本人并未有创制贰个 store
的实例? 这种格局的来头是基于贰个非常复杂的内容叫做
依赖反转原则(dependency inversion principle)
那个规格的中坚目的是经过反转它们中间的一些依赖关系来解耦应用程序中的对象。
那会扭转更加强硬和可保证的代码。

依赖反转原则提议:

  1. 高端对象不应当借助于初级对象。 两个都应当依据于肤浅。
  2. 空洞不应当借助细节。 细节应当在于抽象。

Homepwner 中的重视反转原则所需求的架空是 store 的定义。一个 store
是二个十分的低端其余目的,它经过唯有该类知道的内幕来查找和保存 Item
实例。 ItemsViewController
是三个更加高端别的靶子,只晓得它将被提供一个实用程序对象(store),从该目的足以得到
Item 实例的列表,况且它能够通过新的或更新的 Item
实例来一以贯之存款和储蓄。 那将导致解耦,因为 ItemsViewController 不借助于
ItemStore。 实际上,只要 store 是空泛的,ItemStore
能够被别的三个比不上的 Item 对象替代(举个例子通过利用 Web服务),而
ItemViewController 没有须要任何的修改。

福寿康宁依据反转原则时使用的常用格局是 依赖注入(dependency injection)
在最轻松易行的款型中,较高端其余目的不会假定他们必要动用什么比较低档的靶子。
相反,它们是经过构造器或质量来传递的。 在 ItemsViewController
的兑现中,您通过叁本性情来注入它来给它二个 store。

留心:选择贰个魅族模型,这里作者选取了诺基亚 6s模型。

4.3 最后深远的优化

此次优化的目标是:日后星级评价更加的的方便.—>外界不管用纯代码还应该有用xib依旧用storyboard都相比方便.
4858.com 30
什么样不想使用代码形式
4858.com 31
使用storyboard
4858.com 32

4858.com 33

4858.com 34

4858.com 35
运行ok
4858.com 36


兑现数据源方法

明日 store 里有一点 item,你须求在 ItemViewController 准将这个 item
转产生 UITableView 可以显得的行。 当 UITableView
想知道要显得的开始和结果时,它会从 UITableViewDataSource
合同中注解的一组方法中调用方法。

打开文书档案并找出 UITableViewDataSource 合同参照他事他说加以考察。 向下滚动到
Configuring a Table View 部分(图10.7)。

图10.7 UITableViewDataSource合计文书档案

Configuring a Table View 部分中,请留意,当中七个方法标志为
Required。 因为 ItemsViewController 符合
UITableViewDataSource,所以它必需贯彻
tableView(_:numberOfRowsInSection 🙂
tableView(_:cellForRowAt 🙂
那么些点子告诉表视图应该出示多少行以及在每行中展现的内容。

每当 UITableView 显示时,它会在其 dataSource
上调用一多重措施(必须的方法以及已经实现的别的可选的法子)。 必得的法子
tableView(_:numberOfRowsInSection 🙂 返回 UITableView
应彰显的行数的整数值。 在 Homepwner 的表视图中,store 中的每一种 item
应为一行。

ItemsViewController.swift 中,实现
tableView(_:numberOfRowsInSection:)

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
  return itemStore.allItems.count
}

想精通有关这些措施所指的 section 吗?
表视图能够分为多少个部分,每一个部分都有自身的一组行。 比如,在简报录中,以
“C” 早先的具有名称在多个 section 中。 暗许景况下,一个表视图会有一个section,在本章中,您将只行使一个。
一旦你了排毒视图的办事原理,就一蹴而就使用多个 section。 实际上,使用多个section 是本章结尾的率先个挑衅。

UITableViewDataSource 协议中的第三个必备方法是
tableView(_:cellForRowAt 🙂
要达成此措施,您须要了然另三个类——UITableViewCell

4858.com 37

支付宝口碑案例早先

自定义cell有3种形式,分别是storyboard,xib,手写代码.
我们都利用三次.这里大家先采用第一种情势使用–>storyboard情势

需求分析

1.顶部是一个TableViewHeaderView
2.组的头部标题可以停留,说明tableView是plain样式
3.cell中的数据比较多,系统无法满足,自定义cell

1.种类准备:
内定类前缀(举个例子ZFB),分文件夹(指标是为着前边集成到一个项目中),删除原有Main.storyboard
自己创办三个storyboard(钦定class进行关联,设置为运维分界面),并别忘钦定Main interface

4858.com 38
4858.com 39
开创调节器:
注意承继UITableViewController(因为storyboard中我们拖拽的UITableViewControler,所以创立的类也要承接UITableViewControler)
4858.com 40

若果运转后,是青黄面板. 排错方法:打断点
——>给数据源方法打断点——->(没执到时)-往上打断点
——->[super viewDidLoad]—–>(也绝非执行到时)
——>查看有没有钦点调节器class;

把打算好的plist文件和图片素材拖入案例中
4858.com 41

动用了UITableViewController后,内定数据源和遵循公约都不用大家管了.下一步我们来贯彻相应的数据源方法

2.贯彻相应的数据源方法

亟需的数据,通过第4步会获得.

3.有多少行应有依靠数量来

4858.com 42

4.加载plist字典调换模型

去modal文件夹中,成立模型类
4858.com 43
4858.com 44

//--------------------ZFBBusinessModel.h------------------------
#import <Foundation/Foundation.h>

@interface ZFBBusinessModel : NSObject
///头像
@property (nonatomic, copy) NSString *icon;
///优惠信息
@property (nonatomic, copy) NSString *discount;
///人均消费
@property (nonatomic, strong)  NSNumber *averagePrice;
///距离
@property (nonatomic, strong)  NSNumber *distance;
///打折
@property (nonatomic, strong)  NSNumber *offNum;
///评分
@property (nonatomic, strong)  NSNumber *level;
///店名
@property (nonatomic, copy) NSString *name;

+ (instancetype)businessWithDict:(NSDictionary *)dict;

@end
//--------------------ZFBBusinessModel.m------------------------
#import "ZFBBusinessModel.h"

@implementation ZFBBusinessModel

+(instancetype)businessWithDict:(NSDictionary *)dict{
    id obj = [[self alloc]init];

    [obj setValuesForKeysWithDictionary:dict];
    //--->注意使用KVC编码,属性名和字典中的key的名字一定要相同.
    return obj;
}

@end

5.在storyboard中给自带的UITableViewCell钦命重用标识必定要和重临cell的数据源方法是的标记要一律

4858.com 45

UITableView *cell = [tableView dequeueReusableCellWithIdentifier:@"business" forIndexPath:indexPath];

6.在storyboard中给cell加多拖拽相应的控件,并累加约束

6.1拖拽imageView
(plus是3X的在那除以3
做事中貌似都2x的,到时候除以2)
4858.com 46

4858.com 47

因为各种集团图片差别大,顾为了统一
4858.com 48
Scale:拉伸图片
Aspect:图片长度宽度的比重,保持图形的长度宽度比,保持图片不改变形。

Aspect Fill:在保险长宽比的前提下,缩放图片,使图片充满容器。
Aspect
Fit:在保证长宽比的前提下,缩放图片,使得图片在容器内总体彰显出来。
Scale to Fill:
缩放图片,使图片充满容器。图片未必保持长度宽度比例和谐,有望会拉伸至变形。

6.1拖拽lable设置店名
4858.com 49
6.3 先拖个view给点儿占个位,后边再管理
4858.com 50

4858.com 51


4858.com 52

6.4拖拽lable设置评分

4858.com 53
设置约束
4858.com 54

4858.com 55

6.4拖拽lable设置人均花费
设置字体大小,设置约束
4858.com 56

4858.com 57

运作一下看成效,然后再持续
4858.com 58

6.5拖拽lable设置折扣
4858.com 59

4858.com 60
设置约束
4858.com 61
4858.com 62
6.6拖拽lable设置距离
设置约束 x和Y
4858.com 63
4858.com 64
6.7拖拽lable设置减字
4858.com 65
设置约束(上面距8,与地点左侧对齐,)略
减字外面包车型地铁是星型,大家设置一下宽高比为1:1,文字居中就ok了
4858.com 66

4858.com 67
6.8拖拽lable设置随机立减…
(设置字体及颜色,设置约束,略..)
6.9 修改一下cell的万丈
4858.com 68
上边修改了business中的中度,只是修改了模型的尺寸,运维起来还得看 table
View的冲天大小,所以还得修改table View的万丈
4858.com 69
6.10 把背景颜色去掉运转一下拜访
4858.com 70

7.创制三个三番五次至UITableViewCell的类,并且钦定SB中的UITableViewCell的class为友好创办出来的那么些类

4858.com 71

8.把SB中Cell内部的子控件连线到钦赐class的.m中的延展中

8.1增添一个延展
4858.com 72
8.2指定class
4858.com 73
8.3 子控件连线
4858.com 74

9.把模型形成属性定义在自定义cell类的.h中,在.m重写模型属性的set方法在此措施设置数据

9.1 把模型造成属性定义在自定义cell类的.h中
4858.com 75

//  ZFBBusinessCell.h

#import <UIKit/UIKit.h>
@class ZFBBusinessModel;
@interface ZFBBusinessCell : UITableViewCell
///模型属性    注意模型是个对象 用strong
@property (nonatomic , strong) ZFBBusinessModel *businessModel;

@end

9.2 在.m重写模型属性的set方法在此方法设置数据
4858.com 76

//
//  ZFBBusinessCell.m


#import "ZFBBusinessCell.h"
#import "ZFBBusinessModel.h"//注意导入头文件
@interface ZFBBusinessCell ()
///商店的头像
@property (weak, nonatomic) IBOutlet UIImageView *iconView;
///店名
@property (weak, nonatomic) IBOutlet UILabel *nameLabel;
///评分
@property (weak, nonatomic) IBOutlet UILabel *levelLabel;
///人均消费
@property (weak, nonatomic) IBOutlet UILabel *averagePriceLabel;
///打折
@property (weak, nonatomic) IBOutlet UILabel *offNumLabel;
///距离
@property (weak, nonatomic) IBOutlet UILabel *distanceLabel;
///优惠信息 
@property (weak, nonatomic) IBOutlet UILabel *discountLabel;

@end

@implementation ZFBBusinessCell

//重写模型属性的set方法在此方法中给子控件设置数据
-(void)setBusinessModel:(ZFBBusinessModel *)businessModel{
    _businessModel =businessModel;

    _iconView.image = [UIImage imageNamed:businessModel.icon];
    _nameLabel.text = businessModel.name;
    _levelLabel.text = businessModel.level.description;//level是NSNumber类型的,转字符串掉description方法
    _averagePriceLabel.text = [NSString stringWithFormat:@"人均消费 %@ 元",businessModel.averagePrice];  //plist中只存了个数字,要显示人均消费**元,顾要拼接字符串
    _offNumLabel.text = [NSString stringWithFormat:@"%@ 折",businessModel.offNum];
    _distanceLabel.text = [NSString stringWithFormat:@"距离北京石油化工学院 %@ m",businessModel.distance];
    _discountLabel.text = businessModel.discount;
}


@end

回到ZFBBusinessController.m中
修改创设的cell类型
4858.com 77
设置数据
4858.com 78

 //2.设置数据        (传递模型,这里是单组数据展示, 当只有一组数据的时候用row,多组的时候才先用section再用row)
    cell.businessModel = self.businessData[indexPath.row];

运作看现阶段效果
4858.com 79
数据类型有一些小标题,8.8折和9.8折前边都一大串,接下去修改

10.降价 把模型中的offNum 改float类型,前提要改一下引进系统框架 UIKit

10.1修改ZFBBusinessModel.h文件
4858.com 80
10.2修改ZFBBusinessCell.m文件
4858.com 81

运作看效果
4858.com 82

11.集成星星,把轻便的文书拖拽出来后,内定Storyboard中表示个其他特别View的Class

4858.com 83

4858.com 84

12.把用来代表星星view连线在自定义cell.m中,引进星星类的头文件

4858.com 85
4858.com 86

13.在模型属性的set方法中,给点儿view传level

 _leverView.lever = businessModel.level;

4858.com 87
上边报错是因为大家传的是CGFloat类型导致的
4858.com 88

4858.com 89
顾改一下
4858.com 90
这是这里又报错
4858.com 91
还要改一下
4858.com 92
把个别前边栗褐背景颜色去掉,运维看作用
4858.com 93

14.创办了多个xib 把xib中的view 宽361 高960

4858.com 94

4858.com 95

4858.com 96

4858.com 97

15.在里面拖入一个imageView何况设置好图片, 注意先 com + = 自适应下
为了前边使用Aspect ratio后图片不会变形

  1. 第一个imageView 上 左 右 8间距 + 宽高比

先选取 com + = 自适应
,后边再安装宽高比,那样他会依附实际的尺寸算比例,图片不会变形

4858.com 98

17.再复制3个imageView 但注意它身上约束

即复制完后,clear复制的束缚

18.全部入选(com+a) 左对齐 等宽等高 上下间距8

左对齐
4858.com 99
等宽等高
4858.com 100

上下间距8:

当心在装置内外间距前,要保管七个图片之间并不是有重合

19.创造二个后续 至UIView的类,而且给xib钦命class

承继关系是跟最顶层有涉及的,最顶层是UIView,就继续UIView,最顶层是UIButton,那就三番五次UIButton.
这里是顶层是UIView.

4858.com 101

4858.com 102

给xib钦命class(其实不钦赐也足以,因为里面平昔不连线,但可是内定一下,表达它们中间是平等档期的顺序)

4858.com 103

20.把加载xib细节封装在刚刚成立的类中,提供一个方可让外界调用的类方式,目标降低耦合度.

//#########################
//  ZFBBusinessPictureView.h
#import <UIKit/UIKit.h>

@interface ZFBBusinessPictureView : UIView

/**
 加载xib创建头部视图
 */
+(instancetype)businessPictureView;

@end


//#########################
//  ZFBBusinessPictureView.m
#import "ZFBBusinessPictureView.h"

@implementation ZFBBusinessPictureView


/*

 知识点回顾--两种加载xib的方式
 1.NSBundle加载XIB  iOS 2.0 的方法
 2.使用UINib加载XIB  iOS 4.0 的方法   做了内存优化"如果内存紧张"内存警告,可以自动释放,如果有需要会重新自动加载""
 */

+(instancetype)businessPictureView{

    UINib *nib = [UINib nibWithNibName:@"ZFBBusinessPicture" bundle:nil];
    return [[nib instantiateWithOwner:nil options:nil]firstObject];
}

@end

21.给tableView设置tableHeaderView 注意要总计它现在实际的可观

回到ZFBBusinessController.m
回去调节器中,导入刚才创设类的头文件ZFBBusinessPictureView.h

在调控器设置tableView的底部 视图.
4858.com 104

 //2.设置tableView的头部视图
    ZFBBusinessPictureView *pictureView = [ZFBBusinessPictureView businessPictureView];
    //375 * 960 / 361
    CGFloat pictureViewHeight = self.tableView.bounds.size.width * pictureView.bounds.size.height / pictureView.bounds.size.width;  //根据比例计算真实高度
        pictureView.frame = CGRectMake(0, 0, self.tableView.bounds.size.width, pictureViewHeight); //宽度最好不要给了,以防有问题

    self.tableView.tableHeaderView = pictureView;  //设置为头部视图

22.给tableView设置tableFooterView

 //3.设置tableView的尾部视图
    UIButton *footerBtn = [[UIButton alloc]init];
    [footerBtn setTitle:@"点击加载更多" forState:UIControlStateNormal];

    [footerBtn setTitleColor:[UIColor colorWithWhite:0.3 alpha:1] forState:UIControlStateNormal];
    [footerBtn setTitleColor:[UIColor colorWithWhite:0.7 alpha:1] forState:UIControlStateHighlighted];

    footerBtn.backgroundColor = [UIColor colorWithWhite:0.5 alpha:1];
    footerBtn.frame = CGRectMake(0, 0, 0, 30);


    self.tableView.tableFooterView = footerBtn;//设置为尾部视图

23.落实没点击加载越来越多一遍,多输出四个cell

把从前定义是不可变数组,修改为可变数组,不然就能够报告警察方告
4858.com 105
4858.com 106
4858.com 107
4858.com 108

24.优化让其机动滚动

1.atScrollPosition: 的参数为UITableViewScrollPositionMiddle时
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:self.businessData.count-1 inSection:0]; [self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
4858.com 109
2.atScrollPosition: 的参数为UITableViewScrollPositionBottom时
4858.com 110

25.细节拍卖 让cell的分界线从最左侧先河

self.tableView.separatorInset = UIEdgeInsetsZero;

末尾还没会慢慢对细节进行拍卖

UITableViewCells

表视图的每一行都以三个视图。 那个视图是 UITableViewCell 的实例。
在本节中,您将创制 UITableViewCell 的实例来填充表视图。

五个 单元(cell) 本人有多少个子视图——它的 contentView(图10.8)。
contentView 是 cell 内容的父视图。 cell 还能有所 附属类小部件视图(accessory
view)。

图10.8 UITableViewCell 布局

附属类小部件视图呈现面向操作的Logo,比方复选标志,公开Logo或新闻开关。
这个Logo可由此预订义的常数来修改附属类小部件视图的外观。 暗中同意值为
UITableViewCellAccessoryType.none,那是本章要动用的。
您将在第23章再度察看附属类小部件视图(现在感兴趣的话请参阅 UITableViewCell
的文书档案以询问越多详细消息。)

一个 UITableViewCell 的真正实用的是
contentView,它有多少个子视图(图10.9)。 个中七个子视图是 UILabel
实例,它们是名叫 textLabeldetailTextLabelUITableViewCell
的属性。 第1个子视图是一个称作 imageViewUIImageView
在本章中,您将运用 textLabeldetailTextLabel

图10.9 UITableViewCell层级

各样 cell 还会有二个
UITableViewCellStyle,它调控了怎么子视图被应用,以及它们在
contentView 中的地点。 那几个样式及其常量的以身作则如图10.10所示。

图10.10 UITableViewCellStyle:样式和常量

从品种导航器视图中选用ViewController.swift并退换类注脚,使其改为贰个UITableViewController子类。

检查评定自身


拜会哪些没驾驭:

1、能够说出tableViewController的view属性和tableView是一致的

2、能够使用xib布局口碑的头顶视图

3、能够揭露使用代码重新恢复设置nib中度的意义是为着适配差别幅度的荧屏

4、能够采纳代码设置后面部分的开关

5、能够透露代码会调用view的initWithFrame方法

6、能够透露sb/xib会调用view的aweakFromNib方法

7、能够将星级评价小框架移植到口碑调整器中

8、能够缓和浮点数转字符串精度的难题

9、能够使用代码分析plist数据

10、能够运用代码将字典转化成模型

11、能够利用代码设置tableView的headerView和footerView

创造和查找 UITableViewCells

现在,每个 cell 将以 textLabel 的款式展现 Itemname,并将
ItemitemInDollars 作为 detailTextLabel 展现。
为了兑现那点,您须要实现 UITableViewDataSource
公约第1个供给的措施 tableView(_:cellForRowAt 🙂
此方法将开创三个 cell,将其 textLabel 设置为 Itemname,将其
detailTextLabel 设置为 ItemvalueInDollars,并将其回到到
UITableView(图10.11)。

图10.11 检索 UITableViewCell

您哪些调节哪三个 Item 对应哪二个 cell? 发送给
tableView(_:cellForRowAt 🙂 的参数之一是
IndexPath,它富有几个属性: sectionrow
当在数据源上调用此方式时,表视图会询问
“笔者得以在X,Y行中呈现叁个单元格吗?”,因为本演练中唯有三个有的,您的兑现只会涉嫌到目录路线的行。

ItemsViewController.swift 中,实现 tableView(_:cellForRowAt
🙂
,使第 n 行显示 allItems 数组中的第 n 个 item。

override func tableView(_ tableView: UITableView,cellForRowAt indexPath: IndexPath) -> UITableViewCell {
  // Create an instance of UITableViewCell, with default appearance
  let cell = UITableViewCell(style: .value1, reuseIdentifier: "UITableViewCell")

  // Set the text on the cell with the description of the item
  // that is at the nth index of items, where n = row this cell
  // will appear in on the tableview
  let item = itemStore.allItems[indexPath.row]

  cell.textLabel?.text = item.name
  cell.detailTextLabel?.text = "$\(item.valueInDollars)"

  return cell
}

今天创设并运营应用程序,您将看到二个 UITableView,当中蕴藏了三个item 列表。

class ViewController: UITableViewController {

简单易行回想进程


重用 UITableViewCells

iOS 设备的内部存款和储蓄器量有限。 假使你在 UITableView 中展现了数千个 item
的列表,那么您将有数千个 UITableViewCell 的实例。 大多数那些 cell
将不须要地侵夺内部存储器。 终究,如果客商不只怕在显示器上看看一个 cell,那么该
cell 未有理由占用内部存款和储蓄器。

为了节本省部存款和储蓄器并加强质量,您能够采纳表视图 cell。 当客商滚动表格时,某个cell 就要显示屏外活动。 将显示屏外的 cell 归入可用于再使用的 cell 中。
然后,数据源首先检查 cell 池,并非为各类央求创立一个全新的单元。
倘使有三个未选用的
cell,数据源将使用新数据举办计划并将其归来给表视图(图10.12)。

图10.12 UITableViewCell的可重用实例

有七个标题亟需潜心:一时 UITableView 会有两样等级次序的 cell。
有的时候候,您会将 UITableViewCell 子类化以创立特殊的外观或作为。
然则,在可选用 cell
池周围浮动的不等子类有希望会导致重返错误类型的单元格。 您必得确定保障再次来到的
cell 的连串,以便你能够规定其具有的习性和办法。

请留意,您无需关切从池中收取的 cell 是哪些项指标,因为您就要更换 cell
内容。 您供给的是特定类型的 cell。 好音信是各种单元格都有一个连串为
StringreuseIdentifier 属性。 当数据源向表视图询问可选拔的 cell
时,它传递二个字符串,并说:“笔者需求三个存有这种重用标志符的
cell。”依据常规,重用标志符平时是单元类的称谓。

要重用 cell,您须求运用表视图注册 cell
原型或类,以取得特定的录取标志符。 您将登记默许的 UITableViewCell
类。 你告知表格视图,“嘿,任什么日期候,小编须求贰个这么些重用标记符的
cell,给小编多少个以此特定类的 cell。” 表视图将从重用池给你七个 cell
或只要在重用池中并未该项指标 cell 则实例化一个新的。

打开 Main.storyboard。 请注意,在报表视图中有多少个 Prototype Cells
的部分(图10.13)。

图10.13原型单元格

在那边,您可以布署相关表格视图所需的不及种类的 cell。
借让你正在创造自定义 cell,那么您将要其间设置 cell 的分界面。
ItemsViewController 只须要一种
cell,并且今后采纳在那之中一种内置样式就够用了,因而你只要求在早就在画布上的
cell 上安插部分本性。

选料原型 cell 并开垦其性质量检验查器。 将 Style 更改为
Right Detail(对应于 UITableViewCellStyle.value1),并给它贰个
UITableViewCellIdentifier(图10.14)。

图10.14表视图 cell 属性

接下来,在 ItemsViewController.swift 中,更新
tableView(_:cellForRowAt 🙂 来重用单元格。

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
  **`// Create an instance of UITableViewCell, with default appearance`**
  **`//let cell = UITableViewCell(style: .value1, reuseIdentifier: “UITableViewCell”)`**

  // Get a new or recycled cell
  let cell = tableView.dequeueReusableCell(withIdentifier: "UITableViewCell", for: indexPath)

  ...
}

dequeueReusableCell(withIdentifier:for 🙂 方法将检查 cell
的池或队列,以查看全体无可争论重用标记符的 cell 是不是已存在。
假设是如此,它会 “出队(dequeue)” 那二个 cell。 若无存活
cell,将创制并回到一个新的 cell。

创设并运维应用程序。 应用程序的一言一行应当保险不改变。 重新利用 cell
意味着你只须求创建一些些的cell,那对内存的渴求很低。
您的应用程序的顾客(及其设备)将会多谢您。

切换回storyboard,选中TableViewController,然后在Identity inspector
中校类设置为ViewController。

1.口碑-数据深入分析

Content Insets

正如你在本章中一贯运营应用程序同样,您也许已经注意到,第一个表视图 cell
处于状态栏下方(图10.15)。 您创立的应用程序的分界面填满了道具的全套窗口。
状态栏(假如可知)都会停放在分界面顶上部分,因而你的分界面必需考虑状态栏的职位。

图10.15 表视图 cell 与气象栏重叠

要使表格视图 cell 不会覆盖状态栏,您将要表视图的最上端加多一些填写。
UITableViewUIScrollView 的子类,它从中承继 contentInset
属性。 您能够将 content insets 想象为滚动视图的四条边的 填充(padding)。

ItemsViewController.swift 中,覆盖 viewDidLoad() 来更新表视图
content inset。

override func viewDidLoad() {

  super.viewDidLoad()
  // Get the height of the status bar
  let statusBarHeight = UIApplication.shared.statusBarFrame.height

  let insets = UIEdgeInsets(top: statusBarHeight, left: 0, bottom: 0, right: 0)
  tableView.contentInset = insets
  tableView.scrollIndicatorInsets = insets
}

表视图的最上端被予以了与气象栏中度相等的 content inset。
当表视图滚动到顶上部分时,那将使内容映今后状态栏的下方。
滚动提示器也会来得在情景栏下方,由此你能够给他俩一致的
inset,让它们出现在状态栏的正下方。

请留神,您能够访问 ItemsViewController 上的 tableView
属性以获得表视图。 该属性从 UITableViewController
承接,并赶回调节器的表视图。 固然你能够通过寻访
UITableViewController 的视图来收获同样的靶子,可是采纳 tableView
会告诉编写翻译器再次来到的靶子将是 UITableView 的多个实例。 由此,调用特定于
UITableView 的法子或访谈特定于 UITableView 的性质不会发生错误。

创设并运营应用程序。
当表视图滚动到最上端时,表视图单元格内容不再和情况栏重叠(图10.16)。

图10.16 具备确切的 content inset 的表视图

4858.com 111

2.口碑-成立模型

  • 类前缀
  • 分文件夹
  • 素材

青铜挑衅:多少个 section

UITableView 显示多个 section—— 三个用以价值超越 $50 的
item,另一个用于剩余的 item。
在最初此挑衅在此之前,请复制满含项指标文书夹及其全体源文件。
然后在复制的类型中实现挑战; 原品种要封存到事后的章节用。

接下来大家设置好 identifier

3.口碑-分析数据

黄金挑战:固定行

使 UITableView 的最终一行展现为文本 “No more items!” 且无论 store
中的 item 数量是有一点(包含0项),都会突显。

4858.com 112

4.口碑-通过sb加载cell

  • 由此sb加载cell,必要设置cell的Identifier,给sb中的cell设置重用标记即达成登记单元格

黄金挑衅:自定义表

使每一行的中度达到 60 点(point),除了黄金挑衅中最后一行应该保险 44
point。 然后,将除最后一行之外的每行的字体大小改造为 20 点。 最终,使
UITableView 的背景体现叁个图像。
(要使此像素完美,您将要求一个正确大小的图像,具体取决于你的设备,请参见第1章中的图表)

分界面搭建好了,我们可以初始编码了

5.口碑-cell的布局

1.商家配图图片大小不一样,所以最好给imageView设置宽高约束,达到配图尺寸统一
2.label如果只有一行时,只用设置位置即可,尺寸让它自适应
3.评分view,它的大小不能自适应,所以必须设置宽高约束"60,12"
4.减是个方形的,所以最好设置个宽高比


店名字体:14字体
其它是12号字体
到cell边距15
内部边距11

第一,我们做好属性证明

6.口碑-自定义cell控件连线

1.cell中的子控件不能连线到控制器,因为当前控制器只有一个,但是将来cell有很多个,所以会报重复连接"连线"错误
2.正确作法,应该创建一个继承至UITableViewCell的类,来管理"描述/表示",设置cell的customClass
3.把cell中子控件的连线,连入到管理cell类的.m的延展中
var refreshCtrl: UIRefreshControl!var tableData:[AnyObject]!var task: URLSessionDownloadTask!var session: URLSession!var cache:NSCache<AnyObject, AnyObject>!

7.口碑-设置cell数据

引入模型属性"strong"
重写模型属性set方法给cell内部子控件设置数据

数量将采用 U库罗德LSessionDownloadTask 类下载,那就分解了干吗您表明了地点的
task 和 session 属性。 另外,tableData
属性将用作表视图数据源,缓存变量是缓存字典的参照,APP将要下载图片在此以前使用缓存字典乞求缓存中的图片。

8.口碑-移植星级评价

  • 拖拽文件,设置sb中的view为自定义view,连线并在set数据的诀窍中把多少放在控件上
  • 用自定义星星的类来描述cell中的星星view,加载sb中的小点儿view创造出来的对象,正是她点名类成立来出的指标”他们是同一个东西了”
  • 再给level传入分值就能够

接下去,找到viewDidLoad方法,并在super.viewDidLoad调用之后施行以下代码:

9.口碑-底部视图-布局

  • 第贰个视图分别安装 上,左,右,宽高比
  • 背后的视图与前四个等宽,等高,垂直距离,水平居中
session = URLSession.sharedtask = URLSessionDownloadTask() self.refreshCtrl = UIRefreshControl()self.refreshCtrl.addTarget(self, action: #selector(ViewController.refreshTableView), for: .valueChanged)self.refreshControl = self.refreshCtrl self.tableData = []self.cache = NSCache()

4858.com ,10.口碑-底部视图-代码设置缩放比例

- 在xib中调整好位置高度,并在视图加载完成之后根据'宽度'和'屏幕的宽度'和'屏幕的高度'设置**高度**
- tableView的宽 / xib中HeaderView的宽 * xib中HeaderView的高 得到按比例缩放后的realHeaderView的高
- xib中的高 * tableView的宽 / xib中的宽
- xib的高 / xib的宽  * tableView的宽
2: 3 == 4: 6?

上边的代码将初步化 session、task,tableData,以及 cache 缓存对象。
还加多了一个刷新的调用方法,以便在历次提取表视图时运转。
让我们后续实现“refreshTableView”选拔器。

11.口碑-尾部视图

  • 灰度,R=G=B
  • 1为白色 0为黑色
func refreshTableView(){ let url:URL! = URL(string: "https://itunes.apple.com/search?term=flappy&entity=software")task = session.downloadTask(with: url, completionHandler: { (location: URL?, response: URLResponse?, error: Error?) -> Void in if location != nil{ let data:Data! = try? Data(contentsOf: location!) do{ let dic = try JSONSerialization.jsonObject(with: data, options: .mutableLeaves) as AnyObject self.tableData = dic.value(forKey : "results") as? [AnyObject] DispatchQueue.main.async(execute: { () -> Void in self.tableView.reloadData() self.refreshControl?.endRefreshing }catch{ print("something went wrong, try again") } }}) task.resume()}

12.口碑-组头

  • tableview两种样式组头的差距?
    • grouped跟着tableView滚动出去
    • plain能够告一段落在上面

许多,上述方法将异步央求iTunes寻找API重临其标题或描述。
一旦接到到多少,tableData
数据源数组就被记录填充,况兼表视图会从主线程重新加载。

13.口碑-加多领航调节器

Editor -> Embedin -> Navgation Contorller

然后,完毕四个强制性数据源左券格局来填充表视图行中下载的数码,特别是图片数据。

20-马克Man度量截图地点

将以下代码复制到类中:

21-KVC

        // 方法1 传统写法
        _name = dict[@"name"];
        // 字典不能保存基本数据类型
        _age = [dict[@"age"] integerValue];

        // 方法2 KVC - `间接`通过 key 设置数值
        // 注意:基本数据类型,不需要转换,KVC 可以自动实现转换!
        [self setValue:dict[@"name"] forKey:@"name"];
        [self setValue:dict[@"age"] forKey:@"age"];

        // 方法3 KVC - 进阶,循环遍历字典
        for (NSString *key in dict) {
            // 1. 通过 key 取值
            id value = dict[key];

            // 2. 通过 KVC 方法设置数值
            // -key 对应对象的 `属性`,如果属性不存在就崩溃
            [self setValue:value forKey:key];
        }

        // 方法4 KVC 字典转模型大招 -> 内部实际上就是方法3的实现
        [self setValuesForKeysWithDictionary:dict];
  • 在承接保险模型的习性和字典中的key一样的动静下,字典转模型使用setValuesForKeysWithDictionary:方法
  • keys value coding 键值编码

附全体代码

/**总步骤:
 1.指定类前缀,分文件夹'删除原有Main.sb' 自己创建一个,并别忘指定 Main interface
 2.实现相应的数据源方法
 3.有多少行应该根据数据来
 4.加载plist字典转换模型
 5.在sb中给自带的UITableViewCell指定重用标识 一定要和返回cell的数据源方法是的标识要一样
 6.在SB中给cell添加拖拽相应的控件,并添加约束
 7.创建一个继承至UITableViewCell的类,并且指定SB中的UITableViewCell的class为自己创建出来的这个类
 8.把SB中Cell内部的子控件连线到指定class的.m中的延展中
 9.把模型变成属性定义在自定义cell类的.h中,在.m重写模型属性的set方法在此方法设置数据
 10.打折 把模型中的offNum 改float类型,前提要改一下引入系统框架 UIKit
 11.集成星星,把星星的文件拖拽出来后,指定SB中代表星星的那个View的Class 
 12.把用来表示星星view连线在自定义cell.m中,引入星星类的头文件
 13.在模型属性的set方法中,给星星view传level
 14.创建了一个xib 把xib中的view 宽361 高960
 15.在里面拖入一个imageView并且设置好图片, com + = 自适应下 为了不用改宽高比约束值
 16. 第一个imageView  上  左 右  8间距  + 宽高比
 17. 复制3个imageView 但注意它身上约束
 18. 全部选中左对齐 等宽等高  上下间距8
 19. 创建一个继承 至UIView的类,并且指定class
 20. 把加载xib细节封装在此类中,提供一个可以让外部调用的类方法
 21.给tableView设置tableHeaderView  注意要计算它将来真实的高度
 22.给tableView设置tableFooterView
 23.实现没点击加载更多一次,多输出一个cell
 24.优化让其自动滚动
 25.细节处理  让cell的分割线从最左边开始
*/

4858.com 113

4858.com 114

//********************ZFBBusinessController.h*****************************
//  ZFBBusinessController.h
#import <UIKit/UIKit.h>

@interface ZFBBusinessController : UITableViewController

@end

//**********************ZFBBusinessController.m***************************
//
//  ZFBBusinessController.m
//  LJX_支付宝口碑(stroyboard搭建)

#import "ZFBBusinessController.h"
#import "ZFBBusinessModel.h"
#import "ZFBBusinessCell.h"
#import "ZFBBusinessPictureView.h"

@interface ZFBBusinessController ()
//数据在多个数据源方法中使用,顾声明一个属性
@property (nonatomic ,strong)NSMutableArray *businessData;
@end

@implementation ZFBBusinessController

- (void)viewDidLoad {
    [super viewDidLoad];
    //1.加载数据
    self.businessData = [self loadBusinessData];
    //-->使用方式2时,直接调用方法就行[self loadBusinessData];

    //2.设置tableView的头部视图
    ZFBBusinessPictureView *pictureView = [ZFBBusinessPictureView businessPictureView];

    //如果不设置,因为xib中设置的宽度是361,如果在375的手机屏幕上显示,因为xib中的图片的宽度没有写死,当到了宽屏幕下,变宽,高也会变高,导致图片会超出父控件, 出了头部视图的边界.
    //375 * 960 / 361
    CGFloat pictureViewHeight = self.tableView.bounds.size.width * pictureView.bounds.size.height / pictureView.bounds.size.width;  //计算真实高度
        pictureView.frame = CGRectMake(0, 0, self.tableView.bounds.size.width, pictureViewHeight); //宽度最好不要给了,以防有问题

    self.tableView.tableHeaderView = pictureView;  //设置为头部视图


    //3.设置tableView的尾部视图
    UIButton *footerBtn = [[UIButton alloc]init];
    [footerBtn setTitle:@"点击加载更多" forState:UIControlStateNormal];

    // colorWithWhite:0.3 alpha:1  通过灰度来设置颜色 第一个参数如果 传0就是黑色  如果传1就表示白色
    [footerBtn setTitleColor:[UIColor colorWithWhite:0.3 alpha:1] forState:UIControlStateNormal];
    [footerBtn setTitleColor:[UIColor colorWithWhite:0.7 alpha:1] forState:UIControlStateHighlighted];

    footerBtn.backgroundColor = [UIColor colorWithWhite:0.5 alpha:1];
    footerBtn.frame = CGRectMake(0, 0, 0, 30);

    [footerBtn addTarget:self action:@selector(loadMoreData) forControlEvents:UIControlEventTouchUpInside]; //添加点击事件

    self.tableView.tableFooterView = footerBtn;//设置为尾部视图


    //小细节处理 :让分割线从最左边开始
    self.tableView.separatorInset = UIEdgeInsetsZero;

 }

#pragma mark -尾部按钮点击后会调用此方法
//1.加载更多数据
-(void)loadMoreData{
    //1.创建一个新的模型
    ZFBBusinessModel *businessModel = [[ZFBBusinessModel alloc]init];
    businessModel.icon = @"ss";

    businessModel.discount = @"全场大派送,2元一件,快来买!";
    businessModel.averagePrice = @2;
    businessModel.distance = @20;
    businessModel.offNum = 2;
    businessModel.level = 5;
    businessModel.name = @"西单大悦城!";

    //2.把新模型添加到保存所以数据的数组中
    [self.businessData addObject:businessModel];

    //3.让数据源方法重新执行
    [self.tableView reloadData];

    NSIndexPath *indexPath = [NSIndexPath indexPathForRow:self.businessData.count-1 inSection:0];
    [self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES];

}


#pragma mark -数据源方法
// 应该口碑案例表格就1组,默认就1组,所以不用再写
//- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
//    return 1;
//}

// 行数
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return self.businessData.count;
   //--->使用方式2时: return _businessList.count;
}

//
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{

    //1.创建cell
    ZFBBusinessCell *cell = [tableView dequeueReusableCellWithIdentifier:@"business" forIndexPath:indexPath];
    //-->这里在storyboard中进行注册cell

    //2.设置数据        (传递模型,这里是单组数据展示, 当只有一组数据的时候用row,多组的时候才先用section再用row)
    cell.businessModel = self.businessData[indexPath.row];

    //3.返回cell
    return cell;
}


#pragma mark -加载数据

- (NSMutableArray *)loadBusinessData{
    //1.加载plist
    NSMutableArray *dictArr = [NSMutableArray arrayWithContentsOfURL:[[NSBundle mainBundle]URLForResource:@"business.plist" withExtension:nil]];

    //2. 创建可变数组保存模型     /kə'pæsɪtɪ/ 容量
    NSMutableArray *arryM = [NSMutableArray arrayWithCapacity:dictArr.count];

    //3.字典转模型
    for(NSDictionary *dict in dictArr){

        [arryM addObject:[ZFBBusinessModel businessWithDict:dict]];

        /* 分步写
        //3.1 把字典传递给模型,让模型设置自己的属性
        ZFBBusinessModel *groupModel = [ZFBBusinessModel businessWithDict:dict];
        //3.2 把创建好的模型添加到可变数组中.
         [arryM addObject: groupModel];
        */
     }

    return arryM;
}

/*

 方式2. 加载数据方法的返回值设置为空 ,声明一个成员变量(给成员变量设置泛型),把可变数组赋值给成员变量

 1.  NSArray<ZFBBusinessModel *> *_businessList; //声明成员变量,设置泛型
 2.
 - (void)loadBusinessData{
    ...code略....
   _businessList = arryM;
 }

 */

@end

4858.com 115

//
//  ZFBBusinessModel.h
#import <UIKit/UIKit.h>

@interface ZFBBusinessModel : NSObject
///头像
@property (nonatomic, copy) NSString *icon;
///优惠信息
@property (nonatomic, copy) NSString *discount;
///人均消费
@property (nonatomic, strong)  NSNumber *averagePrice;
///距离
@property (nonatomic, strong)  NSNumber *distance;
///打折
//浮点数字转出成字符串时,可能会出现问题,把它转换成float 
@property (nonatomic, assign)  float offNum;
///评分
@property (nonatomic, assign)  CGFloat level;
///店名
@property (nonatomic, copy) NSString *name;

+ (instancetype)businessWithDict:(NSDictionary *)dict;

@end

#import "ZFBBusinessModel.h"

@implementation ZFBBusinessModel

+(instancetype)businessWithDict:(NSDictionary *)dict{
    id obj = [[self alloc]init];

    [obj setValuesForKeysWithDictionary:dict];
    //--->注意使用KVC编码,属性名和字典中的key的名字一定要相同.
    return obj;
}


@end

4858.com 116

//
//  ZFBLeverStar.h
//  LJX_星级评价

#import <UIKit/UIKit.h>

@interface ZFBLeverStar : UIView
///接收评分
@property (nonatomic, assign) CGFloat lever;

@end

//
//  ZFBLeverStar.m
//  LJX_星级评价

#import "ZFBLeverStar.h"

@implementation ZFBLeverStar

//当一个视图从xib或storyboard中创建完成之后就会调用此方法
-(void)awakeFromNib{
    [super awakeFromNib];

    //来了 直接把5个imageView创建添加好
    for (NSInteger i = 0; i < 5; i++) {
        UIImageView *imageView =[[UIImageView alloc]initWithImage:[UIImage imageNamed:@"full_star"]];//这个目的不是拿图片,目的是随便拿一张图片,图片就有了大小了
        //设置图片的x
        CGFloat imageViewX = i * imageView.image.size.width;
        imageView.frame = CGRectMake(imageViewX, 0, imageView.image.size.width, imageView.image.size.height);

        [self addSubview:imageView];
    }
}

//此方法只有用代码创建时才会调用
- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        //来了 直接把5个imageView创建添加好
        for (NSInteger i = 0; i < 5; i++) {
            UIImageView *imageView =[[UIImageView alloc]initWithImage:[UIImage imageNamed:@"full_star"]];//这个目的不是拿图片,目的是随便拿一张图片,图片就有了大小了
        //设置图片的x
         CGFloat imageViewX = i * imageView.image.size.width;
            imageView.frame = CGRectMake(imageViewX, 0, imageView.image.size.width, imageView.image.size.height);

             [self addSubview:imageView];

        }
    }
    return self;
}


//重写set方法在里面处理显示星星图片的细节
-(void)setLever:(CGFloat)lever{
    _lever = lever;

    //1.满星
    //把传过来的分数强转为整型,整数值就是满星的个数
    NSInteger fullStartCount = (NSInteger)lever;
    for(NSInteger i = 0 ; i < fullStartCount ; i++){
        [self makeLeverStarWithImageName:@"full_star" andPosition:i];
    }
    //2.半星
    // (取传过来的个数) - (强转后的整数),如果 > 0   就表示有半星
    if((lever - fullStartCount)>0){
        [self makeLeverStarWithImageName:@"half_star" andPosition:fullStartCount];
        //如果添加了一个半星那把满星个数的值做加1 用它当做 目前已经添加星星的总个数了
        fullStartCount++;
    }

    //3.空星 "如果到这来还不够5颗星 那剩下的就是空星的个数了"
    for (NSInteger i = fullStartCount; i < 5; i++) {
        [self makeLeverStarWithImageName:@"empty_star" andPosition:i];
    }
}

/**
 创建星星imageView

 @param imageName 星星图片
 @param position 星星位置 从0开始
 */
-(void)makeLeverStarWithImageName:(NSString *)imageName andPosition:(NSInteger)position{
    //获取相应位置的子控件
    UIImageView *imageView = self.subviews[position];
    //设置图片
    imageView.image = [UIImage imageNamed:imageName];
}

@end

/*
 //1.创建imageView设置图片,那么imageView创建出来的就有尺寸
 UIImageView *imageView = [[UIImageView alloc]initWithImage:[UIImage imageNamed:imageName]];
 //2.设置图片的x
 CGFloat imageViewX = position * imageView.image.size.width;
 imageView.frame = CGRectMake(imageViewX, 0, imageView.image.size.width, imageView.image.size.height);
 [self addSubview:imageView];

 */

4858.com 117

//
//  ZFBBusinessCell.h

#import <UIKit/UIKit.h>
@class ZFBBusinessModel;
@interface ZFBBusinessCell : UITableViewCell
///模型属性    注意模型是个对象 用strong
@property (nonatomic , strong) ZFBBusinessModel *businessModel;

@end

//
//  ZFBBusinessCell.m

#import "ZFBBusinessCell.h"
#import "ZFBBusinessModel.h"//注意导入头文件
#import "ZFBLeverStar.h"
@interface ZFBBusinessCell ()
///商店的头像
@property (weak, nonatomic) IBOutlet UIImageView *iconView;
///店名
@property (weak, nonatomic) IBOutlet UILabel *nameLabel;
///评分
@property (weak, nonatomic) IBOutlet UILabel *levelLabel;
///人均消费
@property (weak, nonatomic) IBOutlet UILabel *averagePriceLabel;
///打折
@property (weak, nonatomic) IBOutlet UILabel *offNumLabel;
///距离
@property (weak, nonatomic) IBOutlet UILabel *distanceLabel;
///优惠信息 
@property (weak, nonatomic) IBOutlet UILabel *discountLabel;
///星级评价
@property (weak, nonatomic) IBOutlet ZFBLeverStar *leverView;


@end

@implementation ZFBBusinessCell

//重写模型属性的set方法在此方法中给子控件设置数据
-(void)setBusinessModel:(ZFBBusinessModel *)businessModel{
    _businessModel =businessModel;

    _iconView.image = [UIImage imageNamed:businessModel.icon];
    _nameLabel.text = businessModel.name;
    _levelLabel.text = @(businessModel.level).description;//level是NSNumber类型的,转字符串掉description方法
    _averagePriceLabel.text = [NSString stringWithFormat:@"人均消费 %@ 元",businessModel.averagePrice];  //plist中只存了个数字,要显示人均消费**元,顾要拼接字符串
    _offNumLabel.text = [NSString stringWithFormat:@"%@ 折",@(businessModel.offNum).description];
    _distanceLabel.text = [NSString stringWithFormat:@"距离北京石油化工学院 %@ m",businessModel.distance];
    _discountLabel.text = businessModel.discount;

     //传分
    _leverView.lever = businessModel.level;
}

- (void)awakeFromNib {
    [super awakeFromNib];
    // Initialization code
}

- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
    [super setSelected:selected animated:animated];

    // Configure the view for the selected state
}

@end

4858.com 118

//
//  ZFBBusinessPictureView.h

#import <UIKit/UIKit.h>

@interface ZFBBusinessPictureView : UIView

/**
 加载xib创建头部视图
 */
+(instancetype)businessPictureView;

@end

//
//  ZFBBusinessPictureView.m

#import "ZFBBusinessPictureView.h"

@implementation ZFBBusinessPictureView


/*

 知识点回顾--两种加载xib的方式
 1.NSBundle加载XIB  iOS 2.0 的方法
 2.使用UINib加载XIB  iOS 4.0 的方法   做了内存优化"如果内存紧张"内存警告,可以自动释放,如果有需要会重新自动加载""
 */

+(instancetype)businessPictureView{

    UINib *nib = [UINib nibWithNibName:@"ZFBBusinessPicture" bundle:nil];
    return [[nib instantiateWithOwner:nil options:nil]firstObject];
}

@end

4858.com 119
4858.com 120
4858.com 121

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return self.tableData.count} override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { // 1 let cell = tableView.dequeueReusableCell(withIdentifier: "GameCell", for: indexPath) let dictionary = self.tableData[(indexPath as NSIndexPath).row] as! [String:AnyObject] cell.textLabel!.text = dictionary["trackName"] as? String cell.imageView?.image = UIImage(named: "placeholder") if (self.cache.object(forKey: (indexPath as NSIndexPath).row as AnyObject) != nil){ // 2 // Use cache print("Cached image used, no need to download it") cell.imageView?.image = self.cache.object(forKey: (indexPath as NSIndexPath).row as AnyObject) as? UIImage } else { // 3 let artworkUrl = dictionary["artworkUrl100"] as! String let url:URL! = URL(string: artworkUrl) task = session.downloadTask(with: url, completionHandler: { (location, response, error) -> Void in if let data = try? Data(contentsOf: url){ // 4 DispatchQueue.main.async(execute: { () -> Void in // 5 // Before we assign the image, check whether the current cell is visible if let updateCell = tableView.cellForRow(at: indexPath) { let img:UIImage! = UIImage(data: data) updateCell.imageView?.image = img self.cache.setObject(img, forKey: (indexPath as NSIndexPath).row as AnyObject) } }) } }) task.resume() } return cell}

刚巧完成的应用程序的基本点部分,让自己解释下面的代码,以便更加好地问询:

1:这里,tableView 将使 cell 出列以便重用。 如果未有分配 cell,则
tableVeiw 将分配,调治大小并赶回八个新的 cell。
接下来,在字典对象中领到数额源数组中的当前缓存记录。 游戏标题被安装为
cell 的文书标签,並且 cell 被一时分配给占位图片,同时等待其下载。

2:cache 是二个近似群集的容器,特别周围于 NSDictionary 实例。
这里您利用它看作 UIImage
对象的集聚,在那之中第一是行索引(那是至极首要的,以便追踪对应于各类 cell
的不易的缓存图片)。 所以基本上,你首先检查是还是不是有近年来图片的缓存别本。
借使别本已经存在,则将其加载到 cell
中。3:若无给定的缓存副本,那么就能从服务器去异步下载它。4:就算图片下载成功,将切换来主线程,以更新视图。
那很入眼,因为全部 UI
职分都应当在主线程中施行,实际不是在后台线程中实行。5:那是无法子的一些,在创新图片在此之前检查
cell 是或不是在荧屏上可知。 不然,图片就要滚动时在每种 cell 上海重机厂复使用。
借使相关 cell 是可知的,那么只需将图片分配给
cell,并将其增多到缓存中以供之后使用。

别的,禁止使用 应用软件 的ATS,以便能够施行网络操作。
要禁止使用ATS,请从种类导航器视图中精选Info.plist文件,并将其转移为以下内容:

4858.com 122

更加多开源,尽在

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图
Copyright @ 2010-2019 美高梅手机版4858 版权所有