UIView
サブクラスを書き始めました。に非常によく似た、ユーザーがブロックをスクロールして任意のブロックを押すことができるコントロールになりますUITableView
。以下に投稿した画像へのリンクを確認してください。
http://i.piccy.info/i7/0577902c1bdba79ac3a26a5d3ce322f9/4-50-285/17627834/2012_12_12_14_06_11.jpg
http://i.piccy.info/i7/91a0a1eec95d74e7c13a789fc69a5582/4-50-285/20953097/2012_12_12_14_06_39.jpg
ソースコードは次のとおりです: (.h ファイル)
#import <UIKit/UIKit.h>
#import "Quartzcore/Quartzcore.h"
@interface OSBlockView : UIView
{
CGFloat scrollPosition; // from 0 to 1
CGFloat zoneForFlexion;
CGSize blockSize;
NSUInteger blockQuantity;
CGFloat projection;
NSDictionary *anglesForHeights;
CGFloat oldTranslatedY;
UIGestureRecognizer *panGesture;
NSArray *blocks;
}
@property (nonatomic, readwrite) CGFloat scrollPosition;
@property (nonatomic, readwrite) CGFloat zoneForFlexion;
@property (nonatomic, readwrite) CGSize blockSize;
@property (nonatomic, readwrite) NSUInteger blockQuantity;
@property (nonatomic, readwrite) CGFloat projection;
@property (nonatomic, retain) NSDictionary *anglesForHeights;
@property (nonatomic, readwrite) CGFloat oldTranslatedY;
@property (nonatomic, retain) UIGestureRecognizer *panGesture;
@property (nonatomic, retain) NSArray *blocks;
- (id)initWithFrame:(CGRect)frame blockHeight:(CGFloat)bHeight projection:(CGFloat)proj andBlocks:(NSArray *)blcks;
+(NSDictionary *)calculateAllHeightsForAnglesWithBlockHeight:(CGFloat)blockHeight andProjection:(CGFloat)proj;
-(NSNumber *)getBlockTopWithBlockNumber:(NSNumber *)bn;
-(NSNumber *)angleValueForBlockHeight:(NSNumber *)h;
+(NSArray *)multicoloredViewsWithSize:(CGSize)size;
@end
.m ファイルは次のとおりです。
#import "OSBlockView.h"
@implementation OSBlockView
@synthesize scrollPosition, zoneForFlexion;
@synthesize blockSize, blockQuantity, projection;
@synthesize anglesForHeights;
@synthesize oldTranslatedY;
@synthesize panGesture;
@synthesize blocks;
int logForTimingIndex=0;
+(void)logFotTimingWithString:(NSString *)str
{
NSLog(@"time log: %08i %@", logForTimingIndex, str);
logForTimingIndex++;
}
- (id)initWithFrame:(CGRect)frame blockHeight:(CGFloat)bHeight projection:(CGFloat)proj andBlocks:(NSArray *)blcks;
{
[[self class] logFotTimingWithString:@"begin init"];
self = [super initWithFrame:frame];
if (self)
{
self.blockSize=CGSizeMake(frame.size.width, bHeight);
self.blockQuantity=[blcks count];
self.blocks=blcks;
self.projection=proj;
self.anglesForHeights=[OSBlockView calculateAllHeightsForAnglesWithBlockHeight:bHeight andProjection:proj];
self.scrollPosition=0.2;
self.zoneForFlexion=frame.size.height*0.9;
//adding pan gesture
self.panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanGesture:)];
[self addGestureRecognizer:panGesture];
};
[[self class] logFotTimingWithString:@"end init"];
return self;
}
+(NSArray *)multicoloredViewsWithSize:(CGSize)size
{
[[self class] logFotTimingWithString:@"multicoloredViewsWithSize begin"];
//generating multicoloured blocks
NSMutableArray *blocks=[[NSMutableArray alloc] init];
for (int i=0; i<13; i++)
{
UIColor *blockColor;
switch (i)
{
case 0:
{
blockColor=[UIColor colorWithRed:0.75 green:0.75 blue:0.75 alpha:1.0];
break;
}
case 1:
{
blockColor=[UIColor colorWithRed:0.5 green:0.5 blue:0.5 alpha:1.0];
break;
}
case 2:
{
blockColor=[UIColor colorWithRed:1.0 green:0.0 blue:0.0 alpha:1.0];
break;
}
case 3:
{
blockColor=[UIColor colorWithRed:0.5 green:0.0 blue:0.0 alpha:1.0];
break;
}
case 4:
{
blockColor=[UIColor colorWithRed:1.0 green:1.0 blue:0.0 alpha:1.0];
break;
}
case 5:
{
blockColor=[UIColor colorWithRed:0.5 green:0.5 blue:0.0 alpha:1.0];
break;
}
case 6:
{
blockColor=[UIColor colorWithRed:0.0 green:1.0 blue:0.0 alpha:1.0];
break;
}
case 7:
{
blockColor=[UIColor colorWithRed:0.0 green:0.5 blue:0.0 alpha:1.0];
break;
}
case 8:
{
blockColor=[UIColor colorWithRed:0.0 green:1.0 blue:1.0 alpha:1.0];
break;
}
case 9:
{
blockColor=[UIColor colorWithRed:0.0 green:0.5 blue:0.5 alpha:1.0];
break;
}
case 10:
{
blockColor=[UIColor colorWithRed:0.0 green:0.0 blue:1.0 alpha:1.0];
break;
}
case 11:
{
blockColor=[UIColor colorWithRed:0.0 green:0.0 blue:0.5 alpha:1.0];
break;
}
case 12:
{
blockColor=[UIColor colorWithRed:1.0 green:0.0 blue:1.0 alpha:1.0];
break;
}
case 13:
{
blockColor=[UIColor colorWithRed:0.5 green:0.0 blue:0.5 alpha:1.0];
break;
}
default:
{
NSLog(@"PANIC: unknown block index");
blockColor=[UIColor blackColor];
break;
}
}
UIView *bView=[[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, size.width, size.height)];
[bView setBackgroundColor:blockColor];
[bView.layer setBorderWidth:3];
[bView.layer setBorderColor:[[UIColor whiteColor] CGColor]];
UILabel *blockLabel=[[UILabel alloc] initWithFrame:CGRectMake(2.5, 2.5, bView.frame.size.width-5, bView.frame.size.height-5)];
[blockLabel setBackgroundColor:[UIColor clearColor]];
[blockLabel setText:[NSString stringWithFormat:@"block number %i", i]];
[blockLabel setFont:[UIFont boldSystemFontOfSize:20]];
[blockLabel setTextColor:[UIColor whiteColor]];
[blockLabel setTextAlignment:UITextAlignmentCenter];
[bView addSubview:blockLabel];
[blocks addObject:bView];
};
[[self class] logFotTimingWithString:@"multicoloredViewsWithSize end"];
return blocks;
}
- (void)drawRect:(CGRect)rect
{
//NSLog(@"\n\n\n\n\n\n\n\n\n");
[[self class] logFotTimingWithString:@"drawRect begin"];
//NSLog(@"drawRect called");
//NSLog(@"self.subview.count: %i", [self.subviews count]);
//[[self class] logFotTimingWithString:@"subviews remove begin"];
for (UIView* subview in self.subviews)
{
//NSLog(@"subview removed");
[subview removeFromSuperview];
};
//[[self class] logFotTimingWithString:@"subviews remove end"];
//calculating top coordinates of blocks
//NSLog(@"calculating block tops...");
//[[self class] logFotTimingWithString:@"calculating block tops begin"];
NSMutableArray *blockTops=[[NSMutableArray alloc] init];
for (unsigned int blockCounter=0; blockCounter<blockQuantity; blockCounter++)
{
[blockTops addObject:[self getBlockTopWithBlockNumber:[NSNumber numberWithInt:blockCounter]]];
};
//[[self class] logFotTimingWithString:@"calculating block tops end"];
/*
for (unsigned int index=0; index!=[blockTops count]; index++)
{
NSLog(@"top %i at %i", index, [[blockTops objectAtIndex:index] intValue]);
};
*/
//calculating block heights
//NSLog(@"calculating block heights...");
//[[self class] logFotTimingWithString:@"calculating block heights begin"];
NSMutableArray *blockHeights=[[NSMutableArray alloc] init];
for (unsigned int index=0; index<[blockTops count]; index++)
{
if (([blockTops count]-1)!=index)
{
[blockHeights addObject:[NSNumber numberWithInt:([[blockTops objectAtIndex:(index+1)] intValue]-[[blockTops objectAtIndex:index] intValue])]];
}
else
{
if ((self.frame.size.height-[[blockTops objectAtIndex:index] intValue])<blockSize.height)
{
[blockHeights addObject:[NSNumber numberWithInt:(self.frame.size.height-[[blockTops objectAtIndex:index] intValue])]];
}
else
{
[blockHeights addObject:[NSNumber numberWithInt:blockSize.height]];
};
};
};
//[[self class] logFotTimingWithString:@"calculating block heights end"];
for (unsigned int blockCounter=0; blockCounter<blockQuantity; blockCounter++)
{
//NSLog(@"\n\n");
//[[self class] logFotTimingWithString:@"draw cycle begin"];
//[self preLoadBlockAtY:[blockTops objectAtIndex:blockCounter] withHeight:[blockHeights objectAtIndex:blockCounter] andIndex:[NSNumber numberWithUnsignedInt:blockCounter]];
//NSLog(@" preLoadingSampleBlockAtY: %g withHeight: %g andIndex: %i", [y doubleValue], [height doubleValue], [index unsignedIntValue]);
NSNumber *y=[blockTops objectAtIndex:blockCounter];
NSNumber *height=[blockHeights objectAtIndex:blockCounter];
//NSNumber *index=[NSNumber numberWithUnsignedInt:blockCounter];
//NSLog(@"loading block %02u y:%g h:%g", [index unsignedIntValue], [y doubleValue], [height doubleValue]);
if ([height intValue]>2)
{
BOOL up;
if ([y doubleValue]>self.frame.size.height/2.0)
{
up=true;
}
else
{
up=false;
};
//[[self class] logFotTimingWithString:@"bview init begin"];
UIView *bView=[NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:[blocks objectAtIndex:blockCounter]]];
[bView setFrame:CGRectMake(0.0, [y doubleValue], self.frame.size.width, blockSize.height)];
//[bView.layer setMasksToBounds:YES];
//[[self class] logFotTimingWithString:@"bview init end"];
//NSLog(@" blockImage frame y: %g", blockImage.frame.origin.y);
//NSLog(@" blockImage frame height: %g", blockImage.frame.size.height);
double oldHeight=bView.frame.size.height;
//NSLog(@" preanchor: %@", NSStringFromCGRect(bView.frame));
//[[self class] logFotTimingWithString:@"bview anchor begin"];
//setting anchor point
if (up)
{
[bView.layer setAnchorPoint:CGPointMake(0.5, 1.0)];
[bView setFrame:CGRectMake(bView.frame.origin.x, bView.frame.origin.y+bView.frame.size.height/2.0, bView.frame.size.width, bView.frame.size.height)];
}
else
{
[bView.layer setAnchorPoint:CGPointMake(0.5, 0.0)];
[bView setFrame:CGRectMake(bView.frame.origin.x, bView.frame.origin.y-bView.frame.size.height/2.0, bView.frame.size.width, bView.frame.size.height)];
};
//[[self class] logFotTimingWithString:@"bview anchor end"];
//NSLog(@" postanchor: %@", NSStringFromCGRect(bView.frame));
//[[self class] logFotTimingWithString:@"bview transform begin"];
if ([height doubleValue]!=blockSize.height)
{
//preparing transform
CATransform3D basicTrans = CATransform3DIdentity;
basicTrans.m34 =1.0/-projection;
//calculating angle
double angle= [[self angleValueForBlockHeight:height] doubleValue];
double rangle;
if (up)
{
rangle=angle/360*(2.0*M_PI);
}
else
{
rangle=(360.0-angle)/360*(2.0*M_PI);
};
//NSLog(@" angle: %g", angle);
//transforming
bView.layer.transform = CATransform3DRotate(basicTrans, rangle, 1.0f, 0.0f, 0.0f);
//NSLog(@" blockImage frame y: %g", blockImage.frame.origin.y);
//NSLog(@" blockImage frame height: %g", blockImage.frame.size.height);
};
//[[self class] logFotTimingWithString:@"bview transform end"];
//[[self class] logFotTimingWithString:@"bview post transform begin"];
double newHeight=bView.frame.size.height;
if (up)
{
[bView setCenter:CGPointMake(bView.center.x, bView.center.y-(oldHeight-newHeight))];
};
//NSLog(@" blockImage frame y: %g", bView.frame.origin.y);
//NSLog(@" blockImage frame height: %g", bView.frame.size.height);
//adding subview
[self addSubview:bView];
//[[self class] logFotTimingWithString:@"bview post transform end"];
}
else
{
//do not need to draw blocks with very low height
};
//[[self class] logFotTimingWithString:@"draw cycle end"];
//NSLog(@"\n\n");
};
[[self class] logFotTimingWithString:@"drawRect end"];
//NSLog(@"\n\n\n\n\n\n\n\n\n\n");
}
-(NSNumber *)angleValueForBlockHeight:(NSNumber *)h
{
//[[self class] logFotTimingWithString:@" angleValueForBlockHeight begin"];
//NSLog(@"angleValueForBlockHeight: %g called", [h doubleValue]);
//searching for closest key
double minDistance=blockSize.height;
double distance;
NSString *minKey=@"";
for(NSString *key in anglesForHeights)
{
if ([[anglesForHeights objectForKey:key] doubleValue]==[h doubleValue])
{
//match found
//NSLog(@"returned: %g", [key doubleValue]);
return [NSNumber numberWithDouble:[key doubleValue]];
};
distance=fabs([[anglesForHeights objectForKey:key] doubleValue]-[h doubleValue]);
if (distance<minDistance)
{
minDistance=distance;
minKey=key;
};
};
//NSLog(@"returned: %g", [blockSizesForAngles objectForKey:minKey]);
//[[self class] logFotTimingWithString:@" angleValueForBlockHeight end"];
return [NSNumber numberWithDouble:[minKey doubleValue]];
}
+(NSDictionary *)calculateAllHeightsForAnglesWithBlockHeight:(CGFloat)blockHeight andProjection:(CGFloat)proj
{
//NSLog(@"calculateAllHeightsForAnglesWithBlockHeight:%g andProjection:%g called", blockHeight, proj);
//[[self class] logFotTimingWithString:@" calculateAllHeightsForAnglesWithBlockHeight:andProjection begin"];
NSMutableDictionary *res=[[NSMutableDictionary alloc] init];
for (double i=0.0; i<=90.0; i=i+1.0)
{
//NSLog(@"i: %g", i);
UIView *block=[[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, 100.0, blockHeight)];
[block.layer setAnchorPoint:CGPointMake(0.5, 1.0)];
CATransform3D basicTrans = CATransform3DIdentity;
double rangle;
basicTrans.m34 =1.0/-proj;
rangle=i/360*(2.0*M_PI);
//NSLog(@"rangle: %g Pi", rangle/M_PI);
block.layer.transform = CATransform3DRotate(basicTrans, rangle, 1.0f, 0.0f, 0.0f);
//NSLog(@"calculated block height: %g for angle: %g", block.frame.size.height, i);
if ([res objectForKey:[NSString stringWithFormat:@"%i", (int)roundf(i)]]==nil)
{
[res setObject:[NSString stringWithFormat:@"%i", (int)floor(block.frame.size.height)] forKey:[NSNumber numberWithDouble:i]];
};
};
//NSLog(@" result (size: %i): %@", [res count], [res debugDescription]);
//[[self class] logFotTimingWithString:@" calculateAllHeightsForAnglesWithBlockHeight:andProjection end"];
return [NSDictionary dictionaryWithDictionary:res];
}
-(NSNumber *)getBlockTopWithBlockNumber:(NSNumber *)bn
{
//NSLog(@"getBlockTopWithBlockNumber: %i", [bn intValue]);
//[[self class] logFotTimingWithString:@" getBlockTopWithBlockNumber begin"];
int t=[bn intValue];
CGFloat h=self.blockSize.height;
float v = self.blockQuantity * h - self.zoneForFlexion;
float vp = v * self.scrollPosition;
float z = t * h;
float alpha = (self.frame.size.height-self.zoneForFlexion) / (self.scrollPosition*self.scrollPosition + (1-self.scrollPosition) * (1-self.scrollPosition));
float f;
if (z < vp)
{
f = z / v;
//NSLog(@" %i", (int)(alpha * (f * f)));
//[[self class] logFotTimingWithString:@" getBlockTopWithBlockNumber end"];
return [NSNumber numberWithInt:(int)(alpha * (f * f))];
};
if (z <= vp + self.zoneForFlexion)
{
//NSLog(@" %i", (int)(z + alpha * (self.scrollPosition * self.scrollPosition) - vp));
//[[self class] logFotTimingWithString:@" getBlockTopWithBlockNumber end"];
return [NSNumber numberWithInt:(int)(z + alpha * (self.scrollPosition * self.scrollPosition) - vp)];
}
else
{
f = (blockQuantity*h-z) / v;
//NSLog(@" %i", (int)(self.frame.size.height - alpha * (f * f)));
//[[self class] logFotTimingWithString:@" getBlockTopWithBlockNumber end"];
return [NSNumber numberWithInt:(int)(self.frame.size.height - alpha * (f * f))];
};
}
double oldTranslatedY=0.0;
-(IBAction)handlePanGesture:(UIPanGestureRecognizer *)sender
{
//NSLog(@"handlePanGesture: called");
//[[self class] logFotTimingWithString:@" handlePanGesture begin"];
double translatedY = [sender translationInView:self].y;
double delta;
if (fabs(translatedY)<fabs(oldTranslatedY))
{
[sender setTranslation:CGPointZero inView:self];
oldTranslatedY=0.0;
delta=0.0;
}
else
{
delta=translatedY-oldTranslatedY;
oldTranslatedY=translatedY;
};
//NSLog(@"translatedY: %g", translatedY);
double pOffset=delta/((blockQuantity*blockSize.height)-self.frame.size.height);
self.scrollPosition=self.scrollPosition-pOffset;
if (self.scrollPosition<0.0)
{
self.scrollPosition=0.0;
}
else if(self.scrollPosition>1.0)
{
self.scrollPosition=1.0;
};
[[self class] logFotTimingWithString:@" handlePanGesture end"];
[self setNeedsDisplay];
}
シミュレーターではすべて正常に動作しています。しかし、実際のデバイスでは遅すぎます。
2 つの点で助けが必要です。
ブロックを描画する瞬間: ブロックの高さの値がわかっている場合、高さに合わせてブロックを回転させるために使用する角度を計算するにはどうすればよいですか? また、投影値にも依存します。現在、ビューが初期化されて辞書に保存された後、すべての角度 (0 から 90 まで) の高さを事前に計算する醜いソリューションを使用しています。高さに合わせてブロックを回転させるための特定の角度を知る必要がある場合は、このハードコードされた辞書から高さの最も近い値を取得し、適切な角度を使用します。醜すぎる。描画中に必要な値を計算してこれを回避しようとしましたが、運がありませんでした(式が必要です)。
別の部分での最適化。
助けてください、私はこの問題に行き詰まっています。