bm-core-ui 2.8.2 → 2.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,7 +4,7 @@ import {YES, NO, BMExtend, BMScrollBarGetSize, BMAddSmoothMousewheelInteractionT
4
4
  import {BMInset} from '../Core/BMInset'
5
5
  import {BMPointMake} from '../Core/BMPoint'
6
6
  import {BMSizeMake} from '../Core/BMSize'
7
- import {BMRect, BMRectMake, BMRectMakeWithNodeFrame, BMRectByInterpolatingRect} from '../Core/BMRect'
7
+ import {BMRect, BMRectMake, BMRectMakeWithNodeFrame, BMRectMakeWithOrigin, BMRectByInterpolatingRect} from '../Core/BMRect'
8
8
  import {BMIndexPathNone} from '../Core/BMIndexPath'
9
9
  import {BMAnimateWithBlock, BMAnimationContextBeginStatic, BMAnimationContextGetCurrent, BMAnimationContextAddCompletionHandler, BMAnimationApply, __BMVelocityAnimate, BMHook} from '../Core/BMAnimationContext'
10
10
  import {BMView, BMViewLayoutQueue} from '../BMView/BMView_v2.5'
@@ -693,7 +693,7 @@ BMCollectionView.prototype = BMExtend(BM_COLLECTION_VIEW_USE_BMVIEW_SUBCLASS ? O
693
693
  /**
694
694
  * Defaults to `YES`. When set to `NO`, keyboard navigation is disabled.
695
695
  */
696
- _supportsKeyboardNavigation: YES, // <Boolean>
696
+ _supportsKeyboardNavigation: NO, // <Boolean>
697
697
 
698
698
  get supportsKeyboardNavigation() {
699
699
  return this._supportsKeyboardNavigation;
@@ -1024,8 +1024,6 @@ BMCollectionView.prototype = BMExtend(BM_COLLECTION_VIEW_USE_BMVIEW_SUBCLASS ? O
1024
1024
  this.__awaitsLayout = YES;
1025
1025
  return;
1026
1026
  }
1027
-
1028
- this._blocksLayoutInvalidation = YES;
1029
1027
 
1030
1028
  // Do not invalidate the layout during an animated data update
1031
1029
  if (this.isUpdatingData) {
@@ -1033,6 +1031,8 @@ BMCollectionView.prototype = BMExtend(BM_COLLECTION_VIEW_USE_BMVIEW_SUBCLASS ? O
1033
1031
  this.registerDataCompletionCallback(function () { self.invalidateLayout(); });
1034
1032
  return;
1035
1033
  }
1034
+
1035
+ this._blocksLayoutInvalidation = YES;
1036
1036
 
1037
1037
  // If the layout supports animated changes and an animation context is active, make this change animated
1038
1038
  let animation;
@@ -1185,8 +1185,13 @@ BMCollectionView.prototype = BMExtend(BM_COLLECTION_VIEW_USE_BMVIEW_SUBCLASS ? O
1185
1185
  * Should be invoked by layout objects when the content size changes, but no other aspect of the layout does.
1186
1186
  */
1187
1187
  invalidateContentSize() {
1188
- // If an update is in progress, ignore this
1189
- if (!this._collectionEnabled) return;
1188
+ // If an update is in progress, delay this until after the update
1189
+ if (!this._collectionEnabled) {
1190
+ this.registerDataCompletionCallback(() => {
1191
+ this.invalidateContentSize();
1192
+ })
1193
+ return;
1194
+ }
1190
1195
 
1191
1196
  var size = this._layout.contentSize();
1192
1197
 
@@ -1451,6 +1456,8 @@ BMCollectionView.prototype = BMExtend(BM_COLLECTION_VIEW_USE_BMVIEW_SUBCLASS ? O
1451
1456
  return;
1452
1457
  }
1453
1458
 
1459
+ const previousBounds = BMRectMakeWithOrigin(previousBoundsOffset, {size: this._bounds.size.copy()});
1460
+
1454
1461
  // Dequeue the layout queue after a minimal delay to minimize visual artifacts
1455
1462
  (async () => {
1456
1463
  await 0;
@@ -1459,19 +1466,21 @@ BMCollectionView.prototype = BMExtend(BM_COLLECTION_VIEW_USE_BMVIEW_SUBCLASS ? O
1459
1466
 
1460
1467
 
1461
1468
  // Invalidate the layout if the layout object requires invalidation when the bounds change
1462
- if (this._layout.shouldInvalidateLayoutForBoundsChange(this._bounds)) {
1469
+ if (this._layout.shouldInvalidateLayoutForBoundsChange(this._bounds, {fromBounds: previousBounds})) {
1463
1470
  this.invalidateLayoutForBoundsChange();
1464
1471
 
1465
1472
  // Let the delegate know that the bounds were updated
1466
1473
  if (this.delegate && typeof this.delegate.collectionViewBoundsDidChange == 'function') {
1467
- this.delegate.collectionViewBoundsDidChange(this, this._bounds);
1474
+ this.delegate.collectionViewBoundsDidChange(this, this._bounds, {fromBounds: previousBounds});
1468
1475
  }
1469
1476
 
1470
1477
  // Let the layout know that the invalidation was successful.
1471
1478
  this._layout.didInvalidateLayoutForBoundsChange();
1472
1479
 
1473
1480
  // Prepare to request and apply the snapping offset from the layout
1474
- snapsScrollPosition && this._prepareForSnappingScrollOffsetWithPreviousOffset(previousBoundsOffset);
1481
+ if (snapsScrollPosition) {
1482
+ this._prepareForSnappingScrollOffsetWithPreviousOffset(previousBoundsOffset);
1483
+ }
1475
1484
 
1476
1485
  return;
1477
1486
  }
@@ -1619,12 +1628,14 @@ BMCollectionView.prototype = BMExtend(BM_COLLECTION_VIEW_USE_BMVIEW_SUBCLASS ? O
1619
1628
 
1620
1629
  // Let the delegate know that the bounds were updated
1621
1630
  if (this.delegate && typeof this.delegate.collectionViewBoundsDidChange == 'function') {
1622
- this.delegate.collectionViewBoundsDidChange(this, this._bounds);
1631
+ this.delegate.collectionViewBoundsDidChange(this, this._bounds, {fromBounds: previousBounds});
1623
1632
  }
1624
1633
 
1625
1634
 
1626
1635
  // Prepare to request and apply the snapping offset from the layout
1627
- snapsScrollPosition && this._prepareForSnappingScrollOffsetWithPreviousOffset(previousBoundsOffset);
1636
+ if (snapsScrollPosition) {
1637
+ this._prepareForSnappingScrollOffsetWithPreviousOffset(previousBoundsOffset);
1638
+ }
1628
1639
 
1629
1640
  return;
1630
1641
  }
@@ -1644,13 +1655,15 @@ BMCollectionView.prototype = BMExtend(BM_COLLECTION_VIEW_USE_BMVIEW_SUBCLASS ? O
1644
1655
 
1645
1656
  // Let the delegate know that the bounds were updated
1646
1657
  if (this.delegate && typeof this.delegate.collectionViewBoundsDidChange == 'function') {
1647
- this.delegate.collectionViewBoundsDidChange(this, this._bounds);
1658
+ this.delegate.collectionViewBoundsDidChange(this, this._bounds, {fromBounds: previousBounds});
1648
1659
  }
1649
1660
 
1650
1661
  // Prepare to request and apply the snapping offset from the layout, unless the collection view is performing an
1651
1662
  // automated scrolling animation
1652
1663
  if (!this._isPerformingAnimatedScrolling) {
1653
- snapsScrollPosition && this._prepareForSnappingScrollOffsetWithPreviousOffset(previousBoundsOffset);
1664
+ if (snapsScrollPosition) {
1665
+ this._prepareForSnappingScrollOffsetWithPreviousOffset(previousBoundsOffset);
1666
+ }
1654
1667
  }
1655
1668
 
1656
1669
 
@@ -3988,7 +4001,7 @@ BMCollectionView.prototype = BMExtend(BM_COLLECTION_VIEW_USE_BMVIEW_SUBCLASS ? O
3988
4001
  if (options && options.withEvent) options.withEvent._BMOriginalTarget = cell.node;
3989
4002
 
3990
4003
  // Acquire focus if not already owned
3991
- if (document.activeElement != this.node) {
4004
+ if (document.activeElement != this.node && this._supportsKeyboardNavigation) {
3992
4005
  this.node.focus();
3993
4006
  }
3994
4007
 
@@ -4053,7 +4066,7 @@ BMCollectionView.prototype = BMExtend(BM_COLLECTION_VIEW_USE_BMVIEW_SUBCLASS ? O
4053
4066
  if (options && options.withEvent) options.withEvent._BMOriginalTarget = cell.node;
4054
4067
 
4055
4068
  // Acquire focus if not already owned
4056
- if (document.activeElement != this.node) {
4069
+ if (document.activeElement != this.node && this._supportsKeyboardNavigation) {
4057
4070
  this.node.focus();
4058
4071
  }
4059
4072
 
@@ -4075,7 +4088,7 @@ BMCollectionView.prototype = BMExtend(BM_COLLECTION_VIEW_USE_BMVIEW_SUBCLASS ? O
4075
4088
  if (options && options.withEvent) options.withEvent._BMOriginalTarget = cell.node;
4076
4089
 
4077
4090
  // Acquire focus if not already owned
4078
- if (document.activeElement != this.node) {
4091
+ if (document.activeElement != this.node && this._supportsKeyboardNavigation) {
4079
4092
  this.node.focus();
4080
4093
  }
4081
4094
 
@@ -5028,6 +5041,10 @@ BMCollectionView.prototype = BMExtend(BM_COLLECTION_VIEW_USE_BMVIEW_SUBCLASS ? O
5028
5041
  }
5029
5042
  }
5030
5043
 
5044
+ if (options && options.completionHandler) {
5045
+ options.completionHandler();
5046
+ }
5047
+
5031
5048
  this._executeDataCompletionCallbacks();
5032
5049
  resolveUpdateData();
5033
5050
  // Resolve the static animation context prior to returning
@@ -1699,9 +1699,15 @@ BMCollectionViewFlowLayout.prototype = BMExtend(Object.create(BMCollectionViewLa
1699
1699
  * When using automatic cell sizes, the generator will yield execution and only calculate portions of the layout on demand.
1700
1700
  * With the exeption of the first invocation, it is required to pass an index path to the iterator's `next` method. The layout process
1701
1701
  * will continue until it fully lays out the cell for that index path, at which point it will yield again.
1702
- * @param useOffset <Boolean> When set to `YES` the layout will take the scrollbar size into account.
1702
+ * @param useOffset <Boolean> When set to `YES` the layout will take the scrollbar size into account.
1703
+ * {
1704
+ * @param targetRect <BMRect, nullable> When computing a layout using automatic cell sizes, this is an optional rect up to which
1705
+ * to measure cells. This is reserved for when this is invoked as a delegate.
1706
+ * @param targetIndexPath <BMIndexPath, nullable> When computing a layout using automatic cell sizes, this is an optional index path up to which
1707
+ * to measure cells. This is reserved for when this is invoked as a delegate.
1708
+ * }
1703
1709
  */
1704
- *_prepareLayoutWithScrollbarOffsetGenerator(useOffset) {
1710
+ *_prepareLayoutWithScrollbarOffsetGenerator(useOffset, target) {
1705
1711
 
1706
1712
  // To standardize naming in order to support the orientation setting, the following terms are used:
1707
1713
  // Length - refers to the dimension across the axis on which collection view scrolls: height for vertical and width for horizontal
@@ -1803,7 +1809,6 @@ BMCollectionViewFlowLayout.prototype = BMExtend(Object.create(BMCollectionViewLa
1803
1809
  targetRect?: BMRect;
1804
1810
  }
1805
1811
  */
1806
- let target;
1807
1812
 
1808
1813
  // The sections that contain the preliminary layout
1809
1814
  const preliminarySections = [];
@@ -1977,15 +1982,15 @@ BMCollectionViewFlowLayout.prototype = BMExtend(Object.create(BMCollectionViewLa
1977
1982
 
1978
1983
  numberOfColumns++;
1979
1984
  } while (usedLength < maximumLength && expectedSize);
1980
-
1981
- // Ensure that there is at least one column
1982
- numberOfColumns = Math.max(numberOfColumns, 1);
1983
1985
  }
1984
1986
  else {
1985
1987
  // If there is no minimum spacing required, then the number of columns is simply the available space divided
1986
1988
  // by the cell width
1987
- numberOfColumns = Math.max((maximumLength / expectedSize) | 0, 1);
1989
+ numberOfColumns = (maximumLength / expectedSize) | 0;
1988
1990
  }
1991
+
1992
+ // Ensure that there is at least one column
1993
+ numberOfColumns = Math.max(numberOfColumns, 1);
1989
1994
  }
1990
1995
 
1991
1996
  // Create the preliminary sections
@@ -2069,7 +2074,12 @@ BMCollectionViewFlowLayout.prototype = BMExtend(Object.create(BMCollectionViewLa
2069
2074
 
2070
2075
  cachedLayout.size = BMSizeMake(this.collectionView.frame.size.width, height);
2071
2076
 
2072
- target = yield;
2077
+ // Yield after building the preliminary layout and wait for a target rect or index path up to which to measure
2078
+ if (!target) {
2079
+ // If a target is already supplied (as a parameter) this is a delegate generator and processing should continue
2080
+ // until this target
2081
+ target = yield;
2082
+ }
2073
2083
 
2074
2084
  // When requesting a rect initially, pre-measure the cells up to that rect
2075
2085
  if (target.rect) {
@@ -2085,7 +2095,13 @@ BMCollectionViewFlowLayout.prototype = BMExtend(Object.create(BMCollectionViewLa
2085
2095
 
2086
2096
  cachedLayout.size = BMSizeMake(width, this.collectionView.frame.size.height);
2087
2097
 
2088
- target = yield;
2098
+ // Yield after building the preliminary layout and wait for a target rect or index path up to which to measure
2099
+ if (!target) {
2100
+ // If a target is already supplied (as a parameter) this is a delegate generator and processing should continue
2101
+ // until this target
2102
+ target = yield;
2103
+ }
2104
+
2089
2105
  // When requesting a rect initially, pre-measure the cells up to that rect
2090
2106
  if (target.rect) {
2091
2107
  measureCellsInRect(target.rect);
@@ -2220,7 +2236,15 @@ BMCollectionViewFlowLayout.prototype = BMExtend(Object.create(BMCollectionViewLa
2220
2236
 
2221
2237
  // If the row height exceeds the collection view frame height, perform a second layout pass to account for the scrollbars
2222
2238
  if (row[rowStartAttribute] + rowBreadth > frameSize) {
2223
- if (this.collectionView.scrollBarSize && !useOffset) return this._prepareLayoutWithScrollbarOffset(YES);
2239
+ if (this.collectionView.scrollBarSize && !useOffset) {
2240
+ if (target) {
2241
+ // With automatic cell size delegate iteration to the new invocation
2242
+ return yield *this._prepareLayoutWithScrollbarOffsetGenerator(YES, target);
2243
+ }
2244
+ else {
2245
+ return this._prepareLayoutWithScrollbarOffset(YES);
2246
+ }
2247
+ }
2224
2248
  }
2225
2249
 
2226
2250
  // if there is one
@@ -2383,7 +2407,15 @@ BMCollectionViewFlowLayout.prototype = BMExtend(Object.create(BMCollectionViewLa
2383
2407
 
2384
2408
  // If the total height exceeds the collection view frame height, perform a second layout pass to account for the scrollbars
2385
2409
  if (height > frameHeight) {
2386
- if (this.collectionView.scrollBarSize && !useOffset) return this._prepareLayoutWithScrollbarOffset(YES);
2410
+ if (this.collectionView.scrollBarSize && !useOffset) {
2411
+ if (target) {
2412
+ // With automatic cell size delegate iteration to the new invocation
2413
+ return yield *this._prepareLayoutWithScrollbarOffsetGenerator(YES, target);
2414
+ }
2415
+ else {
2416
+ return this._prepareLayoutWithScrollbarOffset(YES);
2417
+ }
2418
+ }
2387
2419
  }
2388
2420
  }
2389
2421
  else {
@@ -2404,7 +2436,15 @@ BMCollectionViewFlowLayout.prototype = BMExtend(Object.create(BMCollectionViewLa
2404
2436
 
2405
2437
  // If the total height exceeds the collection view frame height, perform a second layout pass to account for the scrollbars
2406
2438
  if (width > frameWidth) {
2407
- if (this.collectionView.scrollBarSize && !useOffset) return this._prepareLayoutWithScrollbarOffset(YES);
2439
+ if (this.collectionView.scrollBarSize && !useOffset) {
2440
+ if (target) {
2441
+ // With automatic cell size delegate iteration to the new invocation
2442
+ return yield *this._prepareLayoutWithScrollbarOffsetGenerator(YES, target);
2443
+ }
2444
+ else {
2445
+ return this._prepareLayoutWithScrollbarOffset(YES);
2446
+ }
2447
+ }
2408
2448
  }
2409
2449
  }
2410
2450
  }
@@ -2418,7 +2458,15 @@ BMCollectionViewFlowLayout.prototype = BMExtend(Object.create(BMCollectionViewLa
2418
2458
 
2419
2459
  // If the total height exceeds the collection view frame height, perform a second layout pass to account for the scrollbars
2420
2460
  if (height > frameHeight) {
2421
- if (this.collectionView.scrollBarSize && !useOffset) return this._prepareLayoutWithScrollbarOffset(YES);
2461
+ if (this.collectionView.scrollBarSize && !useOffset) {
2462
+ if (target) {
2463
+ // With automatic cell size delegate iteration to the new invocation
2464
+ return yield *this._prepareLayoutWithScrollbarOffsetGenerator(YES, target);
2465
+ }
2466
+ else {
2467
+ return this._prepareLayoutWithScrollbarOffset(YES);
2468
+ }
2469
+ }
2422
2470
  }
2423
2471
  }
2424
2472
  else {
@@ -2429,7 +2477,15 @@ BMCollectionViewFlowLayout.prototype = BMExtend(Object.create(BMCollectionViewLa
2429
2477
 
2430
2478
  // If the total height exceeds the collection view frame height, perform a second layout pass to account for the scrollbars
2431
2479
  if (width > frameWidth) {
2432
- if (this.collectionView.scrollBarSize && !useOffset) return this._prepareLayoutWithScrollbarOffset(YES);
2480
+ if (this.collectionView.scrollBarSize && !useOffset) {
2481
+ if (target) {
2482
+ // With automatic cell size delegate iteration to the new invocation
2483
+ return yield *this._prepareLayoutWithScrollbarOffsetGenerator(YES, target);
2484
+ }
2485
+ else {
2486
+ return this._prepareLayoutWithScrollbarOffset(YES);
2487
+ }
2488
+ }
2433
2489
  }
2434
2490
  }
2435
2491
 
@@ -2440,7 +2496,15 @@ BMCollectionViewFlowLayout.prototype = BMExtend(Object.create(BMCollectionViewLa
2440
2496
 
2441
2497
  // If the total height exceeds the collection view frame height, perform a second layout pass to account for the scrollbars
2442
2498
  if (height > frameHeight) {
2443
- if (this.collectionView.scrollBarSize && !useOffset) return this._prepareLayoutWithScrollbarOffset(YES);
2499
+ if (this.collectionView.scrollBarSize && !useOffset) {
2500
+ if (target) {
2501
+ // With automatic cell size delegate iteration to the new invocation
2502
+ return yield *this._prepareLayoutWithScrollbarOffsetGenerator(YES, target);
2503
+ }
2504
+ else {
2505
+ return this._prepareLayoutWithScrollbarOffset(YES);
2506
+ }
2507
+ }
2444
2508
  }
2445
2509
 
2446
2510
  // If the height is lower than the collection view's height, the content gravity must be applied to all cached attributes
@@ -2534,7 +2598,15 @@ BMCollectionViewFlowLayout.prototype = BMExtend(Object.create(BMCollectionViewLa
2534
2598
  else {
2535
2599
  // If the total height exceeds the collection view frame height, perform a second layout pass to account for the scrollbars
2536
2600
  if (width > frameWidth) {
2537
- if (this.collectionView.scrollBarSize && !useOffset) return this._prepareLayoutWithScrollbarOffset(YES);
2601
+ if (this.collectionView.scrollBarSize && !useOffset) {
2602
+ if (target) {
2603
+ // With automatic cell size delegate iteration to the new invocation
2604
+ return yield *this._prepareLayoutWithScrollbarOffsetGenerator(YES, target);
2605
+ }
2606
+ else {
2607
+ return this._prepareLayoutWithScrollbarOffset(YES);
2608
+ }
2609
+ }
2538
2610
  }
2539
2611
 
2540
2612
  // If the height is lower than the collection view's height, the content gravity must be applied to all cached attributes
@@ -2607,7 +2679,7 @@ BMCollectionViewFlowLayout.prototype = BMExtend(Object.create(BMCollectionViewLa
2607
2679
  const scaledRowHeight = rowWidth * scaleFactor | 0;
2608
2680
  for (const attribute of row.attributes) {
2609
2681
  attribute.frame.origin.x += displacement;
2610
- attribute.frame.size.height = attribute.frame.size.height * scaleFactor | 0;
2682
+ attribute.frame.size.width = attribute.frame.size.width * scaleFactor | 0;
2611
2683
  }
2612
2684
 
2613
2685
  // Advance the displacement based on the row's new width then displace the row end
@@ -2710,14 +2782,14 @@ BMCollectionViewFlowLayout.prototype = BMExtend(Object.create(BMCollectionViewLa
2710
2782
 
2711
2783
  numberOfColumns++;
2712
2784
  } while (usedLength < length && cellSize);
2713
-
2714
- numberOfColumns = Math.max(numberOfColumns, 1);
2715
2785
  }
2716
2786
  else {
2717
2787
  // If there is no minimum spacing required, then the number of columns is simply the available space divided
2718
2788
  // by the cell width
2719
- numberOfColumns = Math.max((length / cellSize) | 0, 1);
2789
+ numberOfColumns = (length / cellSize) | 0;
2720
2790
  }
2791
+
2792
+ numberOfColumns = Math.max(numberOfColumns, 1);
2721
2793
  }
2722
2794
  cachedLayout.numberOfColumns = numberOfColumns;
2723
2795
 
@@ -3263,10 +3335,18 @@ BMCollectionViewFlowLayout.prototype = BMExtend(Object.create(BMCollectionViewLa
3263
3335
  // for the requested index path
3264
3336
  if (this._expectedCellSize) {
3265
3337
  if (this.cachedLayout.resolvedIndexPath.section < indexPath.section ||
3266
- (this.cachedLayout.resolvedIndexPath.section == indexPath.section && this.cachedLayout.resolvedIndexPath.row < indexPath.row)) {
3267
- // If the layout has not been resolved up to this index path, continue computing it until it has
3268
- this._layoutIterator.next({indexPath: indexPath});
3338
+ (this.cachedLayout.resolvedIndexPath.section == indexPath.section && this.cachedLayout.resolvedIndexPath.row < indexPath.row)
3339
+ ) {
3340
+ const currentResolvedHeight = this.cachedLayout.resolvedHeight;
3341
+
3342
+ // If the layout has not been resolved up to this index path, continue computing it until it has
3343
+ this._layoutIterator.next({indexPath: indexPath});
3344
+
3345
+ // If the resolved height changes, invalidate the content size
3346
+ if (currentResolvedHeight != this.cachedLayout.resolvedHeight) {
3347
+ this.invalidateContentSize();
3269
3348
  }
3349
+ }
3270
3350
  }
3271
3351
 
3272
3352
  // Return the cached attributes when using dynamic sizes
@@ -4252,22 +4332,33 @@ BMCollectionViewFlowLayout.prototype = BMExtend(Object.create(BMCollectionViewLa
4252
4332
  var attributes;
4253
4333
  var self = this;
4254
4334
 
4255
- this.collectionView.usingOldDataSet(function () {
4256
- // When using automatic cell sizes, it is important to ensure that the sizing information has been computed
4257
- // for the requested index path
4258
- if (self._expectedCellSize) {
4259
- if (self.previousLayout.resolvedIndexPath.section < indexPath.section ||
4260
- (self.previousLayout.resolvedIndexPath.section == indexPath.section && self.previousLayout.resolvedIndexPath.row < indexPath.row)) {
4335
+ // When using automatic cell sizes, it is important to ensure that the sizing information has been computed
4336
+ // for the requested index path
4337
+ if (self._expectedCellSize) {
4338
+ if (self.previousLayout.resolvedIndexPath.section < indexPath.section ||
4339
+ (self.previousLayout.resolvedIndexPath.section == indexPath.section && self.previousLayout.resolvedIndexPath.row < indexPath.row)) {
4340
+ self.collectionView.usingOldDataSet(function () {
4261
4341
  // If the layout has not been resolved up to this index path, continue computing it until it has
4262
4342
  self.previousLayout.iterator.next({indexPath: indexPath});
4263
- }
4264
- }
4343
+ });
4344
+ }
4345
+ }
4346
+
4347
+ this.collectionView.usingOldDataSet(function () {
4265
4348
 
4266
4349
  if (self._cellSize && !self._expectedCellSize) {
4267
4350
  attributes = self.computedAttributesForCellAtIndexPath(indexPath, {usingCache: self.previousLayout});
4268
4351
  }
4269
4352
  else {
4270
4353
  attributes = self.cachedAttributesForCellAtIndexPath(indexPath, {usingCache: self.previousLayout});
4354
+
4355
+ if (!attributes) {
4356
+ // When using automatic cell sizes, it is possible that the attributes for the moving cell were not computed
4357
+ // in this case, return a copy of the current attributes, with the opacity set to 0
4358
+ attributes = options.withTargetAttributes.copy();
4359
+
4360
+ attributes.style = Object.assign(attributes.style, {opacity: 0});
4361
+ }
4271
4362
  }
4272
4363
  });
4273
4364
 
@@ -4278,6 +4369,21 @@ BMCollectionViewFlowLayout.prototype = BMExtend(Object.create(BMCollectionViewLa
4278
4369
  initialAttributesForMovingSupplementaryViewWithIdentifier: function (identifier, options) {
4279
4370
  var attributes;
4280
4371
  var self = this;
4372
+
4373
+ // When using automatic cell sizes, ensure that the sizing information has been computed for the requested index path
4374
+ if (self._expectedCellSize) {
4375
+ if (self.cachedLayout.resolvedIndexPath.section < options.atIndexPath.section ||
4376
+ (self.cachedLayout.resolvedIndexPath.section == options.atIndexPath.section && identifier == BMCollectionViewFlowLayoutSupplementaryView.Footer)) {
4377
+ if (options.atIndexPath.section == self.collectionView.numberOfSections() - 1) {
4378
+ // If there is no following section, compute until the last index path
4379
+ self._layoutIterator.next({indexPath: self.collectionView.indexPathForObjectAtRow(self.collectionView.numberOfObjectsInSectionAtIndex(options.atIndexPath.section) - 1, {inSectionAtIndex: options.atIndexPath.section})});
4380
+ }
4381
+ else {
4382
+ // If the layout has not been resolved up to this index path, continue computing up to the beginning of the next section
4383
+ self._layoutIterator.next({indexPath: self.collectionView.indexPathForObjectAtRow(0, {inSectionAtIndex: options.atIndexPath.section + 1})});
4384
+ }
4385
+ }
4386
+ }
4281
4387
 
4282
4388
  this.collectionView.usingOldDataSet(function () {
4283
4389
  if (identifier === BMCollectionViewTableLayoutSupplementaryView.Empty) {
@@ -4288,6 +4394,14 @@ BMCollectionViewFlowLayout.prototype = BMExtend(Object.create(BMCollectionViewLa
4288
4394
  }
4289
4395
  else {
4290
4396
  attributes = self.cachedAttributesForSupplementaryViewWithIdentifier(identifier, {atIndexPath: options.atIndexPath, usingCache: self.previousLayout});
4397
+
4398
+ if (!attributes) {
4399
+ // When using automatic cell sizes, it is possible that the attributes for the moving supplementary view were not computed
4400
+ // in this case, return a copy of the current attributes, with the opacity set to 0
4401
+ attributes = options.withTargetAttributes.copy();
4402
+
4403
+ attributes.style = Object.assign(attributes.style, {opacity: 0});
4404
+ }
4291
4405
  }
4292
4406
  });
4293
4407
 
@@ -4489,19 +4603,22 @@ BMCollectionViewFlowLayout.prototype = BMExtend(Object.create(BMCollectionViewLa
4489
4603
  const row = indexPath.row;
4490
4604
 
4491
4605
  // Find the row where the current index path is
4492
- const sectionRows = this.cachedLayout.sections[section];
4606
+ const cachedSection = this.cachedLayout.sections[section];
4493
4607
 
4494
- if (!sectionRows) return indexPath;
4608
+ if (!cachedSection) return indexPath;
4609
+
4610
+ const sectionRows = cachedSection.rows;
4495
4611
 
4496
4612
  let rowIndex = 0;
4497
4613
  const rowCount = sectionRows.length;
4498
4614
  for (rowIndex; rowIndex < rowCount; rowIndex++) {
4499
- if (row.startIndex <= row && row.endIndex >= row) {
4615
+ const sectionRow = sectionRows[rowIndex];
4616
+ if (sectionRow.startIndex <= row && sectionRow.endIndex >= row) {
4500
4617
  break;
4501
4618
  }
4502
4619
  }
4503
4620
 
4504
- const attributes = sectionRows[rowIndex].attributes;
4621
+ const attributes = sectionRows[rowIndex].attributes.find(a => a.indexPath.row == row);
4505
4622
  const positionProperty = this._orientation == BMCollectionViewFlowLayoutOrientation.Vertical ? 'x' : 'y';
4506
4623
  const position = attributes.frame.center[positionProperty];
4507
4624
 
@@ -4616,19 +4733,22 @@ BMCollectionViewFlowLayout.prototype = BMExtend(Object.create(BMCollectionViewLa
4616
4733
  const row = indexPath.row;
4617
4734
 
4618
4735
  // Find the row where the current index path is
4619
- const sectionRows = this.cachedLayout.sections[section];
4736
+ const cachedSection = this.cachedLayout.sections[section];
4737
+
4738
+ if (!cachedSection) return indexPath;
4620
4739
 
4621
- if (!sectionRows) return indexPath;
4740
+ const sectionRows = cachedSection.rows;
4622
4741
 
4623
4742
  let rowIndex = 0;
4624
4743
  const rowCount = sectionRows.length;
4625
4744
  for (rowIndex; rowIndex < rowCount; rowIndex++) {
4626
- if (row.startIndex <= row && row.endIndex >= row) {
4745
+ const sectionRow = sectionRows[rowIndex];
4746
+ if (sectionRow.startIndex <= row && sectionRow.endIndex >= row) {
4627
4747
  break;
4628
4748
  }
4629
4749
  }
4630
4750
 
4631
- const attributes = sectionRows[rowIndex].attributes;
4751
+ const attributes = sectionRows[rowIndex].attributes.find(a => a.indexPath.row == row);
4632
4752
  const positionProperty = this._orientation == BMCollectionViewFlowLayoutOrientation.Vertical ? 'x' : 'y';
4633
4753
  const position = attributes.frame.center[positionProperty];
4634
4754
 
@@ -659,9 +659,12 @@ BMCollectionViewLayout.prototype = {
659
659
  * Layout objects should return YES from this method if the new bounds require a new layout.
660
660
  * The default implementation returns NO in all cases.
661
661
  * @param bounds <BMRect> The new bounds.
662
+ * {
663
+ * @param fromBounds <BMRect> The previous bounds.
664
+ * }
662
665
  * @return <Boolean> True if the new frame requires a new layout, false otherwise.
663
666
  */
664
- shouldInvalidateLayoutForBoundsChange: function (bounds) {
667
+ shouldInvalidateLayoutForBoundsChange: function (bounds, {fromBounds}) {
665
668
  return NO;
666
669
  },
667
670