【iOS】—— UIKit関連の問題

UIキット

#import <UIKit/UIKit.h> 

UIKit フレームワークは、iOS または tvOS アプリに必要なインフラストラクチャを提供します。これは、インターフェイスを実装するためのウィンドウとビューのアーキテクチャ、アプリにマルチタッチやその他のタイプの入力を提供するためのイベント処理インフラストラクチャ、およびユーザー、システム、およびユーザー間の対話を管理するために必要なメイン実行ループを提供します。アプリ。このフレームワークによって提供されるその他の機能には、アニメーション サポート、ドキュメント サポート、描画と印刷のサポート、現在のデバイスに関する情報、テキストの管理と表示、検索サポート、アクセシビリティ サポート、アプリ拡張機能のサポート、リソース管理などがあります。

一般的に使用される UIKit コンポーネント

  • UIView とそのサブクラス (UIScrollView (UITableView、UICollectionView、UITextView)、UILabel、UIControl (UIButton、UITextField)、UIPickerView など)。
  • UIViewController、UIImageなど、人には直感的に見えないコントロールに関わるグラフィックス、描画、印刷、テキストなどの構成と制御。

遅延読み込みの利点

遅延読み込み (遅延読み込み) では、オブジェクトのインスタンス化をできるだけ遅らせます。つまり、このリソースはアプリケーションの起動時には読み込まれず、実行時に使用されるときにのみ読み込まれます (オンデマンド読み込み)。

たとえば、APP の起動後に大量のデータ、写真、オーディオ、ビデオ リソースが一度に読み込まれると、モバイル デバイスのメモリが使い果たされる可能性があります。現時点では、遅延読み込みテクノロジを使用できます。具体的には、属性のgetterメソッドを書き換えて、まず属性がnilかどうかを判定し、空であればインスタンス化し、そうでなければ属性を直接返すようにします。

遅延読み込みの利点は、オブジェクトを作成するためのすべてのコードを viewDidLoad メソッドで記述する必要がなく、コードが読みやすいこと、各コントロールが独自のインスタンス化処理を担当し、コードの結合が低いことです。初期化フェーズですべてのオブジェクトをロードする必要はなく、データ、メモリの節約、つまりシステムのメモリ使用量が削減され、サーバー側の負荷が軽減されます。

CALayer と UIView

違い

UIViewから継承され、主にイベント送信、イベント応答を担当し、フレームベースUIResponderに属します。から継承され、画像レンダリング、アニメーション、ビュー表示を担当し、フレームワークに属しますUIKit
CALayerNSObjectQuartzCore

これら 2 つの内容は、単一責任の原則に準拠してい
ますCALayer

  • hitTest
  • convert

layerイベントにクリック イベントを追加するために、イベントがオンであるかどうかを判断する 2 つの方法

関係

  • すべてのインターフェイス要素は から継承しますUIView実際の描画部分はCALayerクラスによって管理されます。UIViewこれはマネージャーに似ておりCALayer、描画や座標に関連する属性 ( frameandなど) にアクセスすることは、実際には内部に含まれる関連属性にboundsアクセスすることになります。CALayer
  • UIViewlayerメインインスタンスを返すプロパティがありますCALayerメインで使用するクラスを返すメソッドUIViewがあり(デフォルトの戻り値は)、このメソッドをオーバーロードすることでサブクラスを異なる表示にすることができますlayerClasslayer[CALayer class]UIViewUIViewCALayer
  • UIViewCALayerの同様のUIViewサブツリー構造に、サブツリーviewを追加して、いくつかの特別な表現を完成させることもできます。layerlayer
    UIView *firstView = [[UIView alloc] init];
    firstView.frame = CGRectMake(200, 200, 200, 200);
    firstView.backgroundColor = [UIColor redColor];
    [self.view addSubview:firstView];
    
    CALayer *layer = [[CALayer alloc] init];
    layer.backgroundColor = [[UIColor greenColor] CGColor];
    layer.position = CGPointMake(100,100);  //中心点
    layer.bounds = CGRectMake(100,100,80,80);
    [firstView.layer addSublayer:layer];

新しいレイヤーは追加されませんが、新しいレイヤーが元のビューに追加されます。これは addsubview とは異なります。
ここに画像の説明を挿入

  • CALayer上向きに追加するのと同様に、ビュー構造に似たUIViewサブツリー構造をビュー構造viewlayer追加して、いくつかの特別な表現を完成させることができます。子layerViewView
  • UIVIewツリーlayer構造はシステム内にあり、システムによって維持されます。三份copy
    • 最初のもの、逻辑树、コードはその中で操作できます。たとえば、コードを介してレイヤーのプロパティを変更することは、【比如frame\bounds】このコードで操作できます。
    • 2 番目の部分 は动画树中間層であり、システムは属性を変更し、さまざまなレンダリング操作を実行します。
    • 3 番目の は显示树、現在画面に表示されているツリーの内容です。

UITableView

2 つのデリゲートの後に UITableView と実装する必要があるメソッド

  • UITableViewデータを表示するにはデータ ソース ( dataSource) が必要で、UITableViewデータ ソースが何行あるか、各行にどのようなデータが表示されるかなどをデータ ソースに問い合わせます。データ ソースを設定しない単なる空のシェルですUITableViewUITableViewDataSourceプロトコルに準拠する任意の OC オブジェクトをデータUITableViewソースにすることができます。
  • また、特定の行の選択など、特定のイベントがトリガーされたときに、対応する処理のためにUITableViewプロキシ オブジェクト ( delegate)を設定する必要があります。プロトコルUITableViewに準拠する任意の OC オブジェクト。どちらもプロキシ オブジェクトにUITableViewDelegateすることができ、通常はコントローラーをプロキシ オブジェクトとして機能させ、実装はプロトコル内のいくつかのメソッドを手動で実装することによって行われます。UITableViewUITableViewdataSourcedelegatetableView

データソース

//返回组数
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView

//返回每组里的行数
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 

//cell的实现
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 

代表者

//返回每行高度
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath

//不必须实现,cell点击事件
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath

上記 4 つはメソッドの実行順序を実装する必要があります

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    
    
    NSLog(@"numberOfSectionsInTableView");
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    
    
    NSLog(@"numberOfRowsInSection");
    return 1;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)
indexPath {
    
    
    UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cell"];
    NSLog(@"cellForRowAtIndexPath");
    return cell;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    
    
    NSLog(@"heightForRowAtIndexPath");
    return 20;
}

結果:

  • TableView のセクション数
  • セクションの行数
  • cellForRowAtIndexPath
  • インデックスパスの行の高さ

他のメソッドの実行順序:

最初のラウンド:
1.numberOfSectionsInTableView:section=2 の場合、この関数は 1 回だけ実行されます。section=0 の場合、関数は実行されません。デフォルトは 1 2、heightForHeaderInSection、2 回実行されます。
この関数の実行回数はセクション番号
3、heightForFooterInSection、関数のプロパティは上記と同じで、2 回実行されます
4、numberOfRowsInSection、このメソッドは 1 回実行されます
5、heightForHeaderInSection、このメソッドは 2 回実行されます、なぜこのメソッドがここで呼び出されるのか、実際には少し混乱しています
6、 heightForFooterInSection、このメソッドは 2 回実行されます。
7、numberOfRowsInSection、1 回実行されます。 8、
heightForRowAtIndexPath、行の高さ、最初に実行されます。 Section=0、対応する行の回数が 2 回目です:
1
、numberOfSectionsInTableView、1 回
2、heightForHeaderInSection、セクション時間
3、heightForFooterInSection、セクション4
、numberOfRowsInSection、1 回
5、heightForHeaderInSection、セクションを実行
6、heightForFooterInSection、セクションを実行
7、numberOfRowsInSection、1 回実行
8. heightForRowAtIndexPath、行の高さ、最初に 1 回実行します。
9. cellForRowAtIndexPath
10、willDisplayCell を
実行し、すべてのセルが描画されるまで 8、9、10 を順番に実行します。

UICollectionView と UITableView の違い

  • 各行には複数のより多様なレイアウトを表示できます
  • 1行で複数のビューを表示できるため、行を正確に表現できないため、項目を導入します。
  • すべてのビューは私たちだけが実装できます(カスタムセルを実装する必要があります)

カスタム スタイルに関する限り、レイアウトには UICollectionViewLayoutAttributes と呼ばれる属性があります。

@property (nonatomic) CGRect frame;
@property (nonatomic) CGPoint center;
@property (nonatomic) CGSize size;
@property (nonatomic) CATransform3D transform3D;
@property (nonatomic) CGRect bounds API_AVAILABLE(ios(7.0));
@property (nonatomic) CGAffineTransform transform API_AVAILABLE(ios(7.0));
@property (nonatomic) CGFloat alpha;
@property (nonatomic) NSInteger zIndex; // default is 0
@property (nonatomic, getter=isHidden) BOOL hidden; // As an optimization, UICollectionView might not create a view for items whose hidden attribute is YES
@property (nonatomic, strong) NSIndexPath *indexPath;

@property (nonatomic, readonly) UICollectionElementCategory representedElementCategory;
@property (nonatomic, readonly, nullable) NSString *representedElementKind; // nil when

表示される例UICollectionViewLayoutAttributesとしては、境界線、中心点、サイズ、形状、透明度、階層関係、非表示の有無などの情報が挙げられます。

各セルは 1 つに対応しますAttributes全体のロジックは、デフォルトを設定することでlayoutlayout各セルに対応する を設定し、Attributes全体を にlayout代入しますcollectionView

UICollectionViewFlowLayoutとUICollectionViewLayoutの違い

  • UICollectionViewLayoutこれは抽象クラスであり、サブクラスのいくつかの共通の属性と動作を定義するだけであり、これらを直接使用することはできません。
  • UICollectionViewFlowLayoutこれはフロー レイアウトであり、UI コントロールは流水のようになり、1 つの行がいっぱいになると、次の行が自動的に埋められます。

UICollectionViewFlowLayoutをカスタマイズした際に書き換えられるメソッド

-(void)prepareLayout
prepare方法被自动调用,以保证layout实例的正确。

-(CGSize)collectionViewContentSize
返回collectionView的内容的尺寸

-(NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
 1. 返回rect中的所有的元素的布局属性
 2. 返回的是包含UICollectionViewLayoutAttributes的NSArray
 3. UICollectionViewLayoutAttributes可以是cell,追加视图或装饰视图的信息,通过不同的UICollectionViewLayoutAttributes初始化方法可以得到不同类型的UICollectionViewLayoutAttributes:
  1)layoutAttributesForCellWithIndexPath:
  2)layoutAttributesForSupplementaryViewOfKind:withIndexPath:
  3)layoutAttributesForDecorationViewOfKind:withIndexPath:

-(UICollectionViewLayoutAttributes )layoutAttributesForItemAtIndexPath:(NSIndexPath )indexPath
返回对应于indexPath的位置的cell的布局属性

-(UICollectionViewLayoutAttributes )layoutAttributesForSupplementaryViewOfKind:(NSString )kind atIndexPath:(NSIndexPath *)indexPath
返回对应于indexPath的位置的追加视图的布局属性,如果没有追加视图可不重载

-(UICollectionViewLayoutAttributes * )layoutAttributesForDecorationViewOfKind:(NSString)decorationViewKind atIndexPath:(NSIndexPath )indexPath
返回对应于indexPath的位置的装饰视图的布局属性,如果没有装饰视图可不重载

-(BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
当边界发生改变时,是否应该刷新布局。如果YES则在边界变化(一般是scroll到其他地方)时,将重新计算需要的布局信息。

呼び出しシーケンス:

1-(void)prepareLayout  
  a. collection view 只会在第一次layout的时候调用一次`-prepareLayout`,作为第一次通知layout实例对象的消息
  b. collection view 会在 layout 对象 invalidated 之后并且requerying之前再次调用
  c. 继承自UICollectionViewLayout都需要重写这个方法,一般都是在这个方法里面准备好`layoutAttributesForElements(in:)`这个方法要使用到的`UICollectionViewLayoutAttributes`数组。
  
2)  -(CGSize) collectionViewContentSize 
  a. 确定collectionView的所有内容的尺寸
  b. 每一次移动collection view时都会调用这个方法,并且这个方法会被调用多次

3-(NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
  初始的layout的外观将由该方法返回的UICollectionViewLayoutAttributes来决定。

4)在需要更新layout时,需要给当前layout发送 
     1)-invalidateLayout, 该消息会立即返回,并且预约在下一个loop的时候刷新当前layout
     2)-prepareLayout,
     3)依次再调用-collectionViewContentSize和-layoutAttributesForElementsInRect来生成更新后的布局。

行の高さを手動で計算する 2 つの方法

  • フィットするサイズ
  • 境界RectWithSize

sizeToFit と sizeThatFit の違い:

  • -(CGSize)sizeThatFits:(CGSize)size;最適なサイズを返しますが、元のビューのフレームのサイズは変更されません。
  • -(void)sizeToFit;- (CGSize)sizeThatFits:(CGSize)size; メソッドは、最適なサイズを取得するために内部的に呼び出され、このサイズを使用して現在のビューのフレームのサイズを調整します。

簡単に言うと、両方- (CGSize)sizeThatFits:(CGSize)size;または- (void)sizeToFit;最適なサイズを取得しますが、- (CGSize)sizeThatFits:(CGSize)size;元の画像のフレームは変更せず、サイズを返すだけですが、- (void)sizeToFit;最適なサイズを計算し、このサイズに従って現在のサイズを調整します。ビューのフレーム

境界RectWithSize

テキスト描画が占める長方形のスペースを返します。

CGRect rect=[(NSString *)obj boundingRectWithSize:CGSizeMake(1000, FONTHEIGHT) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{
    
    NSFontAttributeName:[UIFont systemFontOfSize:16]} context:nil].size.width;

内部のパラメータは次のとおりです。

  • obj計算して表示する文字列を指します
  • boundingRectWithSize計算された幅と高さの制約を示します
  • 高さを計算するときは、幅を固定する必要があります。CGSizeMake(1000, CGFLOAT_MAX)
  • ここでの 1000 は、決定されたコントロールの幅に置き換えることもできます。self.label.width
  • 計算結果は、幅が最大 1000、高さが無制限の場合、完全な文字列を表示するために必要な高さを示します。
  • 幅を計算するときは、高さを固定する必要があります。CGSizeMake(CGFLOAT_MAX, 200)
  • 同様に、200 を既知の高さに置き換えることもできます。self.label.height
  • 計算の結果、高さが 200 を超えない場合、指定された文字列を実現するために必要な幅が完全に必要であることがわかります。
  • optionsテキスト描画の追加オプションであり、NSStringDrawingUsesLineFragmentOriginデフォルトのベースラインです
  • attributes辞書形式。文字列の表示スタイルを制限し、通常は次のようなより多くのフォントを制限します。@{NSFontAttributeName:[UIFont systemFontOfSize:16]}
  • context単語の間隔や拡大縮小を調整する方法などの情報が含まれます。最終的に、このオブジェクトにはテキストの描画に使用される情報が含まれます。通常はnilを書きます。

ViewControllerのライフサイクル

[iOS] - ViewControllerのライフサイクル

  • loadView: ビューをロードします。このメソッドでは、View は正式にロードされ、コントローラー ビューは遅延ロードによってロードされます。つまり、使用時にロードされます。ビューの読み込みプロセスでは、loadView メソッドが最初に呼び出され、いくつかの主要なビュー初期化タスクは主にこのメソッドで完了します。
  • viewWillAppear: ビューが表示されます
  • viewWillLayoutSubviews: コントローラーのビューにはサブコントロールがレイアウトされます
  • viewDidLayoutSubviews: コントローラーのビューレイアウトサブコントロールが完成しました
  • viewDidAppear: すでに表示されているビュー
  • viewWillDisappear: ビューが消えます
  • viewDidDisappear: ビューが消えました

UIViewControllerとUIResponder

私たちが最もよく知っているクラスUIApplicationUIView、 、は から直接継承されておりこのクラスは、ユーザー操作に応答し、さまざまなイベントを処理するために特別に使用されます ( )。UIViewControllerUIResponderUIResponderUIEvent

UIResponderユーザーのクリック、押下検出 ( presses)、およびジェスチャ検出 ( ) のコールバック メソッドを提供します。これらは、それぞれユーザーの開始、移動、終了、およびキャンセルに対応します。キャンセル イベントは、プログラムが強制終了されるか、呼び出しが受信された場合にのみ呼び出されます。motion

UIコントロール

UIControlこのビューに基づいて、より対話的なサポートが追加されました。最も重要なのは、target / actionモードが追加されることです。Date pickers特定のサブクラスを見ると、ボタン、日付ピッカー ( )、テキストボックスなどを見ることができます。インタラクティブ コントロールを作成するときは、通常、 1 つをサブクラス化する必要がありますUIControlbar buttons( もサポートされていますがtarget / action) や(ここでは通知を取得するためにプロキシを使用する必要があります)などの一部の一般的なクラスは でtextViewはありませんUIControl

UIレスポンダー

UIResponderUIViewはの親クラスです。responderタッチ、ジェスチャ、リモコンなどのイベントを処理できます。これが別のクラスであり、マージされない理由は、より多くのサブクラス (最も顕著なのは と )があるUIViewためですメソッドをオーバーライドすることにより、クラスが現在の入力フォーカス要素などのファーストレスポンダ ( ) になれるかどうかを判断できます。UIResponderUIApplicationUIViewControllerUIResponderfirst responder

touches(タッチ) や(一連のモーション センサーを参照)motionなどのインタラクションが発生すると、ファーストレスポンダ (通常はビュー) に送信されます。ファースト レスポンダーによって処理されない場合、アクションはレスポンダー チェーンを遡ってビュー コントローラーに到達し、それでもアクションが処理されない場合は、アプリに渡されます。シェイク ジェスチャを監視したい場合は、必要に応じてこれら 3 つのレイヤーのどこでも処理できます。

UIResponderまた、inputAccessoryViewキーボードへのセカンダリ ビューの追加から、inputView完全なカスタム キーボードの提供まで、カスタム入力メソッドも可能です。

フレームと境界

  • Frame: ビューの位置とサイズには親ビューの座標系が使用されるため、ビューを親に配置することが重要です。
  • Bounds: 独自の座標系を使用したビューの位置とサイズ。このためには、ビューのコンテンツまたはサブビューをビュー自体内に配置することが重要です。
    ここに画像の説明を挿入

まずビューを初期化して、次のことを確認しましょう。

    UIView *firstView = [[UIView alloc] init];
    firstView.frame = CGRectMake(200, 200, 200, 200);
    firstView.backgroundColor = [UIColor redColor];
    [self.view addSubview:firstView];
    NSLog(@"frame = %@\n", NSStringFromCGRect(firstView.frame));
    NSLog(@"bounds = %@", NSStringFromCGRect(firstView.bounds));

ここに画像の説明を挿入
次に、ビューを 90 度回転して次を確認します。

firstView.transform = CGAffineTransformMakeRotation(M_PI * 0.25);

ここに画像の説明を挿入
Boundsまだ同じですが、変化が生じていることがわかりますFrameframeboundsの違いがわかりやすくなりました。
画像の説明を追加してください

いつフレームを使用するか、いつ境界を使用するか

  • frame関連付けられたビューはスーパービュー内で位置するため、幅を変更したり、ビューとスーパービューの上部の間の距離を調べたりするなど、外側に変更を加えるときにこれを使用します
  • を使用するとbounds、何かを描画したり、ビュー内にサブビューを配置したりするなど、内側に向かって変更することになります。ビューに何らかの変換を行った場合に、ビューのサイズを取得するために使用することもできますbounds

おすすめ

転載: blog.csdn.net/m0_62386635/article/details/131974018