一、子类父类的调用问题
1、我们为什么要抽取父类呢?
当我们多个界面拥有相同的功能或者页面有相同的部分,那么我们就可以考虑抽取父类以减少我们的代码量。
BTW:我们可以多重继承,比如原始父类A只负责页面未请求导数据时动画的加载,子父类B继承父类A,拥有多个控制器相同的页面功能。然后子类C、D、E、F等类纷纷继承B,只需要根据多个界面不同的布局,重新设置。
2、重用父类的问题
a、父类方法被执行的条件:
1、父类方法在子类中被重写,在重写的方法中super.父类方法(),那么父类的方法会被执行。
2、子类没有重写父类方法,此时父类方法会被自动调用。
b、父类方法被不执行的条件:
1、子类方法重写父类方法,但是子类方法没有调用super.父类方法()。
3、子类和父类使用相同的代理
问题:
在使用子类和父类使用相同的代理的时候,我们发现会报如下错误:
错误提示:Redundant conformance of 'RecommendViewController' to protocol 'UICollectionViewDataSource'
问题分析:
Redundant:多余的,过剩的 也就是说父类已经有了,我们再用相同的代理就多余了。那子类又必须要用代理怎么办呢?例子如下:
父类A中使用了UICollectionViewDataSource、UICollectionViewDelegate,子类B中不能使用,但是我们又需要在子类中添加代理来设置不同的cell。那我们该怎么办呢?
解决办法:
我们使用继承自代理的子代理来替代父类的代理,在本例中我们就可以用继承自UICollectionViewDataSource代理的子代理UICollectionViewDelegateFlowLayout来实现,这样就避免了问题的出现。
extension RecommendViewController : UICollectionViewDelegateFlowLayout{
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
//~~~2、只有section == 1的参数的cell是不同的,所以子类专门区分出来,然后进行布局和显示。
if indexPath.section == 1 {
// 1.取出PrettyCell
let prettyCell = collectionView.dequeueReusableCell(withReuseIdentifier: kPrettyCellID, for: indexPath) as! CollectionPrettyCell
// 2.设置数据
prettyCell.anchor = recommendVM.anchorGroups[indexPath.section].anchors[indexPath.item]
return prettyCell
} else {
// 3、其余的和父类相同,我们直接使用父类的方法super,来显示数据。 由于index.section==1的cell比较特殊,且只有在Recommend界面才有这个cell,所以我们再子类中加载其数据,其余的section我们加载到父类中。
//提取父类的意义:公共的方法和内容的加载我们都放到父类中,不同页面不同的界面展示,我们再在子类的上面添加相应的UI以及数据的处理,然后再调用父类的方法。
print("打印的传给父类的索引为\(indexPath.section)")
return super.collectionView(collectionView, cellForItemAt: indexPath)
}
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
if indexPath.section == 1 {
return CGSize(width: kNormalItemW, height: kPrettyItemH)
}
return CGSize(width: kNormalItemW, height: kNormalItemH)
}
}
BTW:我们在子类中重写创建Cell的代理方法,根据条件过滤属于子类的cell,针对父类的cell,我们通过重写调用,然后传值给父类相应的数据。
之后再reload,这样就完成了数据在父类、子类控制器上的加载。
至此完美完成了父子控件的数据加载。
二、控件的多次嵌套问题
例子:
下图就是一个经典的嵌套,UIView里面嵌套一个UICollectionView,UICollectionView创建两个cell,再在每个cell里面添加UICollectionView,然后再根据返回的数据创建多个cell,这样就完成了数据的展示。
遇到的问题:
此时相当于UIView嵌套了两层UICollectionView,那么我们怎么将事件的点击直接传递出来呢,我想到的方法是用代理,两层的代理将点击的cell的indexPatch传递给Controller,然后在Controller的代理中进行页面的跳转等操作。但是这一点需要我们设置一个代理的传递链,将数据一层层传递出来,
会比较麻烦,感觉通知传值可能更好一点,一步到位。
下面是代理传递的流程:
a、我们先设置最底层cell的代理穿参。
public protocol AmuseMenuViewCellDelegate {
func amuseCellDelegate(indexPath : IndexPath)
}
b、声明代理方法
open var delegate : AmuseMenuViewCellDelegate?//设置代理方法
c、点击cell后直接调用传值的代理
//点击事件后直接调用传值的代理
extension AmuseMenuViewCell : UICollectionViewDelegate{
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("点击了collectionView中的cell中的collectionview中的cell:\(indexPath.row)")
self.delegate?.amuseCellDelegate(indexPath: indexPath)
}
}
d、在当前UIView的collectionView的cell中添加代理接收到穿参。
1、View中设置代理
protocol AmuseMenuViewDelegate {
func amuseMenuViewDelegate(indexPatch:IndexPath)
}
2、设置底层cell的接收代理
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
// 1.取出cell
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: kMenuCellID, for: indexPath) as! AmuseMenuViewCell
cell.delegate = self
// 2.给cell设置数据
setupCellDataWithCell(cell: cell, indexPath: indexPath)
return cell
}
3、当前UIView的代理使用底层cell的代理将值传递出来。
extension AmuseMenuView : AmuseMenuViewCellDelegate{
func amuseCellDelegate(indexPath: IndexPath) {
print("采集了cell的数据信息的第几个item呢:\(indexPath.row)")
// 再传递给控制器页面
self.delegate?.amuseMenuViewDelegate(indexPatch: indexPath)
}
}
三、方法调用
1、设置类方法加载nib的View:
extension AmuseMenuView {
class func amuseMenuView() -> AmuseMenuView {
return Bundle.main.loadNibNamed("AmuseMenuView", owner: nil, options: nil)?.first as! AmuseMenuView
}
}
2、懒加载
// MAR:- 游戏条,加载多个游戏。
fileprivate lazy var gameView : RecommendGameView = {
let gameView = RecommendGameView.recommendGameView()
gameView.frame = CGRect(x: 0, y: -kGameViewH, width: kScreenW, height: kGameViewH)
return gameView
}()
3、网络请求封装
1、枚举
enum MethodType {
case get
case post
}
2、网络请求
class NetworkTools {
class func requestData(_ type : MethodType, URLString : String, parameters : [String : Any]? = nil, finishedCallback : @escaping (_ result : Any) -> ()) {
// 1.获取类型
let method = type == .get ? HTTPMethod.get : HTTPMethod.post
// 2.发送网络请求
Alamofire.request(URLString, method: method, parameters: parameters).responseJSON { (response) in
// 3.获取结果 guard校验
guard let result = response.result.value else {
print(response.result.error!)
return
}
// 4.将结果回调出去
finishedCallback(result)
}
}
}
4、GCD的网络顺序请求
func requestData(_ finishCallback : @escaping () -> ()) {
// 1.定义参数
let parameters = ["limit" : "4", "offset" : "0", "time" : Date.getCurrentTime()]
// 2.创建Group
let dGroup = DispatchGroup()
// 3.请求第一部分推荐数据
dGroup.enter()
NetworkTools.requestData(.get, URLString: "http://capi.douyucdn.cn/api/v1/getbigDataRoom", parameters: ["time" : Date.getCurrentTime()]) { (result) in
// 1.将result转成字典类型
guard let resultDict = result as? [String : NSObject] else { return }
// 2.根据data该key,获取数组
guard let dataArray = resultDict["data"] as? [[String : NSObject]] else { return }
// 3.遍历字典,并且转成模型对象
// 3.1.设置组的属性
self.bigDataGroup.tag_name = "热门"
self.bigDataGroup.icon_name = "home_header_hot"
// 3.2.获取主播数据
for dict in dataArray {
let anchor = AnchorModel(dict: dict)
self.bigDataGroup.anchors.append(anchor)
}
// 3.3.离开组
dGroup.leave()
}
// 4.请求第二部分颜值数据
dGroup.enter()
NetworkTools.requestData(.get, URLString: "http://capi.douyucdn.cn/api/v1/getVerticalRoom", parameters: parameters) { (result) in
// 1.将result转成字典类型
guard let resultDict = result as? [String : NSObject] else { return }
// 2.根据data该key,获取数组
guard let dataArray = resultDict["data"] as? [[String : NSObject]] else { return }
// 3.遍历字典,并且转成模型对象
// 3.1.设置组的属性
self.prettyGroup.tag_name = "颜值"
self.prettyGroup.icon_name = "home_header_phone"
// 3.2.获取主播数据
for dict in dataArray {
let anchor = AnchorModel(dict: dict)
self.prettyGroup.anchors.append(anchor)
}
// 3.3.离开组
dGroup.leave()
}
// 5.请求2-12部分游戏数据
dGroup.enter()
// http://capi.douyucdn.cn/api/v1/getHotCate?limit=4&offset=0&time=1474252024
loadAnchorData(isGroupData: true, URLString: "http://capi.douyucdn.cn/api/v1/getHotCate", parameters: parameters) {
dGroup.leave()
}
// 6.所有的数据都请求到,之后进行排序
dGroup.notify(queue: DispatchQueue.main) {
self.anchorGroups.insert(self.prettyGroup, at: 0)
self.anchorGroups.insert(self.bigDataGroup, at: 0)
finishCallback()
}
}