用Cocoa绘图,错误的xy值

时间:2010-11-11 16:31:18

标签: objective-c cocoa macos drawing nsview

我正在尝试在NSScrollView内部的NSView中绘制一堆矩形。代码工作得很好,但似乎计算机正在绘制错误的点。

当我使用NSLog()记录当前xy时,值是正确的,但是当我查看我的视图时,在绘图坐标之前会添加额外的空间。< / p>

这是代码:

//
//  CoverFlowView.m
//  iWatch
//
//  Created by ief2 on 03/11/10.
//  
//

#import "CoverFlowView.h"

#pragma mark Defaults
#define COVER_HEIGHT2WIDTH(a) ((float)a / 1.4)
#define COVER_HEIGHT (200.0)
#define COVER_WIDTH (COVER_HEIGHT2WIDTH(COVER_HEIGHT))
#define COVER_SPACE_MIN (20.0)

#pragma mark Private Methods
@interface CoverFlowView (PrivateMethods)
- (CGFloat)coverSpaceForMoviesPerLine:(NSUInteger *)n;
- (void)frameDidChange;
- (void)updateFrame;
@end

NSColor *RandomColor() {
    float red = (float)(rand() % 255) / 254.0;
    float green = (float)(rand() % 255) / 254.0;
    float blue = (float)(rand() % 255) / 254.0;

    NSColor *theColor = [[NSColor colorWithDeviceRed:red 
                                               green:green 
                                                blue:blue 
                                               alpha:1.0] retain];
    return [theColor autorelease];
}

#pragma mark Implementation
@implementation CoverFlowView
#pragma mark Init and Dealloc
- (void)awakeFromNib {
    srand(time(NULL));
    NSMutableArray *colors = [[NSMutableArray alloc] initWithCapacity:100];
    register int i;
    for(i = 0; i < 100; i++) {
        [colors addObject:RandomColor()];
    }
    self.movies = colors;
    [colors autorelease];

    [[NSNotificationCenter defaultCenter] addObserver:self 
                                             selector:@selector(frameDidChange) 
                                                 name:NSViewFrameDidChangeNotification
                                               object:self];

}

- (id)initWithFrame:(NSRect)frame movies:(NSArray *)movs {

    self = [super initWithFrame:frame];
    if(self != nil) {
        self.movies = movs;

        // FIXME: Debug color array to movie
        NSMutableArray *colors = [[NSMutableArray alloc] initWithCapacity:100];
        register int i;
        srand((unsigned int)time(NULL));
        for(i = 0; i < 100; i++) {
            [colors addObject:RandomColor()];
        }
    }
    return self;
}

- (void)dealloc {
    [_movies release];

    [super dealloc];
}

#pragma mark Drawing
- (void)drawRect:(NSRect)dirtyRect {
    [[NSColor blackColor] setFill];
    NSRectFill(dirtyRect);

    NSUInteger moviesPerLine = 0;
    CGFloat coverSpace = 0;

    // Get the values
    coverSpace = [self coverSpaceForMoviesPerLine:&moviesPerLine];



    // if it's less than 1, break the drawing
    if(moviesPerLine < 1) {
        return;
    }

    // get the rows not to draw
    // p = top left point's y
    // h = movie cover height ->\
    // s = space between      -->The movies height and it's space above it
    // 
    //           p
    // f(x) = -------
    //         h + s
    double rows = ((dirtyRect.origin.y) / (COVER_HEIGHT + coverSpace));


    // get the first movie that has to be drawn
    // could be half-draw
    //
    // (NSUInteger)rows * moviesPerLine
    NSUInteger index = (NSUInteger)rows * moviesPerLine;
    if(!(index < [self.movies count]))
        return;


    // variable declaration
    CGFloat curY;
    CGFloat curX;

    // start drawing until the line after max
    // start the loop
    do {
        NSUInteger rowIndex;
        // get the y for the movie
        // x = movie index (start from 1)
        // m = movies per row
        // h = movie height
        // s = movie space
        //
        //         [x - (x % m)]
        // f(x) = (|-----------|) * (h + s) + s
        //         [     m     ]
        // BUT: if rows == 2, rows -= 1
        rowIndex = ((index + 1) - ((index + 1) % moviesPerLine)) / moviesPerLine;
        ((index + 1) % moviesPerLine == 0) ? (rowIndex -= 1) : (rowIndex = rowIndex);
        curY = rowIndex * (COVER_HEIGHT + coverSpace) + coverSpace;


        // get the x fot the movie
        // x = movie index (start from 1)
        // m = movies per line
        // w = movie width
        // s = cover space
        //
        //              
        // f(x) = ((x - (x - (x % m))) - 1) * (w + s) + s
        // BUT: if row == 0, row = m
        //
        rowIndex = (index+1) - (((index+1) - ((index + 1) % moviesPerLine)));
        (rowIndex == 0) ? (rowIndex = moviesPerLine) : (rowIndex = rowIndex);
        curX = (rowIndex - 1) * (COVER_WIDTH + coverSpace) + coverSpace;
        NSLog(@"%s index: %3u || x: %5.0f || y: %5.0f", __FUNCTION__, index, curX, curY);

        // Start the drawing
        NSRect bezierPathRect = NSMakeRect(curX + coverSpace, curY, COVER_WIDTH, COVER_HEIGHT);
        NSBezierPath *path = [NSBezierPath bezierPathWithRect:bezierPathRect];
        [path setLineWidth:2.0];
        [[NSColor whiteColor] setStroke];
        [(NSColor *)[self.movies objectAtIndex:index] setFill];
        [path fill];
        [path stroke];

        // add up values
        index++;
        if(!(index < [self.movies count]))
            return;

    } while(curY - (COVER_HEIGHT + coverSpace) < dirtyRect.origin.y + dirtyRect.size.height);
}

- (BOOL)isFlipped {
    return YES;
}

#pragma mark Private Methods
- (void)frameDidChange {

}

- (CGFloat)coverSpaceForMoviesPerLine:(NSUInteger *)n {
    // x = number of covers
    // w = view width
    // m = cover width
    // y = the space
    //
    //        w - (mx)
    // f(x) = --------
    //         x + 1

    CGFloat m = COVER_WIDTH;
    CGFloat w = [self bounds].size.width;

    register NSUInteger x = 1;
    CGFloat space;
    while(1) {
        space = ((w-(m*x)) / ( x + 1.0));
        if(space < COVER_SPACE_MIN) {
            x--;
            space = ((w - (m*x)) / (x + 1.0));
            break;
        }
        x++;
    }

    if(n) *n = x;
    return space;
}

#pragma mark Properties
@synthesize movies=_movies;
- (void)setMovies:(NSArray *)ar {

    if(ar != _movies) {
        [_movies release];
        _movies = [ar retain];

        // Update frame size
        NSUInteger m;
        CGFloat space = [self coverSpaceForMoviesPerLine:&m];
        NSUInteger lines = [ar count] / m;
        CGFloat height = (COVER_HEIGHT + space) * ((CGFloat)lines) + space;
        CGFloat width = [self frame].size.width;
        NSRect frame = [self frame];
        frame.size = NSMakeSize(width, height);
        [self setFrame:frame];
        [self setNeedsDisplay:YES];
    }
}
@end

在这里您可以找到正在运行的应用程序的屏幕截图: Screenshot

如果有人能告诉我我做错了什么,我真的很感激!

谢谢你, ief2

编辑:不介意错误的标题,我错误地给文件命名: - )

1 个答案:

答案 0 :(得分:1)

对我来说,你是以错误的方式计算X坐标。在行的开头有两倍的间距,在行的末尾没有空格。因此,如果从curX中减去一个间距,则会得到正确的结果:

curX = (rowIndex - 1) * (COVER_WIDTH + coverSpace);

此外,您的代码比它需要的更复杂。例如,您并不需要coverSpaceForMoviesPerLine:中的循环,drawRect:中的循环也可以简化(并且同时提高效率)。