2011-02-24 37 views
35

Tôi đang cố gắng vẽ một bánh xe màu cho iPhone, nhưng tôi không thể có được độ dốc xoay quanh một điểm. Tôi đang cố gắng sử dụng gradient nhưng mục tiêu-c cung cấp một gradient tuyến tính, mà vẽ một gradient theo một đường thẳng như sau: linear gradientLàm thế nào để vẽ một bánh xe màu trong mục tiêu-c

và một gradient xuyên tâm, vẽ gradient bắt đầu tại điểm và tỏa ra theo mọi hướng như thế này: radial gradient

tôi muốn vẽ một gradient tuyến tính mà xoay quanh một điểm như thế này:

colorwheel

+1

văn hôm nay http://code.google.com/p/ios-color-wheel/ –

+0

Xin lỗi để đưa lên một câu hỏi cũ, nhưng khi nhập thư viện của mình vào dự án của tôi, tôi luôn nhận được lỗi về "Mach-O-Linker" từ dòng sau: MainViewController * mainViewController = [[MainViewController alloc] init]; – CHRIS

+0

Tốt hơn để đánh giá lại mức độ giao diện người dùng của con trỏ lớn đến mức nào. Tốt hơn nhiều để xem xét swatches, có thể trong một cái cây. Nhưng đối với độ chính xác thực, hãy sử dụng thanh trượt thành phần không gian màu và một mẫu xem trước. – uchuugaka

Trả lời

23

Sau đây vẽ một bánh xe màu HSL trong phân lớp UIView. Nó thực hiện điều này bằng cách tạo ra một bitmap bằng cách tính toán, cho mỗi pixel, giá trị màu sắc chính xác. Đây không phải là chính xác những gì bạn đang cố gắng làm (có vẻ như nó chỉ là màu sắc khác nhau trong vòng tròn với một độ sáng liên tục/độ bão hòa), nhưng bạn sẽ có thể thích ứng nó cho nhu cầu của bạn.

Lưu ý rằng điều này có thể không có hiệu suất tối ưu, nhưng nó sẽ giúp bạn bắt đầu. Ngoài ra, bạn có thể sử dụng getColorWheelValue() để xử lý đầu vào của người dùng (số lần nhấp/lần chạm tại một tọa độ nhất định).

- (void)drawRect:(CGRect)rect 
{ 
    int dim = self.bounds.size.width; // should always be square. 
    bitmapData = CFDataCreateMutable(NULL, 0); 
    CFDataSetLength(bitmapData, dim * dim * 4); 
    generateColorWheelBitmap(CFDataGetMutableBytePtr(bitmapData), dim, luminance); 
    UIImage *image = createUIImageWithRGBAData(bitmapData, self.bounds.size.width, self.bounds.size.height); 
    CFRelease(bitmapData); 
    [image drawAtPoint:CGPointZero]; 
    [image release]; 
} 

void generateColorWheelBitmap(UInt8 *bitmap, int widthHeight, float l) 
{ 
    // I think maybe you can do 1/3 of the pie, then do something smart to generate the other two parts, but for now we'll brute force it. 
    for (int y = 0; y < widthHeight; y++) 
    { 
     for (int x = 0; x < widthHeight; x++) 
     { 
      float h, s, r, g, b, a; 
      getColorWheelValue(widthHeight, x, y, &h, &s); 
      if (s < 1.0) 
      { 
       // Antialias the edge of the circle. 
       if (s > 0.99) a = (1.0 - s) * 100; 
       else a = 1.0; 

       HSL2RGB(h, s, l, &r, &g, &b); 
      } 
      else 
      { 
       r = g = b = a = 0.0f; 
      } 

      int i = 4 * (x + y * widthHeight); 
      bitmap[i] = r * 0xff; 
      bitmap[i+1] = g * 0xff; 
      bitmap[i+2] = b * 0xff; 
      bitmap[i+3] = a * 0xff; 
     } 
    } 
} 

void getColorWheelValue(int widthHeight, int x, int y, float *outH, float *outS) 
{ 
    int c = widthHeight/2; 
    float dx = (float)(x - c)/c; 
    float dy = (float)(y - c)/c; 
    float d = sqrtf((float)(dx*dx + dy*dy)); 
    *outS = d; 
    *outH = acosf((float)dx/d)/M_PI/2.0f; 
    if (dy < 0) *outH = 1.0 - *outH; 
} 

UIImage *createUIImageWithRGBAData(CFDataRef data, int width, int height) 
{ 
    CGDataProviderRef dataProvider = CGDataProviderCreateWithCFData(data); 
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); 
    CGImageRef imageRef = CGImageCreate(width, height, 8, 32, width * 4, colorSpace, kCGImageAlphaLast, dataProvider, NULL, 0, kCGRenderingIntentDefault); 
    UIImage *image = [[UIImage alloc] initWithCGImage:imageRef]; 
    CGDataProviderRelease(dataProvider); 
    CGColorSpaceRelease(colorSpace); 
    CGImageRelease(imageRef); 
    return image; 
} 

// Adapted from Apple sample code. See http://en.wikipedia.org/wiki/HSV_color_space#Comparison_of_HSL_and_HSV 
void HSL2RGB(float h, float s, float l, float* outR, float* outG, float* outB) 
{ 
    float temp1, temp2; 
    float temp[3]; 
    int i; 

    // Check for saturation. If there isn't any just return the luminance value for each, which results in gray. 
    if(s == 0.0) 
    { 
     *outR = l; 
     *outG = l; 
     *outB = l; 
     return; 
    } 

    // Test for luminance and compute temporary values based on luminance and saturation 
    if(l < 0.5) 
     temp2 = l * (1.0 + s); 
    else 
     temp2 = l + s - l * s; 
    temp1 = 2.0 * l - temp2; 

    // Compute intermediate values based on hue 
    temp[0] = h + 1.0/3.0; 
    temp[1] = h; 
    temp[2] = h - 1.0/3.0; 

    for(i = 0; i < 3; ++i) 
    { 
     // Adjust the range 
     if(temp[i] < 0.0) 
      temp[i] += 1.0; 
     if(temp[i] > 1.0) 
      temp[i] -= 1.0; 


     if(6.0 * temp[i] < 1.0) 
      temp[i] = temp1 + (temp2 - temp1) * 6.0 * temp[i]; 
     else { 
      if(2.0 * temp[i] < 1.0) 
       temp[i] = temp2; 
      else { 
       if(3.0 * temp[i] < 2.0) 
        temp[i] = temp1 + (temp2 - temp1) * ((2.0/3.0) - temp[i]) * 6.0; 
       else 
        temp[i] = temp1; 
      } 
     } 
    } 

    // Assign temporary values to R, G, B 
    *outR = temp[0]; 
    *outG = temp[1]; 
    *outB = temp[2]; 
} 
+0

Lãng phí năng lượng pin để tính toán liên tục. Đẹp mã hóa, nhưng chỉ không sử dụng pin linh sam tuyệt vời. – uchuugaka

+0

@uchuugaka, điều này sẽ chỉ được gọi một lần khi chế độ xem được vẽ và sau đó chỉ khi 'setNeedsDisplay' được gọi. –

+0

Thực ra, bất cứ khi nào màn hình được gọi. setNeedsDisplay chỉ thiết lập một cờ, vòng lặp chính đi và các cuộc gọi hiển thị trên bất cứ thứ gì có nhu cầu hiển thị và bất kỳ thứ gì bên trong nó. (cũng có thể được gọi dưới bố cục) – uchuugaka

1

tôi không biết bất cứ điều gì mà sẽ làm việc này cho yo u. Bạn có thể ước tính nó bằng cách drawing triangles mà đi từ (màu vàng-> đỏ, đỏ-> purp, purp-> màu xanh, vv) và sau đó đặt một circle mask over it. Nhìn vào CGGradient.

Rất tiếc, tôi không thể trợ giúp thêm.

4

Tôi đã làm một cái gì đó như thế này bằng cách tạo một bitmap RGBA lớn, tô màu từng pixel tùy thuộc vào vị trí của nó được chuyển đổi thành tọa độ cực, sau đó chuyển đổi bitmap thành hình ảnh và vẽ hình ảnh thu nhỏ. Việc giảm tỷ lệ là để giúp các pixel chống răng cưa gần trung tâm.

2

Bạn sẽ phải tạo bitmap theo gợi ý của hotpaw2. Cách hiệu quả nhất để tính toán này là sử dụng HSL color space. Điều đó sẽ cho phép bạn tạo ra một chức năng như đầu vào một vị trí điểm ảnh và phun ra một giá trị RGB, chỉ sử dụng một vài phép toán số học cơ bản và một chút trig để tính toán nó.

Nếu bạn muốn màu tùy ý dọc theo "nan hoa" của bánh xe, tốt, điều đó cần nhiều công việc hơn. Bạn cần tính toán các giá trị hòa trộn cho mỗi pixel, và nó liên quan đến một tầm nhìn công bằng hơn lượng giác, và không phải là nhanh. Khi tôi đã làm điều này, tôi đã phải vectorize và song song nó để loại bỏ sự chậm trễ làm mới (mặc dù nhận thức) từ tính toán lại bitmap - trên một MacBook Pro. Trên iPhone bạn không có các tùy chọn này, vì vậy bạn sẽ phải sống với sự chậm trễ.

Hãy cho tôi biết nếu bạn cần giải thích chi tiết hơn về các kỹ thuật này; Tôi sẽ rất vui khi bắt buộc.

17

Chỉ sử dụng phương pháp UIKit:

// ViewController.m; assuming ViewController is the app's root view controller 
#include "ViewController.h" 
@interface ViewController() 
{ 
    UIImage *img; 
    UIImageView *iv; 
} 
@end 

@implementation ViewController 
- (void)viewDidLoad 
{ 
    [super viewDidLoad]; 

    CGSize size = CGSizeMake(self.view.bounds.size.width, self.view.bounds.size.height); 
    UIGraphicsBeginImageContextWithOptions(CGSizeMake(size.width, size.height), YES, 0.0); 
    [[UIColor whiteColor] setFill]; 
    UIRectFill(CGRectMake(0, 0, size.width, size.height)); 

    int sectors = 180; 
    float radius = MIN(size.width, size.height)/2; 
    float angle = 2 * M_PI/sectors; 
    UIBezierPath *bezierPath; 
    for (int i = 0; i < sectors; i++) 
    { 
     CGPoint center = CGPointMake(size.width/2, size.height/2); 
     bezierPath = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:i * angle endAngle:(i + 1) * angle clockwise:YES]; 
     [bezierPath addLineToPoint:center]; 
     [bezierPath closePath]; 
     UIColor *color = [UIColor colorWithHue:((float)i)/sectors saturation:1. brightness:1. alpha:1]; 
     [color setFill]; 
     [color setStroke]; 
     [bezierPath fill]; 
     [bezierPath stroke]; 
    } 
    img = UIGraphicsGetImageFromCurrentImageContext(); 
    UIGraphicsEndImageContext(); 

    iv = [[UIImageView alloc] initWithImage:img]; 
    [self.view addSubview:iv]; 
} 
@end 

Về cơ bản tất cả những gì đoạn mã trên không là đi xung quanh vòng tròn vẽ ngành hẹp điền chúng với màu sắc của tăng từng bước sắc.

Bạn có thể thực hiện tất cả bản vẽ trực tiếp vào ngữ cảnh đồ họa của chế độ xem với drawRect() và không phải tạo ngữ cảnh hình ảnh rõ ràng.

enter image description here

Các vấn đề liên quan