这节课主要是两个部分,前面的Calculator Demo完善和View视图。
Demo
Demo基于前面的作业。
1、可编程性,添加一些API,作用是返回计算程序!Api要保证程序向上兼容。Brain是操作数和操作符的组合。通过类方法执行这个程序,也就是计算运算结果。API向上兼容, 就是说他没有改变Brain的公有接口,Controlller程序不需要修改一样能正常运行。
2、演示使用到了id类型、property 、数组的可变和不可变复制、内省、还有递归,一下子关联了这么多知识。
3、里面的描述的API留到作业了,应该就是把计算时的操作数和操作符 组合成字符串,这样Controller就可以把这些操作显示到view上。
4、课程提问讨论下nil判断包含的问题,老师说这是编码艺术的范畴,他喜好更少的代码,在确定不需nil判断时,尽量不要添加。less is better.
下面贴一下我完善了一点的程序,添加清除程序栈等功能:
CalculatorBrain.h头文件
NS_ASSUME_NONNULL_BEGIN
@interface CalculatorBrain : NSObject
- (void) pushOperand: (double) operand;
- (double) performOperation:(NSString *) operation;
- (void) clearStack;
@property (readonly) id program;
+ (double) runProgram: (id)program;
+ (NSString *)descriptionOfPorgram:(id) program;
@end
NS_ASSUME_NONNULL_END
CalculatorBrain.m主程序
#import "CalculatorBrain.h"
@interface CalculatorBrain()
@property (nonatomic, strong) NSMutableArray *programStack; // private variable
@end
@implementation CalculatorBrain
@synthesize programStack = _programStack;
- (NSMutableArray *)programStack
{ if(_programStack == nil) _programStack = [[NSMutableArray alloc] init];
return _programStack;
}
- (void) setOperandStack:(NSMutableArray *)operandStack
{
_programStack = operandStack;
}
- (void) pushOperand: (double) operand
{
NSNumber *operandObject = [NSNumber numberWithDouble:operand]; // convert double to NSNumber object
[self.programStack addObject:operandObject];
}
- (double) performOperation:(NSString *) operation
{
[self.programStack addObject: operation];
return [CalculatorBrain runProgram:self.program];
}
- (id) program
{
return [self.programStack copy];
}
+ (NSString *)descriptionOfPorgram:(id)program
{
return @"This is the description of the calculator";
}
+ (double) runProgram:(id)program
{
NSMutableArray *stack;
if([program isKindOfClass:[NSArray class]]){
stack = [program mutableCopy];
}
return [self popOperandOffStack:stack];
}
+ (double) popOperandOffStack:(NSMutableArray *)stack
{
double result = 0;
id topOfStack = [stack lastObject];
if(topOfStack) [stack removeLastObject];
if([topOfStack isKindOfClass:[NSNumber class]]){
result = [topOfStack doubleValue];
}
else if([topOfStack isKindOfClass:[NSString class]]){
NSString *operation = topOfStack;
if([operation isEqualToString:@"+"]){
result = [self popOperandOffStack:stack] + [self popOperandOffStack:stack];
}else if ([@"*" isEqualToString:operation]){
result = [self popOperandOffStack:stack] * [self popOperandOffStack:stack];
}else if([@"-" isEqualToString:operation]){
result = -[self popOperandOffStack:stack] + [self popOperandOffStack:stack];
}else if([@"/" isEqualToString:operation]){
result = (1/[self popOperandOffStack:stack])*[self popOperandOffStack:stack];
}else if([operation isEqualToString:@"sin"]){
result = sin([self popOperandOffStack: stack]);
}else if([operation isEqualToString:@"cos"]){
result = cos([self popOperandOffStack: stack]);
}else if([operation isEqualToString:@"sqrt"]){
result = sqrt([self popOperandOffStack: stack]);
}else if([operation isEqualToString:@"π"]){
// if([self.programStack lastObject]){
result = [self popOperandOffStack: stack]* M_PI;
}
}
return result;
}
- (void) clearStack
{
[_programStack removeAllObjects];
}
@end
View 视图
- view代表屏幕上矩形区域
- view: 画出矩形空间,处理时间
- view有组织结构,是层次的,一个view只有一个superview - (UIView ) superview, 但是可以有多个subview -(NSArray ) subviews。子view的显示顺序和在数组中的位置有关,越在数组后面,越显示在前面。
- UIView在层次结构最上层, IOS中只有一个UIWindow, 但是没有那么重要了交给view, viewcontroller处理。
View的结构
- 可以通过工具图形化的构建
- 也可以通过代码构建,通过superview添加subview,通过subview自己移除自己
-(void) addSubview: (UIView *) aView;
-(void) removeFromSuperView;
View Coordinates
- CGFloat: float数,经常在绘图任务中使用
- CGPoint: 有两个CGFloats(x,y)的表示位置C结构体
CGPoint p = CGPointMake(34.5, 22.0);
p.x += 20; // move right by 20 points
- CGSize: 有两个CGFloats(width, height)的表示大小C结构体
CGSize s = CGSizeMake(100.0, 200.0);
s.height += 50; // make the size 50 points taller
- CGRect: 由CGPoint origin和CGSize size组成
CGRect aRect = CGRectMake(45.0, 75.5, 300, 500);
aRect.size.height += 45;
aRect.origin.x += 30;
Coordinates
- 视图的起点在左上角开始
- 以点为单元,而不是像素数目,一般而言不需要关注一个点对应多少个像素, 可以通过@property CGFloat contentScaleFactor获取一个点是多少个像素
- 视图有3个关于位置和大小的属性,其中center和frame是superview用来定位你的view的属性:
- CGRect bounds: 自己内部绘制空间的起点和大小
- CGPoint center: 你的view在superview坐标系统内的位置
- CGRect frame: 你的superview坐标系统中完全包含你的view的bounds.size
bounds和frame的区别:

Creating Views
通过继承UIView,alloc, init创建
CGRect labelRect = CGRectMake(20, 20, 50, 30);
UILabel *label = [[UILabel alloc] initWithFrame:labelRect];
label.text = @”Hello!”;
[self.view addSubview:label];
什么时候需要创建自己的view?
需要特殊图形,需要处理特殊触摸时间的时候
drawRect
如何绘制?
继承UIView覆盖一个方法,-(void) drawRect:(CGRect) aRect;
绝对不能自己调用drawRect方法,系统会调用,如果需要重新绘制的话,就告知IOS你的view 过期了,发送这两个消息:
- (void)setNeedsDisplay;
- (void)setNeedsDisplayInRect:(CGRect)aRect;
Custom Views
如何实现自己的drawRect?
使用core graphics frameworks,它是C接口的不是面向对象的。基本流程为:
- 创建一个环境context去绘制
- 创建轨迹,path(比如直线,弧线等)
- 设置颜色,字体,线宽等
- 描边/填充上面创建的轨迹
Context
- 决定你在哪绘制:screen, offscreen bitmap, pdf, printer
- 每次drawRect是的环境都是不一样的,所以不要缓存context
- 获取环境的代码:
CGContextRef context = UIGraphicsGetCurrentContext();
Path
CGContextBeginPath(context);
CGContextMoveToPoint(context, 75, 10);
CGContextAddLineToPoint(context, 160, 150);
CGContextAddLineToPoint(context, 10, 150);
[[UIColor greenColor] setFill];
[[UIColor redColor] setStroke];
CGContextDrawPath(context,kCGPathFillStroke); //kCGPathFillStroke is a constant
上面代码:绘制的过程,先开始一个轨迹,移动轨迹的点,添加线,填充绿色,描边是红色, 颜色不需要指定context,默认就是当前的context。
也可以保存轨迹path,然后重用,这里就不介绍了,可以自己去看文档。
Graphics State
颜色
UIColor类用来设置颜色;
UIColor *red = [UIColor redColor]; //class method returns autoreleased instance
UIColor *custom = [[UIColor alloc] initWithRed:(CGFloat) red
blue: (CGFloat) blue
green: (CGFloat) green
alpha: (CGFloat) alpha];
透明度
alpha用来设置drawRect中的透明度,UIView也有background color用来设置为透明值。如果要将view设为部分或者完全透明,将@property BOOL opaque设置为NO,如果不设置结果不可预测(是由于性能优化带来的). UIView @property CGFloat alpha用于设置整个view的透明度。
- views重叠的时候lower views(在subviews array前面的)可以透过transparent view看到
- 默认的drawing是不透明的, 透明有一定的代价
- 通过设置view的@property (nonatomic) BOOL hidden也可以将view完全隐藏,这样该view不会出现在屏幕上,也不会处理事件。
其他设置
CGContextSetLinewidth(context, 1.0); // line width in points(not pixels)
CGContextSetFillPattern(context, (CGPatternRef) pattern, (CGFloat[]) components);
如果你要画的内容和当前graphics state有所差别,但是你又不想mess up当前的graphics state这个时候你就可以push和pop context函数。
- (void) drawGreenCircle:(CGContextRef) ctxt{
UIGraphicsPushContext(ctxt);
[[UIColor greenColor] setFill];
// draw my circle
UIGraphicsPopContext();
}
- (void) drawRect:(CGRect) aRect {
CGContextRef context = UIGraphicsGetCurrentContext(); //保存当前context
[[UIColor redColor] setFill];
// do some stuff
[self drawGreenCircle:context];
// do more stuff and expect fill color to be red
}
画文字
用UILabel
用UIFont设置字体 大小
UIFont *myFont = [UIFont systemFontOfSize:12.0];
UIFont *theFont = [UIFont fontWithName:@“Helvetica” size:36.0];
NSArray *availableFonts = [UIFont familyNames];
最后一个获取可用的字体。
用NSString 来画文字
NSString *text = ...;
[text drawAtPoint:(CGPoint)p withFont:theFont]; // NSString instance method
查看text占用多少空间:
CGSize textSize = [text sizeWithFont:myFont]; //NSString instance method
NNString居然有drawing方法(drawAtPoint)实际上NNString的drawing方法是通过categories在UIKit定义的,categories是一种在OC中加入方法而不用继承的机制。
画图像
- 通过UIImage 从resource加载
UIImage *image = [UIImage imageNamed:@“foo.jpg”];
- 从文件路径或者原始数据加载
UIImage *image = [[UIImage alloc] initWithContentsOfFile:(NSString *)fullPath];
UIImage *image = [[UIImage alloc] initWithData:(NSData *)imageData];
- 通过CGContext函数
UIGraphicsBeginImageContext(CGSize);
// draw with CGContext functions
UIImage *myImage = UIGraphicsGetImageFromCurrentContext();
UIGraphicsEndImageContext();
绘制:
[image drawAtPoint:(CGPoint)p]; // p is upper left corner of the image
[image drawInRect:(CGRect)r]; // scales the image to fit in r
[image drawAsPatternInRect:(CGRect)patRect; // tiles the image into patRect 平铺
网友评论