iOS 子控件布局 决定父控件大小 AutoLayout && Frame 的坑

一般都是父控件布局子控件, 但是如何让子控件布局父控件呢?
目的是 子控件大小改变, 父控件也改变!

很多情况下, 都是子控件改变了, 但是父控件的 layoutSubviews不调用, 这里就是解决这个问题的, 强制父控件调用layoutSubviews

这里针对的是自定义的view
三个好方法 例文中用到了前面两个
[self.superview setNeedsLayout];
[self setNeedsUpdateConstraints];
[self.superview setNeedsUpdateConstraints];

1,对于用传统的 frame 来布局的方式

layoutSubviews[self.superview setNeedsLayout];
使父类也调用layoutSubviews, 一层一层往上传
并且要把布局的关键代码 写到layoutSubviews

stemLabel.frame是在界面上可以手动改变的

这是在子控件(stemLabel)的代码

- (void)layoutSubviews
{
    [super layoutSubviews];
    [self.superview setNeedsLayout];
}

这是在父控件(stemView)的代码

- (void)layoutSubviews
{
    [super layoutSubviews];
     // 布局关键代码
    self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, self.frame.size.width, CGRectGetMaxY(self.stemLabel.frame)+20);

    [self.superview setNeedsLayout];
}

2,对于用 AutoLayout 来布局的方式

layoutSubviews[self setNeedsUpdateConstraints]; 使父类也调用layoutSubviews, 一层一层往上传 并且要把布局的关键代码 写到layoutSubviews

对于自定义的view这里面有一个方法- (void)updateConstraints 可以重写

对于我,目前没有用到这个方法 因为我的布局代码要么写到[[view alloc] init]后面, 要么把关键布局代码写到 layoutSubviews

这是在子控件(ExerciseStemView)的代码

- (void)updateConstraints
{
    [super updateConstraints];
}

- (void)layoutSubviews
{
    [super layoutSubviews];
    // 布局关键代码
    self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, self.frame.size.width, CGRectGetMaxY(self.stemView.frame)+20);

    // 这里不能像上面那样调用 [self.superview setNeedsLayout]; 会发生死循环
    // 可能原因是 这个控件是 frame 布局, 而他的父控件是 AutoLayout 布局的 所以要调用  [self setNeedsUpdateConstraints];
    [self setNeedsUpdateConstraints];
}

在这个ExerciseStemView 的父控件ExerciseCell上, 用的是AutoLayout
这是在父控件(ExerciseCell)的代码

- (void)updateConstraints
{
    [super updateConstraints];
}

- (void)layoutSubviews
{
    [super layoutSubviews];
    // 布局关键代码
    self.stemViewHeight.constant = self.stemViewModel.cellHeight;

}

除了oneViewExerciseCellXib上, 其他的都是自定的控件
我的层级结构是

ExerciseCell ---xib包含----->oneView---addSubview----->ExerciseStemView ---addSubview----->stemView---addSubview----->stemLabel

这样做的原因是 是因为stemLabel 一直会改变大小, 所以这样一层一层传出去.
这里面既有 xib AutoLayout布局 也有 代码frame布局 所以坑很多. 记录一下.

至于什么是布局的关键代码, 就是能影响其他其他控件的布局的代码.
例如 子控件改变,父控件要改变,然后父控件的左右控件要改变 等等


PS

这里还有一个方法, 让这个自定义的控件 强制调用它所在的控制器viewDidLayoutSubviews 来重新布局控件
有的时候会用到

自定义的控件的 内部代码如下

- (void)layoutSubviews
{
    [super layoutSubviews];
    self.stemLabel.width = self.frame.size.width;
    
    self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, self.frame.size.width, CGRectGetMaxY(self.stemLabel.frame)+20);

    // 强制viewController 调用 viewDidLayoutSubviews
    [[self viewController] viewDidLayoutSubviews];
}


/**
 *  返回当前视图的控制器
 */
- (UIViewController *)viewController {
    for (UIView* next = [self superview]; next; next = next.superview) {
        UIResponder *nextResponder = [next nextResponder];
        if ([nextResponder isKindOfClass:[UIViewController class]]) {
            return (UIViewController *)nextResponder;
        }
    }
    return nil;
}

3,更新 继续填坑

由于我以前的ExerciseCell 这个cell 的尺寸是整个屏幕,固定大小, 里面有个Scollview, 所以, 里面控件改变,不用改变最外层的 ExerciseCell的尺寸, 只要改变 Scollview的contentsize 就行了其实如果约束加好, Scollview的contentsize不用代码改变,只要更新下就行了
但是, 当我把这个ExerciseStemView加到一个tableview的cell 的时候,就出现了问题.

问题 1

table加载的时候, cell 高度不正确

由于cell的高度是动态的,所以要根据ExerciseStemView 的高度而决定 当然 cell里面还有其他控件

我开始是在tableviewcell的xib上添加了一个oneView 一个twoView

设置约束,
oneView距离tableviecell的 contenview 上 左 右 各20, 高度为100 并拖拽到tableviewcell 的.m文件中 为 oneViewHeight
twoView距离tableviecell的 contenview 下 左 右 各20, 高度为50 并拖拽到tableviewcell 的.m文件中为 twoViewHeight
oneView底部距离twoView 顶部 20

很简单的布局

然后在oneView添加 ExerciseStemView,并在layoutsubview中设置oneViewHeight = ExerciseStemView的高度(其实是一个模型的cellheight)
但是运行的时候约束会报错, 不过不会崩溃

但是cell的高度其实是不正常的. 出现了ExerciseStemView比cell还高.
对于这个问题,我用了上面1 和2的所有方法, 都无效.

后来,我想到增加一个中间层. 为 tempView, 成为 cell的 contenview的子控件 成为 oneviewtwoview的父控件. 然后设置 oneviewtwoview的约束和以前一样
对于tempView的约束, 只增加3个 下 左 右 距离父控件为0, 下面不设置, 这样不会报错

然后 在layoutsubview的时候, 起始我是想把tempView的高度直接赋值给 cell的 高度,后来发现不行, 因为tempView的高度虽然在界面显示后是正常的, 但是在layout中并不正确, 而是 在你的xib的起始的大小. 所以
我就把所有的子控件都加到了一起
像这样

self.cellViewModel.cellHeight = self.oneViewHeight.constant + self.twoViewHeight.constant + 1 + 60;

其实这个当初加的中间层也就没有意义了,完全没用到

这样以后 , 在控制器的 tableview的heightForRowAtIndexPath代理方法中 ,根据indexPath获取对应模型的 .cellHeight即可

这样 tableview加载以后,高度就动态的高度了.

问题 2

当点击ExerciseStemView的按钮的时候,需要增加填充一段数据,这个时候 ExerciseStemView的高度改变了,但是 cell的高度并没有改变,

layoutSubview的方法也只是调用到 ExerciseStemView就截止了,不会调用父类tableview的celllayoutSubview的方法.

我一直想去调用,但是无果,况且调用也没有用, 因为整个tableview 还要刷新,不然影响其他cell的显示.

我的错误尝试
1, 在ExerciseStemView调用 layoutSubview的方法的时候 发通知通知控制器, 让[self.tableView reloadData];
发现 会发通知很多次, 并且界面显示卡顿,空白,飞走,等各种奇怪现象.

2, 我试图接收到通知只执行一次, 但是依然各种问题

3, 我在ExerciseStemView点击方法中 发通知通知控制器, 让[self.tableView reloadData]; 但是这个时候子控件还没有刷新完成, 无法获取 ExerciseStemView的真正高度,失败

4, 后来我想, 不如直接更新模型,然后再[self.tableView reloadData]; 这样就会显示对了

当然前提是需要把点击事件后,增加的填充一段数据传出去, 这就用到了通知, 直接同第三步一样发通知, 不同的是包装一个字典把数据传到控制器,当然也应该保护cell的indexpath, 然后拼接到控制器的 cell的模型数组中的对应的元素上
然后调用[self.tableView reloadData];

这个时候就能显示正常了.

indexpath 我是变成了模型的一个属性层层传进去, 然后发通知出来.


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,922评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,591评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,546评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,467评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,553评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,580评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,588评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,334评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,780评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,092评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,270评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,925评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,573评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,194评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,437评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,154评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,127评论 2 352

推荐阅读更多精彩内容