2019独角兽企业重金招聘Python工程师标准>>>
PaintCode 是一个能够让你像Photoshop一样去设计你的用户界面的软件 – 但是它并不是仅仅保存一张图片当作资源让你来使用,它能够为你生成 CoreGraphic 源码直接使用到View的绘制中.
可以简单地说,我们看重的是下面几点:
- 节省了开发时间。 如果一行一行的去写 Core Graphics 代码,绝对会比用 PaintCode多很多时间。 而时间就是金钱!
- 减少了程序包大小。 用代码去绘制各种view,可以让你不再去添加图片资源,减少了资源包大小。
- 让它能做更动态的改变。. 最后,我们可以做更多相比于使用图片做不到的事情,比如:可以在程序运行时轻松的改变控件的颜色。
深入: 这篇教程实际基于我们收到的一些 PaintCode 的使用反馈来写的。 但是, 教程里的的所有意见和建议绝对没有一点瞎掰,绝对原创。
想要尽可能的利用学习这篇教程,大家至少知道一点iOS开发。 并且,懂一些 Core Graphics知识会更好(不是100%要求)。 如果大家从来没听说过 Core Graphics,真心推荐去看看 Core Graphics系列教程 。
废话不多说,切入正题吧!
准备开始
要学习这一系列的PaintCode 教程, 首先我们需要有个 PaintCode 软件。大家可以在这里下载 试用版, 或者从 Mac App Store购买完整版。
尽管这个app还是算比较贵的,但是就我看来,他在生产中的能发挥的价值完全能物超所值。 如果大家还不确定, 下载一个试用版在这个教程里试试吧!
创建第一个动态按钮
在敲任何代码之前,我们将会使用 PaintCode 来设计一个全新的按钮。
打开 PaintCode,选择 FileSave,然后把这个项目命名为 DynamicButton.。这样的话我们就可以在进行这个教程的时候时时的保存了。
点击屏幕右下的Canvas 按钮, 然后把画布大小设置为宽 480 高 150 pixels,如下图所示:
然后通过设置 Underlay color 的RGB值为 50 50 50来吧画布的颜色改为浅灰色,如下:
注意: 我们默认是在非高清画布上工作,如果大家想看高清的效果,点击Canvas按钮边上的 Retina按钮。这个按钮可以让你在高清和非高清之间切换。
现在选中工具栏里的Round Rect 按钮,然后在画布里拖出一个方形来。
我们能看到,当我们在画布里选中一个形状的时候,左边会出现这个形状的所有属性。确保选中了刚才的这个方形,然后把它的属性修改为如下:
- X: 4
- Y: 4
- Width: 473
- Height: 41
接下来,为了改变按钮的颜色,我们需要设置它的Fill值。
在左边的属性栏里, 点击 Fill,,在弹出的选项里选择 Add New Gradient,然后命名为 ButtonGradient。现在, 点击 左下的 color stop。 这些在颜色区域底部圆形的颜色选取点是用来给渐变提供所需参数的。
接着,选着右下方的调色盘,然后RGB输入255 0 0,如下所示:
再次点击调色盘按钮来关掉它。
现在, 用同样的办法把右边的颜色选取点的 RGB 改为 112 1 0 ,如下:
PaintCode 让你能够去给每个使用在形状上的颜色命名,因为它们以同样的名字出现在代码里。 如果界面下方没有代码窗口,选择 View > Code 来打开它。 下面图片里高亮的那两行应该就是刚才我们创建的2个颜色。
为了更好的找到这些颜色,我们最好给他们一个描述性较高的名字。
选中 Colors 选择栏,在左边栏下面找到并双击 Fill Color。在弹窗里把它的名字改为 ButtonColorDark,如下所示:
同理把Stroke Color 名字改为 ButtonColorLight。
现在回到位于中间的形状属性栏,选择 Stroke 下来框里的 System Colors > Common Colors > BlackColor,如下图所示,把描边颜色改为黑色。
Increase the Width parameter to 2 under the Stroke sub-section, as so:
现在,我们要给按钮加一个为发光 ,选择 Add New Shadow… 在Fill的下拉框里选择 Outer Shadow,如下所示:
像上面的按钮一样给这个阴影一个好的名字,把它名字改为 OuterGlow。 接下来, 选中 Color边上的下拉框,然后在颜色选取框里选着白色框, 打开边上的那个颜色调整按钮
把阴影偏移和阴影大小调整为下图一样:
切换到 Colors 选项卡下面然后把 Glow 改为 InnerGlowColor。
接着,在Add New Shadow的下拉框里面给 Inner Shadow 加一个内高光。把这个高光命名为Highlight, 颜色设置为 白色,透明度为 130, 然后把offsets 和 blur radius设置为如下所示:
根据前面的教程。这个颜色的名字可能已经是默认的 Shadow Color 2 ,如果不一样,就把它改成这个名字。
现在,我们的按钮应该和下面一样了:
到这个时候,我们的按钮已经看起来挺不错的了,但是他的大小是定死了的。要让它可以随意的改变大小,我们需要给他加一个frame。 我们会在代码里改变它的大小,接下来我们会在PaintCode里面设置frame,这样可以让里面的所有东西都是可变大小的。
给按钮加Frame
在顶部工具栏里选择 Frame ,然后画一个框包住按钮。把这个frame的属性设置为如下:
同时选中Frame 和 Rounded Rectangle, 按下 Option-Command-G 组合键来把它们放在一个group里。 或者, 我们可以选择去按菜单里的 Selection > Group来达到同样的效果。 最后吧这个group米命名为Button。
我们现在可以看到在左边的边栏最顶上,可能看到button group 和它包括2个元素,如下所示:
在左边面板里选中 Rounded Rectangle。 找到设置这个形状大小限定的区域 – 它是在坐标设置左边的一个方框, 里面包含了几个杆状,弹簧状的横条。
我们现在需要这个按钮能够水平的竖直的去改变大小,同时又能保持水平中心不变。 所以我们就要吧外部的约束设置为直的(固定了大小), 然后底部应该设置为弹簧条(能够随意的改变大小)。 这样就能让按钮保持在水平中心对齐了。
但是,为了能让按钮能随意的改变大小和frame一样,我们需要吧方框里面的杆都改成弹簧。
我们仅仅需要点一下这些杆,就可以让他们在直杆和弹簧之间切换。
当大家完成了修改约束,样子应该如下所示:
到这里,已经完成了大部分工作了! 现在我们选中按钮边上的 Frame,然后随便把这个按钮左右上下拖动试试,它的样子大概如下图:
现在PaintCode里的画图这部分的工作已经完成 —我们接下来要开始把它加入xcode里了。
加入iOS项目里
在完成了我们项目所用到的按钮以后— 当然,我们没有写一行代码!别急,现在卷起袖口准备开始撸代码吧!
我已经为大家准备了一个用开测试这个按钮的起始项目。下载这个起始项目, 解压,然后在xcode里打开。看看里面有什么吧。
注意: 这个项目里的storyboard是关掉了Autolayout的。并不是每个项目都需要这样,但是在这里,我们更偏向于使用springs和struts约束。
这是一个Tabbar 应用,里面包含了3个view controller,他们分别是为这个系列教程每次创建的控件准备的。 在这次的 PaintCode 教程里,我们将会使用 ButtonViewController 来展示我们的按钮。
在 Classes > Views下面有一个空文件夹。这次, 我们在这里面创建自己的类。我们会继承一个现有的控件,然后去改变他的绘制代码,而不是完全的用一个空UIView来实现。
而在这个教程里,我们继承UIButton。让我们开始吧!
创建按钮
在 Classes 组下面, 右键 Views 组。选择 NewFile… 然后创建一个 iOSCocoa TouchObjective-C class模板,把这个类命名为 ButtonView,然后继承自 UIButton。
这个类将会使用我们在 PaintCode里创建的绘制代码。
现在我们做一些准备工作, 打开 ButtonView.m,删除 initWithFrame: 方法,然后反注释 drawRect:。如下所示:
- #import "ButtonView.h"
- @implementation ButtonView
- - (void)drawRect:(CGRect)rect
- {
- // Drawing code
- }
- @end
drawRect: 这里就是我们施展魔法的地方 – 这里我们会把PaintCode生成的代码加上。 如果你不熟悉 drawRect:甚至不知道它是做什么的,最好先去看看我们的 Core Graphics 系列教程吧。
回到 PaintCode 然后找到 Code View。如果没有找到, 选择 View > Code 来打开这个面板。找到代码生成配置模块:
把平台设置为 iOS > Objective-C,系统版本设置为 iOS 5+, 默认原点为Default Origin,内存管理为 ARC。如果这是你第一次使用 PaintCode — 或者你从来没弄过这些东西 — PaintCode已经帮你做了完好的内存管理了。
复制并且把这些代码从 PaintCode里粘贴到 drawRect:里面。这个方法应该和下图一样了:
- - (void)drawRect:(CGRect)rect
- {
- General Declarations
- CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
- CGContextRef context = UIGraphicsGetCurrentContext();
- Color Declarations
- UIColor* buttonColorDark = [UIColor colorWithRed: 0.439 green: 0.004 blue: 0 alpha: 1];
- UIColor* buttonColorLight = [UIColor colorWithRed: 1 green: 0 blue: 0 alpha: 1];
- UIColor* innerGlowColor = [UIColor colorWithRed: 1 green: 1 blue: 1 alpha: 0.502];
- UIColor* shadowColor2 = [UIColor colorWithRed: 1 green: 1 blue: 1 alpha: 0.51];
- Gradient Declarations
- NSArray* buttonGradientColors = [NSArray arrayWithObjects:
- (id)buttonColorLight.CGColor,
- (id)buttonColorDark.CGColor, nil nil];
- CGFloat buttonGradientLocations[] = {0, 1};
- CGGradientRef buttonGradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef)buttonGradientColors, buttonGradientLocations);
- Shadow Declarations
- UIColor* outerGlow = innerGlowColor;
- CGSize outerGlowOffset = CGSizeMake(0.1, -0.1);
- CGFloat outerGlowBlurRadius = 3;
- UIColor* highlight = shadowColor2;
- CGSize highlightOffset = CGSizeMake(0.1, 2.1);
- CGFloat highlightBlurRadius = 2;
- Frames
- CGRect frame = CGRectMake(0, 0, 480, 49);
- Button
- {
- Rounded Rectangle Drawing
- CGRect roundedRectangleRect = CGRectMake(CGRectGetMinX(frame) + 4, CGRectGetMinY(frame) + 4, CGRectGetWidth(frame) - 7, 41);
- UIBezierPath* roundedRectanglePath = [UIBezierPath bezierPathWithRoundedRect: roundedRectangleRect cornerRadius: 6];
- CGContextSaveGState(context);
- CGContextSetShadowWithColor(context, outerGlowOffset, outerGlowBlurRadius, outerGlow.CGColor);
- CGContextBeginTransparencyLayer(context, NULL);
- [roundedRectanglePath addClip];
- CGContextDrawLinearGradient(context, buttonGradient,
- CGPointMake(CGRectGetMidX(roundedRectangleRect), CGRectGetMinY(roundedRectangleRect)),
- CGPointMake(CGRectGetMidX(roundedRectangleRect), CGRectGetMaxY(roundedRectangleRect)),
- 0);
- CGContextEndTransparencyLayer(context);
- // Rounded Rectangle Inner Shadow
- CGRect roundedRectangleBorderRect = CGRectInset([roundedRectanglePath bounds], -highlightBlurRadius, -highlightBlurRadius);
- roundedRectangleBorderRect = CGRectOffset(roundedRectangleBorderRect, -highlightOffset.width, -highlightOffset.height);
- roundedRectangleBorderRect = CGRectInset(CGRectUnion(roundedRectangleBorderRect, [roundedRectanglePath bounds]), -1, -1);
- UIBezierPath* roundedRectangleNegativePath = [UIBezierPath bezierPathWithRect: roundedRectangleBorderRect];
- [roundedRectangleNegativePath appendPath: roundedRectanglePath];
- roundedRectangleNegativePath.usesEvenOddFillRule = YES;
- CGContextSaveGState(context);
- {
- CGFloat xOffset = highlightOffset.width + round(roundedRectangleBorderRect.size.width);
- CGFloat yOffset = highlightOffset.height;
- CGContextSetShadowWithColor(context,
- CGSizeMake(xOffset + copysign(0.1, xOffset), yOffset + copysign(0.1, yOffset)),
- highlightBlurRadius,
- highlight.CGColor);
- [roundedRectanglePath addClip];
- CGAffineTransform transform = CGAffineTransformMakeTranslation(-round(roundedRectangleBorderRect.size.width), 0);
- [roundedRectangleNegativePath applyTransform: transform];
- [[UIColor grayColor] setFill];
- [roundedRectangleNegativePath fill];
- }
- CGContextRestoreGState(context);
- CGContextRestoreGState(context);
- [[UIColor blackColor] setStroke];
- roundedRectanglePath.lineWidth = 2;
- [roundedRectanglePath stroke];
- }
- Cleanup
- CGGradientRelease(buttonGradient);
- CGColorSpaceRelease(colorSpace);
- }
如果大家仔细的浏览了代码,会发现它是用Core Graphics来绘制的。 或多或少的方法大家可能都没见过,但是它们并不难于去理解。 并且,这也是我们使用 PaintCode 的原因之一;为了省掉学习 Core Graphics并且花更少的时间去在那里慢慢的调试绘制代码! :]
注意: 大家暂时不用去太担心 drawRect: 里面代码的缩进和语法。我们等会儿会再回来,并且做一些修改 ,包括把语法改为 moden Objective-C。
现在该来测试我们的按钮了。
在 Project Navigator 找到我们的storyboard 然后选中 ButtonViewController。在Object Library里拖出来一个View 然后把它的 X 和 Y坐标为 20, 35。然后, 把它的 Width 和 Height 设置为 280,41。
最后, 把它的 Autosizing 设置 修改为只能水平扩张,如下所示:
切换到Identity Inspector 然后把它的类名设置为 ButtonView。
注意:大家可能有疑问为什么在storyboard里面是一个 UIView , 但是 ButtonView又继承与UIButton。 原因很简单 UIButton 它继承于 UIView因此一个按钮任然是一个 view。
在这次 PaintCode 教程的后部分 ,我们会让它和真正的按钮一样起作用。毕竟 —他继承了UIButton。
是时候检测我们漂亮的新按钮了!
点击 Run 看看我们的按钮什么样的吧:
额…按钮貌似被砍掉了一块,怎么回事儿?
现在找到ButtonView.m里的 drawRect::
- - (void)drawRect:(CGRect)rect
- {
- ...
- Frames
- CGRect frame = CGRectMake(0, 0, 480, 49);
- ...
- }
原来!!!!这个按钮的frame是定死了的。我们想要的是一个可以动态改变大小的按钮 — 并且能够适应我们给它设置的大小。
这个问题很简单,修改设置 frame的那行代码为如下:
- - (void)drawRect:(CGRect)rect
- {
- ...
- Frames
- CGRect frame = rect;
- ...
- }
上面那段代码是把view的frame设置为绘制的frame的大小。
点击Run,让我们再来一发!
耶!按钮的绘制终于和我们想要的一样了。旋转模拟器(或者设备) 我们会看到按钮和我们期望的一样保持水平位置,改变着宽。
我们已经完成了大部分了 — 但是我们怎让才能让它变得更动态呢?
动态的设置按钮属性
动态的控制按钮大小是挺不错的 —但是现在,我们要做一件更神奇的事情;在 ButtonViewController 里面加一些滑动条来控制按钮的RGB值。
回到storyboard,拖3个Slider 并且把它们放在按钮下面。 在 Size Inspector里面, 把3个slider的 Autosizingwidget 设置为如下所示:
大家还可以把按钮往下移一点,然后放一个label 写着 “Tap Me”,这样的话可以让你的用户知道按钮是可以点击的。Storyboard应该如下图一样的:
找到Attributes Inspector,把slider的 Min Track Tint 的颜色设置为如下的RGB值:
- Top slider: R:255 G:0 B:0
- Middle slider: R:0 G:255 B:0
- Bottom slider: R:0 G:0 B:255
注意: 有的时候slider的背景色也会随着Min Tint color 的改变而改变。这是一个xcode的bug,我们可以直接吧slider的背景色设置为默认的。
然后找到 ButtonViewController.m,在顶部加入下面的代码:
- #import "ButtonView.h"
- @interface ButtonViewController ()
- @property (weak, nonatomic) IBOutlet UISlider *blueSlider;
- @property (weak, nonatomic) IBOutlet ButtonView *buttonView;
- @property (weak, nonatomic) IBOutlet UISlider *greenSlider;
- @property (weak, nonatomic) IBOutlet UISlider *redSlider;
- @end
把 buttonView 连接到我们设置的那个 UIView 上,然后把3个 UISlider 和storyboard里面各自对应的连接上。如果大家还不知道怎么连接view和outlet,可以去看看我们的 How To Create a Simple iPhone App 系列教程。
为了能让按钮的颜色随着slider的拖动而改变, 我们的 ButtonView 需要随着slider 去更新。
找到 ButtonView.h 然后把下面3行加入 @interface 和 @end 之间:
- @property (assign, nonatomic) CGFloat blueColor;
- @property (assign, nonatomic) CGFloat greenColor;
- @property (assign, nonatomic) CGFloat redColor;
这些是储存按钮RGB值的变量。
然后切换到 ButtonView.m ,找到 drawRect:。我们会注意到这段 Gradient Declarations 用的是旧的NSArray代码。为了保持良好的代码风格,把 Gradient Declarations 那行替换成如下:
- NSArray *buttonGradientColors = @[(id)buttonColorLight.CGColor, (id)buttonColorDark.CGColor];
然后,把所有带 “Color Declarations” 注释的代码替换成如下:
- -(void)drawRect:(CGRect)rect {
- ...
- // 1
- UIColor *buttonColorLight = [UIColor colorWithRed:self.redColor green:self.greenColor blue:self.blueColor alpha: 1];
- // 2
- if (self.state == UIControlStateHighlighted) {
- buttonColorLight = [UIColor colorWithRed:self.redColor green:self.greenColor blue:self.blueColor alpha:0.5];
- }
- // 3
- CGFloat buttonColorLightRGBA[4];
- [buttonColorLight getRed:&buttonColorLightRGBA[0]
- green:&buttonColorLightRGBA[1]
- blue:&buttonColorLightRGBA[2]
- alpha:&buttonColorLightRGBA[3]];
- // 4
- UIColor *buttonColorDark = [UIColor colorWithRed:(buttonColorLightRGBA[0] * 0.5)
- green:(buttonColorLightRGBA[1] * 0.5)
- blue:(buttonColorLightRGBA[2] * 0.5)
- alpha:(buttonColorLightRGBA[3] * 0.5 + 0.5)];
- // 5
- UIColor *innerGlowColor = [UIColor colorWithRed:1 green:1 blue:1 alpha:0.53];
- UIColor *shadowColor2 = [UIColor colorWithRed:1 green:1 blue:1 alpha:0.51];
- ...
- }
我们在这里具体做了:
- 申明一个局部 UIColor 变量作为按钮的浅色 (记住,我们在渐变编辑器里设置的那个),但是现在我们使用的是来自slider的RGB值。
- 如果用户安下了按钮,把按钮的颜色的alpha降低50%, 这时候按钮的状态是 UIControlStateHighlighted。这能在按下按钮时提供用户一些视觉上的反馈。
- 把按钮的rgb值储存到一个CGFloat的数组里。
- 基于浅色创建按钮的深色 (记住,我们在渐变编辑器里设置的那个) 。
- 基于一些固定值创建内发光和阴影。
在这里我们最好给按钮一个初始颜色。 因为我们用的是Storyboard,所以我们需要重载 initWithCoder: (在ButtonView.m) 来实现:
- -(id)initWithCoder:(NSCoder *)coder {
- if (self = [super initWithCoder:coder]) {
- self.redColor = 1.0f;
- self.greenColor = 0.0f;
- self.blueColor = 0.0f;
- self.contentMode = UIViewContentModeRedraw;
- }
- return self;
- }
这个设置把view的内容模式设置为了 UIViewContentModeRedraw,并且让按钮初始的时候是红色的。
注意: 如果大家好奇为什么重载 initWithCoder: 而不是 initWithFrame:, 这是因为initWithFrame:是用来在代码里手动生成view的, 然而 initWithCoder: 是用来初始化一个从Nib或者Storyboard里面生成的view的。
在我们开始测试之前,还有最后一件事做。找到 ButtonViewController.m 然后吧 viewDidLoad 替换成如下:
- -(void)viewDidLoad {
- [super viewDidLoad];
- [self.redSlider setValue:self.buttonView.redColor];
- [self.greenSlider setValue:self.buttonView.greenColor];
- [self.blueSlider setValue:self.buttonView.blueColor];
- }
上面的代码把每个slider的颜色设置为按钮各个RGB通道的值。
编译运行, 试试滑动这些slider,如下所示:
等下!!为什么没有反应!
我们确实连接了slider, 但是现在在 ButtonViewController 没有任何方法去响应slider值的改变。我们需要加一些 IBAction 代码来处理这些值。
响应事件
响应按钮和slider的事件其实很简单。
在ButtonViewController.m里加入如下代码:
- -(IBAction)sliderValueChanged:(UISlider *)slider {
- if (slider == self.redSlider) {
- self.buttonView.redColor = self.redSlider.value;
- } else if (slider == self.greenSlider) {
- self.buttonView.greenColor = self.greenSlider.value;
- } else if (slider == self.blueSlider) {
- self.buttonView.blueColor = self.blueSlider.value;
- }
- [self.buttonView setNeedsDisplay];
- }
上面的能收到一个值改变了的slider的指针。 我们通过和属性里声明的slider比较来确定具体修改哪个slider的值。
我们可以注意到,在代码的最后我们调用了setNeedsDisplay 来让按钮能够更新到最新的颜色。
现在回到Storyboard, 依次把slider的 Value Changed event 和ButtonViewController里的 sliderValueChanged: 连接起来,如下所示:
Build and run your project again, and try once more to move the sliders around to change the button’s color, as below:
叼炫酷!现在我们的按钮,不经能任意改变大小!还能随便的改变颜色了!
现在我们来试试按钮按下有啥反应。
额~~~ 啥都没发生! 按钮并没有高亮来表示按下,也没有任何提示你的信用卡刚被划掉了99.99$. (开个玩笑! :])
我们还没有给按钮连接事件 — 接下来我们就开始吧!
最后的点击
回到 ButtonView.m 然后加入下面的方法:
- -(void)setEnabled:(BOOL)enabled {
- [super setEnabled:enabled];
- [self setNeedsDisplay];
- }
- -(void)setHighlighted:(BOOL)value {
- [super setHighlighted:value];
- [self setNeedsDisplay];
- }
- -(void)setSelected:(BOOL)value {
- [super setSelected:value];
- [self setNeedsDisplay];
- }
上面的代码是让按钮在 enabled 和 disabled,selected 和 unselected,highlighted 或者 unhighlighted 的时候能够重新绘制一次。 这是为了让我们能看到具体的改变,当我们按下按钮时候。
接着,把下面的代码加进ButtonViewController.m:
- -(IBAction)buttonTapped:(UIButton *)button {
- ButtonView *buttonView = (ButtonView *)button;
- NSString *messageString = [NSString stringWithFormat:@"Red: %fnGreen: %fnBlue: %fn Alpha: %f",
- buttonView.redColor,
- buttonView.greenColor,
- buttonView.blueColor,
- buttonView.alpha];
- UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Button Colors"
- message:messageString
- delegate:nil
- cancelButtonTitle:@"Dismiss"
- otherButtonTitles:nil];
- [alertView show];
- }
这个方法的作用是在按钮按下以后,弹出一个提醒框,显示当前的RGB和Alpha值。
在Storyboard里,把 Touch Up Inside 和这个方法联系在一起。
最后在运行一次程序,按下按钮看看结果吧! 应该出现和下面一样的画面:
太棒了! -一切都正常工作。
何去何从?
大家可以在这里下载所需的PaintCode文件,和相关的Xcode项目。
恭喜大家现在已经掌握了大部分的PaintCode功能了。它能极大的缩短设计app调试UI的时间。
这个系列教程还剩下2部分;第二部分是教大家如何去创建一个自定义进度条,第三部分是使用贝塞尔曲线绘制箭头。
现在,我们还可以再给这个项目增加一下功能让锦上添花:
- 自己慢慢的熟悉 PaintCode 的其他功能和特性。
- 增加字符串,阴影,以及更多的其他特效。
- 重写ButtonView里的 initWithFrame: ,这样能让你使用代码生成动态按钮。
- 给 ButtonView 创建自己的初始化方法,让我们能初始按钮的颜色参数。
希望大家能喜欢这个 PaintCode 教程,并且希望大家能继续关注我们接下来的2部。接下来的加成将会越来越深入,并且会有一点的难度 — 但是我知道这对大家来说是小菜一碟。
http://www.raywenderlich.com/36341/paintcode-tutorial-dynamic-buttons