尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

为 iPhone Duo 做准备:containerConcentric 自适配指南

发布时间:2026/9/26 18:41:50

资讯中心
01
ARTICLE

为 iPhone Duo 做准备:containerConcentric 自适配指南

为 iPhone Duo 做准备:containerConcentric 自适配指南
从 RectTest 演示项目出发聊聊 iOS 与 macOS 怎样处理嵌套圆角以及“四个角长得不一样”时该怎么办。文中的郭靖、黄蓉故事是为解释技术编写的戏说并非金庸原著情节。核对环境Xcode 27.0项目最低支持 iOS 26.0、macOS 27.0。文中运行证据来自 iOS 27.0 模拟器与本机 macOS 27.0不能据此声称已经实测所有支持版本。第一回靖哥哥一掌下去四个角都圆了襄阳城里要修一座小院。郭靖负责外墙黄蓉负责院中亭台。郭大侠办事向来实在量了外墙圆角又量了亭子的圆角大笔一挥都是二十八。“整整齐齐。”他说。黄蓉把亭子往墙角一推停住了。“靖哥哥你看这条缝像不像有人拿降龙十八掌啃过”这事做过卡片界面的开发者大概都见过外面一个圆角容器里面一个圆角矩形两边的半径看起来很讲究嵌在一起却总有些别扭。尤其是内层贴近外层时直边之间的距离挺匀拐到圆角附近就忽宽忽窄。毛病通常不在半径够不够大而在内外两条轮廓有没有配合。先拿最简单的圆弧说话外层半径为R内层沿两条相邻边等距缩进d如果要让两条圆弧共享圆心内层半径应当是R - d。例如28 - 8 20。这个算式适合解释同心关系不是 Apple 自适应算法的完整实现。实际视图还会移动、缩放有最小半径限制连续圆角也不能完全当成四分之一圆来算。把这个减法写死进布局里后面很容易修成另一座襄阳城。RectTest 做的事很简单浅绿色是容器深绿色是可以拖动的内矩形。我们移动它、改变容器半径看系统怎样调整内矩形的圆角。图 1iPhone 18 Pro 模拟器运行截图。容器半径为 28 pt内矩形靠近右下角两边留出 8 pt当前统一模式显示四角均为 20 pt。这个数值是该位置的运行结果不是所有位置的通用公式。第二回桃花岛的口诀只有半句写在 minimum 里黄蓉递给郭靖一张纸上面没写招式只有一行 Swiftinner.cornerConfiguration.corners(radius:.containerConcentric(minimum:0))郭靖看了半晌“这里没有二十八。”“对。你终于不用替每个角算命了。”这行代码值得拆成三层看部分负责什么UIView.cornerConfiguration把圆角配置交给视图.corners(radius:)将这套半径策略分别用于四个角.containerConcentric(minimum:)根据视图与容器的几何关系计算半径并受最小值约束最容易看走眼的是第二层。同一个策略不代表同一个结果。四个角都使用containerConcentric但它们相对容器的位置不一样最后算出的数值可以不一样。Apple 对corners(radius:)的定义就是独立应用到各角。本机 UIKit 与 AppKit 的 SDK 头文件也明确写了配合同心策略时各角允许解析成不同的半径。minimum则是下限不是建议值也不是额外增加的半径。设为8就不要指望受该策略控制的角变成0。需要演示直角就从0开始。它约束的是内部这套半径策略不会把外部容器也改成直角。containerConcentric(minimum:)官方说明同心圆角也没有替我们包办布局。矩形放在哪里、间距留多少、拖动能否越界仍然是应用自己的事。RectTest 的边界限制和圆点吸附就是自己写的系统负责圆角的几何解析。第三回左右互搏也不能拿 uniform 代替 independent郭靖想起周伯通的左右互搏觉得四个角各练各的甚是合理。然后他写成了inner.cornerConfiguration.uniformCorners(radius:.containerConcentric(minimum:0))黄蓉叹了口气“你这是让四个人分头练功最后又要求他们摆同一个姿势。”这两个 API 只差一个名字设计意图却很不一样// 四角分别解析允许出现不同半径。.corners(radius:.containerConcentric(minimum:0))// 四角统一维持常见的等圆角矩形外观。.uniformCorners(radius:.containerConcentric(minimum:0))原来的 RectTest 使用第二种方式所以内矩形贴近右下角时另外三个角也会跟着变圆。这是统一模式的预期效果。新增的“不同内圆角容器”开关则同时改变两件事外部容器改成四种不同的半径。内部矩形切换到.corners(radius:)允许四角独立适配。只做第一步是不够的。外墙已经修成四种样子亭子还坚持四角统一黄蓉看了还是要返工。项目把外层半径安排成下面这样R继续由原来的滑块控制物理位置半径规则R 28 时左上00 pt直角右上R28 pt左下0.5R14 pt右下1.5R42 pt这里说的是物理左、右不是随文字方向变化的 leading、trailing。后面借用 SwiftUI 形状生成绘制路径时要记得自己正在映射哪套坐标。第四回先过 iOS 这一关——容器报家门内层见招拆招UIKit 的配置可以直接赋给视图。以下是项目核心逻辑的简化版本importUIKitavailable(iOS26.0,*)funcconfigureCorners(container:UIView,inner:UIView,radius:Double,minimum:CGFloat,usesDifferentCorners:Bool){ifusesDifferentCorners{container.cornerConfiguration.corners(topLeftRadius:.fixed(0),topRightRadius:.fixed(radius),bottomLeftRadius:.fixed(radius/2),bottomRightRadius:.fixed(radius*1.5))}else{container.cornerConfiguration.uniformCorners(radius:.fixed(radius))}letadaptiveUICornerRadius.containerConcentric(minimum:minimum)inner.cornerConfigurationusesDifferentCorners?.corners(radius:adaptive):.uniformCorners(radius:adaptive)}前提也得摆明白本例中inner是container的子视图。container.addSubview(inner)外层给出可被系统理解的圆角配置内层声明自己的适配策略。内层并没有写“左上必须为零”之类的设备特判容器的形状和几何关系才是依据。UIKit 圆角配置文档项目用UIPanGestureRecognizer更新内矩形的位置。动画播放时则由CADisplayLink逐帧推进位置让系统在移动过程中持续解析圆角。这个显示链接是项目的动画实现不是使用同心圆角 API 的必选配件。要知道系统到底算了多少可以读取// 放在 UIView 子类中。overridefunclayoutSubviews(){super.layoutSubviews()lettopLefteffectiveRadius(corner:.topLeft)lettopRighteffectiveRadius(corner:.topRight)letbottomLefteffectiveRadius(corner:.bottomLeft)letbottomRighteffectiveRadius(corner:.bottomRight)// 比较数值有无变化再通知界面更新。}按当前 SDK 的说明在layoutSubviews()等受支持的更新方法里读取这个值会建立半径变化时的自动失效关系。还有个小坑effectiveRadius(corner: .allCorners)返回的是这些角里的最大半径不是四个数值的集合。统一模式拿它显示一个数没问题独立模式继续这样读就会把差异藏起来。RectTest 因此改为显示两行上面两个角下面两个角。四个人练了四套功夫总得让观众看清谁在出掌。这些读取与失效语义可核对 Xcode SDK 的UIView.h中CornerConfiguration分类以及effectiveRadius(corner:)。第五回转战 macOS——内功算出来了招式还得自己画到了 AppKit郭靖照着 iOS 抄了一遍发现不能照样给cornerConfiguration赋值。黄蓉倒不意外“换了门派先翻秘籍。”两边主要差异如下。这里的版本要求针对本文讨论的原生 API不代表所有 SwiftUI 圆角功能都要这些版本。对照项UIKitAppKit本文 API 的最低系统版本iOS 26.0macOS 27.0半径类型UICornerRadiusNSViewCornerRadius配置类型UICornerConfigurationNSViewCornerConfiguration如何提供配置设置UIView.cornerConfiguration在NSView子类中重写只读属性自适应下限写法.containerConcentric(minimum: 0).containerConcentric(0)如何读取结果effectiveRadius(corner:)effectiveCornerRadii本例如何显示圆角UIKit 应用配置在回调中把解析结果用于图层绘制版本信息与签名已按 Xcode 27.0 自带 SDK 核对。AppKit 的配置入口见NSViewCornerConfiguration。内部视图的配置其实很短// 放在 NSView 子类中。overridevarcornerConfiguration:NSViewCornerConfiguration?{letradius:NSViewCornerRadius.containerConcentric(minimumCornerRadius)returnusesDifferentCorners?.corners(radius:radius):.uniformCorners(radius:radius)}接下来要接住系统返回的结果overridefuncviewDidChangeEffectiveCornerRadii(){super.viewDidChangeEffectiveCornerRadii()applyResolvedCorners()}funcapplyResolvedCorners(){guardletradiieffectiveCornerRadiielse{return}// 用 topLeft、topRight、bottomLeft、bottomRight 分别绘制。}effectiveCornerRadii是解析后的半径配置为空时结果也可以为空。回调的职责是让视图按需应用这些半径。原生 API 把几何信息交给你并没有替这个项目的自定义填色视图写完绘制逻辑。解析结果、变化回调项目还在位置、尺寸和配置参数变化时调用invalidateCornerConfiguration()让配置及其依赖重新计算。例如varminimumCornerRadius:CGFloat8{didSet{ifoldValue!minimumCornerRadius{invalidateCornerConfiguration()}}}overridefuncsetFrameOrigin(_newOrigin:NSPoint){super.setFrameOrigin(newOrigin)invalidateCornerConfiguration()}这不是 UIKit 那套直接赋值代码的逐字翻译。共享的是“容器提供形状内层声明策略”的思路接入点仍然各走各的。第六回一个 cornerRadius装不下四门武功最初的 Mac 版本在拿到四角结果后干了这么一件事layer.cornerRadiusmax(radii.topLeft,radii.topRight,radii.bottomLeft,radii.bottomRight)统一圆角演示里这么处理说得过去。到了独立圆角模式就相当于四位师父各教一招郭靖最后只记住声音最大的那位。CALayer.cornerRadius只有一个数不能完整表达0、28、14、42这种配置。maskedCorners能选哪些角变圆也不等于能给每个角指定不同半径。项目的做法是四角相同时继续用layer.cornerRadius不同时按四个半径生成连续圆角路径用CAShapeLayer做 mask。下面截取的是非统一分支radii为项目的DemoCornerRadiilayer.cornerRadius0letshapeUnevenRoundedRectangle(topLeadingRadius:radii.topLeft,bottomLeadingRadius:radii.bottomLeft,bottomTrailingRadius:radii.bottomRight,topTrailingRadius:radii.topRight,style:.continuous)letpathshape.path(in:CGRect(origin:.zero,size:bounds.size)).cgPathvartransformCGAffineTransform(translationX:0,y:bounds.height).scaledBy(x:1,y:-1)cornerMask.framebounds cornerMask.pathpath.copy(using:transform)cornerMask.fillColorNSColor.black.cgColor layer.maskcornerMask这段里有两件事不能漏。第一半径仍由 AppKit 计算。借用UnevenRoundedRectangle是为了生成绘制路径不是拿 SwiftUI 重新实现一套同心算法。这里也不声称它与 UIKit 的最终轮廓逐像素一致。第二当前画布是默认未翻转的NSView坐标原点在左下上面的形状路径按顶部方向构造所以做了纵向翻转。如果视图改为isFlipped true或者用了不同的坐标安排就要重新检查变换。不能把这两行奉为祖传心法见一个视图就贴一次。mask 要同时覆盖背景和子视图这样容器里的圆点才不会漏到圆角之外。切回统一模式时也要清掉layer.mask否则旧轮廓还在暗中发功。图 2直接运行项目 AppKit 画布后捕获的视图图像不是完整应用窗口截图。容器四角为 0、28、14、42 pt内矩形靠近左上角最小半径为 0此位置解析出的四角均为 0。图 3同一个运行时画布把内矩形移到右下角两边留出 8 pt。内部解析结果为左上 0、右上 0、左下 0、右下 34 pt。只有需要顺应容器的角弯了下来。第二张图的结果很能说明问题.corners并不要求内部四角每次都不一样也不是把容器的四个半径照抄进来。它允许每个角根据自己的处境作出不同响应。第七回开关只添一个状态别打成一团黄蓉给演示添了一个开关郭靖看着很满意“这回只改了一行吧”开发者听见这种话通常会先喝一口水。开关背后有两套最小半径状态。统一模式保留原来的默认值8独立模式默认0。这样用户打开开关时能看到直角关闭后也不会发现自己原先的设置没了。StateprivatevarminimumInnerRadius:Double8StateprivatevarindependentMinimumRadius:Double0StateprivatevarusesDifferentCornersfalseprivatevaractiveMinimumRadius:BindingDouble{usesDifferentCorners?$independentMinimumRadius:$minimumInnerRadius}演示界面由 SwiftUI 持有这些值再通过UIViewRepresentable和NSViewRepresentable传给原生画布。原生画布把圆角结果回传SwiftUI 负责显示数值。协调器只记按钮的触发标记没有再藏一套圆角状态。Mac 端原先就有半径平滑过渡。改成逐角之后平滑也必须逐角做不能最后又压回一个最大值letalpha1-exp(-dt/tau)letnextcurrent(target-current)*alpha这是每个角各执行一次的指数平滑示意项目中tau为0.12秒。目标值来自系统当前显示值来自动画过程Mac 界面显示的是平滑后的值。UIKit 当前读数则来自系统解析结果。两端演示的几何目标相同动画过程并非完全相同。如果录屏时发现 Mac 的数字晚一点追上目标先别给 Apple 提反馈。那是我们自己加的缓动还在路上。第八回城修好了别顺手宣布天下太平做到这里很容易冒出一个念头既然有左上直角、其余角不同的容器那是不是已经适配了所有非对称屏幕还没有。本文实测的对象是我们明确配置过形状的绿色容器。真实设备里窗口、根视图、安全区域、视图层级、横竖屏状态都可能参与最终布局。把一个演示容器做对不等于已经证明应用能正确获得并跟随某台设备的屏幕轮廓。因此本文不把某个机型的角写进代码也不把“左上为零”当成设备检测结果。这只是一个刻意设计的几何测试用例。以后需要适配真实屏幕再到相应设备或模拟器里检查实际层级与系统行为。还有旧系统。这个项目的最低版本已经设为 iOS 26 和 macOS 27所以正文可以直接使用这些 API。若要塞进支持更早系统的应用相关类型、属性和实现都要放进可用性边界旧系统提供固定圆角或其他降级绘制。不要只给某一行套个if #available旁边却还存着一个旧系统根本不认识的类型。这次项目的验证范围也记在这里免得文章写着写着就把“能编译”写成“全平台无敌”验证项结果与范围iOS 与 macOS 构建Xcode 27.0 下均通过iOS 界面iPhone 18 Pro 模拟器已启动并获取统一模式截图AppKit 容器四角运行时检查得到0、28、14、42AppKit 内部独立适配左上位置四角为零右下位置仅右下为34AppKit 绘制方向检查 mask 路径左上为直角、右下为圆角模式恢复关闭独立模式后内部回到统一半径外层独立 mask 清除iOS 新开关的完整自动交互回归尚未完成不能用 Mac 检查替代这项验证试用这个演示时可以先把最小半径设为0拖到四个角再把最小半径调到8观察直角怎样受下限约束。最后关闭开关看看独立的四个角如何重新统一。只盯着右下角看一次容易漏掉左下半径和坐标方向的问题。尾声同心不必同形小院终于修好了。郭靖绕着亭子看了一圈“左上是直的右下是圆的倒也妥帖。”黄蓉把尺子收起来“墙长什么样你就先看清它长什么样。别一上来就打十八掌。”写界面也是如此。希望四角统一就选.uniformCorners需要每个角顺着容器变化就选.corners。容器声明形状系统解析半径应用把结果完整地画出来。至于四个角是不是必须一样——小院住得舒服就好又不是丐帮排队报数。感谢宝子们的观赏我们下回不见不散附录代码与资料Apple 官方入口UICornerConfigurationUICornerRadius.containerConcentric(minimum:)NSViewCornerConfigurationNSView.effectiveCornerRadiiNSView.viewDidChangeEffectiveCornerRadii()本文代码摘自项目或按项目逻辑简化。标注“放在子类中”的片段需结合所在类型使用截图与数值只对应文中说明的环境和布局。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。