Good! Morning!

GoodMorning!

a SINGLE coder


关于UIPageControl的frame 问题

Discuss about when UIPageControl is in an auto-layout environment, sometimes we should use frame to locate UIPageControl, and in some situation we will lose control of it.

If it’s hard for you to read Chinese, maybe you can click here to download the demo and in which I wrote some brief description among my code.

前言

此文为原创博客,如需转载,请注明出处。

如果读者有更深的见解或者更好的解决方法,或是对本文有任何建议或者看法,欢迎发送邮件到我的邮箱:spechles@spechles.com告诉我,我采纳后会更新博客,并注明出处,给出相关链接。

代码

GitHub

概述

在我完成我的毕业设计的时候,我遇到了一个非常奇怪的问题。当我将UIPageControl加入到一个设定了自动布局约束的UIScrollView中时,我想要UIPageControl水平居中,但我的代码在iphone5和5s上是奏效的,但到了iphone6以上,位置就放生的非常诡异的偏移。通过NSLog查看相关参数,并未察觉有什么问题。这引发了我的好奇。我尝试解决这个问题。这篇文章中,我将通过新写一个demo来详细讨论和解决这个问题。

发现问题

先让我们来看看这个问题是怎么样的。

新建一个工程,在main.storyboard的控制器上拖一个scrollView,给他设置约束,离四周间距为0,拖到ViewController.m的私有拓展,命名为backgroundView。添加我要的UIPageControl:

1
2
3
4
5
6
7
8
9
UIPageControl *pageContrlA = [[UIPageControl alloc]init];
pageContrlA.numberOfPages = COUNT;
CGRect frame = CGRectMake(0, 20, self.view.frame.size.width, 30);
pageContrlA.frame = frame;
pageContrlA.backgroundColor = [UIColor blackColor];
pageContrlA.currentPageIndicatorTintColor = [UIColor redColor];
pageContrlA.pageIndicatorTintColor = [UIColor greenColor];
[self.backgroundView addSubview:pageContrlA];
NSLog(@"pageContrlA's width = %lf",pageContrlA.frame.size.width);

为了让其更好辨别,我将他们添加了背景颜色。其中页数为一个宏定义,为7。最后我们NSLog一下看看pageContrl的宽度是多少。

先让我们看看IPhone5s的运行效果:

image

嗯,效果不错,控制台打印的信息呢?

2016-03-03 15:10:37.550 UIPageControllerFrameProblem[25128:1401126] pageContrlA’s width = 320.000000

嗯,也很对。

那让我们看看6sPlus的运行结果:

image

可以看得出,偏右严重,这显然违反了我的初衷。那控制台信息呢?

2016-03-03 15:17:39.715 UIPageControllerFrameProblem[25207:1406791] pageContrlA’s width = 414.000000

咦,看上去并没有问题。可能是小圆点的位置并不是居中显示的呢?
于是我尝试了用UIPageControl提供的-(CGSize)sizeForNumberOfPages方法,重新给定一个frame,然后手动居中它:

1
2
3
4
5
6
7
8
9
10
11
12
UIPageControl *pageContrlB = [[UIPageControl alloc]init];
pageContrlB.numberOfPages = COUNT;
CGSize size = [pageContrlB sizeForNumberOfPages:COUNT];
frame.size = size;
frame.origin = CGPointMake((self.view.frame.size.width-size.width)/2, 55);
pageContrlB.frame = frame;
pageContrlB.backgroundColor = [UIColor blackColor];
pageContrlB.currentPageIndicatorTintColor = [UIColor redColor];
pageContrlB.pageIndicatorTintColor = [UIColor greenColor];
[self.backgroundView addSubview:pageContrlB];
NSLog(@"pageContrlB's width = %lf",pageContrlB.frame.size.width);
NSLog(@"pageContrlB's origin.x = %lf",pageContrlB.frame.origin.x);

这次让我们看看它的宽度和起始的x值,依旧在6sPlus上运行:

image

Oh! No!我们清楚得看到小圆点均匀居中地排列在黑色矩形中,不是小圆点排版的问题,那NSLog的信息呢?

UIPageControllerFrameProblem[25228:1412550] pageContrlB’s width = 103.000000
2016-03-03 15:27:32.924 UIPageControllerFrameProblem[25228:1412550] pageContrlB’s origin.x = 155.500000

很有趣,让我们计算一下,6sPlus的屏幕宽度应该是414,减去系统给出的这个宽度103是311,除以2,得105.5,完全没错。

怎么了?为什么数值这么正确,但显示的确严重偏右了?我怀疑起了我看到的这个黑色框是否按照我给她的frame来显示了。我决定新建一个view,给它赋上一样的宽度和起始点很坐标,加到同一个父视图上看看结果:

1
2
3
4
5
UIView *viewB = [[UIView alloc]init];
frame.origin.y = 95;
viewB.frame = frame;
viewB.backgroundColor = [UIColor orangeColor];
[self.backgroundView addSubview:viewB];

image

很好地居中了,起始点并没有错。那么显而易见,应该是UIPageControl的锅了。这太奇怪了,UIPageControl竟然自己变长,并偏离了起始点。

测试到这里我有不知所措了。我要怎么去解决这个这么反常的结果呢?事实上此处我花了很长的时间去解决并尝试理解这样一个unexpect的结果。人为调整它的frame显然不是我想要的结果,因为现在它在5s上是运行正常的,如果手动修改,那在5s上就不再正常了。此处不将所有尝试手段进行展示了,大多数是没有价值的。直接给出一个可信可靠的方式。

解决问题

既然UIView加上去没问题,pageControl加在不加约束的父视图也没问题,那要不这样:我创建一个大小正好、位置居中的view,然后把pageControl添加到它之上不就好了么?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
UIPageControl *pageContrlC = [[UIPageControl alloc]init];
pageContrlC.numberOfPages = COUNT;
pageContrlC.backgroundColor = [UIColor blackColor];
pageContrlC.currentPageIndicatorTintColor = [UIColor redColor];
pageContrlC.pageIndicatorTintColor = [UIColor greenColor];

UIView *viewC = [[UIView alloc]init];
size = [pageContrlC sizeForNumberOfPages:COUNT];
frame = CGRectMake((self.view.frame.size.width-size.width)/2, 185, size.width, size.height);
viewC.frame = frame;
viewC.backgroundColor = [UIColor blueColor];
[self.backgroundView addSubview:viewC];

pageContrlC.frame = CGRectMake(0, 0, size.width, size.height);
[viewC addSubview:pageContrlC];

让我们还是用6sPlas运行一下:

image

确实居中了。那这个问题基本上已经解决了。

事实上根本不用计算着么个大小正好的view,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
UIPageControl *pageContrlD = [[UIPageControl alloc]init];
pageContrlD.numberOfPages = COUNT;
pageContrlD.backgroundColor = [UIColor blackColor];
pageContrlD.currentPageIndicatorTintColor = [UIColor redColor];
pageContrlD.pageIndicatorTintColor = [UIColor greenColor];

UIView *viewD = [[UIView alloc]init];
frame = CGRectMake(0, 225, self.view.frame.size.width, 35);
viewD.frame = frame;
viewD.backgroundColor = [UIColor blueColor];
[self.backgroundView addSubview:viewD];

pageContrlD.frame = viewD.bounds;
[viewD addSubview:pageContrlD];

image

同样很好的显示了。

其他重要内容

其实从全文一始就存在一个陷阱,让我们用self.backgroundView.frame.size.width来代替第二段代码的self.view.frame.size.width:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
UIPageControl *pageContrlE = [[UIPageControl alloc]init];
pageContrlE.numberOfPages = COUNT;
size = [pageContrlE sizeForNumberOfPages:COUNT];
frame.size = size;
frame.origin = CGPointMake((self.backgroundView.frame.size.width-size.width)/2, 300);
pageContrlE.frame = frame;
pageContrlE.backgroundColor = [UIColor blackColor];
pageContrlE.currentPageIndicatorTintColor = [UIColor redColor];
pageContrlE.pageIndicatorTintColor = [UIColor greenColor];
[self.backgroundView addSubview:pageContrlE];
NSLog(@"pageContrlE's width = %lf",pageContrlE.frame.size.width);
NSLog(@"pageContrlE's origin.x = %lf",pageContrlE.frame.origin.x);

UIView *viewE = [[UIView alloc]init];
frame.origin.y = 340;
viewE.frame = frame;
viewE.backgroundColor = [UIColor orangeColor];
[self.backgroundView addSubview:viewE];

image

我们看到非常奇怪的,pageControl虽然居中正常了,但是view却偏左了。让我们看看NSLog打印的信息:

2016-03-03 16:09:54.049 UIPageControllerFrameProblem[25279:1432117] pageContrlE’s width = 103.000000
2016-03-03 16:09:54.050 UIPageControllerFrameProblem[25279:1432117] pageContrlE’s origin.x = 108.500000

我们来计算一下,2*108.5+103应该是屏幕的宽度才对,结果是320。换成6s,结果是一样的。似乎在做pageControl的布局的时候,使用的坐标就自动变成了5s的尺寸了,怪不得5s上的显示是正确的!可是view却并不一样。view和pageControl似乎在处理这一种情况的时候,底层并未采取相同的方法。具体原因,我不得而知。

总之我们在处理相关问题的时候需要小心,个人还是推荐使用我给出的解决方法来解决。如果读者有更深的见解或者更好的解决方法,欢迎发送邮件到我的邮箱:spechles@spechles.com告诉我,我采纳后会更新博客,并注明出处,给出相关链接。