2012年12月8日 星期六

Core Animation的基本使用(九)

CALayer的时间系统



參考網頁: http://blog.csdn.net/leonpengweicn/article/details/8265290



CALayer通过CAMediaTiming协议实现了一个有层级关系的时间系统.除了CALayer,CAAnimation也采纳了此协议,用来实现动画的时间系统.

在 CA中,有一个Absolute Time(绝对时间)的概念,可以通过CACurrentMediaTime()获得,其实这个绝对时间就是将mach_absolute_time() 转换成秒后的值.这个时间和系统的uptime有关,系统重启后CACurrentMediaTime()会被重置.
就和座标存在相对座标一样,不同的实现了CAMediaTiming协议的存在层级关系的对象也存在相对时间,经常需要进行时间的转换,CALayer提供了两个时间转换的方法:
- (CFTimeInterval)convertTime:(CFTimeInterval)t fromLayer:(CALayer *)l;
- (CFTimeInterval)convertTime:(CFTimeInterval)t toLayer:(CALayer *)l;  
现在来重点研究CAMediaTiming协议中几个重要的属性.

beginTime

无论是图层还是动画,都有一个时间线Timeline的概念,他们的beginTime是相对于父级对象的开始时间. 虽然苹果的文档中没有指明,但是通过代码测试可以发现,默认情况下所有的CALayer图层的时间线都是一致的,他们的beginTime都是0,绝对时 间转换到当前Layer中的时间大小就是绝对时间的大小.所以对于图层而言,虽然创建有先后,但是他们的时间线都是一致的(只要不主动去修改某个图层的 beginTime),所以我们可以想象成所有的图层默认都是从系统重启后开始了他们的时间线的计时.
但是动画的时间线的情况就不同了,当一个动画创建好,被加入到某个Layer的时候,会先被拷贝一份出来用于加入当前的图层,在CA事务被提交的时候,如 果图层中的动画的beginTime为0,则beginTime会被设定为当前图层的当前时间,使得动画立即开始.如果你想某个直接加入图层的动画稍后执 行,可以通过手动设置这个动画的beginTime,但需要注意的是这个beginTime需要为 CACurrentMediaTime()+延迟的秒数,因为beginTime是指其父级对象的时间线上的某个时间,这个时候动画的父级对象为加入的这 个图层,图层当前的时间其实为[layer convertTime:CACurrentMediaTime() fromLayer:nil],其实就等于CACurrentMediaTime(),那么再在这个layer的时间线上往后延迟一定的秒数便得到上面的 那个结果.

timeOffset

这个timeOffset可能是这几个属性中比较难理解的一个,官方的文档也没有讲的很清楚. local time也分成两种一种是active local time 一种是basic local time.
timeOffset则是active local time的偏移量.
你将一个动画看作一个环,timeOffset改变的其实是动画在环内的起点,比如一个duration为5秒的动画,将timeOffset设置为2(或者7,模5为2),那么动画的运行则是从原来的2秒开始到5秒,接着再0秒到2秒,完成一次动画.

speed

speed属性用于设置当前对象的时间流相对于父级对象时间流的流逝速度,比如一个动画beginTime是0,但是speed是2,那么这个动画的1秒 处相当于父级对象时间流中的2秒处. speed越大则说明时间流逝速度越快,那动画也就越快.比如一个speed为2的layer其所有的父辈的speed都是1,它有一个 subLayer,speed也为2,那么一个8秒的动画在这个运行于这个subLayer只需2秒(8 / (2 * 2)).所以speed有叠加的效果.

fillMode

fillMode的作用就是决定当前对象过了非active时间段的行为. 比如动画开始之前,动画结束之后。如果是一个动画CAAnimation,则需要将其removedOnCompletion设置为NO,要不然 fillMode不起作用. 下面来讲各个fillMode的意义
kCAFillModeRemoved 这个是默认值,也就是说当动画开始前和动画结束后,动画对layer都没有影响,动画结束后,layer会恢复到之前的状态
kCAFillModeForwards 当动画结束后,layer会一直保持着动画最后的状态
kCAFillModeBackwards 这 个和kCAFillModeForwards是相对的,就是在动画开始前,你只要将动画加入了一个layer,layer便立即进入动画的初始状态并等待 动画开始.你可以这样设定测试代码,将一个动画加入一个layer的时候延迟5秒执行.然后就会发现在动画没有开始的时候,只要动画被加入了 layer,layer便处于动画初始状态
kCAFillModeBoth 理解了上面两个,这个就很好理解了,这个其实就是上面两个的合成.动画加入后开始之前,layer便处于动画初始状态,动画结束后layer保持动画最后的状态.
其他的一些参数都是比较容易理解的.

实际应用

参见苹果官方 QA1673 How to pause the animation of a layer tree
-(void)pauseLayer:(CALayer*)layer
{
    CFTimeInterval pausedTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil];
    layer.speed = 0.0;
    layer.timeOffset = pausedTime;
}

-(void)resumeLayer:(CALayer*)layer
{
    CFTimeInterval pausedTime = [layer timeOffset];
    layer.speed = 1.0;
    layer.timeOffset = 0.0;
    layer.beginTime = 0.0;
    CFTimeInterval timeSincePause = [layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;
    layer.beginTime = timeSincePause;
}
 

以下實驗暫停及啓動的實作,本實驗一併加上時間的顯示。


1.延續前一個專案,在storyboard再加上一個Label及一個Button
2. 在mainViewController.h加上計算時間所需的變數。
#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>

@interface mainViewController : UIViewController

@property (weak)   CALayer *layer;
@property (weak)   CALayer *layer2;

@property (weak, nonatomic) IBOutlet UISwitch *actionsSwitch;

@property (nonatomic) BOOL pauseState;
@property (nonatomic) double currentTimeValue;

@property (nonatomic) NSTimer  *timer;

@property (weak, nonatomic) IBOutlet UILabel *timeShowLabel;

@property (weak, nonatomic) IBOutlet UIButton *pauseButton;

@end

3. 在mainViewController.m加上程式碼,此時間的處理只加在Scale這個變化上,其它的用法是一樣的,就不再做了。
@synthesize pauseState;
@synthesize timeShowLabel;
@synthesize timer;
@synthesize currentTimeValue;
@synthesize pauseButton;

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
   
    layer = [CALayer layer];
    layer.bounds = CGRectMake(0, 0, 200, 200);
    layer.position =  ORGINAL_POSITION;   ///CGPointMake(500, 300);
    layer.backgroundColor = [UIColor redColor].CGColor;
    layer.borderColor = [UIColor blackColor].CGColor;
    layer.opacity = 1.0f;
    [self.view.layer addSublayer:layer];
   
    actionsSwitch.on = NO;
   
    UIImage *image = [UIImage imageNamed:@"TaiwanMap.jpg"];
    layer2 = [CALayer layer];
    layer2.contents = (id)image.CGImage;
    layer2.bounds = CGRectMake(0, 0, 200, 200);
    layer2.position = CGPointMake(350, 650);
    [self.view.layer addSublayer:layer2];
   
    pauseState = NO;
}

- (IBAction)scale:(id)sender {
   
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
    animation.toValue = [NSNumber numberWithFloat:2.0];
    animation.fromValue = [NSNumber numberWithFloat:1.0];
    animation.duration = 3.0;
    animation.repeatCount = 3;
    animation.removedOnCompletion=NO;
    animation.autoreverses=YES;
    animation.fillMode=kCAFillModeForwards;
   
    [layer2 addAnimation:animation forKey:@"scale"];
   
    timer = [NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:@selector(timeLoader) userInfo:nil repeats:YES];
   
    currentTimeValue = [layer2 convertTime:CACurrentMediaTime() fromLayer:nil];

 

}

-(void)pauseLayer:(CALayer*)layer
{
    CFTimeInterval pausedTime = [layer2 convertTime:CACurrentMediaTime() fromLayer:nil];
    layer2.speed = 0.0;
    layer2.timeOffset = pausedTime;
}

-(void)resumeLayer:(CALayer*)layer
{
    CFTimeInterval pausedTime = [layer2 timeOffset];
    layer2.speed = 1.0;
    layer2.timeOffset = 0.0;
    layer2.beginTime = 0.0;
    CFTimeInterval timeSincePause = [layer2 convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;
    layer2.beginTime = timeSincePause;

   
}
- (IBAction)pause:(id)sender {
  
    pauseState = ~pauseState;
  
    if (pauseState != NO){
        [pauseButton setTitle:@"啟動" forState:UIControlStateNormal];
        [self pauseLayer:layer];
    }
    else {
        [pauseButton setTitle:@"暫停" forState:UIControlStateNormal];
        [self resumeLayer:layer];
    }
}

-(void)timeLoader
{

    [self showTime];
}


- (void) showTime
{
    NSTimeInterval  time =  [layer2 convertTime:CACurrentMediaTime() fromLayer:nil];
  
    time = time - currentTimeValue;
  
    if (time >(3*3*2)){  // duration * repeat *autoreverse
       
        [timer invalidate];
      
        timer = nil;
      
        [pauseButton setTitle:@"暫停" forState:UIControlStateNormal];
    }
  
    NSInteger min;
    NSInteger sec;
    NSInteger hour;
  
    float minsec;
  
    min = (NSInteger ) time/60;
  
    sec = (NSInteger ) time%60;
  
    hour = (NSInteger) time/(60*24);
  
    minsec = time - sec - min*60;
  
    NSString *showTimeText =  [NSString stringWithFormat:@"Sec = %2.2f", sec+minsec];
  
    timeShowLabel.text = showTimeText;
  
}


4. 結果,在動作中可以顯示時間,然後暫停後再次啓動。




2012年12月7日 星期五

Core Animation的基本使用(八)

將CABasicAnimation 動作組合起來一起的效果



實作如下

1. 在StoryBoard加上一個新個Button





2. 將mainViewController.m稍作修改
- (IBAction)translationX:(id)sender {
   
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.translation.x"];
    animation.toValue = [NSNumber numberWithFloat:100.0];
    //animation.fromValue = [NSNumber numberWithFloat:layer.opacity];
    animation.duration = 3.0;
    animation.removedOnCompletion=NO;
    animation.fillMode=kCAFillModeForwards;
   
    [layer2 addAnimation:animation forKey:@"translationX"]; // 每一個key都要不同,才會同時動作
   
}
- (IBAction)translationY:(id)sender {
   
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.translation.y"];
    animation.toValue = [NSNumber numberWithFloat:100.0];
    //animation.fromValue = [NSNumber numberWithFloat:layer.opacity];
    animation.duration = 3.0;
    animation.removedOnCompletion=NO;
    animation.fillMode=kCAFillModeForwards;
   
    [layer2 addAnimation:animation forKey:@"translationY"];

}
- (IBAction)translationZ:(id)sender {
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.translation.z"];
    animation.toValue = [NSNumber numberWithFloat:100.0];
    //animation.fromValue = [NSNumber numberWithFloat:layer.opacity];
    animation.duration = 3.0;
    animation.removedOnCompletion=NO;
    animation.fillMode=kCAFillModeForwards;
   
    [layer2 addAnimation:animation forKey:@"translationZ"];   
}
- (IBAction)scale:(id)sender {
   
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
    animation.toValue = [NSNumber numberWithFloat:2.0];
    animation.fromValue = [NSNumber numberWithFloat:1.0];
    animation.duration = 3.0;
    animation.removedOnCompletion=NO;
    animation.fillMode=kCAFillModeForwards;
   
    [layer2 addAnimation:animation forKey:@"scale"];
}
- (IBAction)rotationX:(id)sender {
   
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.x"];
    animation.toValue =  [NSNumber numberWithFloat:(DEGREES_TO_RADIANS(120))];
    animation.fromValue = [NSNumber numberWithFloat:1.0];
    animation.duration = 3.0;
    animation.removedOnCompletion=NO;
    animation.fillMode=kCAFillModeForwards;
   
    [layer2 addAnimation:animation forKey:@"rotationX"];

}
- (IBAction)rotationY:(id)sender {
   
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.y"];
    animation.toValue =  [NSNumber numberWithFloat:(DEGREES_TO_RADIANS(120))];
    animation.fromValue = [NSNumber numberWithFloat:1.0];
    animation.duration = 3.0;
    animation.removedOnCompletion=NO;
    animation.fillMode=kCAFillModeForwards;
   
    [layer2 addAnimation:animation forKey:@"rotationY"];

}
- (IBAction)rotationZ:(id)sender {
   
   
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
    animation.toValue =  [NSNumber numberWithFloat:(DEGREES_TO_RADIANS(120))];
    animation.fromValue = [NSNumber numberWithFloat:(DEGREES_TO_RADIANS(30))];
    animation.duration = 3.0;
    animation.fillMode=kCAFillModeForwards;
   
    animation.removedOnCompletion= YES;
    animation.autoreverses=YES;
   
    [layer2 addAnimation:animation forKey:@"rotationZ"];
 
}



- (IBAction)combineAnimation:(id)sender {
 

    // 串列呼叫就會有同時動的效果了   


    [self translationX:sender];
    [self translationY:sender];
    [self translationZ:sender];
   
    [self rotationX:sender];
    [self rotationY:sender];
    [self rotationZ:sender];
   
    [self scale:sender];
   
}


3. 執行結果











2012年12月6日 星期四

Core Animation的基本使用(七)

CABasicAnimation的一些使用細節資料整理

因為class繼承的關係,所以會用到父Class的屬性。因此資料會跨到其他的Class。

Animation Class Roadmap


官方主要參考區
https://developer.apple.com/library/ios/#documentation/GraphicsImaging/Reference
/CAMediaTiming_protocol/Introduction/Introduction.html#//apple_ref/occ/intf/CAMediaTiming

Autoreverses

当你设定这个属性为YES时,在它到达目的地之后,动画的属性会返回到开始的值,代替了直接跳转到开始的值。意思就是如果fromValue = A, toValue=B, 這樣原本動畫會從A => B,結束後,會有另一個B => A 的動畫。

example code
Animation.autoreverses=YES; //自动回复

CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
animation.toValue =  [NSNumber numberWithFloat:(DEGREES_TO_RADIANS(120))];
animation.fromValue = [NSNumber numberWithFloat:(DEGREES_TO_RADIANS(30))];
animation.removedOnCompletion= NO; 
animation.autoreverses=YES;  // 先到toValue的120度,再回轉到fromValue的30度
未啓動動畫前


動畫結束後


RemovedOnCompletion

这个属性默认为YES,那以为着,在指定的时间段完成后,动画就自动的从层上移除了。这个一般不用。假如你想要再次用这个动画时,你需要设定这个属性为NO。这样的话,下次你在通过-set方法设定动画的属性时,它将再次使用你的动画,而非默认的动画。

使用例
//設定當動畫結束後,該物件停留在最後位置(=NO),而不是回到開始位置(=YES)
animation.removedOnCompletion = YES; 
//回到開始位置,但不是fromValue的位置,而是圖最早出現的位置。

動畫結束前與後都會一樣


Duration

Duration这个参数你已经相当熟悉来了。它设定开始值到结束值花费的时间。期间会被速度的属性所影响。

Speed

默认的值为1.0.这意味着动画播放按照默认的速度。如果你改变这个值为2.0,动画会用2倍的速度播放。这样的影响就是使持续时间减半。如果你指定的持续时间为6秒,速度为2.0,动画就会播放3秒钟---一半的持续时间。

BeginTime

这个属性在组动画中很有用。它根据父动画组的持续时间,指定了开始播放动画的时间。默认的是0.0.,另外配合有组动画“Animation Grouping”,需再花時間瞭解
此變數的值要小於Durtation,否則就不會有動畫出現,因為此變數是指定開始動作的起始時間。

TimeOffset

如果一个时间偏移量是被设定,动画不会真正的可见,直到根据父动画组中的执行时间得到的时间都流逝了。就是說已經執行了這個動作多久了,因此暫停時,就可從這裡看到執行時間。

RepeatCount

默认的是0,意味着动画只会播放一次。如果指定一个无限大的重复次数,使用1e100f。这个不应该和repeatDration属性一块使用。

RepeatDuration

这个属性指定了动画应该被重复多久。动画会一直重复,直到设定的时间流逝完。它不应该和repeatCount一起使用。


FillMode

Determines if the receiver’s presentation is frozen or removed once its active duration has completed. (required)
kCAFillModeRemoved
The receiver is removed from the presentation when the animation is completed.
kCAFillModeForwards
The receiver remains visible in its final state when the animation is completed.
kCAFillModeBackwards
The receiver clamps values before zero to zero when the animation is completed.
kCAFillModeBoth
The receiver clamps values at both ends of the object’s time space
kCAFillModeFrozen
The mode was deprecated before OS X v10.5 shipped.

TimingFunction

在動畫變化中所採取的漸變模式。

官方資料區
https://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/Animation_Types_Timing/Articles/Timing.html

使用方法
animation.timingFunction=[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];

可替換的變數
    • kCAMediaTimingFunctionLinear specifies linear pacing. A linear pacing causes an animation to occur evenly over its duration.
    • kCAMediaTimingFunctionEaseIn specifies ease-in pacing. Ease-in pacing causes the animation to begin slowly, and then speed up as it progresses.
    • kCAMediaTimingFunctionEaseOut specifies ease-out pacing. An ease-out pacing causes the animation to begin quickly, and then slow as it completes.
    • kCAMediaTimingFunctionEaseInEaseOut specifies ease-in ease-out pacing. An ease-in ease-out animation begins slowly, accelerates through the middle of its duration, and then slows again before completing.
    • kCAMediaTimingFunctionDefault(自定義時間函數)Specifies the timing function used as the default by most animations. It approximates a Bézier timing function using the control points [(0.0,0.0), (0.25,0.1), (0.25,0.1), (1.0,1.0)]. By using this constant you ensure that your animations will use the current default timing.
Cubic Bezier curve representations of the predefined timing functions


自定义的Timing Function的函数图像就是一条三次贝塞尔曲线Cubic Bezier Curve,贝塞尔曲线的优点就是光滑,用在这里就使得变化显得光滑.一条三次贝塞尔曲线可以由起点终点以及两个控制点决定. 
上面的kCAMediaTimingFunctionDefault对应的函数曲线其实就是通过[(0.0,0.0), (0.25,0.1), (0.25,0.1), (1.0,1.0)]这四个点决定的三次贝塞尔曲线,头尾为起点和终点,中间的两个点是控制点. 

上图中P0是起点,P3是终点,P1和P2是两个控制点
如果时间变化曲线既不是直线也不是贝塞尔曲线,而是自定义的,又或者某个图层运动的轨迹不是直线而是一个曲线,这些是基本动画无法做到的,所以引入CAKeyframeAnimation,也即所谓的关键帧动画.

2012年12月5日 星期三

Core Animation的基本使用(六)

在CABasicAnimation中,除了前一篇的淡化外,還有一些功能,如移動/縮放/旋轉。

官方文件
Core Animation Extensions To Key-Value Coding



實作的部分,就沿用前一篇的專案來作修改

1. 在StoryBoard上加入一些Button


2. 在mainViewController.m加入所需程式碼。

#import "mainViewController.h"
#import "CALayerAdditions.h"

@interface mainViewController ()

@end

#define ORGINAL_POSITION CGPointMake(350, 450)
#define MOVED_POSITON CGPointMake(450, 350)


#define DEGREES_TO_RADIANS(d) (d * M_PI / 180)

@implementation mainViewController

@synthesize layer, actionsSwitch;
@synthesize layer2;

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
   
    layer = [CALayer layer];
    layer.bounds = CGRectMake(0, 0, 200, 200);
    layer.position =  ORGINAL_POSITION;   ///CGPointMake(500, 300);
    layer.backgroundColor = [UIColor redColor].CGColor;
    layer.borderColor = [UIColor blackColor].CGColor;
    layer.opacity = 1.0f;
    [self.view.layer addSublayer:layer];
   
    actionsSwitch.on = NO;
   
    UIImage *image = [UIImage imageNamed:@"TaiwanMap.jpg"];
    layer2 = [CALayer layer];
    layer2.contents = (id)image.CGImage;
    layer2.bounds = CGRectMake(0, 0, 200, 200);
    layer2.position = CGPointMake(350, 650);
    [self.view.layer addSublayer:layer2];
   
    //UIGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(fadeIt)];
    //[self.view addGestureRecognizer:recognizer];


}

/*
- (void)fadeIt {
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"opacity"];
    animation.toValue = [NSNumber numberWithFloat:0.0];
    animation.fromValue = [NSNumber numberWithFloat:layer.opacity];
    animation.duration = 3.0;
    layer2.opacity = 0.0; // This is required to update the model's value.  Comment out to see what happens.
    [layer2 addAnimation:animation forKey:@"animateOpacity"];
}
*/


- (IBAction)opacity:(id)sender {
   
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"opacity"];
    animation.toValue = [NSNumber numberWithFloat:0.0];
    animation.fromValue = [NSNumber numberWithFloat:layer.opacity];
    animation.duration = 3.0;
    layer2.opacity = 0.0; // This is required to update the model's value.  Comment out to see what happens.
    [layer2 addAnimation:animation forKey:@"animateOpacity"];
}
- (IBAction)translationX:(id)sender {
   
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.translation.x"];
    animation.toValue = [NSNumber numberWithFloat:100.0];
    //animation.fromValue = [NSNumber numberWithFloat:layer.opacity];
    animation.duration = 3.0;
    animation.removedOnCompletion=NO;
    animation.fillMode=kCAFillModeForwards;
   
    [layer2 addAnimation:animation forKey:@"Open"];
   
   // [imageLayer addAnimation:[self animationOpen] forKey:@"Open"];
   
}
- (IBAction)translationY:(id)sender {
   
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.translation.y"];
    animation.toValue = [NSNumber numberWithFloat:100.0];
    //animation.fromValue = [NSNumber numberWithFloat:layer.opacity];
    animation.duration = 3.0;
    animation.removedOnCompletion=NO;
    animation.fillMode=kCAFillModeForwards;
   
    [layer2 addAnimation:animation forKey:@"Open"];

}
- (IBAction)translationZ:(id)sender {
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.translation.z"];
    animation.toValue = [NSNumber numberWithFloat:100.0];
    //animation.fromValue = [NSNumber numberWithFloat:layer.opacity];
    animation.duration = 3.0;
    animation.removedOnCompletion=NO;
    animation.fillMode=kCAFillModeForwards;
   
    [layer2 addAnimation:animation forKey:@"Open"];   
}
- (IBAction)scale:(id)sender {
   
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
    animation.toValue = [NSNumber numberWithFloat:2.0];
    animation.fromValue = [NSNumber numberWithFloat:1.0];
    animation.duration = 3.0;
    animation.removedOnCompletion=NO;
    animation.fillMode=kCAFillModeForwards;
   
    [layer2 addAnimation:animation forKey:@"Open"];

}
- (IBAction)rotationX:(id)sender {
   
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.x"];
    animation.toValue =  [NSNumber numberWithFloat:(DEGREES_TO_RADIANS(120))];
    animation.fromValue = [NSNumber numberWithFloat:1.0];
    animation.duration = 3.0;
    animation.removedOnCompletion=NO;
    animation.fillMode=kCAFillModeForwards;
   
    [layer2 addAnimation:animation forKey:@"Open"];

}
- (IBAction)rotationY:(id)sender {
   
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.y"];
    animation.toValue =  [NSNumber numberWithFloat:(DEGREES_TO_RADIANS(120))];
    animation.fromValue = [NSNumber numberWithFloat:1.0];
    animation.duration = 3.0;
    animation.removedOnCompletion=NO;
    animation.fillMode=kCAFillModeForwards;
   
    [layer2 addAnimation:animation forKey:@"Open"];

}
- (IBAction)rotationZ:(id)sender {
   
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
    animation.toValue =  [NSNumber numberWithFloat:(DEGREES_TO_RADIANS(120))];
    animation.fromValue = [NSNumber numberWithFloat:1.0];
    animation.duration = 3.0;
    animation.removedOnCompletion=NO;
    animation.fillMode=kCAFillModeForwards;
   
    [layer2 addAnimation:animation forKey:@"Open"];

}


- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (IBAction)toggleCorner:(id)sender {
    [CATransaction setDisableActions:actionsSwitch.on];
    layer.cornerRadius = (layer.cornerRadius == 0.0f) ? 30.0f : 0.0f;
}
- (IBAction)toggleColor:(id)sender {
    [CATransaction setDisableActions:actionsSwitch.on];
    CGColorRef redColor = [UIColor redColor].CGColor, blueColor = [UIColor blueColor].CGColor;
    layer.backgroundColor = (layer.backgroundColor == redColor) ? blueColor : redColor;
}
- (IBAction)toggleOpacity:(id)sender {
    [CATransaction setDisableActions:actionsSwitch.on];
    layer.opacity = (layer.opacity == 1.0f) ? 0.5f : 1.0f;
}
- (IBAction)togglePosition:(id)sender {
    [CATransaction setDisableActions:actionsSwitch.on];
    layer.position = layer.position.x == 500 ? MOVED_POSITON : ORGINAL_POSITION;
}
- (IBAction)toggleBorders:(id)sender {
    [CATransaction setDisableActions:actionsSwitch.on];
    layer.borderWidth = (layer.borderWidth == 0.0f) ? 10.0f : 0.0f;
}
- (IBAction)toggleBounds:(id)sender {
    [CATransaction setDisableActions:actionsSwitch.on];
   
    [layer adjustWidthBy:layer.bounds.size.width == layer.bounds.size.height ? 100 : -100];
   
}

@end

3. 執行結果

只有z方向的移動,因看不出結果,就不貼結果了,另外opacity也不再重做。然後第一張為不同的原始狀,其他依序印出。










2012年12月4日 星期二

xCode 除錯技巧

測試版本
Xcode版本4.5.2

參考
1. http://www.erawppa.com/xcode-%E9%99%A4%E9%8C%AF%E6%8A%80%E5%B7%A7/
2. http://www.cnblogs.com/kesalin/archive/2012/01/31/xcode_debug.html


常用指令
po
print
info

GDB
help
show

除了以上這些基本的debug方法,目前還有一種很簡當的方式,使用方法簡述如下,方便簡化大家的偵錯時間。


1. 先讓程式執行,然後停在想要偵錯的位置

2. 讓鼠標指到所要看的變數位置上,xcode會自動顯示變數的值。

3.再一次指到黃標上三角形的位置,就會出現內部其他變數的值,在原來黃標的位置下面

4.  只要有三角形的位置,都可以指定來出現內部的資料,層次幾乎可以無限制。

5. 如果想要將值印到下面的debug視窗,可以點選變數前面的小三角形的上面那一個,就會出現白色功能窗,選擇Print Description就會將值印到下面的debug窗上了



如果要在consle 上列印hex模式就用  print/x   var

其他模式可參考


x Hex
d 有號整數
u 無號整數
o 八進位
t binary
a 位址
c 字元
f 浮點數


參考網頁
http://disanji.net/iOS_Doc/#documentation/DeveloperTools/gdb/gdb/gdb_toc.html#//apple_ref/doc/uid/TP40000996


而一些Class內部的特殊變數就可用

print (CGPoint) [self center]
或 print (CGRect) [self frame] 這種語法來印出


印出整個 view heirachy
po [[[[UIApplication sharedApplication]windows] objectAtIndex:0] recursiveDescription]
 
 
 
 
 

2012年12月3日 星期一

class and instance method 測試例

http://kirenenko-tw.blogspot.tw/2012/06/class-and-instance-method.html

原本寫得不夠完整,因此寫了一個實際的測試例,來確認細節

1. 開啓一個專案

2. 加一個新的Class檔來做測試



3.  先在StoryBoard上加入一個Button及Label來顯示結果

4. 在calculate.h加上所需變數及method的設定
#import <Foundation/Foundation.h>

@interface calculate : NSObject
{
    CGFloat  x_value; //  instance variables
}

@property (nonatomic) CGFloat y_value;  // ARC 變數

- (void) set_x_value:(CGFloat)value;
- (CGFloat) get_x_value;


+ (calculate *) get_new_instance:(CGFloat) xvalue  yvalue:(CGFloat)yvalue;

@end

5. 在calculate.m加上method的程式碼
#import "calculate.h"

@implementation calculate

@synthesize y_value;

- (void) set_x_value:(CGFloat)value
{
    x_value = value*2;
}

- (CGFloat) get_x_value
{
    return x_value;
}


+ (calculate *) get_new_instance:(CGFloat) xvalue  yvalue:(CGFloat)yvalue
{
    CGFloat  temp;
   
   calculate *xx = [[calculate alloc] init];
   
    xx->x_value = xvalue; // set instance variable
   
    // 兩種方法的結果相同
    //xx.y_value = yvalue;  // 內部使用set的method
    [xx setY_value:yvalue]; // 使用synthesis所產生的method

   
    [xx set_x_value:100]; // 呼叫 instance method
   
    return xx;
}


@end

6. 在mainViewController.m加入所需程式碼
#import "mainViewController.h"

@interface mainViewController ()

@end

@implementation mainViewController
@synthesize cal_inst;
@synthesize showLabel;


- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
- (IBAction)new_cal_instance:(id)sender {
   
    cal_inst = [calculate get_new_instance:99 yvalue:99];
   
    showLabel.text = [NSString stringWithFormat:@"x = %2.2f, y= %2.2f",
                      [cal_inst get_x_value],  // 不能直接使用cal_inst->x_value, 因為protect
                      cal_inst.y_value
                      ];
}


@end

7.執行結果

HD天籟美聲 齊豫 佛子行37誦觸動深處的誦讚

最近聽到齊豫的佛子行唱頌,
很好聽,感覺心裡平靜許多

在Youtube上的影音檔
http://www.youtube.com/watch?v=Oa1EZwDuR3Q


轉帖自 http://www.thranguhk.org/buddhism/cn_37practice.html?keepThis=true&TB_iframe=true&height=600&width=1050

佛子行三十七頌

《佛子行三十七頌》是「無著賢」菩薩的作品。在西藏歷代上師之中他是集佛學、博學及德行(戒律)三者條件皆具足之上師,可以說已具足聖者圓滿的特色,是一位廣大明深的成就者。
《佛子行三十七頌》包括了 菩薩所行持的見地、觀修、行持;即見、修、行三者(指大乘不共行持之特色)。一共有三十七句的頌文,因取其三十七實修要點故名三十七頌。全論分三大部分:初義,論義,末義 。

初義 

初義分三部分:1.題義,2.禮供,3.立誓。
題義:《佛子行三十七頌》。即作為一大乘佛子所應具有的行為,分三十七個頌詞闡述。
禮供: 1.略言;2. 廣言。

略言

第一句所引用的是印度四大語言之一的善構語,那摩洛給秀拉雅(Namo Lokeshwaraya),此為印度古梵文的名稱南無觀世音菩薩! 意為頂禮觀世音菩薩。

廣言 

雖見諸法無來去 唯一勤行利眾生 殊勝師及觀自在 恭敬三門恆頂禮
( 大慈大悲的觀世音菩薩已照見一切法皆無有來去,已經徹見諸法無來去之自性,並恆時精進利樂眾生,故在觀自在菩薩以及與觀自在菩薩無二無別的殊勝上師前,著者土美大師以自己的身語意三門恆時恭敬頂禮。 )

立誓

即表明本論的目的。
利樂之源諸圓覺 從修正法而出生 彼復依於知其行 是故當說佛子行
世間所有的暫時安樂與究竟利益皆來源於諸佛,能得如是殊勝果位是由修持正法而證得的,而修持正法則首先必須明瞭修持的方法,對於一想修學大乘法的弟子來說,即需明瞭修學菩薩道的方法。因此,土美大師於此處為我們修持正法者宣說佛子應有的行為。

論義

下面轉入(二)正文論義,逐一闡述大乘佛子的應有行為,共分三十七條。在內容上分成兩大部分:前行與正行。前行又分七個部分:

1. 觀想人身難得

獲得暇滿大舟時 為自他渡輪迴海 晝夜恆時不懈怠 聞思修是佛子行
如今我們已得八無暇十圓滿的人身,這個人身非常寶貴,猶如生死輪迴大海中的大船,既可以渡自己出生死苦海,又能使輪迴中的其他眾生得渡,但同時人身是無常的,非常短暫,故應珍惜這一難得的機遇,在晝夜六時中恆時不懶惰不散亂地精進於聞思修。
其次,人身是極為難得的而又是短暫的,借此難得之人身應該選擇做有意義的事,所有有意義的事中聞思修最為殊勝,因為這才是佛法的根本所在。

2. 應遠離三毒之源的家鄉

親方貪心如沸水 怨方嗔心似烈火 取捨皆忘癡黑暗 拋棄故鄉佛子行
因為住在家鄉親人、怨敵低頭不見抬頭見,對親人的貪心會增盛到如沸水一般,對怨敵的嗔心同樣也會猛似烈 火,自己難以抑制;又常常與俗家人在一起,受世俗習氣的熏染,不注意因果善惡的取捨,天天如夢一般的度日,迷迷糊糊,越來越愚癡。所以,佛子當拋棄故鄉而 修行。依以前高僧大德開示的標準,上根者應遠離家鄉一年的路程,中根者要遠離家鄉一個月的路程,下根者遠離家鄉至少要走四天。

3. 功德之源依止靜處

捨惡境故漸減惑 無懶散故善自增 心澄於法起正見 依住靜處佛子行
靜處具有減惑、增善和生起正見的功德。靜處自然沒有貪、嗔、癡等五毒煩惱的外緣,依靠傳承上師的加持,通 過聞思或修行,自己俱生的無明業惑自然就漸漸地減滅,又沒有懶散和邪知邪見人的影響,自己對眾生的大慈大悲及精進聞思修等等善業自然而然地增上,自心融入 於正法之中從而生起殊勝的定解,能對上師三寶生起不共的信心,這恰恰與上述家鄉的過失相反。歷代的高僧大德也多是依止靜處才有所成就的。一個修學佛法的 人,若是想離開靜地和上師,那肯定是著了魔,或是尚未如實生起希求正法與解脫的心,自己應該好好地祈禱懺悔。

4. 觀無常即捨今世 

長伴親友各分離 勤聚財物留後世 識客舍棄身客堂 捨此世為佛子行
人有生必有死,在死的時候,自己一生中的親朋好友都不得不從此分離,辛苦一生從點點滴滴中集聚起來的財物 一分一厘也帶不走,全都遺留在這個人世間,甚至連自己的色身,在心識遷移之時也不得不捨棄在人世間,所以,應看破今生所有世間八法,應當捨棄有漏的色法斷 除一切執著的分別念,否則,若不能看破,更難放下,放不下則會起執著,執著便是輪迴之根,這樣便不算是一個從心地上修行的人。

5. 遠離愚伴使修道無違緣

交近彼使增三毒 並減聞思修事業 能轉慈悲滅盡者 離惡友為佛子行
對於一個尚未成就的修行者來說,必須遠離惡友,否則修行不會增上,反而增長自己的貪嗔癡三毒,即便一個人最初人格很好,但後來結交了一個人格不好的人,慢慢地潛移默化,即所謂的"近墨者黑"。即常遠離惡知識,永離一切諸惡道。

6. 修道順緣依止上師

依止何者滅罪過 功德增如上弦月 勝善知識比自身 尤為愛重佛子行
何者是代指上師善知識,依止善知識,以其引導及加持,使自己能精進於聞思修,消盡往昔所造的種種罪業,戒 律清淨,慈悲心增長等各種功德也會如初一至十五的上弦月一樣不斷地增長乃至圓滿,對一位能使自己滅盡罪過、功德增長的殊勝善知識,愛重之心當勝過自己的生 命,對於自己的身心,每個人都倍加珍惜愛重,但對於金剛上師勝善知識,念其恩德,更應勝於愛重自身的百倍。上師法王如意寶也講過,修行人若欲得解脫成就, 必須依止善知識,愛重善知識,善知識在自己修行中甚為重要。

7. 入佛教之門為皈依

自亦縛於輪迴故 世間天力能救誰 是故依止不虛者 皈依三寶佛子行
真實不虛的皈依處是佛法僧三寶,而不是世間的天神、地神、護法神等,因為他們自己還未從煩惱中徹底解脫出 來,仍束縛在輪迴中,他們還有什麼能力救護我們呢?對於世間神祇,我們可祈禱供養,使其護持弘法利生順利圓滿,但對他們皈依、頂禮是不應理的,更不能把他 們作為究竟的皈依處,始終真實不欺的唯有佛法僧三寶,至心皈依三寶方可得到究竟的解脫,故每位佛弟子應一心一意地皈依三寶。對此,我們應該時時處處從心裡 來衡量自己是否已真實地皈依?每當遇到違緣時,每當遇到外道時,是否能自然而然地祈禱三寶?皈依是一種決心和信心,而不是暫時的依托,故佛子當至心皈依佛 法僧三寶。
以上是一個大乘弟子所應具備的前提條件,也即是前行。
接下來闡述一個大乘弟子的發心與行為的標準,也即是正行,以修行人從低到高的根機共分三個方面 : 下士道、中士道、上士道 。

8 .下士道

諸極難忍惡趣苦 能仁說為惡業果 是故縱遇命難時 終不造罪佛子行
現在三界輪迴的各道眾生,尤其是地獄、餓鬼、畜生道的眾生,恆時都在遭受難以忍受的各種痛苦,人道也有生老病死,事與願違等等三苦八苦,釋迦能仁說此為往昔所造惡業之果報,這在《百業經》等講述因果的經中有詳細說明。
如果在遇到違緣和痛苦時,都能自然地認為肯定是自己以前所造惡業的報應,這樣今生很多的違緣和痛苦能轉為道用,成為修道中的逆增上緣。
所以,現在得此暇滿之人身,縱遇命難也絕不能再造惡業。正像《白蓮花·釋迦牟尼佛傳記》中世尊在因地中修行時,曾這樣說過:因為人生的短暫,我不願造惡業,也因為人生的短暫,我只願做善業。佛弟子應該學佛如是行,現在世間上很多人整天造惡業也難以維持生活。

9. 中士道

三有樂如草頭露 一瞬剎那毀滅法 恆時不變解脫果 希求其是佛子行
視三界輪迴的剎那安樂如草頭之露珠,日光一照便會消失,苦苦求得的財色名食睡等暫時的安樂也是無常的,因 為這些都是有漏之樂,正如《金剛經》中雲"一切有為法,如夢幻泡影,如露亦如電,應作如是觀。"所以我們不應貪求世間的暫時安樂,而應該希求恆時不變的佛 果,這是佛子的行為。
上士道的學處很豐富,分為:應發菩提心的理由,修兩種菩提心,學菩薩戒三個方面。

10. 應發菩提心的理由

無始時來慈我者 諸母若苦我何樂 是故為渡諸有情 發菩提心佛子行
自無始以來,一切輪迴中的眾生都作過我的母親,慈愛過我,這些輪迴中的母親如果沉溺在輪迴中恆受苦,那麼 我怎能忍心只有我一個人得到解脫,享受安樂?作為一個學佛的人,應該學佛之願行,為一切如母眾生得到最究竟的解脫而發無上菩提心,這是成佛的根本。佛無始 劫來因為利益眾生而成佛,而眾生無始時來為了自己而輪迴,因此菩提心是一個修行人成佛的必由之路。
修兩種菩提心:修世俗菩提心,修勝義菩提心。修世俗菩提心又分兩個方面:入根本慧定時修自他交換;後得(出定)時違緣轉為道用。

11. 入根本慧定時修自他交換

諸苦生於欲自樂 諸佛生於利他心 由此自樂與他苦 確當交換佛子行
每一道的眾生痛苦其來源,都是因為無始劫來只求自己得到安樂而造下了很多惡業之故。而像釋迦牟尼佛那樣的 三世諸佛因為饒益了眾生的緣故,而成熟了究竟的安樂智慧。所以,我們在修行中,應學習把自己的安樂讓給別人,把別人身心的不安與痛苦拿來由自己代受,應當 這樣修自他交換。這個修法加持力很大,能在短時間裡使自己的性格轉變得很好,資糧積累得很快,上師法王如意寶也一直這樣發願:願世間所有眾生痛苦自己代 受,自己所有安樂一切眾生都能得到。我們大家也應該常常在遇到病痛等違緣時發願一切眾生遠離這種痛苦,遇到欣喜順緣尤其是法喜時,發願一切眾生都得到如是 安樂。科學也已經證明了心的力量是不可思議的。所以,我們的發心肯定對眾生有利。以前阿底峽尊者請弟子加持他,也是因為弟子已發起了菩提心。
後得(出定)時違緣轉為道用分四個方面:將不欲意的四法轉為道用;將二種難忍轉為道用;將二種盛衰轉為道用;將二種煩惱轉為道用。將不欲意的四法轉爲道用分四個方面:將無得轉爲道用,將痛苦轉爲道用,將誹謗轉爲道用,將挖苦轉爲道用。

12. 將無得轉為道用

誰以大欲圖財物 一切奪或令人奪 乃身受用三世善 回向於他佛子行 
如果一個人以大貪慾心將我的衣食等資生之財物搶去了或偷去了,或教唆別人如此對待我,我非但不生嗔恨心, 也不報仇,反而能把自己所受用的財物善根及身體全部回向給他,這行為確實非常高尚。但有的人往往連幾句話的氣都受不了,有的人被偷了,簡直恨得想馬上殺掉 小偷,這就現出了我們的心理與菩提心之間的巨大差距。一個真正發起大悲心的人,凡見到他、聽到他的聲音及與他結緣的眾生,都會蒙受利益。

13. 將痛苦轉為道用

吾雖無有少罪過 何人若斷吾之頭 然以悲心於諸罪 自身代受佛子行
我們一個修行人,各方面都做得很好,突然有一人無緣無故地對自己生起了大嗔心,製造各種違緣,甚至還要砍 我的頭,學菩薩道的人,這時就應對他生一個大悲心,非但不起怨憎,還要發心代受一切罪過。但有些人不要說殺頭,甚至連別人不小心踩了一下坐墊就討厭得不得 了,這就是修行不好的表現。

14. 將誹謗轉為道用

何人百般誹謗吾 雖已傳遍三千界 吾猶深懷仁慈心 讚歎他德佛子行 
如果我自己對別人沒做任何有害之事,而別人卻對我作無因誹謗,並大肆宣揚,使自己的臭名遠揚,此時,對於 修行者來說,非但不憎恨他,而是真實地慈悲他可憐他,而且不斷讚歎他的功德。但對我們一般人來說,往往是自己確實做錯了,但在別人批評時,還是氣得臉紅脖 子粗,過後還耿耿於懷,開始去對他人作無因誹謗,這對一個修行者來說是極不應該的。當遇到別人的誹謗時,可以這樣多向內觀自己:這是因果報應、是空谷聲, 是對自己修行的考驗,自己不能被八風吹動,對方可能是佛菩薩的化現,即便是個凡夫我也不能對他生嗔心,因為,他已經造了惡業,非常可憐,應從心底裡對他生 起一個悲心,並經常讚歎他的功德,這才是大乘佛子的行為。

15. 將挖苦轉為道用

何人於聚眾人處 揭發吾過說粗語 於彼還生善師想 恭敬其是佛子行
一個人在眾人集聚之處,故意揭發自己的過失,並說一些難以入耳的粗語,自己不恨他,還把他看成是自己的善 知識,使自己能發現缺點,故應恭恭敬敬地對待他。沒有修行的人自尊心往往很強,有的不要說是一個平常的人對他說過失,甚至連上師指出他的過失,也想離開上 師,這就失去了許多發現缺點,洗心革面的良好機會。
以前,西瓦格西經常嚴厲地對他的弟子說:「如果不能接受,你明天就可以離開。」所以,對自己說過失者,應該看著是自己的勝善上師。
將二種難忍轉為道用又分二種:將反報恩德轉為道用;將侮辱轉為道用。

16. 將反報恩德轉為道用

吾以如子護養人 彼若視我如怨仇 猶如母對重病兒 倍悲憫是佛子行
如果我對一個人像對自己親生的兒子一樣,慈愛地養育、百般地愛護,但是,這個人不但不報恩,反而把我看成 怨恨的仇敵一般,這時我仍舊對他如母親對病重的兒子一樣對待,不計較恩怨,總是以慈母之心體諒他,對他倍加悲憫,這對我們初學者較難做到,但如果大家把這 些頌詞能背誦下來,經常薰習自己的身語意,久而久之,就能生起這些功德。

17. 將侮辱轉為道用

與吾同等或下士 雖以傲慢作侮辱 吾恭敬彼如上師 恆頂戴是佛子行
我們對上師及各方面優於自己的人會恭敬頂戴,但對於從智慧、人格、地位、能力等各方面與我同等或不及於自 己的人,如果他們以傲慢心來侮辱、欺凌我,我不但不生嗔心,應把他作為上師一樣對他恭敬恆時頂禮,並應反思是否在平時的待人接物中傷害了別人,即使沒有也 是宿業現前,現正是還債的時候,故《遺教經》中說:人與傍生的差別在於人知道慚愧。所以,修行人應該修忍辱度,把侮辱轉為道用,這是佛子行。
將二種盛衰轉為道用時又分兩個方面:將衰弱轉為道用;將盛勢轉為道用。

18. 將衰弱轉為道用

貧窮恆被他人欺 又遭重病及魔障 眾生諸苦己代受 無怯弱是佛子行
在修行的過程中,有時是現前順緣;有時是現前違緣,一般的人遇順緣時易生傲慢心,遇違緣時易生怯弱心,這 兩種心都要對治,尤其是平時業障現前,自己無吃無穿,又遭病苦和魔障,同時還受別人的欺負,此時的自己應該觀想眾生的一切苦難由我代受,自然把違緣轉為道 用,面對痛苦毫不怯弱,這才是大乘菩薩的行為。

19. 將盛勢轉為道用

眾人恭敬遍榮譽 財物量如多聞天 然知世妙無實義 無有驕傲佛子行
有的人受到眾人恭敬,名譽也傳遍十方,財物富如多聞天子一般,這些世間福報都是不可靠、無常的,並沒有什麼可執著與驕傲,一個修行人要修平常心,逆來順受,順來順受,無論外境是逆是順,只管自心不動。
將二境轉為道用也分兩個方面:將嗔境轉為道用;將貪境轉為道用。

20. 將嗔境轉為道用

若未降伏自嗔心 外敵雖伏又增盛 故以慈悲之心軍 調伏自心佛子行
如果一個人沒有調伏自己的嗔心,必然會與人結仇,但是殺掉了自己怨恨的敵人,又會遇到後來為他復仇的人, 以至子子孫孫互相爭鬥不息,使嗔心越來越增盛。但是,若以慈悲心來調伏自己,那麼,外境的怨敵自然就不會存在,《入菩薩行》中詳細地講述了調伏嗔心的方 法。大家可以認真地學習。

21. 將貪境轉為道用

一切妙欲如鹽水 如何享受貪越增 於何能生貪心物 頓時捨棄佛子行
口渴時喝鹽水,越喝反而越渴,世間一切妙用欲樂也一樣,越享用越生貪心,對於修行人,對那些凡是令自己生貪心的財色名食等,應該頓時捨棄,令其不復再生,斷除自己的貪心是大乘菩薩道的根本。
以上是兩種菩提心中世俗菩提心的學處。接著是勝義菩提心的學處分兩個方面:入定時觀修離戲無執;出定遇貪、嗔時遠離實執。

22. 入定時觀修離戲無執

一切現象唯自心 心性本為離邊戲 領悟彼諸二取相 不作意是佛子行
一切所顯之現象唯是自己的心,而心的本體遠離八邊戲論,這是大中觀、大手印、大圓滿的密意,所以,領悟遠離能取的心與所取的境這二取的體相,心不作意是大乘佛子之密行。
出定遇貪、嗔時遠離實執又分兩個方面:於貪境離實執、觀想一切痛苦為幻境 。

23. 於貪境離實執

會遇悅意之境時 當視猶如夏時虹 雖然美妙然無實 斷除貪慾佛子行
在生活中,我們經常會遇到一些悅意的外境,如人或物等,此時應當把這些看做是夏天的彩虹,雖然十分美麗,但並無精華、實質,也不長久,認識到這一點,自然會淡化、斷除因之而生起的貪慾。

24. 觀想一切痛苦為幻境

諸苦如同夢子死 實執幻象極勞累 是故遭遇違緣時 視為幻景佛子行
我們每個凡夫人都是因業報而來的,必然有各自的痛苦,但這些痛苦如同有一個少女,夢見自己生了個兒子,後 兒子又不幸死去而悲痛欲絕一樣,其本體都不是實有的,只因將其實執為有,自心才深感勞累與痛苦。人生本來是如夢如幻的,無論在修法及日常生活中遇到什麼違 緣,都應視為幻化所生,本無實有。
 以上闡述了上士道三個方面中的前兩個:應發菩提心的理由與修兩種菩提 心。接下來是:學菩薩戒。學菩薩戒又分五個方面:學六度;修學經說四法;修學斷除一切煩惱;修學以正知正念度化眾生;修學善根回向。初學六度又分六個方 面:修學佈施度;修學持戒度;修學忍辱度;修學精進度;修學靜慮度;修學智慧度。

25. 修學佈施度

欲菩提者應捨身 何況一切身外物 是故不望報異熟 佈施便是佛子行
修行者如果想得最究竟的解脫菩提果,則應該捨棄自己的身體,連身體既然都應該捨棄,則一切身外之物更是應 該上供下施。而供施時不能有所求的不清淨心,有求的佈施將是有漏之福報,得不到究竟的菩提果,最如理如法的是以三輪體空來佈施,不希求未來得異熟果,這種 以無緣之發心必得無相之佛果。

26. 修學持戒度

無戒自利尚不成 欲能利他豈可能 是故無存貪有心 護持淨戒佛子行
如果佛弟子沒有戒規,連自己事業都不可能成辦,更談不上去利益他人。

27. 修學忍辱度

欲享善樂之佛子 一切損害如寶藏 故於眾生無怨心 修忍辱是佛子行
每位想得到佛果的佛子,應當把一切損害視作寶藏,如《修心八頌》所云:秉性邪惡眾有情,恆為猛烈罪苦迫,見時如遇大寶藏,願恆惜此難得寶。因此,很多人無緣無故地害你時,不應有絲毫的怨恨心,應當看成是自己修忍辱度的善緣。

28. 修學精進度

唯求自利小乘士 見勤如救頭燃故 為利眾生功德源 發精進是佛子行
那些只為立志自我解脫的小乘弟子,為了自利尚且日日夜夜精進不懈,如頭髮燃火而急切地去撲滅一樣,那麼,為了一切眾生的利益安樂,立志成就佛的一切功德,這樣的大事,更應該刻不容緩地精進修法了,往昔諸佛、現在諸大成就者也無一不是精進的,故發起精進是佛子行。

29. 修學靜慮度

已知具寂之勝觀 能斷一切煩惱障 遠離四處無色心 修習禪定佛子行
如果修行者已經了知具有寂止的勝觀能斷除一切煩惱障,遠離無色界的四空定,即遠離四處無色心(空無邊處、識無邊處、無所有處、非想非非想處),則當如理體證這個境界。若想如理體證這個境界,必須修習止觀雙運的禪定,這在《大圓滿心性休息》中有廣講。

30. 修學智慧度

若無智慧以五度 不得圓滿菩提果 故具方便離三輪 修習智慧佛子行
如果沒有智慧僅僅修持前面的佈施、持戒、忍辱、精進、禪定五度,則不能得到圓滿的菩提果位,因為,智慧是成佛的主因。因此,應該遠離對能、所、物這三輪的執著,(如佈施時執著有能佈施的人,接受佈施的人,和佈施的物品)而使前五度都融入智慧度之中。
修學經說四法分四種: 觀察己惑而捨棄;不說菩薩過失; 遠離貪慾; 不說粗語。

31. 觀察己惑而捨棄

若不細察己迷惑 以法形象行非法 是故恆察己過失 斷除其是佛子行
內心的煩惱必須要自已去看,自己仔細的 檢討反省,就知道有沒有煩惱存在。當內心仍有疑惑存在,就是未看到實相的情形。當自己執著有一個實體,就會產生迷惑與煩惱,對於這一種情況,只要自己反省 檢討,觀察內心就可以知道。自己的內心如果有做壞事等不好的念頭,只要看著自己的心就會知道,當自己的內心生起憤怒之時,只要看著自己的心就會了解,因此 我們要經常反躬自省,觀察自己的內心。

32. 不說菩薩過失

以惑宣說佛子過 以彼減弱自功德 故入大乘士之過 不宣說是佛子行
如果一個人因業力現前,一時的迷惑,生起了煩惱,評說了其他大乘佛子的過失,這樣,說過失的人會漸漸地減 弱自己聞思修的全部功德,這在戒律中有詳細的說明。一個人如果以說別人的過失為樂事,這個人的修行肯定不好。如果對方能接受批評,可以提醒他做錯了事,反 之,若對方剛愎自用,則徒然使雙方都產生煩惱,這時只能在心裡默默為他唸咒加持。

33. 遠離貪慾

貪圖財敬互爭執 減弱聞思修之業 故於親友施主家 斷除貪心佛子行
由於對方是我的朋友,我的功德主,因此就告訴他我最好,別人不好。這是為了名聞利養而發生爭執,這種爭吵 是非常不好的,別人也會因為聽了這樣的說法,而對某些上師喪失了信心,這是對聽聞者製造了障礙。其次自己是一個實修者,自己經常觀修菩提心,現在菩提心中 產生了這個好那個不好的念頭,就造成了觀修的退轉。因此可以說是造成了聽聞者及觀修兩方面的障礙。所以當我們聽到了說這裡不好那裡不好的時候,要能夠生起 兩邊都好的想法,應當安住在平等捨中,沒有你好她不好的差別相。

34. 不說粗語

粗語傷害他人心 失誤佛子之行儀 故於他人所不悅 斷惡言是佛子行
如果佛子不注意自己的語言,說了粗語,既傷害了別人的心,又有失於佛子的律儀,所以,凡是他人所不喜歡聽的話,都應盡量斷除,令他人歡喜。

35. 修學斷除一切煩惱

煩惱串習則難治 念知士持對治劍 貪等煩惱初生時 剷除其是佛子行
從無始以來,我們的貪嗔癡煩惱的串習極為嚴重,極難對治,需要正知正念象勇士守城門一樣時時地仔細地觀察,見到貪嗔癡等煩惱一出現,馬上用正知正念的寶劍調伏它,久而久之,自然能剷除煩惱的根。

36. 修學以正知正念度化眾生

總之何處行何事 當察自心何相狀 恆以正知及正念 修行利他佛子行
 總而言之,無論任何時候在行住坐臥之中,一定要經常看著自己的心,要清楚知道現在我在做什麼事情,現在我心裡在想些什麼,煩惱升起之時要立刻將它去除掉,要以正念正知使煩惱去除乾淨。

37. 修學善根迴向

如是勤修所生善 為除無邊眾生苦 以淨三輪之智慧迴向菩提佛子行
如是以上述勤修所生的一切善法,為了斷除無始以來無邊眾生的痛苦而以本淨三輪體空的智慧,回向於無上菩提,這是大乘菩薩的行為。

末義

以上是論義正行部分,接下來是本文第三大部分:末義 。末義分五個方面:
  • 為誰而著;
  • 顯明此論無誤;
  • 謙虛並祈諒解;
  • 作者迴向菩提;
  • 以具備四種圓滿而結尾

為誰而著

為利慾學佛子道 顯密經論所言義 跟隨聖賢之竅訣 著佛子行卅七頌
為了利益想修學菩薩行為的後學們,作者依顯密之經論及上師的口傳竅訣,而著述了三十七個頌詞,明示大乘菩薩所應遵循的行為。

顯明此論無誤

吾淺智慧少學故 亦無智者所喜詩 然以經典教言故 佛子行頌應無誤
我智慧淺薄又沒有廣博的學問,雖然沒有智者所喜愛的韻辭,而有依經典教言所生的實義,故此三十七頌中所講明的佛子所應行的道理應該是不會有誤的。

謙虛並祈諒解

然諸廣闊佛子行 愚鈍如我難測故 違理無關等過失 祈諸聖賢予寬恕
然而佛子所應行的有很廣很多,像我這樣愚笨遲鈍的人是難以推測的,故而如果此頌文於義於理有違於經典和與之無關的不如法之處,本人至心祈求諸佛菩薩傳承上師給予寬恕。

作者迴向菩提

以此善業諸有情 依勝俗諦菩提心 無住有寂之邊際 願成怙主觀世音
此偈頌作總迴向。以此善業願一切有情,依於勝義世俗二諦菩提心,不住三界輪迴與寂滅涅槃的二種邊際,成就怙主觀世音菩薩的果位。
以具備四種圓滿而結尾 。