【ReactNative】FlastListがスクロールできなくなるissueに取り組んだログ
@ggtmtmggです。
FlatList
の小要素にmargintTop: '5%'
とかを指定するとスクロールできなくなる問題に、
@saitoxuさんと取り組んだ学びのメモです。
前回までのメモはこちら https://gist.github.com/ggtmtmgg/9aaea0c6ddb1819623e3ef83e44e3b51
今回は主にObjective-Cのコードを読んでました。Objective−Cに関して初心者同然なのでそれに関する学びが多かったです。
- Isuue: marginTop=percent: FlatList does not scroll #20410
- Demo: https://github.com/ggtmtmgg/TestReactNative/tree/20410/0.57
Issueの再定義
元々は「FlatList
の小要素にmargintTop: '5%'
とかを指定するとスクロールできなくなる問題」だったのですが、問題の根源を追っていった結果、「ScrollView
がcontentHeight
を取るタイミングで小要素のheight
を正しく取れていない問題」に帰着しました。
これがどのようにしては導き出されたかは割愛します。
思考のフロー
前回までの思考で、RCTScrollContentShadowView
の中で、ScrollViewのContent
のLayout(要するにHeight)の計算をしているという認識を持っています。
このLayoutの計算を正しくすればIssueは解決するというのがこの瞬間の一番の仮説です。
今気になっていることを整理
RCTScrollContentShadowView
の実装はどうなっているのかScrollView
のConetntHeight
は本当に間違っているのかScrollView
のContentHeight
が間違っていることによってスクロールできなくなるという仮説が本当に正しいのか再調査したい
ScrollView
のConetntHeight
はいつ確定するのかContentHeight
が間違っているのであれば実際に計算して値が確定する瞬間を見たい
ScrollView
のConetntHeight
はどういった計算式で確定するのかContentHeight
が間違っているのであれば実際にどんな計算をしているのか知りたい
RCTScrollContentShadowView
の実装を覗いていく
"shadow"は"follow and observe (someone) closely and secretly"という意味で使っているっぽい。にしてもShadowViewの比喩はもっと腹落ちできそう。
ざっくりRCTScrollContentShadowView
は、ScrollView
のContent部分の裏側で計算とかしてくれるViewという認識。
RCTScrollContentShadowView
はRCTShadowView
を継承している。
@implementation RCTScrollContentShadowView - (void)layoutWithMetrics:(RCTLayoutMetrics)layoutMetrics layoutContext:(RCTLayoutContext)layoutContext { ... [super layoutWithMetrics:layoutMetrics layoutContext:layoutContext]; } @end
[super layoutWithMetrics:layoutMetrics layoutContext:layoutContext]
でScrollContentのLayoutoの計算をしてそう。
今でこそ簡単に読めるけどこのコードは[インスタンス名 メソッド名:第一引数 引数名:名前付き引数]
という意味でインスタンスメソッドを実行している。
super
はRCTShadowView
なのそちらを見に行く
RCTShadowView
の実装を読み解く
https://github.com/facebook/react-native/tree/master/React/Views/RCTShadowView.m
RCTShadowView+Layout.m
ってファイル見かけたけどなんだろう。UIEdgeInsets
ってクラスについて- UIKitの構造体。insetというレイアウトの概念があるらしい。
- yogaConfigはシングルトンらしい ref: ObCのシングルトンの話
+ (YGConfigRef)yogaConfig { static YGConfigRef yogaConfig; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ yogaConfig = YGConfigNew(); YGConfigSetPointScaleFactor(yogaConfig, RCTScreenScale()); YGConfigSetUseLegacyStretchBehaviour(yogaConfig, true); }); return yogaConfig; }
- Yoga.cppのL3957に必要のなさそうな分岐を発見
// We store points for Pixel as we will use it for rounding if (pixelsInPoint == 0.0f) { // Zero is used to skip rounding config->pointScaleFactor = 0.0f; } else { config->pointScaleFactor = pixelsInPoint; }
↓ 以下の一行だけで良さそう
config->pointScaleFactor = pixelsInPoint;
- RCTShadowView.mのL22
meta_prop_t
という列挙型を作っている。enumの使い方として参考になった ref: Modern Objective-Cのenumの書き方
typedef NS_ENUM(unsigned int, meta_prop_t) { META_PROP_LEFT, META_PROP_TOP, META_PROP_RIGHT, META_PROP_BOTTOM, META_PROP_START, META_PROP_END, META_PROP_HORIZONTAL, META_PROP_VERTICAL, META_PROP_ALL, META_PROP_COUNT, };
RCTShadowView.m L200
(__bridge void *)self
はどういう記法だろうRCTComponent.hにプルリクのチャンスを発見。RCT系インスタンスがReactRootViewであるかどうかの判定がイマイチみたい。
// TODO: this is kinda dumb - let's come up with a // better way of identifying root React views please! static inline BOOL RCTIsReactRootView(NSNumber *reactTag) { return reactTag.integerValue % 10 == 1; }
- objcにはid型がある
Objective-Cでは「id」型という型が定義されています。このid型は汎用的な型なのでどんな型でもセットすることができます。(JavaでいうObject型のようなものです。) (id型は内部的には対象オブジェクトへのポインタを持っています。) このid型も参照型の1つですが、id型に限り宣言時に「*」(アスタリスク)は不要です。
今回よくわからなくて調べたこと
- @synthesizeってアノテーションはなんだろう
- @propertyを使うと、コンパイル時に勝手にゲッターとセッターを定義してくれる
- @synthesizeは、ヘッダーファイルで@propertyとして定義したプロパティをどのメンバ変数に適用するかを示すのに使う
- 要するに@propertyではゲッターセッター名、@synthesizeでは実際のメンバ変数を指定する
- ちなみに、synthesizeは合成するという意味
- deallocってメソッドはなんだろう
- NSObjectのインスタンスメソッド
- TipsとしてObjective-Cの全てのオブジェクトはNSObjectを継承している
- インスタンスが破棄される時に呼ばれると予想される。自信はない。
- deallocはオブジェクトの参照の開放のために使う
- C系のメモリの管理をもう少し理解してからまたしらべます。
- ARCってなんだろう
- Automatic Reference Countingの略
- NSObjectのretain/release/autoreleaseをいい感じにしてくれる
無名カテゴリってなんだろう
関数ではなくマクロをつかっている理由はなんだろう
- 結果よくわからなかったです
#define RCT_SET_YGVALUE(ygvalue, setter, ...) \ switch (ygvalue.unit) { \ case YGUnitAuto: \ case YGUnitUndefined: \ setter(__VA_ARGS__, YGUndefined); \ break; \ case YGUnitPoint: \ setter(__VA_ARGS__, ygvalue.value); \ break; \ case YGUnitPercent: \ setter##Percent(__VA_ARGS__, ygvalue.value); \ break; \ }
+ (YGConfigRef)yogaConfig;
の+
の意味はなんだろう+
で定義されるのはクラスメソッド-
で定義されるのはインスタンスメソッド