您的位置:首页 > 编程语言 > PHP开发

40个迹象表明你还是PHP菜鸟

2011-10-30 12:25 357 查看
BuildingtheSectors[/b](构建扇区)[/b]

要让轮子停在当前扇区的中间点,你首先需要把轮子分为几个扇区。当用户的手指离开屏幕时我们要做一下内容:
1.计算弧度值
2.基于上一步的弧度值找出扇区
3.旋转一个弧度到扇区的中间点
举个例子,入股被选中的扇区是zero并且用户只是轻微往上或往下拖拽了轮子,你想让轮子转会到zero扇区的中间点。尽管这有一点棘手,让我们一步一步的来做。
首先,让我们对容器container的世界有一个更好的理解。
在SMRotaryWheel.m[/b]中continueTrackingWithTouch[/b]方法的顶部下列代码:
CGFloatradians=atan2f(container.transform.b,container.transform.a);

NSLog(@"radis%f",radians);

这记录了用户的手指拖拽的每个时刻容器container的旋转弧度。你会注意掉如果轮子被顺时针拖拽,弧度会是正值直到弧度值大于PI弧度(180度),或者如果你愿意,当标号为“0”的扇区处在圆中心点水平线以下的象限时,当你超过180度,你会看到负值,就像下面屏幕输出的。




这是你在计算扇区边界时必须考虑的:它们的最大值、中间值和最小值。被选中的扇区必会在最左边的位置,0扇区被初始化的位置。你要找到这个问题的答案:当弧度值是x的时候,哪个扇区是被定位器标识的?
要回答这个问题,你需要逆向思考。下面的图片显示了一个有八个扇区的轮子。




圆圈周围的数值表示每隔扇区的最大和最小弧度值。例如,不论何时只要容器container的弧度值在-0.39和0.39之间,轮子就应停在扇区0的中间点位置。
再者,你必须考虑象限(正或负)来正确的加减角度差。有一个特殊,你必须处理跨两个象限的扇区0和扇区4。对于扇区0,中间点是0弧度,它还算较为简单。然而对于扇区4,中间点是PI或-PI,因为中间点跨越正负象限的分界线。所以,事情变得有点复杂。
你可以从下面这张图片看到,如果有奇数个扇区,那么扇区中间点的弧度值会稍简单点。




为了保证灵活和全面,这篇教程会考虑偶数和奇数个扇区的情况,并提供各自的程序代码。但是首先,我们要定义一个新类来表示扇区,并存储每个扇区弧度的最大值、中间值和最小值。
用IOS\CocoaTouch\Objective-Cclass模版创建一个新文件。起名类SMSector,并且继承自NSObject。现在来到SMSector.h[/b]文件并用下面的代码替换其中的内容:
@interfaceSMSector:NSObject


@propertyfloatminValue;

@propertyfloatmaxValue;

@propertyfloatmidValue;

@propertyintsector;


@end

转移到SMSector.m[/b]文件,并用下面的实现代码替换其中的内容:
#import"SMSector.h"


@implementationSMSector


@synthesizeminValue,maxValue,midValue,sector;


-(NSString*)description{

return[NSStringstringWithFormat:@"%i|%f,%f,%f",self.sector,self.minValue,self.midValue,self.maxValue];

}


@end

在SMRotaryWheel.h导入SMSector类:
#import"SMSector.h"

然后增加一个新属性property,名叫sectors:
@property(nonatomic,strong)NSMutableArray*sectors;

来到SMRotaryWheel.m[/b]并添加两个新的帮助方法定义来创建扇区(在已经有的caculateDistanceFromCenter的下边):
@interfaceSMRotaryWheel()

...

-(void)buildSectorsEven;

-(void)buildSectorsOdd;

@end

然后,synthesize这个新的属性property:
@synthesizesectors;

下一步,在drawWheel[/b]方法的最后,添加下面的代码这样当你创建转轮的时候扇区就会被初始化。
//8-Initializesectors

sectors=[NSMutableArrayarrayWithCapacity:numberOfSections];

if(numberOfSections%2==0){

[selfbuildSectorsEven];

}else{

[selfbuildSectorsOdd];

}

让我们开始一个比较简单的情况,当有奇数个扇区时。在SMRotaryWheel.m[/b]的底部(@end的上面)添加下面的方法实现代码:
-(void)buildSectorsOdd{

//1-Definesectorlength

CGFloatfanWidth=M_PI*2/numberOfSections;

//2-Setinitialmidpoint

CGFloatmid=0;

//3-Iteratethroughallsectors

for(inti=0;i<numberOfSections;i++){

SMSector*sector=[[SMSectoralloc]init];

//4-Setsectorvalues

sector.midValue=mid;

sector.minValue=mid-(fanWidth/2);

sector.maxValue=mid+(fanWidth/2);

sector.sector=i;

mid-=fanWidth;

if(sector.minValue<-M_PI){

mid=-mid;

mid-=fanWidth;

}

//5-Addsectortoarray

[sectorsaddObject:sector];

NSLog(@"clis%@",sector);

}

}

让我们一步步的分析上面的代码:
首先,我们定义了每隔扇区弧度值的长度(或者叫宽度如果你愿意)。
然后,我们用初始中间点声明了一个变量。既然我们的起始点是0弧度,那它就是我们第一个中间点。
然后我们重复设置每个扇区的最大、中间和最小弧度值。
当计算最小和最大弧度值时,你要加上或减去扇区宽度的一半来得到正确的结果。记得角度变化范围是从-PI到PI,这样才正常,如果一个值超出了PI或-PI,那意味着你改变了象限。你既然是顺时针定位轮子,你就得考虑弧度最小值小于PI的情况,并且改变中间点的标记。
最后,一旦创建一个扇区,我们把这个扇区添加到预先定义的扇区数组中。
现在在SMViewController.m[/b]中修改viewDidLoad[/b]方法的section#2处设置sections值为3,正如下面代码:
SMRotaryWheel*wheel=[[SMRotaryWheelalloc]initWithFrame:CGRectMake(0,0,200,200)andDelegate:self

withSections:3];

如果你现在编译并运行,控制台应该显示以下的结果:




这些数值跟上面被分为三部分的轮子的数值是相同的。所以你的计算工作非常精确!

AnimatingtheSelectionCentering[/b](旋转到扇区中心)[/b]

最后一步是实现校准当前扇区的中心点,让我们温习下这是什么意思。
当用户的手指离开屏幕你必须计算x值,当前的弧度值,并且根据这个值确定选中的扇区。然后你得计算x和扇区中心点的差值,并用它来构建一个仿射变换。
首先在SMRotaryWheel.h[/b]中添加一个新属性property来记录当天扇区:
@propertyintcurrentSector;

然后,在SMRotaryWheel.m[/b]中synthesize这个新属性property:
@synthesizecurrentSector;

要处理手指离开屏幕的事件,你要重写endTrackingWithTouch:withEvent:[/b]方法(重写touchedEnded:withEvent:[/b]方法如果你扩展UIView)。
在SMRotaryWheel.m[/b],在continueTrackingWithTouch:withEvent:[/b]方法的下面添加下列代码来处理奇数个扇区:
-(void)endTrackingWithTouch:(UITouch*)touchwithEvent:(UIEvent*)event

{

//1-Getcurrentcontainerrotationinradians

CGFloatradians=atan2f(container.transform.b,container.transform.a);

//2-Initializenewvalue

CGFloatnewVal=0.0;

//3-Iteratethroughallthesectors

for(SMSector*sinsectors){

//4-Seeifthecurrentsectorcontainstheradianvalue

if(radians>s.minValue&&radians<s.maxValue){

//5-Setnewvalue

newVal=radians-s.midValue;

//6-Getsectornumber

currentSector=s.sector;

break;

}

}

//7-Setupanimationforfinalrotation

[UIViewbeginAnimations:nilcontext:NULL];

[UIViewsetAnimationDuration:0.2];

CGAffineTransformt=CGAffineTransformRotate(container.transform,-newVal);

container.transform=t;

[UIViewcommitAnimations];

}

这个方法相当的简单。它计算当前的弧度冰雨最小和最大弧度进行比较来确定正确的扇区。然后计算出差值并创建一个新的仿射变换,为了让效果看起来很自然,设置这个旋转动画持续0.2秒。
通过修改SMViewController.m[/b]中iewDidLoad的section#2出的代码重新创建了三个扇区,编译、运行……哈哈!它工作了!抓住轮子并按你的意愿拖拽,你会看到当你停止拖拽并把手指抬起时,它选中了右边的扇区。
现在这些代码适合于所有带有奇数个扇区的轮子。要考虑偶数个扇区,你必须重做section#3出的循环类检测异常的情况,在这种情况下,弧度最小值是正的,最大值是负的。用下面的代码替换掉endTrackingWithTouch:withEvent:[/b]中的sections#4,#5和#6部分:
//4-Checkforanomaly(occurswithevennumberofsectors)

if(s.minValue>0&&s.maxValue<0){

if(s.maxValue>radians||s.minValue<radians){

//5-Findthequadrant(positiveornegative)

if(radians>0){

newVal=radians-M_PI;

}else{

newVal=M_PI+radians;

}

currentSector=s.sector;

}

}

//6-Allnon-anomalouscases

elseif(radians>s.minValue&&radians<s.maxValue){

newVal=radians-s.midValue;

currentSector=s.sector;

}

编译、运行,并体验通过改变扇区数来免费进行的实验吧!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: