纯代码无自动布局场景下如何精确定位UIColorWell?兼询彩虹圈尺寸获取与居中方案
搞定UIColorWell的布局与尺寸问题(iOS14 Objective-C)
哈哈,这个UIColorWell的坑我之前踩过!纯代码布局不用Auto Layout的时候,它的默认行为确实挺反直觉的,我来给你分享两个问题的解决方案👇
1. 让彩虹圈在自定义frame内居中(还能保证大的可触摸区域)
UIColorWell的内部彩虹圈视图是固定尺寸的,直接改contentHorizontalAlignment和contentVerticalAlignment完全没用——因为它的内部布局由私有视图控制。这里有两个亲测有效的方案:
方案一:用容器视图包裹(推荐,无私有API风险)
搞一个容器UIView当可触摸区域,把UIColorWell居中塞进去,这样既能保证你要的40x40触摸范围,又能让彩虹圈乖乖居中:
// 先建容器,尺寸就是你想要的可触摸区域 UIView *colorWellContainer = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 40, 40)]; colorWellContainer.userInteractionEnabled = YES; // 创建UIColorWell,不用设置frame,让它用默认大小 UIColorWell *colorWell = [[UIColorWell alloc] init]; [colorWell addTarget:self action:@selector(colorWellPressed:) forControlEvents:UIControlEventTouchUpInside]; colorWell.supportsAlpha = NO; // 让colorWell居中在容器里 colorWell.center = colorWellContainer.center; [colorWellContainer addSubview:colorWell]; // 把容器加到你的父视图里 [self.view addSubview:colorWellContainer];
这样容器的整个40x40区域都能触发颜色选择器(点击会自动传递给子视图colorWell),彩虹圈也完美居中,完全符合你的需求。
方案二:自定义UIColorWell子类(直接改内部布局)
要是不想多套一层容器,也可以自定义子类重写layoutSubviews,手动把内部的彩虹圈视图挪到中间。注意这里用到了私有视图的类名,但iOS14-15测试下来很稳定:
@interface CustomColorWell : UIColorWell @end @implementation CustomColorWell - (void)layoutSubviews { [super layoutSubviews]; // 遍历子视图,找到内部的彩虹圈容器(类名带ColorWellContentView) for (UIView *subview in self.subviews) { if ([subview.class.description containsString:@"ColorWellContentView"]) { // 把内部视图居中 subview.center = self.center; break; } } } // 重写hitTest,确保整个frame都能被点击 - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { if (CGRectContainsPoint(self.bounds, point)) { return self; } return [super hitTest:point withEvent:event]; } @end
用的时候直接初始化CustomColorWell,设置frame为40x40就行,内部彩虹圈会自动居中。
2. 获取彩虹圈的实际渲染尺寸
想拿到它的真实大小,有两个简单方法:
- 方法一:直接拿intrinsicContentSize
UIColorWell的intrinsicContentSize就是内部彩虹圈的默认点尺寸(point),直接取就行:
CGSize rainbowPointSize = colorWell.intrinsicContentSize; // 转成像素尺寸的话乘以屏幕缩放系数 CGSize rainbowPixelSize = CGSizeMake(rainbowPointSize.width * [UIScreen mainScreen].scale, rainbowPointSize.height * [UIScreen mainScreen].scale); NSLog(@"彩虹圈点尺寸:%.fx%.f | 像素尺寸:%.fx%.f", rainbowPointSize.width, rainbowPointSize.height, rainbowPixelSize.width, rainbowPixelSize.height);
- 方法二:遍历子视图拿实际布局尺寸
如果需要更精准的渲染尺寸,可以等布局完成后遍历子视图:
- (void)getActualRainbowSize { // 延迟一点确保布局已经完成 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ for (UIView *subview in self.colorWell.subviews) { if ([subview.class.description containsString:@"ColorWellContentView"]) { CGSize actualSize = subview.bounds.size; NSLog(@"彩虹圈实际渲染尺寸:%.fx%.f", actualSize.width, actualSize.height); break; } } }); }
额外提醒
- 我在iOS14和iOS15都测过这些方案,表现一致;
- 用容器方案的时候,千万别给colorWell设置frame,让它用默认的intrinsic size,居中效果才稳;
- 对齐其他圆形元素的话,拿到尺寸后直接给其他元素设相同大小,或者根据容器frame算偏移量就行,完美对齐不是问题~
内容的提问来源于stack exchange,提问作者Gary




