這個分為三大塊,第一快是簡化Binary Data,具體是App Store那邊搞,開發者不用管,第二個是Slicing,就是下載程序的時候會自動下載你圖片組里面符合設備分辨率的圖片,例如ip6只會下載x3的圖片。第三個叫On Demand Resource。就是你可以給你的資源打上標簽,然后規定哪些在程序下載的時候一同下載的,哪些可以被額外的下載命令觸發,比如一個大型游戲你可以先讓第一章隨程序下載,然后第二章在檢測到用戶快玩玩完的時候再下…看起來挺瘦身的,但要是用戶當時沒網絡怎么辦?
成都創新互聯公司是一家專業從事網站制作、成都做網站的網絡公司。作為專業的建站公司,成都創新互聯公司依托的技術實力、以及多年的網站運營經驗,為您提供專業的成都網站建設、營銷型網站建設及網站設計開發服務!
在開發過程中,有時候UIKit的標準控件并不能滿足我們的需求,例如你需要一個控件能支持用戶方便的選擇0-360°之間的一個角度值,此時就需要根據自己的需求自定義控件了。
對于選擇角度值的控件可以這樣實現:創建一個圓形的滑塊,用戶通過拖動手柄操作就能選擇角度值。實際上這樣的控件在別的一些平臺中你可能看到過,但是在UIKit中并沒有。
本文就實現一個選擇角度值的控件來介紹控件的自定義。下面先來看看到底要做成什么樣子:
1. 子類化UIControl
UIControl 是UIView的子類,它又是所有UIKit控件的父類(例如UIButton、UISlider和UISwitch等)。
UIControl的主要作用是創建相應的邏輯將action分發到對應的target,另外90%的情況下,它會根據自身的狀態(例如Highlighted, Selected和Disabled等)來繪制用戶界面。
通過UIControl,我們主要管理3個重要的任務:
繪制用戶界面
跟蹤用戶的操作
Target-Action模式
在本文的圓形滑塊中,我們要做如下一些事情:
定制一個用戶界面(圓形滑塊本身),通過該界面用戶可以通過手柄進行界面交互。用戶的交互操作會被轉換為控件target對應的action(控件將滑塊按鈕的frame origin轉換為0-360之間的一個值,并用于target/action上)。
建議在學習本文的時候從文章尾部的連接中下載完整的示例工程。
下面我將從上面列出的3個重要任務一一進行分解介紹。
這些步驟都是模塊化的,所以如果你對界面的繪制不感興趣,可以跳 繪制用戶界面 ,直接學習后面的步驟。
打開工程文件中的 TBCircluarSlider.m 文件。然后開始學習下面的內容。
1.1 繪制用戶界面
我比較喜歡使用Core Graphics,唯一用到UIKit的就是通過textfield來顯示滑塊的值。
提醒 :此處需要用到一些 Core Graphics 知識,如果你不懂也沒多大關系,我會盡量把代碼做詳細的講解。
我們先來看看控件的不同組成部分,這樣更有利于后面的學習。
首先,是用一個 黑色的圓環 當做滑塊的背景。
可操作區域(active area) 是一個從藍色到紫色的梯度漸變效果。
用戶通過拖拽下面的這個手柄按鈕來選擇值:
最后,用于顯示選中值的 TextField 。在下一版中,我計劃讓用戶可以通過鍵盤輸入角度值。
控件界面的繪制主要使用drawRect函數,首選我們需要獲取到當前使用的圖形上下文,如下代碼所示:
CGContextRef ctx = UIGraphicsGetCurrentContext();
1.1.1 繪制背景
背景是360°的,所以只要用CGContextAddArc給圖形上下文添加正確的path,并設置正確的stroke即可。
下面的代碼可以就可以完成背景的繪制:
//Add the arc path
CGContextAddArc(ctx, self.frame.size.width/2, self.frame.size.height/2, radius, 0, M_PI *2, 0);
//Set the stroke colour
[[UIColor blackColor]setStroke];
//set Line width and cap
CGContextSetLineWidth(ctx, TB_BACKGROUND_WIDTH);
CGContextSetLineCap(ctx, kCGLineCapButt);
//draw it!
CGContextDrawPath(ctx, kCGPathStroke);
CGContextArc 函數的參數包括圖形上下文,弧度的中心坐標點,以及半徑(是一個私有變量),接著是弧度開始和結束時的角度(在TBCircularSlider.m文件的頭部可以看到一些關于數學計算的方法),最后一個參數標示繪制的方向,0表示逆時針方向。
接下來的3行的代碼是用來設置一些信息的,例如顏色和線條寬度等。最后使用 CGContextDrawPath 方法完成背景的繪制。
1.1.2 繪制用戶的可操作區域
這部分需要利用一點小技巧才行。此處我們繪制一個線性漸變的掩碼圖片,下面看看原理:
此處掩碼圖片的工作原理是可以看到原始漸變矩形框的一個孔。
在這里繪制的弧度有一個陰影,這是創建掩碼圖時使用了一點模糊的效果。
下面是創建掩碼圖的相關代碼:
UIGraphicsBeginImageContext(CGSizeMake(320,320));
CGContextRef imageCtx = UIGraphicsGetCurrentContext();
CGContextAddArc(imageCtx, self.frame.size.width/2 , self.frame.size.height/2, radius, 0, ToRad(self.angle), 0);
[[UIColor redColor]set];
//Use shadow to create the Blur effect
CGContextSetShadowWithColor(imageCtx, CGSizeMake(0, 0), self.angle/20, [UIColor blackColor].CGColor);
//define the path
CGContextSetLineWidth(imageCtx, TB_LINE_WIDTH);
CGContextDrawPath(imageCtx, kCGPathStroke);
//save the context content into the image mask
CGImageRef mask = CGBitmapContextCreateImage(UIGraphicsGetCurrentContext());
UIGraphicsEndImageContext();
在上面的代碼中首先創建了一個圖形上下文,然后設置了一下陰影。通過 CGContextSetShadowWithColor 方法,我們可以設置如下內容:
上下文
偏移量(此處不需要)
模糊值(該值是通過參數控制的:使用當前的角度除以20,當用戶與此控件交互時,以此獲得一個簡單的動畫模糊值)
顏色
接著是根據當前的角度繪制一個相應的弧度。
例如,如果當前的角度變量是360°,那么就繪制一個圓弧,如果是90°,就繪制一個弧度為90°的一個弧。最后,利用 CGBitmapContextCreateImage 方法獲取一張圖片(剛剛繪制的弧)。這個圖片就是我們所需要的掩碼圖了。
裁剪上下文:
現在我們已經有一個漸變的掩碼圖了。接著利用函數 CGContextClipToMask 對上下文進行裁剪——給該函數傳入上面剛剛創建好的掩碼圖。代碼如下所示:
CGContextClipToMask(ctx, self.bounds, mask);
最后我們來繪制漸變效果,代碼如下所示:
//Define the colour steps
CGFloat components[8] = {
0.0, 0.0, 1.0, 1.0, // Start color - Blue
1.0, 0.0, 1.0, 1.0 }; // End color - Violet
CGColorSpaceRef baseSpace = CGColorSpaceCreateDeviceRGB();
CGGradientRef gradient = CGGradientCreateWithColorComponents(baseSpace, components, NULL, 2);
//Define the gradient direction
CGPoint startPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMinY(rect));
CGPoint endPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMaxY(rect));
//Choose a colour space
CGColorSpaceRelease(baseSpace), baseSpace = NULL;
//Create and Draw the gradient
CGContextDrawLinearGradient(ctx, gradient, startPoint, endPoint, 0);
CGGradientRelease(gradient), gradient = NULL;
繪制漸變效果需要很多處理,不過我們可以將其分為4部分:
定義顏色的變化范圍
定義漸變的方向
選擇顏色空間
創建并繪制漸變
最終的顯示效果(看到漸變矩形框的一部分)要歸功于之前創建的掩碼圖。
另外,為了在背景邊框模擬光線反射,我添加了一些燈光效果。
1.1.3 繪制手柄
下面我們根據當前的角度值,在的正確位置繪制出手柄。
實際上,在繪制過程中,這一步非常簡單,復雜一點的就是計算一下手柄所在的位置。
這里我們需要使用三角函數將一個 標量值(scalar number) 轉換為 CGPoint 。不要擔心有多復雜,只需要使用Sin和Cos函數就可以完成。代碼如下所示:
-(CGPoint)pointFromAngle:(int)angleInt{
//Define the Circle center
CGPoint centerPoint = CGPointMake(self.frame.size.width/2 - TB_LINE_WIDTH/2, self.frame.size.height/2 - TB_LINE_WIDTH/2);
//Define The point position on the circumference
CGPoint result;
result.y = round(centerPoint.y + radius * sin(ToRad(-angleInt))) ;
result.x = round(centerPoint.x + radius * cos(ToRad(-angleInt)));
return result;
}
上面的代碼中,指定一個角度值,然后計算出在圓周上面的位置,當然,這里需要圓周的中心點和半徑。
使用sin函數在使用sin函數時,需要一個Y坐標值,而cos函數則需要X坐標值。
需要注意的是此處每個函數返回的值都認為半徑為1,所以需要將所得結果乘以我們指定的半徑大小,并相對于圓周的中心做計算。
希望下面的公式對你的理解有所幫助:
1
2
point.y = center.y + (radius * sin(angle));
point.x = center.x + (radius * cos(angle));
通過上面的計算,現在我們已經知道手柄的具體位置了,所以,接下來就直接將手柄繪制到指定位置即可,如下代碼所示:
-(void) drawTheHandle:(CGContextRef)ctx{
CGContextSaveGState(ctx);
//I Love shadows
CGContextSetShadowWithColor(ctx, CGSizeMake(0, 0), 3, [UIColor blackColor].CGColor);
//Get the handle position!
CGPoint handleCenter = [self pointFromAngle: self.angle];
//Draw It!
[[UIColor colorWithWhite:1.0 alpha:0.7]set];
CGContextFillEllipseInRect(ctx, CGRectMake(handleCenter.x, handleCenter.y, TB_LINE_WIDTH, TB_LINE_WIDTH));
CGContextRestoreGState(ctx);
}
具體操作步驟如下:
保存當前的上下文(當在一個單獨的函數中進行繪制任務時,將上下文的狀態進行保存是編程的一個好習慣)。
給手柄設置一些陰影效果
定義手柄的顏色,然后利用 CGContextFillEllipseInRect 將其繪制出來。
我們在drawRect函數的最后調用上面這個方法:
1
[self drawTheHandle:ctx];
至此,我們就完成了繪制部分的任務。
1.2 跟蹤用戶的操作
在UIControl的子類中,我們可以 override 3個特殊的方法來提供一個自定義的跟蹤行為
1.2.1 開始跟蹤
當在控件的bound內發生了一個觸摸事件,首先會調用控件的 beginTrackingWithTouch 方法。
我們就看看如何 override 這個方法吧:
-(BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event{
[super beginTrackingWithTouch:touch withEvent:event];
//We need to track continuously
return YES;
}
該函數返回的BOOl值決定著:當觸摸事件是dragged時,是否需要響應。在我們這里的自定義控件中,是需要跟蹤用戶的dragging,所以返回YES。
上面這個函數有兩個參數:touch對象和事件。
1.2.2 持續跟蹤
在上一個方法中我們指定了這里的自定義控件需要跟蹤一個持續的事件,所以當用戶進行drag時,會調用一個特殊的方法: continueTrackingWithTouch :
-(BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event
該方法返回的BOOL值標示是否繼續跟蹤touch事件。
通過該方法我們可以根據touch位置對用戶的操作進行過濾。例如,我們可以:僅當touch位置與手柄位置相交的時候才激活控件(activate control)。不過在這里我們的控制邏輯并不是這樣的,我們希望用戶點擊任何位置都能對手柄做出相應的位置處理。
本文的該方法負責更新手柄的位置(在后面的一節中會看到我們把該位置信息傳遞給對應的target上)。
對上面這個方法的override代碼如下所示:
-(BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event{
[super continueTrackingWithTouch:touch withEvent:event];
//Get touch location
CGPoint lastPoint = [touch locationInView:self];
//Use the location to design the Handle
[self movehandle:lastPoint];
//We'll see this function in the next section:
[self sendActionsForControlEvents:UIControlEventValueChanged];
return YES;
}
上面的代碼中,首先利用 locationInView 獲取到touch的位置,然后將該位置傳遞給 moveHandle 方法,該方法會將傳入的值轉換為一個有效的手柄位置(a valid handle position)。
此處“a valid position”的意思是什么呢?
此控件的手柄只能在背景圓弧定義的邊界范圍內做移動,但是我們不希望強制要求用戶必須在很小的圓弧內才可以移動手柄,如果非要這樣的話,用戶體驗會非常的糟糕。
moveHandle 的任務就是負責把任意的位置值轉變為手柄可移動的值,另外,另外,在該函數中,還對指定的滑塊角度值做了轉換,代碼如下所示:
-(void)movehandle:(CGPoint)lastPoint{
//Get the center
CGPoint centerPoint = CGPointMake(self.frame.size.width/2,
self.frame.size.height/2);
//Calculate the direction from the center point to an arbitrary position.
float currentAngle = AngleFromNorth(centerPoint,
lastPoint,
NO);
int angleInt = floor(currentAngle);
//Store the new angle
self.angle = 360 - angleInt;
//Update the textfield
_textField.text = [NSString stringWithFormat:@"%d",
self.angle];
//Redraw
[self setNeedsDisplay];
}
上面代碼中,實際上主要任務都是在 AngleFromNorth 方法中處理的:根據兩個point,就會返回一個連接這兩點對應的一個角度關系, AngleFromNorth 方法的實現如下所示:
static inline float AngleFromNorth(CGPoint p1, CGPoint p2, BOOL flipped) {
CGPoint v = CGPointMake(p2.x-p1.x,p2.y-p1.y);
float vmag = sqrt(SQR(v.x) + SQR(v.y)), result = 0;
v.x /= vmag;
v.y /= vmag;
double radians = atan2(v.y,v.x);
result = ToDeg(radians);
return (result =0 ? result : result + 360.0);
}
提醒: angleFromNorth 方法并不是我的原創,我是直接從蘋果提供的OSX示例clockControl中拿過來用的。
在上面的代碼中,獲得了角度值以后,將其存儲到 angle 中,然后更新一下textfield的值。
接著調用的 setNeedDisplay 是為了確保 drawRect 被調用,以盡快在界面上做出相應的更新。
1.2.3 結束跟蹤
當跟蹤結束的時候,會調用下面這個方法:
-(void)endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event{
[super endTrackingWithTouch:touch withEvent:event];
}
在本文中,我們并不需要override該方法。如果當用戶完成控件的界面操作時,你希望做一些處理,那么該方法會非常有用。
1.3 Target-Action模式
至此,圓形滑塊控件可以工作了,你可以drag手柄,并能看到textfield中值的改變。
發送action——控件事件
如果希望自己定制的控件與UIControl行為保持一致,那么當控件的值發生變化時,需要進行通知處理:使用 sendActionsForControlEvents 方法,并制定特定的事件類型,值改變對應的事件一般是 UIControlEventValueChanged 。
蘋果已經預定義了許多事件類型(Xcode中,在UIControlEventValueChanged上 cmd + 鼠標單擊 )。如果你的控件是繼承自UITextField,那么你可能會對 UIControlEventEdigitingDidBegin 感興趣,如果你要做一個touch Up action,那么可以使用UIControlTouchUpInside。
如果你注意的話,在本文前部分的continueTrackingWithTouch方法里面,我們調用了 sendActionsForControlEvents 方法:
[self sendActionsForControlEvents:UIControlEventValueChanged];
這樣處理之后,當控件值發生變化時,每一個對象(觀察者——注冊該事件)都會收到響應的通知。
所以正常的做法應該是在initWithFrame:方法中創建子控件,注意此時子控件有可能只是一個局部變量,所以想要在layoutSubviews訪問到的話,一般需要創建這個子控件的對應屬性來指向它。
@property (nonatomic, weak) UIButton *button; // 注意這里使用weak就可以,因為button已經被加入到self.view.subviews這個數組里。
...
- (instancetype)initWithFrame: (CGRect)frame
{
if (self = [super initWithFrame: frame]) {
UIButton *button = ... // 創建一個button
[button setTitle: ...] // 設置button的屬性
[self.view addSubview: button]; // 將button加到view中,并不設置尺寸
self.button = button; //將self.button指向這個button保證在layoutSubviews中可以訪問
UILabel *label = ... // 其他的子控件同理
}
}
這樣我們就可以在layoutSubviews中訪問子控件,設置子控件的尺寸,因為此時view的frame已經確定。
- (void)layoutSubviews
{
[super layoutSubviews]; // 注意,一定不要忘記調用父類的layoutSubviews方法!
self.button.frame = ... // 設置button的frame
self.label.frame = ... // 設置label的frame
}
經過以上的步驟,就可以實現自定義控件。
你試試看啊
是有規律變化的。
IOS開發針對不同的屏幕尺寸(包括不同設備以及同設備的橫豎版)的UI布局退出了AutoLayout功能,基本原理就是:你給控件添加一些提條件(比如,距離頂部多少距離,距離左邊框多少距離,距離右邊框多少距離,本身控件的寬高值,等等條件),當屏幕尺寸變化后,系統會自動根據你設定的這些條件來修改控件的尺寸以及位置,達到你想要的顯示效果。
AutoLayout是IOS6推出的,之前關于適應屏幕部分的叫AutoSize,關于AutoLayou部分有很多的教程,你可以在搜索詳細了解下。
你做過表格頁面的開發嗎?
在網上看到兩個實現demo,可以說是兩種方式實現的。
大致就是頂部的航標題用一個TopCollectionView封裝實現,底部的表格使用JContentTableView封裝實現,tableViewCell里是一個collectionView,每一行有一個collectionView,稍微自己處理下數據源還是可以使用的。
大致就是自己封裝了一個view,視圖由LeftTableView,RightTableView,TopScrollView實現。
RightTableViewCell里是scrollView,使用的按鈕實現的表格的效果。看完這個實現還是不推薦吧。
暫時只是學習了下實現,后續有啥問題更新吧~~~
網站名稱:ios開發控件傾斜,ios開發控件傾斜怎么辦
文章轉載:http://vcdvsql.cn/article2/dsdigoc.html
成都網站建設公司_創新互聯,為您提供虛擬主機、電子商務、靜態網站、網站改版、服務器托管、域名注冊
聲明:本網站發布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創新互聯