當前位置:成語大全網 - 書法字典 - 如何在iOS中自定控制

如何在iOS中自定控制

在開發過程中,有時候UIKit的標準控件並不能滿足我們的需求。例如,如果您需要壹個可以支持用戶方便地在0到360之間選擇角度值的控件,那麽您需要根據自己的需要定制該控件。

選擇角度值的控件可以這樣實現:創建壹個圓形滑塊,用戶可以通過拖動手柄來選擇角度值。事實上,妳可能在其他平臺看到過這樣的控件,但在UIKit中沒有。

本文通過實現壹個用於選擇角度值的控件來介紹控件的自定義。讓我們來看看它是什麽樣子的:

1.UIControl子類

UIControl是UIView的子類,是所有UIKit控件(如UIButton、UISlider、UISwitch)的父類。

UIControl的主要功能是創建相應的邏輯,將動作分發到相應的目標。在另外90%的情況下,它會根據自己的狀態(比如高亮、選中、禁用)來繪制用戶界面。

通過UIControl,我們主要管理三項重要任務:

繪制用戶界面

跟蹤用戶的操作

目標行動模式

在本文的圓形滑塊中,我們必須做以下事情:

自定義壹個用戶界面(圓形滑塊本身),通過該界面,用戶可以通過手柄進行交互。用戶的交互將被轉換為控件目標對應的動作(控件將滑塊按鈕的幀原點轉換為0到360之間的值,並在目標/動作上使用)。

學習本文時,建議從文末的鏈接下載完整的示例項目。

下面我將逐壹介紹上面列出的三項重要任務。

這些步驟是模塊化的,所以如果妳對繪制界面不感興趣,可以跳過繪制用戶界面,直接學習下面的步驟。

打開項目文件中的TBCircluarSlider.m文件。然後開始學下面的。

1.1繪圖用戶界面

我更喜歡使用核心圖形,使用UIKit的唯壹方法是通過textfield顯示滑塊的值。

提醒:這裏需要壹些核心圖形的知識。不懂也沒關系。我會盡力詳細解釋代碼。

我們先來看看控件的不同組件,這樣更有利於後面的學習。

首先,壹個黑色的圓環被用作滑塊的背景。

活動區域是從藍色到紫色的漸變效果。

用戶通過拖放以下手柄按鈕來選擇壹個值:

最後,用於顯示所選值的TextField。在下壹個版本中,我計劃讓用戶通過鍵盤輸入角度值。

控制界面的繪制主要使用drawRect函數。首先,我們需要獲取當前使用的圖形上下文,如下面的代碼所示:

1

CGContextRef CTX = UIGraphicsGetCurrentContext();

1.1.1繪制背景。

背景是360°的,只需使用CGContextAddArc將正確的路徑添加到圖形上下文中,並設置正確的筆畫即可。

下面的代碼可以完成背景的繪制:

//添加圓弧路徑

CGContextAddArc(ctx,self.frame.size.width/2, self.frame.size.height/2,半徑,0,M_PI *2,0);

//設置筆畫顏色

[[ui color black color]set stroke];

//設置線寬和線帽

CGContextSetLineWidth(ctx,TB _ BACKGROUND _ WIDTH);

CGContextSetLineCap(ctx,kCGLineCapButt);

//畫出來!

CGContextDrawPath(ctx,kCGPathStroke);

CGContextArc函數的參數包括圖形上下文,弧度的中心坐標點,半徑(這是壹個私有變量),後面是弧度開始和結束的角度(壹些數學計算方法可以在TBCircularSlider.m文件頭看到),最後壹個參數表示繪制方向,0表示逆時針方向。

接下來的三行代碼用於設置壹些信息,比如顏色和線寬。最後使用CGContextDrawPath方法繪制背景。

1.1.2畫出用戶的可操作區域。

這部分需要壹點技巧。這裏我們畫壹個線性漸變的蒙版圖。讓我們來看看原理:

這裏蒙版圖像的工作原理是,妳可以在原來的漸變矩形框裏看到壹個洞。

這裏畫的弧度有陰影,是在創建蒙版貼圖的時候用了壹點模糊的效果。

以下是創建遮罩貼圖的相關代碼:

UIGraphicsBeginImageContext(CGSizeMake(320,320));

CGContextRef imageCtx = UIGraphicsGetCurrentContext();

CGContextAddArc(imageCtx,self.frame.size.width/2,self.frame.size.height/2,半徑,0,ToRad(self.angle),0);

[[ui color red color]set];

//使用陰影創建模糊效果

CGContextSetShadowWithColor(imageCtx,CGSizeMake(0,0),self.angle/20,[ui color black color]。CG color);

//定義路徑

CGContextSetLineWidth(imageCtx,TB _ LINE _ WIDTH);

CGContextDrawPath(imageCtx,kCGPathStroke);

//將上下文內容保存到圖像掩碼中

CGImageRef mask = CGBitmapContextCreateImage(UIGraphicsGetCurrentContext());

UIGraphicsEndImageContext();

在上面的代碼中,首先創建壹個圖形上下文,然後設置壹個陰影。通過CGContextSetShadowWithColor方法,我們可以設置以下內容:

語境

偏移(此處不需要)

模糊值(該值由參數控制:將當前角度除以20,得到用戶與該控件交互時的簡單動畫模糊值)。

顏色

然後根據當前角度畫出相應的弧度。

比如當前角度變量為360,畫壹個圓弧;如果是90,畫壹個弧度為90的圓弧。最後用CGBitmapContextCreateImage方法得到壹張圖片(剛剛畫的弧)。這張圖就是我們需要的掩膜圖。

剪輯上下文:

現在我們有了壹個漸變遮罩圖。然後使用函數CGContextClipToMask來剪切上下文,將剛剛創建的掩碼映射傳遞給這個函數。代碼如下:

CGContextClipToMask(ctx,self.bounds,mask);

最後,我們來畫壹個漸變效果。代碼如下:

//定義顏色步驟

CGFloat組件[8] = {

0.0,0.0,1.0,1.0,//開始顏色-藍色

1.0, 0.0, 1.0, 1.0 };//結束顏色-紫色

cgcolorspace ref base space = cgcolorspace createdevicergb();

CGGradientRef gradient = CGGradientCreateWithColorComponents(baseSpace,Components,NULL,2);

//定義漸變方向

CG point start point = CGPointMake(CGRectGetMidX(rect),CGRectGetMinY(rect));

CG point endPoint = CGPointMake(CGRectGetMidX(rect),CGRectGetMaxY(rect));

//選擇壹個色彩空間

CGColorSpaceRelease(baseSpace),baseSpace = NULL

//創建並繪制漸變

cgcontextdrawlaneGradient(CTX,gradient,startPoint,endPoint,0);

CGGradientRelease(gradient),gradient = NULL

繪制漸變效果需要大量的處理,但我們可以將其分為四個部分:

定義顏色變化的範圍。

定義漸變的方向。

選擇色彩空間

創建並繪制漸變

最終的顯示效果(見漸變矩形框的壹部分)歸功於之前創建的掩膜圖。

另外,為了模擬背景邊框的光線反射,我添加了壹些燈光效果。

1.1.3繪圖手柄

讓我們根據當前的角度值在正確的位置繪制手柄。

其實在畫圖的過程中,這壹步很簡單,比較復雜的是計算手柄的位置。

這裏我們需要用三角函數將壹個標量值(標量數)轉換成CGPoint。別管有多復雜,用Sin和Cos函數就行了。代碼如下:

-(CG point)pointfroangle:(int)angle int {

//定義圓心

CG point center point = CGPointMake(self . frame . size . WIDTH/2-TB _ LINE _ WIDTH/2,self.frame.size.height/2-TB _ LINE _ WIDTH/2);

//定義圓周上的點位置

CGPoint結果;

result . y = round(center point . y+radius * sin(ToRad(-angle int)));

result . x = round(center point . x+radius * cos(ToRad(-angle int)));

返回結果;

}

在上面的代碼中,指定壹個角度值,然後計算圓周上方的位置。當然這裏需要圓周的中心點和半徑。

使用sin函數時,需要壹個Y坐標值,而cos函數需要壹個X坐標值。

需要註意的是,這裏每個函數返回的值都被認為是1的半徑,所以我們需要將結果乘以我們指定的半徑,相對於圓心進行計算。

希望下面的公式能幫助妳理解:

1

2

point.y = center.y +(半徑* sin(角度));

point.x = center.x +(半徑* cos(角度));

通過上面的計算,現在我們已經知道了句柄的具體位置,所以我們可以直接將句柄繪制到指定的位置,如下面的代碼所示:

-(void)drawTheHandle:(CGContextRef)CTX {

CGContextSaveGState(CTX);

//我喜歡影子

CGContextSetShadowWithColor(CTX,CGSizeMake(0,0),3,[UIColor blackColor]。CG color);

//獲取句柄位置!

CG point handle center =[self point from angle:self . angle];

//畫出來!

[[ui color colorWithWhite:1.0 alpha:0.7]set];

cgcontextfilllellipseinrect(CTX,CGRectMake(handleCenter.x,handleCenter.y,TB_LINE_WIDTH,TB _ LINE _ WIDTH));

CGContextRestoreGState(CTX);

}

具體操作步驟如下:

保存當前上下文(在單獨的函數中繪圖時保存上下文的狀態是壹個好習慣)。

在手柄上設置壹些陰影效果。

定義手柄的顏色,然後用CGContextFillEllipseInRect繪制。

我們在drawRect函數的末尾調用上面的方法:

1

【自畫the handle:CTX】;

至此,我們已經完成了繪制零件的任務。

1.2跟蹤用戶的操作

在UIControl的子類中,我們可以覆蓋三個特殊的方法來提供自定義的跟蹤行為。

1.2.1開始跟蹤。

當在控件的邊界內發生觸摸事件時,首先調用控件的beginTrackingWithTouch方法。

讓我們看看如何覆蓋這個方法:

-(BOOL)beginTrackingWithTouch:(ui touch *)touch with event:(ui event *)event {

[super beginTrackingWithTouch:touch withEvent:event];

//我們需要持續跟蹤

返回YES

}

該函數返回的布爾值決定了當觸摸事件被拖動時是否需要響應。在這裏的自定義控件中,我們需要跟蹤用戶的拖動,所以我們返回YES。

上面的函數有兩個參數:觸摸對象和事件。

1.2.2連續跟蹤

在前面的方法中,我們指定這裏的自定義控件需要跟蹤壹個持久事件,所以當用戶拖動時,將調用壹個特殊的方法:continueTrackingWithTouch:

-(BOOL)continueTrackingWithTouch:(ui touch *)touch with event:(ui event *)事件

此方法返回的布爾值指示是否繼續跟蹤觸摸事件。

通過這個方法,我們可以根據觸摸位置來過濾用戶的操作。例如,只有當觸摸位置與手柄位置相交時,我們才能激活控制。但是,我們這裏的控制邏輯不是這樣的。我們希望用戶可以單擊任何位置來相應地處理手柄。

本文中的這個方法負責更新句柄的位置(我們將在後面的小節中看到我們將位置信息傳遞給相應的目標)。

上述方法的覆蓋代碼如下:

-(BOOL)continueTrackingWithTouch:(ui touch *)touch with event:(ui event *)event {

[super continueTrackingWithTouch:touch withEvent:event];

//獲取觸摸位置

CG point last point =[touch location in view:self];

//使用位置來設計句柄

[self move handle:last point];

//我們將在下壹節看到這個函數:

[self sendActionsForControlEvents:UIControlEventValueChanged];

返回YES

}

在上面的代碼中,locationInView用於獲取觸摸的位置,然後將該位置傳遞給moveHandle方法,該方法會將傳入的值轉換為有效的句柄位置。

這裏的“有效位置”是什麽意思?

這個控件的手柄只能在背景弧線定義的邊界內移動,但是我們不想強迫用戶在壹個小弧線內移動手柄。如果這樣的話,用戶體驗會很糟糕。

MoveHandle的任務是將任意位置值轉換成句柄的可移動值。另外,在這個函數中,指定的滑塊角度值也被轉換,代碼如下:

-(void)move handle:(CG point)last point {

//獲取中心

CGPoint center point = CGPointMake(self . frame . size . width/2,

self . frame . size . height/2);

//計算從中心點到任意位置的方向。

float current tangle = angle from north(center point,

最後壹點,

否);

int angle int = floor(current tangle);

//存儲新角度

self . angle = 360-angle int;

//更新文本字段

_ textfield . text =[ns string string with format:@ " % d ",

self . angle];

//重繪

[self set needs display];

}

在上面的代碼中,主要的任務實際上是在AngleFromNorth方法中處理的:根據兩點,會返回壹個連接兩點的角度關系,AngleFromNorth方法的實現如下:

靜態內嵌浮點AngleFromNorth(CGPoint p1,CGPoint p2,布爾值翻轉){

CG point v = CGPointMake(p2 . x-p 1 . x,p2 . y-p 1 . y);

float vmag = sqrt(SQR(v . x)+SQR(v . y)),結果= 0;

v . x/= vmag;

v . y/= vmag;

雙弧度= atan2(v.y,v . x);

result = ToDeg(弧度);

return(結果& gt=0 ?結果:結果+360.0);

}

提醒:angleFromNorth方法不是我獨創的。我直接從蘋果公司提供的OSX樣品時鐘控制。

在上面的代碼中,獲取角度值後,將其存儲在angle中,然後更新textfield的值。

接下來調用setNeedDisplay是為了確保調用drawRect,以便盡快更新界面。

1.2.3末端跟蹤

當跟蹤結束時,將調用以下方法:

-(void)endTrackingWithTouch:(ui touch *)touch with event:(ui event *)event {

[super endTrackingWithTouch:touch withEvent:event];

}

在本文中,我們不需要覆蓋這個方法。如果妳想在用戶完成控件的界面操作時做壹些處理,這個方法非常有用。

1.3目標-行動模式

此時,圓形滑塊控件可以工作了。您可以拖移控制柄,查看文本字段中值的變化。

發送動作控制事件

如果希望自定義的控件與UIControl的行為壹致,當控件的值發生變化時,需要通知它:使用sendActionsForControlEvents方法,並做壹個特定的事件類型。值更改對應的事件壹般為UIControlEventValueChanged。

蘋果預定義了很多事件類型(在Xcode中,cmd+鼠標點擊UIControlEventValueChanged)。如果您的控件繼承自UITextField,您可能會對UIControlEventDigitingDidBegin感興趣。如果妳想做壹個修飾動作,妳可以使用UIControlTouchUpInside。

如果您註意到,在本文前壹部分的continueTrackingWithTouch方法中,我們將sendActionsForControlEvents方法稱為:

[self sendActionsForControlEvents:UIControlEventValueChanged];

在這個處理之後,當控制值改變時,每個對象(觀察者-註冊事件)將被通知響應。