UIViewサブクラスでdrawRectをオーバーライドすることはほとんどありません。通常はlayer.contents
事前レンダリング画像を使用し、多くの場合、複数のサブレイヤーまたはサブビューを使用し、入力パラメーターに基づいてこれらを操作します。 IBがこれらのより複雑なビュースタックをレンダリングする方法はありますか?
@zisoftがprepareForInterfaceBuilder
を教えてくれてありがとう。 Interface Builderのレンダリングサイクルにはいくつかのニュアンスがあり、それが私の問題の原因であり、注目に値します...
-drawRect
を使用する必要はありませんUIButtonコントロールの状態に画像を設定すると機能します。いくつかのことを念頭に置くと、任意のレイヤースタックが機能するように見えます...
initWithFrame:
を使用..not initWithCoder
。 awakeFromNib
も呼び出されません。
init...
はセッションごとに1回だけ呼び出されますつまりファイルに変更を加えるたびに、再コンパイルごとに1回。 IBInspectableプロパティを変更すると、initは再度呼び出されません。しかしながら...
prepareForInterfaceBuilder
はプロパティが変更されるたびに呼び出されますこれは、すべてのIBInspectablesおよびその他の組み込みプロパティにKVOを設定するようなものです。最初に_setup
メソッドからのみinit..
メソッドを呼び出すことで、これを自分でテストできます。 IBInspectableを変更しても効果はありません。次に、prepareForInterfaceBuilder
にも呼び出しを追加します。ファラ!ランタイムコードはprepareForIB
メソッドを呼び出さないため、おそらく追加のKVOが必要になることに注意してください。これについての詳細は...
init...
は描画、レイヤーコンテンツの設定などが早すぎます少なくとも私のUIButton
サブクラスでは、[self setImage:img forState:UIControlStateNormal]
を呼び出してもIBでは効果がありません。 prepareForInterfaceBuilder
から、またはKVOフック経由で呼び出す必要があります。
効果のない変更を行っているときに混乱する可能性があります。ビルドログを確認します。
私は常にいくつかの異なるサポートプロセスでハングアップし、それらはマシン全体をダウンさせます。 Force Quit
を自由に適用します。
(更新:XCode6がベータ版からリリースされて以来、これは本当ではありませんでした。もうハングすることはほとんどありません)
[〜#〜] update [〜#〜]
prepareForInterfaceBuilder
メソッドはすべてのIBInspectable
プロパティを効果的にKVOするため、これは問題ありません。残念ながら、この動作が実行時に何らかの形でミラーリングされないため、手動のKVOが必要になります。以下の更新されたサンプルコードを参照してください。以下は、動作中のIBDesignable UIButton
サブクラスのサンプルコードです。 ~~KVOは関連するプロパティの変更をリッスンし、再描画をトリガーするため、prepareForInterfaceBuilder
は実際には必要ありません。~~ UPDATE:上記のポイント8を参照してください。
IB_DESIGNABLE
@interface SBR_InstrumentLeftHUDBigButton : UIButton
@property (nonatomic, strong) IBInspectable NSString *topText;
@property (nonatomic) IBInspectable CGFloat topTextSize;
@property (nonatomic, strong) IBInspectable NSString *bottomText;
@property (nonatomic) IBInspectable CGFloat bottomTextSize;
@property (nonatomic, strong) IBInspectable UIColor *borderColor;
@property (nonatomic, strong) IBInspectable UIColor *textColor;
@end
@implementation HUDBigButton
{
BOOL _isInterfaceBuilder;
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
[self _setup];
}
return self;
}
//---------------------------------------------------------------------
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self _setup];
}
return self;
}
//---------------------------------------------------------------------
- (void)_setup
{
// Defaults.
_topTextSize = 11.5;
_bottomTextSize = 18;
_borderColor = UIColor.whiteColor;
_textColor = UIColor.whiteColor;
}
//---------------------------------------------------------------------
- (void)prepareForInterfaceBuilder
{
[super prepareForInterfaceBuilder];
_isInterfaceBuilder = YES;
[self _render];
}
//---------------------------------------------------------------------
- (void)awakeFromNib
{
[super awakeFromNib];
if (!_isInterfaceBuilder) { // shouldn't be required but jic...
// KVO to update the visuals
@weakify(self);
[self
bk_addObserverForKeyPaths:@[@"topText",
@"topTextSize",
@"bottomText",
@"bottomTextSize",
@"borderColor",
@"textColor"]
task:^(id obj, NSDictionary *keyPath) {
@strongify(self);
[self _render];
}];
}
}
//---------------------------------------------------------------------
- (void)dealloc
{
if (!_isInterfaceBuilder) {
[self bk_removeAllBlockObservers];
}
}
//---------------------------------------------------------------------
- (void)_render
{
UIImage *img = [SBR_Drawing imageOfHUDButtonWithFrame:self.bounds
edgeColor:_borderColor
buttonTextColor:_textColor
topText:_topText
topTextSize:_topTextSize
bottomText:_bottomText
bottomTextSize:_bottomTextSize];
[self setImage:img forState:UIControlStateNormal];
}
@end
この答えはdrawRectのオーバーライドに関連していますが、おそらくいくつかのアイデアを与えることができます。
DrawRectに複雑な描画を持つカスタムUIViewクラスがあります。設計時に使用できない参照、つまりUIApplicationには注意する必要があります。そのために、prepareForInterfaceBuilder
をオーバーライドします。ここでは、ランタイムとデザインタイムを区別するためにdrawRectで使用するブールフラグを設定します。
@IBDesignable class myView: UIView {
// Flag for InterfaceBuilder
var isInterfaceBuilder: Bool = false
override init(frame: CGRect) {
super.init(frame: frame)
// Initialization code
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func prepareForInterfaceBuilder() {
self.isInterfaceBuilder = true
}
override func drawRect(rect: CGRect)
{
// rounded cornders
self.layer.cornerRadius = 10
self.layer.masksToBounds = true
// your drawing stuff here
if !self.isInterfaceBuilder {
// code for runtime
...
}
}
}
InterfaceBuilderでの外観は次のとおりです。
DrawRectを使用する必要はありません。代わりに、カスタムインターフェイスをxibファイルに作成し、initWithCoderおよびinitWithFrameにロードすると、IBDesignableを追加した後、IBでライブレンダリングが可能になります。この短いチュートリアルを確認してください: https://www.youtube.com/watch?v=L97MdpaF3Xg
LayoutSubviewsは最も単純なメカニズムだと思います。
Swiftの(非常に)簡単な例を次に示します。
@IBDesignable
class LiveLayers : UIView {
var circle:UIBezierPath {
return UIBezierPath(ovalInRect: self.bounds)
}
var newLayer:CAShapeLayer {
let shape = CAShapeLayer()
self.layer.addSublayer(shape)
return shape
}
lazy var myLayer:CAShapeLayer = self.newLayer
// IBInspectable proeprties here...
@IBInspectable var pathLength:CGFloat = 0.0 { didSet {
self.setNeedsLayout()
}}
override func layoutSubviews() {
myLayer.frame = self.bounds // etc
myLayer.path = self.circle.CGPath
myLayer.strokeEnd = self.pathLength
}
}
このスニペットをテストしたことはありませんが、以前にこのようなパターンを使用しました。初期構成を簡素化するために、計算プロパティに委任する遅延プロパティの使用に注意してください。
私の場合、2つの問題がありました。
カスタムビューでinitWithFrame
を実装しませんでした:(通常initWithCoder:
は、IB経由で初期化するときに呼び出されますが、何らかの理由でinitWithFrame:
はIBDesignable
にのみ必要です。 IB経由で実装する場合、実行時に呼び出されません)
カスタムビューのペン先はmainBundle
:からロードしていました[NSBundle bundleForClass:[self class]]
が必要でした。
Hari Karam Singhの答え について詳しく説明するために、このスライドショーではさらに説明します。
http://www.splinter.com.au/presentations/ibdesignable/
Interface Builderに変更が表示されない場合は、次のメニューを試してください。
残念ながら、私のビューをデバッグするとXcodeがフリーズしましたが、小さなプロジェクト(YMMV)で機能するはずです。
prepareForInterfaceBuilder
を実装し、そこでコアアニメーションを実行して、IBに表示できると思います。私はUIButtonのサブクラスでいくつかの派手なことをしました.UIButtonのサブクラスは独自のコアアニメーションレイヤーの作業を行い、境界線または背景を描画し、インターフェイスビルダーでうまくレンダリングしますので、UIViewを直接サブクラス化してからprepareForInterfaceBuilder
は、あなたが異なる方法で行う必要があるすべてです。ただし、メソッドはIBによってのみ実行されることに注意してください
要求に応じてコードを含めるように編集
私は似たようなものを持っていますが、これとはまったく異なります
class BorderButton: UIButton {
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
func commonInit(){
layer.borderWidth = 1
layer.borderColor = self.tintColor?.CGColor
layer.cornerRadius = 5
}
override func tintColorDidChange() {
layer.borderColor = self.tintColor?.CGColor
}
override var highlighted: Bool {
willSet {
if(newValue){
layer.backgroundColor = UIColor(white: 100, alpha: 1).CGColor
} else {
layer.backgroundColor = UIColor.clearColor().CGColor
}
}
}
}
initWithCoder
とinitWithFrame
の両方をオーバーライドします。これは、コードまたはIBでコンポーネントを使用できるようにしたいためです(他の回答状態として、initWithFrame
を実装して、 IBハッピー。
次に、commonInit
で、境界線を描画してきれいにするために、コアアニメーションをセットアップしました。
また、強調表示された変数にwillSet
を実装して背景色を変更します。これは、ボタンが境界線を描くときに嫌いですが、押されたときにフィードバックを提供しないためです(押されたボタンが押されていないボタンのように見える場合は嫌いです)
Swiftマクロ
#if TARGET_INTERFACE_BUILDER
#else
#endif
iBがストーリーボードをレンダリングするときに呼び出される関数を持つクラス
@IBDesignable
class CustomView: UIView
{
@IBInspectable
public var isCool: Bool = true {
didSet {
#if TARGET_INTERFACE_BUILDER
#else
#endif
}
}
override func prepareForInterfaceBuilder() {
// code
}
}
IBInspectableは以下のタイプで使用できます
Int, CGFloat, Double, String, Bool, CGPoint, CGSize, CGRect, UIColor, UIImage