現在、複数のタイマーを使用するアプリを作成していますが、これらは基本的にすべて同じです。
タイマーとレイアウト/アニメーションのすべてのコードを使用するカスタムクラスを作成するため、互いに独立して動作する5つの同一のタイマーを使用できます。
IB(xcode 4.2)を使用してレイアウトを作成しましたが、現在、タイマーのコードはすべてviewcontrollerクラスにあります。
すべてをカスタムクラスにカプセル化し、それをViewControllerに追加する方法について頭を包むのが困難です。どんな助けでも大歓迎です。
概念的に答えると、タイマーはおそらくUIView
ではなくNSObject
のサブクラスである必要があります。
IBでタイマーのインスタンスをインスタンス化するには、単にUIView
をドラッグしてView Controllerのビューにドロップし、そのクラスをタイマーのクラス名に設定します。
View Controllerでタイマークラスを#import
することを忘れないでください。
編集:IBデザインの場合(コードのインスタンス化の場合は改訂履歴を参照)
私はストーリーボードにまったく精通していませんが、ストーリーボードバージョンの使用とほぼ同じ.xib
ファイルを使用して、IBでインターフェイスを構築できることを知っています。ビュー全体を既存のインターフェイスから.xib
ファイルにコピーアンドペーストすることもできます。
これをテストするために、「MyCustomTimerView.xib」という名前の新しい空の.xib
を作成しました。次に、ビューを追加し、それにラベルと2つのボタンを追加しました。そのようです:
「MyCustomTimer」という名前のUIView
をサブクラス化する新しいObjective-Cクラスを作成しました。 .xib
でFile's OwnerクラスをMyCustomTimerに設定します。これで、他のビュー/コントローラーと同様に、アクションとアウトレットを自由に接続できます。結果の.h
ファイルは次のようになります。
@interface MyCustomTimer : UIView
@property (strong, nonatomic) IBOutlet UILabel *displayLabel;
@property (strong, nonatomic) IBOutlet UIButton *startButton;
@property (strong, nonatomic) IBOutlet UIButton *stopButton;
- (IBAction)startButtonPush:(id)sender;
- (IBAction)stopButtonPush:(id)sender;
@end
ジャンプする唯一のハードルは、UIView
サブクラスでこの.xib
を取得することです。 .xib
を使用すると、必要なセットアップが劇的に削減されます。また、ストーリーボードを使用してタイマーをロードしているため、-(id)initWithCoder:
のみが呼び出される初期化子であることがわかります。実装ファイルは次のようになります。
#import "MyCustomTimer.h"
@implementation MyCustomTimer
@synthesize displayLabel;
@synthesize startButton;
@synthesize stopButton;
-(id)initWithCoder:(NSCoder *)aDecoder{
if ((self = [super initWithCoder:aDecoder])){
[self addSubview:
[[[NSBundle mainBundle] loadNibNamed:@"MyCustomTimerView"
owner:self
options:nil] objectAtIndex:0]];
}
return self;
}
- (IBAction)startButtonPush:(id)sender {
self.displayLabel.backgroundColor = [UIColor greenColor];
}
- (IBAction)stopButtonPush:(id)sender {
self.displayLabel.backgroundColor = [UIColor redColor];
}
@end
loadNibNamed:owner:options:
という名前のメソッドは、まさにそのように聞こえます。ペン先をロードし、「File's Owner」プロパティをselfに設定します。配列の最初のオブジェクトを抽出します。これは、ペン先のルートビューです。ビューをサブビューとして追加し、画面に表示します。
明らかに、これはボタンが押されたときにラベルの背景色を変更するだけですが、この例ではうまくいくはずです。
コメントに基づく注記:
無限の再帰問題が発生している場合、おそらくこのソリューションの微妙なトリックを逃したことに注意してください。あなたが思っていることをやっていません。ストーリーボードに配置されているビューは表示されませんが、代わりにサブビューとして別のビューをロードします。ロードするビューは、nibで定義されているビューです。ペン先の「ファイルの所有者」は、その見えないビューです。クールな部分は、この見えないビューがまだnibから取り込むビューのソートのビューコントローラとして使用できるObjective-Cクラスであることです。たとえば、IBAction
クラスのMyCustomTimer
メソッドは、ビューよりもView Controllerで期待されるものです。
サイドノートとして、これがMVC
を壊すと主張する人もいるかもしれませんが、私はいくらか同意します。私の観点から見ると、カスタムのUITableViewCell
にもっと密接に関係しています。これは、一部コントローラーである必要がある場合もあります。
また、この答えは非常に具体的な解決策を提供することであったことも注目に値します。ストーリーボードにレイアウトされた同じビューで複数回インスタンス化できる1つのペン先を作成します。たとえば、これらのタイマーのうち6つがすべてiPadの画面に一度に表示されることを簡単に想像できます。アプリケーション全体で複数回使用されるView Controllerのビューのみを指定する必要がある場合は、 この質問に対してjyavenardが提供するソリューション がほぼ間違いなくあなたにとってより良いソリューションです。
Xcode 10およびSwift 4向けに更新
基本的な手順を次に示します。私はもともとこれをたくさん見ました このYoutubeビデオシリーズ 。後で この記事 に基づいて回答を更新しました。
次の2つのファイルがカスタムビューを形成します。
UIView
サブクラスとしての.Swiftファイルそれらを追加するための詳細は以下です。
Xibファイル
プロジェクトに.xibファイルを追加します(File> New> File ...> User Interface> View)。私はReusableCustomView.xib
と呼んでいます。
カスタムビューに含めるレイアウトを作成します。例として、UILabel
とUIButton
を使用してレイアウトを作成します。自動レイアウトを使用して、後でどのサイズに設定しても自動的にサイズが変更されるようにすることをお勧めします。 (属性メトリックスのxibサイズにFreeformを使用して、シミュレートされたメトリックを調整できるようにしましたが、これは必要ありません。)
Swiftファイル
プロジェクトに.Swiftファイルを追加します(File> New> File ...> Source> Swift File)。これはUIView
のサブクラスであり、私はReusableCustomView.Swift
と呼んでいます。
import UIKit
class ResuableCustomView: UIView {
}
。xibファイルに戻り、ドキュメントアウトラインの[ファイルの所有者]をクリックします。 Identity Inspectorで、カスタムクラス名として。Swiftファイルの名前を記述します。
ReusableCustomView.Swift
ファイルの内容を次のコードに置き換えます。
import UIKit
@IBDesignable
class ResuableCustomView: UIView {
let nibName = "ReusableCustomView"
var contentView:UIView?
@IBOutlet weak var label: UILabel!
@IBAction func buttonTap(_ sender: UIButton) {
label.text = "Hi"
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
func commonInit() {
guard let view = loadViewFromNib() else { return }
view.frame = self.bounds
self.addSubview(view)
contentView = view
}
func loadViewFromNib() -> UIView? {
let bundle = Bundle(for: type(of: self))
let nib = UINib(nibName: nibName, bundle: bundle)
return nib.instantiate(withOwner: self, options: nil).first as? UIView
}
}
.xibファイルの名前のスペルが正しいことを確認してください。
XibレイアウトのラベルとボタンからSwiftカスタムビューコードにコントロールをドラッグして、アウトレットとアクションを接続します。
カスタムビューが完成しました。必要なことは、メインストーリーボードのどこにでもUIView
を追加することです。 Identity Inspectorでビューのクラス名をReusableCustomView
に設定します。
View Controllerに対する回答、ビューではない:
ストーリーボードからxibをロードする簡単な方法があります。コントローラーがUIViewController
を継承するMyClassControllerタイプであるとします。
ストーリーボードでIBを使用してUIViewController
を追加します。クラスの種類をMyClassControllerに変更します。ストーリーボードに自動的に追加されたビューを削除します。
呼び出すXIBの名前がMyClassController.xibであることを確認してください。
ストーリーボードのロード中にクラスがインスタンス化されると、xibが自動的にロードされます。この理由は、クラス名で指定されたXIBを呼び出すUIViewController
のデフォルト実装によるものです。
これは本当の答えではありませんが、このアプローチを共有することは有益だと思います。
Objective-C
Swift
オプション:
以上で、カスタムビューをストーリーボードに追加でき、表示されます:)
CustomViewWithXib.h:
#import <UIKit/UIKit.h>
/**
* All classes inherit from CustomViewWithXib should have the same xib file name and class name (.h and .m)
MyCustomView.h
MyCustomView.m
MyCustomView.xib
*/
// This allows seeing how your custom views will appear without building and running your app after each change.
IB_DESIGNABLE
@interface CustomViewWithXib : UIView
@end
CustomViewWithXib.m:
#import "CustomViewWithXib.h"
@implementation CustomViewWithXib
#pragma mark - init methods
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
// load view frame XIB
[self commonSetup];
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
// load view frame XIB
[self commonSetup];
}
return self;
}
#pragma mark - setup view
- (UIView *)loadViewFromNib {
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
// An exception will be thrown if the xib file with this class name not found,
UIView *view = [[bundle loadNibNamed:NSStringFromClass([self class]) owner:self options:nil] firstObject];
return view;
}
- (void)commonSetup {
UIView *nibView = [self loadViewFromNib];
nibView.frame = self.bounds;
// the autoresizingMask will be converted to constraints, the frame will match the parent view frame
nibView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
// Adding nibView on the top of our view
[self addSubview:nibView];
}
@end
CustomViewWithXib.Swift:
import UIKit
@IBDesignable
class CustomViewWithXib: UIView {
// MARK: init methods
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonSetup()
}
override init(frame: CGRect) {
super.init(frame: frame)
commonSetup()
}
// MARK: setup view
private func loadViewFromNib() -> UIView {
let viewBundle = NSBundle(forClass: self.dynamicType)
// An exception will be thrown if the xib file with this class name not found,
let view = viewBundle.loadNibNamed(String(self.dynamicType), owner: self, options: nil)[0]
return view as! UIView
}
private func commonSetup() {
let nibView = loadViewFromNib()
nibView.frame = bounds
// the autoresizingMask will be converted to constraints, the frame will match the parent view frame
nibView.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
// Adding nibView on the top of our view
addSubview(nibView)
}
}
いくつかの例を見つけることができます こちら 。
お役に立てば幸いです。