1

我已经开始写一个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];
}

在模拟器上一切正常。但是在真机上速度太慢了。

我在两件事上需要帮助:

  1. 在绘制块的那一刻:如果我知道块应该具有什么高度值,我如何计算用于旋转块以适应高度的角度?它还取决于投影值。现在它正在使用一个丑陋的解决方案,在视图初始化并保存到字典后预先计算所有角度(从 0 到 90)的高度,当我们需要知道块旋转以适应高度的特定角度时,我们只需搜索此硬编码字典中最接近的高度值并使用适当的角度。太丑了 我试图通过在绘图期间计算所需的值来避免这种情况,但我没有运气(需要公式)。

  2. 其他部分的优化。

请帮助我,我一直在解决这个问题。

4

1 回答 1

0

将计算放在另一个线程上。避免在后台线程中使用 GUI!:)

[self performSelectorInBackground:<#(SEL)#> withObject:<#(id)#>];
于 2013-01-28T10:27:23.973 回答