iOS 使用 Objective-C 绘制几大常见布局(UIKit / Core Graphics 实战)
在 iOS 开发中,大多数 UI 都是通过 AutoLayout、Frame 或 SwiftUI 构建的。但在一些性能敏感 、高度定制化 的场景(如图表、卡片背景、占位骨架、营销组件)中,直接绘制 UI 反而更高效、可控。
本文将系统讲解:
如何使用 Objective-C 手动绘制常见布局
每种布局的适用场景 + 完整示例代码
一、为什么还要用"绘制布局"?
适合使用绘制的场景
-
📊 图表 / 统计视图(折线图、柱状图)
-
🧩 高度定制化卡片背景
-
⚡ 列表性能优化(减少 View 层级)
-
🦴 Skeleton 骨架屏
-
🎨 渐变 / 圆角 / 不规则形状
不适合的场景
-
表单类 UI
-
强交互控件(按钮 / 输入框)
-
动态约束频繁变化的页面
二、基础知识:drawRect 与 Core Graphics
1️⃣ 自定义 View
@interface CustomLayoutView : UIView
@end
@implementation CustomLayoutView
- (void)drawRect:(CGRect)rect {
[super drawRect:rect];
CGContextRef ctx = UIGraphicsGetCurrentContext();
}
@end
⚠️
drawRect:由系统调用,不要手动调用通过
setNeedsDisplay触发重绘
三、常见布局一:矩形卡片布局(最常见)
适用场景
-
首页卡片
-
营销 Banner
-
模块背景
示例效果
-
圆角矩形
-
阴影 / 描边
绘制代码
- (void)drawRect:(CGRect)rect {
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGRect cardRect = CGRectInset(rect, 16, 16);
UIBezierPath *path =
[UIBezierPath bezierPathWithRoundedRect:cardRect
cornerRadius:12];
[[UIColor whiteColor] setFill];
[path fill];
[[UIColor lightGrayColor] setStroke];
path.lineWidth = 1;
[path stroke];
}
优点
-
无子 View
-
性能极高
-
样式统一
四、常见布局二:九宫格布局(Grid)
适用场景
-
功能入口
-
九宫格菜单
-
轻量展示页
绘制思路
-
计算行 / 列
-
循环绘制 cell
-
可点击:结合
touchesBegan
示例代码
- (void)drawRect:(CGRect)rect {
CGContextRef ctx = UIGraphicsGetCurrentContext();
int column = 3;
CGFloat padding = 10;
CGFloat itemW = (rect.size.width - padding * (column + 1)) / column;
CGFloat itemH = itemW;
for (int i = 0; i < 9; i++) {
int row = i / column;
int col = i % column;
CGFloat x = padding + col * (itemW + padding);
CGFloat y = padding + row * (itemH + padding);
CGRect itemRect = CGRectMake(x, y, itemW, itemH);
UIBezierPath *path =
[UIBezierPath bezierPathWithRoundedRect:itemRect cornerRadius:8];
[[UIColor colorWithWhite:0.95 alpha:1] setFill];
[path fill];
}
}
五、常见布局三:列表布局(模拟 Table)
适用场景
-
骨架屏
-
轻量列表占位
-
加载态 UI
绘制代码
- (void)drawRect:(CGRect)rect {
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGFloat itemH = 60;
int count = rect.size.height / itemH;
for (int i = 0; i < count; i++) {
CGRect rowRect = CGRectMake(16, i * itemH + 8,
rect.size.width - 32, 44);
UIBezierPath *path =
[UIBezierPath bezierPathWithRoundedRect:rowRect cornerRadius:6];
[[UIColor colorWithWhite:0.9 alpha:1] setFill];
[path fill];
}
}
📌 性能优势 :
比 UITableView + Skeleton View 更轻
六、常见布局四:分割线 & 虚线布局
普通分割线
CGContextSetStrokeColorWithColor(ctx, [UIColor lightGrayColor].CGColor);
CGContextSetLineWidth(ctx, 0.5);
CGContextMoveToPoint(ctx, 16, y);
CGContextAddLineToPoint(ctx, rect.size.width - 16, y);
CGContextStrokePath(ctx);
虚线
CGFloat dash[] = {4, 2};
CGContextSetLineDash(ctx, 0, dash, 2);
七、常见布局五:不规则布局(气泡 / 标签)
适用场景
-
聊天气泡
-
标签角标
-
引导浮层
示例:气泡背景
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(10, 0)];
[path addLineToPoint:CGPointMake(rect.size.width - 10, 0)];
[path addQuadCurveToPoint:CGPointMake(rect.size.width, 10)
controlPoint:CGPointMake(rect.size.width, 0)];
// ... 省略
[path closePath];
八、交互怎么做?(重点)
绘制 View 默认没有子控件,交互需自己处理:
- (void)touchesBegan:(NSSet<UITouch *> *)touches
withEvent:(UIEvent *)event {
CGPoint point = [[touches anyObject] locationInView:self];
if (CGRectContainsPoint(self.itemRects[index], point)) {
NSLog(@"点击了第 %d 个", index);
}
}
九、性能与注意事项
✅ 优点
-
极少 View 层级
-
内存占用低
-
滚动流畅
⚠️ 注意事项
-
避免频繁
setNeedsDisplay -
复杂动画建议用
CAShapeLayer -
文本多时结合
CoreText
十、总结
| 布局类型 | 是否推荐绘制 |
|---|---|
| 卡片背景 | ✅ 强烈推荐 |
| 骨架屏 | ✅ 推荐 |
| 图表 | ✅ 必须 |
| 表单 | ❌ 不推荐 |
| 输入控件 | ❌ 不推荐 |