重写UIView的方法,自定义view

  • Post author:
  • Post category:其他


回忆一下,这么个场景。
我们在一个界面上,要排列多个相同的元素。你马上就可以想到:
1.如果要用按钮,可以使用来

UIButton

布局。
如:

– (

void

) showSectionListbyCount:(

float

)totalCount everyColCount:(

float

)paramColCount{






int

curNum=

1

;






int

TotalRow=

ceilf

(totalCount/paramColCount);

//


总行数






for

(

int

curRow=

0

; curRow








for

(

int

curCol=

0

; curCol

//


列数














if

(totalCount










return

;







}
















UIButton


*btn=[


UIButton

buttonWithType

:

UIButtonTypeCustom

];







[btn

setFrame

:

CGRectMake

(

10

+(

67

+

10

)*curCol, (

30

+

10

)*curRow,

67

,

30

)];














[btn

setTitle

:[


NSString

stringWithFormat

:


@”%d”


,curNum]

forState

:

UIControlStateNormal

];








[btn

setBackgroundImage

:[


UIImage

imageNamed

:


@”btn_tv_n”


]

forState

:

UIControlStateNormal

];








[btn

setBackgroundImage

:[


UIImage

imageNamed

:


@”btn_tv_h”


]

forState

:

UIControlStateHighlighte

d

];








[btn

setTitleColor

:[


UIColor

blackColor

]

forState

:

UIControlStateNormal

];








[btn

setTitleColor

:[


UIColor

whiteColor

]

forState

:

UIControlStateHighlighte

d

];








[btn

addTarget

:


self

action

:


@selector


(ClickControlAction:)

forControlEvents

:

UIControlEventTouchUpIns

ide

];













[btn

setTag

:curNum];













[

self

.

view


addSubview

:btn];







curNum++;











}







}

}

如果,我们想用表格,那么每个元素可以使用

UITableViewCell


来定义相近的项。

很简单是吧,因为,xCode,为我们提供了这些UI控件供我们使用!所以,我们只需要用!

那么,今天的问题也就来了!如果,我想搞一个能够满足自己的一些UI元素。别急,也就是这样:

比如,我要搞这么一个界面!


重写UIView-让我们随心所欲的定制属于自己的UI控件

我想,首先可以肯定,苹果不可能为我量身定制了这么一个控件。并且,如果,我用苹果的默认控件来写,那么我肯定会崩溃的。。

也就是说,我们将每一项,写成单独的一个UI控件。那么,可以根据数据进行填充!那就酷毙了!

绕了,一大圈,也就是要引用出来今天要学习的,东西!



自定义UI!!!



说起来,很牛X。其实很简单,说白了,就是子类化(重写)


UIView




我们常常重写,UIButton,UIImageView,UITableView 等等。因为,可能我们用到的跟这些已有控件很相似,稍微修改一些就能够满足我们。

但是,出入大的时候,我们就不能简单的继承于一个已有控件了!

那就是完全重写!就是子类化


UIView




开始动手吧!!!



1.定义一

UIView


个的子类。



这个不解释!






2.重写


drawRect


方法。或者重写


layoutSubviews


方法、







1.



drawRect方法:




– (

void

)drawRect:(

CGRect

)rect

{





// Drawing code




for

(

int

i=

0

; i<<span style=”color: #2f2fd0″>10; i++) {








UIButton


*btn=[


UIButton

buttonWithType

:

UIButtonTypeCustom

];





[btn

setFrame

:

CGRectMake

(

10

,

10

+i*

40

,

67

,

30

)];






[btn

setBackgroundImage

:[


UIImage

imageNamed

:


@”btn_n”


]

forState

:

UIControlStateNormal

];






[btn

setBackgroundImage

:[


UIImage

imageNamed

:


@”btn_h”


]

forState

:

UIControlStateHighlighte

d

];






[btn

addTarget

:


self

action

:


@selector


(ClickControlAction:)

forControlEvents

:

UIControlEventTouchUpIns

ide

];





[btn

setTag

:i];









[

self


addSubview

:btn];



}



}



2.


layoutSubviews 方法


– (

void

)layoutSubviews{






[


super

layoutSubviews

];






[


self

setBackgroundColor

:[


UIColor

clearColor

]];




[


_imgPic

setFrame

:

CGRectMake

(


7


,


6


,


86


,


108


)];




[


_imgPic

setImage

:[


UIImage

imageNamed

:[


self


.


dictData

objectForKey

:


@”img_content”


]]];




[


_imgPic


.


layer

setShouldRasterize

:


NO


];




[


_imgPic


.


layer

setBorderColor

: [[


UIColor

whiteColor

]

CGColor

]];




[


_imgPic


.


layer

setBorderWidth

:


1.0


];




[[


_imgPic

layer

]

setShadowRadius

:


2


];




[[


_imgPic

layer

]

setShadowOpacity

:


1


];




[[


_imgPic

layer

]

setShadowColor

:[


UIColor

blackColor

].


CGColor


];




[[


_imgPic

layer

]

setShadowOffset

:

CGSizeMake

(


0


,


1


)];



[

self


addSubview

:

self

.

imgPic

];



}

可见,这两种方法都可以的。具体的区别,就是

drawRect


方法和


layoutSubviews


方法之间的区别了。







但是,经过测试,我发现,应该使用




drawRect




方法,来完成界面的绘制。因为,如果我们使用的是




layoutSubviews




方法,那么每一次改变frame的时候,




layoutSubviews




都将重新执行。显然不科学。





在网上,我看到,大部分例子使用的是

drawRect方法,来重绘界面。


(绘制界面)



我使用了


layoutSubviews方法。在frame改变时,会自动调用。


(重定向子视图)


根据需要选择使用哪一种方法!




关于两种方法,在网上搜了一下,总结如下:





layoutSubviews在以下情况下会被调用:

1、init初始化不会触发layoutSubviews。

2、addSubview会触发layoutSubviews。

3、设置view的Frame会触发layoutSubviews,当然前提是frame的值设置前后发生了变化。

4、滚动一个UIScrollView会触发layoutSubviews。

5、旋转Screen会触发父UIView上的layoutSubviews事件。

6、改变一个UIView大小的时候也会触发父UIView上的layoutSubviews事件。

7、直接调用setLayoutSubviews。



drawRect在以下情况下会被调用:

1、如果在UIView初始化时没有设置rect大小,将直接导致drawRect不被自动调用。drawRect 掉用是在Controller->loadView,

Controller->viewDidLoad

两方法之后掉用的.所以不用担心在 控制器中,这些View的drawRect就开始画了.这样可以在控制器中设置一些值给View(如果这些View

draw的时候需要用到某些变量 值).

2、该方法在调用sizeToFit后被调用,所以可以先调用sizeToFit计算出size。然后系统自动调用drawRect:方法。

3、通过设置contentMode属性值为UIViewContentModeRedraw。那么将在每次设置或更改frame的时候自动调用drawRect:。

4、直接调用setNeedsDisplay,或者setNeedsDisplayInRect:触发drawRect:,但是有个前提条件是rect不能为0。

以上1,2推荐;而3,4不提倡



drawRect方法使用注意点:

1、 若使用UIView绘图,只能在drawRect:方法中获取相应的contextRef并绘图。如果在其他方法中获取将获取到一个invalidate 的ref并且不能用于画图。drawRect:方法不能手动显示调用,必须通过调用setNeedsDisplay

或 者

setNeedsDisplayInRect,让系统自动调该方法。

2、若使用calayer绘图,只能在drawInContext:

中(类似于drawRect)绘制,或者在delegate中的相应方法绘制。同样也是调用setNeedDisplay等间接调用以上方法

3、若要实时画图,不能使用gestureRecognizer,只能使用touchbegan等方法来掉用setNeedsDisplay实时刷新屏幕



3.自定义UIView的初始化方法。

我们常常在

UIViewController


中,看到


initWithNibName


方法:

– (

id

)initWithNibName:(

NSString

*)nibNameOrNil bundle:(

NSBundle

*)nibBundleOrNil

{




self

= [

super


initWithNibName

:nibNameOrNil

bundle

:nibBundleOrNil];




if

(

self

) {





// coding



}





return self

;

}




这是xCode给我们定义好的初始化方法。



既然,我们要自定义UIView,那么最好,根据自己的业务逻辑,来完成子类化UIView的初始化方法:

#pragma mark – Initialization 根据业务逻辑,传入数据,初始化自己

– (

id

)initItemWithFram:(

CGRect

)frame andData:(

NSMutableDictionary

*)paramData{






self

= [

super


initWithFrame

:frame];




if

(

self

) {







// Initialization code








_dictData


=[[

NSMutableDictionary

alloc


]


init


];






_imgPic

=[[

UIImageView


alloc

]

init

];






_imgCover

=[[

UIImageView


alloc

]

init

];






_btnClick

=[[

UIButton


alloc

]

init

];






_btnDel

=[[

UIButton


alloc

]

init

];






_lblTips

=[[

UILabel


alloc

]

init

];






_lblName

=[[

UILabel


alloc

]

init

];







}




self

.

dictData

=paramData;





return self

;





}

+ (

id

) initItemWithFrame:(

CGRect

)frame andData:(

NSMutableDictionary

*)paramData{




WTVTVCollectionItem

*tmpInstance=[[

WTVTVCollectionItem


alloc

]

initItemWithFram

:frame

andData

:paramData];




return

tmpInstance;

}




这样,就可以初始化:



NSMutableArray

*items = [

NSMutableArray

array


];





[items

addObject

:[

WTVTVCollectionItem


initItemWithFrame

:

CGRectMake

(

10

,

10

,

100

,

150

)

andData

:dict]];



[items

addObject

:[

WTVTVCollectionItem


initItemWithFrame

:

CGRectMake

(

10

,

10

,

100

,

150

)

andData

:dict]];



[items

addObject

:[

WTVTVCollectionItem


initItemWithFrame

:

CGRectMake

(

10

,

10

,

100

,

150

)

andData

:dict]];






4.为UIView加入一些动画





动画是UI操作的灵魂!给予动画效果,就看上去有神了。




添加动画,一般也用2种方法:






1.重写UIView的方法,在调用时,进行动画操作。




# pragma mark –

重写父类的

removeFromSuperview

方法,在删除时,使用相应的动画效果

– (

void

) removeFromSuperview {






[


UIView

animateWithDuration

:


0.2

animations

:^{






self

.

alpha

=

0.0

;





[

self


setFrame

:

CGRectMake

(

self

.

frame

.

origin

.

x

+

50

,

self

.

frame

.

origin

.

y

+

50

,

0

,

0

)];





[

_btnDel


setFrame

:

CGRectMake

(

0

,

0

,

0

,

0

)];



}

completion

:^(

BOOL

finished) {






[


super

removeFromSuperview

];



}];

}






2.直接在UIView元素上,进行动画对象的添加




– (

void

) enableEditing {








if


(


self


.

isInEditingMode

==


YES


)






return

;







//

设置为编辑状态






self


.

isInEditingMode

=


YES


;



[

_btnDel


setHidden

:

NO

];







//

开始晃动动画




CATransform3D

transform;





//

获取

0



x-1

之间的整数




if

(

arc4random

() %

2

==

1

)





transform =

CATransform3DMakeRotatio

n

(-

0.08

,

0

,

0

,

1.0

);




else





transform =

CATransform3DMakeRotatio

n

(

0.08

,

0

,

0

,

1.0

);







CABasicAnimation

*animation = [

CABasicAnimation

animationWithKeyPath


:


@”transform”


];



animation.

toValue

= [

NSValue


valueWithCATransform3D

:transform];



animation.

autoreverses

=

YES

;



animation.

duration

=

0.1

;



animation.

repeatCount

=

10000

;



animation.

delegate

=

self

;



[[

self


layer

]

addAnimation

:animation

forKey

:

@”wiggleAnimation”

];




[(

WTVTVSprintBoard

*)


self


.

delegate

enableEditingMode


];



}



5.子类化UIView的交互



如何将操作链接起来!?就是说,在控件外,对我们自定义的控件进行操作?!

也有2种方法:

1.消息

2.代理





总结:



我想,我过多的将思路就可以了,因为业务需求不同,控件的自定义需求也不同,所以,不能用具体来固定!但是,思路是想通的,知道了原理,我们就可以发挥我们的聪明智慧,创造出千变万化的控件来!

希望对你有所帮助!!!

回忆一下,这么个场景。

我们在一个界面上,要排列多个相同的元素。你马上就可以想到:
1.如果要用按钮,可以使用来

UIButton

布局。
如:

– (

void

) showSectionListbyCount:(

float

)totalCount everyColCount:(

float

)paramColCount{






int

curNum=

1

;






int

TotalRow=

ceilf

(totalCount/paramColCount);

//


总行数






for

(

int

curRow=

0

; curRow








for

(

int

curCol=

0

; curCol

//


列数














if

(totalCount










return

;







}
















UIButton


*btn=[


UIButton

buttonWithType

:

UIButtonTypeCustom

];







[btn

setFrame

:

CGRectMake

(

10

+(

67

+

10

)*curCol, (

30

+

10

)*curRow,

67

,

30

)];














[btn

setTitle

:[


NSString

stringWithFormat

:


@”%d”


,curNum]

forState

:

UIControlStateNormal

];








[btn

setBackgroundImage

:[


UIImage

imageNamed

:


@”btn_tv_n”


]

forState

:

UIControlStateNormal

];








[btn

setBackgroundImage

:[


UIImage

imageNamed

:


@”btn_tv_h”


]

forState

:

UIControlStateHighlighte

d

];








[btn

setTitleColor

:[


UIColor

blackColor

]

forState

:

UIControlStateNormal

];








[btn

setTitleColor

:[


UIColor

whiteColor

]

forState

:

UIControlStateHighlighte

d

];








[btn

addTarget

:


self

action

:


@selector


(ClickControlAction:)

forControlEvents

:

UIControlEventTouchUpIns

ide

];













[btn

setTag

:curNum];













[

self

.

view


addSubview

:btn];







curNum++;











}







}

}

如果,我们想用表格,那么每个元素可以使用

UITableViewCell


来定义相近的项。

很简单是吧,因为,xCode,为我们提供了这些UI控件供我们使用!所以,我们只需要用!

那么,今天的问题也就来了!如果,我想搞一个能够满足自己的一些UI元素。别急,也就是这样:

比如,我要搞这么一个界面!


重写UIView-让我们随心所欲的定制属于自己的UI控件

我想,首先可以肯定,苹果不可能为我量身定制了这么一个控件。并且,如果,我用苹果的默认控件来写,那么我肯定会崩溃的。。

也就是说,我们将每一项,写成单独的一个UI控件。那么,可以根据数据进行填充!那就酷毙了!

绕了,一大圈,也就是要引用出来今天要学习的,东西!



自定义UI!!!



说起来,很牛X。其实很简单,说白了,就是子类化(重写)


UIView




我们常常重写,UIButton,UIImageView,UITableView 等等。因为,可能我们用到的跟这些已有控件很相似,稍微修改一些就能够满足我们。

但是,出入大的时候,我们就不能简单的继承于一个已有控件了!

那就是完全重写!就是子类化


UIView




开始动手吧!!!



1.定义一

UIView


个的子类。



这个不解释!






2.重写


drawRect


方法。或者重写


layoutSubviews


方法、







1.



drawRect方法:




– (

void

)drawRect:(

CGRect

)rect

{





// Drawing code




for

(

int

i=

0

; i<<span style=”color: #2f2fd0″>10; i++) {








UIButton


*btn=[


UIButton

buttonWithType

:

UIButtonTypeCustom

];





[btn

setFrame

:

CGRectMake

(

10

,

10

+i*

40

,

67

,

30

)];






[btn

setBackgroundImage

:[


UIImage

imageNamed

:


@”btn_n”


]

forState

:

UIControlStateNormal

];






[btn

setBackgroundImage

:[


UIImage

imageNamed

:


@”btn_h”


]

forState

:

UIControlStateHighlighte

d

];






[btn

addTarget

:


self

action

:


@selector


(ClickControlAction:)

forControlEvents

:

UIControlEventTouchUpIns

ide

];





[btn

setTag

:i];









[

self


addSubview

:btn];



}



}



2.


layoutSubviews 方法


– (

void

)layoutSubviews{






[


super

layoutSubviews

];






[


self

setBackgroundColor

:[


UIColor

clearColor

]];




[


_imgPic

setFrame

:

CGRectMake

(


7


,


6


,


86


,


108


)];




[


_imgPic

setImage

:[


UIImage

imageNamed

:[


self


.


dictData

objectForKey

:


@”img_content”


]]];




[


_imgPic


.


layer

setShouldRasterize

:


NO


];




[


_imgPic


.


layer

setBorderColor

: [[


UIColor

whiteColor

]

CGColor

]];




[


_imgPic


.


layer

setBorderWidth

:


1.0


];




[[


_imgPic

layer

]

setShadowRadius

:


2


];




[[


_imgPic

layer

]

setShadowOpacity

:


1


];




[[


_imgPic

layer

]

setShadowColor

:[


UIColor

blackColor

].


CGColor


];




[[


_imgPic

layer

]

setShadowOffset

:

CGSizeMake

(


0


,


1


)];



[

self


addSubview

:

self

.

imgPic

];



}

可见,这两种方法都可以的。具体的区别,就是

drawRect


方法和


layoutSubviews


方法之间的区别了。







但是,经过测试,我发现,应该使用




drawRect




方法,来完成界面的绘制。因为,如果我们使用的是




layoutSubviews




方法,那么每一次改变frame的时候,




layoutSubviews




都将重新执行。显然不科学。





在网上,我看到,大部分例子使用的是

drawRect方法,来重绘界面。


(绘制界面)



我使用了


layoutSubviews方法。在frame改变时,会自动调用。


(重定向子视图)


根据需要选择使用哪一种方法!




关于两种方法,在网上搜了一下,总结如下:





layoutSubviews在以下情况下会被调用:

1、init初始化不会触发layoutSubviews。

2、addSubview会触发layoutSubviews。

3、设置view的Frame会触发layoutSubviews,当然前提是frame的值设置前后发生了变化。

4、滚动一个UIScrollView会触发layoutSubviews。

5、旋转Screen会触发父UIView上的layoutSubviews事件。

6、改变一个UIView大小的时候也会触发父UIView上的layoutSubviews事件。

7、直接调用setLayoutSubviews。



drawRect在以下情况下会被调用:

1、如果在UIView初始化时没有设置rect大小,将直接导致drawRect不被自动调用。drawRect 掉用是在Controller->loadView,

Controller->viewDidLoad

两方法之后掉用的.所以不用担心在 控制器中,这些View的drawRect就开始画了.这样可以在控制器中设置一些值给View(如果这些View

draw的时候需要用到某些变量 值).

2、该方法在调用sizeToFit后被调用,所以可以先调用sizeToFit计算出size。然后系统自动调用drawRect:方法。

3、通过设置contentMode属性值为UIViewContentModeRedraw。那么将在每次设置或更改frame的时候自动调用drawRect:。

4、直接调用setNeedsDisplay,或者setNeedsDisplayInRect:触发drawRect:,但是有个前提条件是rect不能为0。

以上1,2推荐;而3,4不提倡



drawRect方法使用注意点:

1、 若使用UIView绘图,只能在drawRect:方法中获取相应的contextRef并绘图。如果在其他方法中获取将获取到一个invalidate 的ref并且不能用于画图。drawRect:方法不能手动显示调用,必须通过调用setNeedsDisplay

或 者

setNeedsDisplayInRect,让系统自动调该方法。

2、若使用calayer绘图,只能在drawInContext:

中(类似于drawRect)绘制,或者在delegate中的相应方法绘制。同样也是调用setNeedDisplay等间接调用以上方法

3、若要实时画图,不能使用gestureRecognizer,只能使用touchbegan等方法来掉用setNeedsDisplay实时刷新屏幕



3.自定义UIView的初始化方法。

我们常常在

UIViewController


中,看到


initWithNibName


方法:

– (

id

)initWithNibName:(

NSString

*)nibNameOrNil bundle:(

NSBundle

*)nibBundleOrNil

{




self

= [

super


initWithNibName

:nibNameOrNil

bundle

:nibBundleOrNil];




if

(

self

) {





// coding



}





return self

;

}




这是xCode给我们定义好的初始化方法。



既然,我们要自定义UIView,那么最好,根据自己的业务逻辑,来完成子类化UIView的初始化方法:

#pragma mark – Initialization 根据业务逻辑,传入数据,初始化自己

– (

id

)initItemWithFram:(

CGRect

)frame andData:(

NSMutableDictionary

*)paramData{






self

= [

super


initWithFrame

:frame];




if

(

self

) {







// Initialization code








_dictData


=[[

NSMutableDictionary

alloc


]


init


];






_imgPic

=[[

UIImageView


alloc

]

init

];






_imgCover

=[[

UIImageView


alloc

]

init

];






_btnClick

=[[

UIButton


alloc

]

init

];






_btnDel

=[[

UIButton


alloc

]

init

];






_lblTips

=[[

UILabel


alloc

]

init

];






_lblName

=[[

UILabel


alloc

]

init

];







}




self

.

dictData

=paramData;





return self

;





}

+ (

id

) initItemWithFrame:(

CGRect

)frame andData:(

NSMutableDictionary

*)paramData{




WTVTVCollectionItem

*tmpInstance=[[

WTVTVCollectionItem


alloc

]

initItemWithFram

:frame

andData

:paramData];




return

tmpInstance;

}




这样,就可以初始化:



NSMutableArray

*items = [

NSMutableArray

array


];





[items

addObject

:[

WTVTVCollectionItem


initItemWithFrame

:

CGRectMake

(

10

,

10

,

100

,

150

)

andData

:dict]];



[items

addObject

:[

WTVTVCollectionItem


initItemWithFrame

:

CGRectMake

(

10

,

10

,

100

,

150

)

andData

:dict]];



[items

addObject

:[

WTVTVCollectionItem


initItemWithFrame

:

CGRectMake

(

10

,

10

,

100

,

150

)

andData

:dict]];






4.为UIView加入一些动画





动画是UI操作的灵魂!给予动画效果,就看上去有神了。




添加动画,一般也用2种方法:






1.重写UIView的方法,在调用时,进行动画操作。




# pragma mark –

重写父类的

removeFromSuperview

方法,在删除时,使用相应的动画效果

– (

void

) removeFromSuperview {






[


UIView

animateWithDuration

:


0.2

animations

:^{






self

.

alpha

=

0.0

;





[

self


setFrame

:

CGRectMake

(

self

.

frame

.

origin

.

x

+

50

,

self

.

frame

.

origin

.

y

+

50

,

0

,

0

)];





[

_btnDel


setFrame

:

CGRectMake

(

0

,

0

,

0

,

0

)];



}

completion

:^(

BOOL

finished) {






[


super

removeFromSuperview

];



}];

}






2.直接在UIView元素上,进行动画对象的添加




– (

void

) enableEditing {








if


(


self


.

isInEditingMode

==


YES


)






return

;







//

设置为编辑状态






self


.

isInEditingMode

=


YES


;



[

_btnDel


setHidden

:

NO

];







//

开始晃动动画




CATransform3D

transform;





//

获取

0



x-1

之间的整数




if

(

arc4random

() %

2

==

1

)





transform =

CATransform3DMakeRotatio

n

(-

0.08

,

0

,

0

,

1.0

);




else





transform =

CATransform3DMakeRotatio

n

(

0.08

,

0

,

0

,

1.0

);







CABasicAnimation

*animation = [

CABasicAnimation

animationWithKeyPath


:


@”transform”


];



animation.

toValue

= [

NSValue


valueWithCATransform3D

:transform];



animation.

autoreverses

=

YES

;



animation.

duration

=

0.1

;



animation.

repeatCount

=

10000

;



animation.

delegate

=

self

;



[[

self


layer

]

addAnimation

:animation

forKey

:

@”wiggleAnimation”

];




[(

WTVTVSprintBoard

*)


self


.

delegate

enableEditingMode


];



}



5.子类化UIView的交互



如何将操作链接起来!?就是说,在控件外,对我们自定义的控件进行操作?!

也有2种方法:

1.消息

2.代理





总结:



我想,我过多的将思路就可以了,因为业务需求不同,控件的自定义需求也不同,所以,不能用具体来固定!但是,思路是想通的,知道了原理,我们就可以发挥我们的聪明智慧,创造出千变万化的控件来!

希望对你有所帮助!!!



版权声明:本文为li_yangyang_li原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。