react-native-pdf-jsi 4.1.2 → 4.2.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.
- package/PdfView.js +36 -8
- package/README.md +3 -0
- package/index.js +1 -0
- package/ios/RNPDFPdf/RNPDFPdfView.mm +635 -26
- package/package.json +1 -1
package/PdfView.js
CHANGED
|
@@ -83,6 +83,7 @@ export default class PdfView extends Component {
|
|
|
83
83
|
scale: this.props.scale,
|
|
84
84
|
contentOffset: {x: 0, y: 0},
|
|
85
85
|
newContentOffset: {x: 0, y: 0},
|
|
86
|
+
previousPage: -1, // Track previous page to prevent unnecessary navigations
|
|
86
87
|
};
|
|
87
88
|
|
|
88
89
|
this._flatList = null;
|
|
@@ -121,10 +122,16 @@ export default class PdfView extends Component {
|
|
|
121
122
|
this.props.onError(error);
|
|
122
123
|
});
|
|
123
124
|
|
|
125
|
+
// Initialize page navigation after PDF loads
|
|
124
126
|
clearTimeout(this._scrollTimer);
|
|
125
127
|
this._scrollTimer = setTimeout(() => {
|
|
126
|
-
if (this._flatList) {
|
|
127
|
-
|
|
128
|
+
if (this._flatList && this._mounted) {
|
|
129
|
+
const initialPage = this.props.page < 1 ? 0 : this.props.page - 1;
|
|
130
|
+
this._flatList.scrollToIndex({
|
|
131
|
+
animated: false,
|
|
132
|
+
index: initialPage
|
|
133
|
+
});
|
|
134
|
+
this.setState({ previousPage: this.props.page < 1 ? 1 : this.props.page });
|
|
128
135
|
}
|
|
129
136
|
}, 200);
|
|
130
137
|
}
|
|
@@ -139,15 +146,30 @@ export default class PdfView extends Component {
|
|
|
139
146
|
});
|
|
140
147
|
}
|
|
141
148
|
|
|
142
|
-
if
|
|
149
|
+
// Only navigate if page actually changed and PDF is loaded
|
|
150
|
+
const pageChanged = this.props.page !== prevProps.page;
|
|
151
|
+
const horizontalChanged = this.props.horizontal !== prevProps.horizontal;
|
|
152
|
+
|
|
153
|
+
if ((horizontalChanged || pageChanged) && this.state.pdfLoaded && this._flatList) {
|
|
143
154
|
let page = (this.props.page) < 1 ? 1 : this.props.page;
|
|
144
155
|
page = page > this.state.numberOfPages ? this.state.numberOfPages : page;
|
|
145
|
-
|
|
146
|
-
if
|
|
156
|
+
|
|
157
|
+
// Only navigate if page actually changed from previous navigation
|
|
158
|
+
if (page !== this.state.previousPage) {
|
|
147
159
|
clearTimeout(this._scrollTimer);
|
|
148
160
|
this._scrollTimer = setTimeout(() => {
|
|
149
|
-
this._flatList
|
|
150
|
-
|
|
161
|
+
if (this._flatList && this._mounted) {
|
|
162
|
+
// Use animated: true for smooth transitions when pagination is disabled
|
|
163
|
+
// Use animated: false only when paging is enabled for instant snap
|
|
164
|
+
const shouldAnimate = !this.props.enablePaging;
|
|
165
|
+
this._flatList.scrollToIndex({
|
|
166
|
+
animated: shouldAnimate,
|
|
167
|
+
index: page - 1,
|
|
168
|
+
viewPosition: 0.5 // Center the page in view
|
|
169
|
+
});
|
|
170
|
+
this.setState({ previousPage: page });
|
|
171
|
+
}
|
|
172
|
+
}, horizontalChanged ? 300 : 100); // Longer delay if orientation changed
|
|
151
173
|
}
|
|
152
174
|
}
|
|
153
175
|
|
|
@@ -339,7 +361,10 @@ export default class PdfView extends Component {
|
|
|
339
361
|
let data = [];
|
|
340
362
|
|
|
341
363
|
if (this.props.singlePage) {
|
|
342
|
-
|
|
364
|
+
// Fix: Use page prop instead of currentPage for singlePage mode
|
|
365
|
+
// This allows the page prop to control which page is displayed
|
|
366
|
+
const pageToShow = this.props.page >= 1 ? this.props.page - 1 : 0;
|
|
367
|
+
data[0] = {key: pageToShow};
|
|
343
368
|
} else {
|
|
344
369
|
for (let i = 0; i < this.state.numberOfPages; i++) {
|
|
345
370
|
data[i] = {key: i};
|
|
@@ -362,6 +387,9 @@ export default class PdfView extends Component {
|
|
|
362
387
|
windowSize={11}
|
|
363
388
|
getItemLayout={this._getItemLayout}
|
|
364
389
|
maxToRenderPerBatch={1}
|
|
390
|
+
// Prevent full rerenders when page changes - only rerender when data actually changes
|
|
391
|
+
extraData={this.props.singlePage ? this.props.page : this.state.numberOfPages}
|
|
392
|
+
removeClippedSubviews={true}
|
|
365
393
|
renderScrollComponent={(props) => <ScrollView
|
|
366
394
|
{...props}
|
|
367
395
|
centerContent={this.state.centerContent}
|
package/README.md
CHANGED
|
@@ -410,6 +410,9 @@ MIT License - see [LICENSE](LICENSE) file for details.
|
|
|
410
410
|
|
|
411
411
|
## Recent Fixes
|
|
412
412
|
|
|
413
|
+
### iOS Performance - Unnecessary Path Handlers (v4.2.0)
|
|
414
|
+
Fixed performance issue where path-related handlers were running unnecessarily when the path value hadn't actually changed. The fix filters out "path" from effectiveChangedProps when pathActuallyChanged=NO, preventing unnecessary reconfigurations of spacing, display direction, scroll views, usePageViewController, and other path-dependent handlers. This reduces unnecessary rerenders and improves performance, especially when navigating between pages. Addresses issue #7 (Page Prop Causes Full Rerender).
|
|
415
|
+
|
|
413
416
|
### iOS Pinch-to-Zoom (v4.1.1)
|
|
414
417
|
Fixed critical issue where pinch-to-zoom gestures were not working on iOS. The fix removes interfering custom gesture recognizers and enables PDFView's native pinch-to-zoom functionality, which now works smoothly on both iOS and Android.
|
|
415
418
|
|
package/index.js
CHANGED
|
@@ -681,6 +681,7 @@ export default class Pdf extends Component {
|
|
|
681
681
|
{...this.props}
|
|
682
682
|
style={[{backgroundColor: '#EEE',overflow: 'hidden'}, this.props.style]}
|
|
683
683
|
path={this.state.path}
|
|
684
|
+
page={this.props.page}
|
|
684
685
|
onLoadComplete={this.props.onLoadComplete}
|
|
685
686
|
onPageChanged={this.props.onPageChanged}
|
|
686
687
|
onError={this._onError}
|
|
@@ -149,6 +149,15 @@ const float MIN_SCALE = 1.0f;
|
|
|
149
149
|
NSMutableDictionary *_searchCache;
|
|
150
150
|
NSString *_currentPdfId;
|
|
151
151
|
NSOperationQueue *_preloadQueue;
|
|
152
|
+
|
|
153
|
+
// Page navigation state tracking
|
|
154
|
+
int _previousPage;
|
|
155
|
+
BOOL _isNavigating;
|
|
156
|
+
BOOL _documentLoaded;
|
|
157
|
+
|
|
158
|
+
// Track usePageViewController state to prevent unnecessary reconfiguration
|
|
159
|
+
BOOL _currentUsePageViewController;
|
|
160
|
+
BOOL _usePageViewControllerStateInitialized;
|
|
152
161
|
}
|
|
153
162
|
|
|
154
163
|
#ifdef RCT_NEW_ARCH_ENABLED
|
|
@@ -233,6 +242,7 @@ using namespace facebook::react;
|
|
|
233
242
|
[updatedPropNames addObject:@"maxScale"];
|
|
234
243
|
}
|
|
235
244
|
if (_horizontal != newProps.horizontal) {
|
|
245
|
+
RCTLogInfo(@"🔄 [iOS Scroll] Horizontal prop changed: %d -> %d", _horizontal, newProps.horizontal);
|
|
236
246
|
_horizontal = newProps.horizontal;
|
|
237
247
|
[updatedPropNames addObject:@"horizontal"];
|
|
238
248
|
}
|
|
@@ -265,6 +275,7 @@ using namespace facebook::react;
|
|
|
265
275
|
[updatedPropNames addObject:@"password"];
|
|
266
276
|
}
|
|
267
277
|
if (_singlePage != newProps.singlePage) {
|
|
278
|
+
RCTLogInfo(@"🔄 [iOS Scroll] SinglePage prop changed: %d -> %d", _singlePage, newProps.singlePage);
|
|
268
279
|
_singlePage = newProps.singlePage;
|
|
269
280
|
[updatedPropNames addObject:@"singlePage"];
|
|
270
281
|
}
|
|
@@ -326,6 +337,15 @@ using namespace facebook::react;
|
|
|
326
337
|
_initialed = YES;
|
|
327
338
|
|
|
328
339
|
[self didSetProps:mProps];
|
|
340
|
+
|
|
341
|
+
// Configure scroll view after layout to ensure it's found
|
|
342
|
+
// This is important because PDFKit creates the scroll view lazily
|
|
343
|
+
if (_documentLoaded && _pdfDocument) {
|
|
344
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
345
|
+
RCTLogInfo(@"🔍 [iOS Scroll] updateLayoutMetrics called, configuring scroll view after layout");
|
|
346
|
+
[self configureScrollView:self->_pdfView enabled:self->_scrollEnabled depth:0];
|
|
347
|
+
});
|
|
348
|
+
}
|
|
329
349
|
}
|
|
330
350
|
|
|
331
351
|
- (void)handleCommand:(const NSString *)commandName args:(const NSArray *)args
|
|
@@ -369,6 +389,38 @@ using namespace facebook::react;
|
|
|
369
389
|
_showsHorizontalScrollIndicator = YES;
|
|
370
390
|
_showsVerticalScrollIndicator = YES;
|
|
371
391
|
_scrollEnabled = YES;
|
|
392
|
+
|
|
393
|
+
// Initialize page navigation state
|
|
394
|
+
_previousPage = -1;
|
|
395
|
+
_isNavigating = NO;
|
|
396
|
+
_documentLoaded = NO;
|
|
397
|
+
|
|
398
|
+
// Initialize usePageViewController state tracking
|
|
399
|
+
_currentUsePageViewController = NO;
|
|
400
|
+
_usePageViewControllerStateInitialized = NO;
|
|
401
|
+
|
|
402
|
+
// #region agent log
|
|
403
|
+
{
|
|
404
|
+
NSString *logPath0 = @"/Users/punithmanthri/Documents/github jsi folder /react-native-enhanced-pdf/.cursor/debug.log";
|
|
405
|
+
NSDictionary *logEntry0 = @{
|
|
406
|
+
@"sessionId": @"debug-session",
|
|
407
|
+
@"runId": @"init",
|
|
408
|
+
@"hypothesisId": @"F",
|
|
409
|
+
@"location": @"RNPDFPdfView.mm:393",
|
|
410
|
+
@"message": @"initCommonProps completed",
|
|
411
|
+
@"data": @{
|
|
412
|
+
@"horizontal": @(_horizontal),
|
|
413
|
+
@"enablePaging": @(_enablePaging),
|
|
414
|
+
@"scrollEnabled": @(_scrollEnabled),
|
|
415
|
+
@"singlePage": @(_singlePage)
|
|
416
|
+
},
|
|
417
|
+
@"timestamp": @((long long)([[NSDate date] timeIntervalSince1970] * 1000))
|
|
418
|
+
};
|
|
419
|
+
NSData *logData0 = [NSJSONSerialization dataWithJSONObject:logEntry0 options:0 error:nil];
|
|
420
|
+
NSString *logLine0 = [[NSString alloc] initWithData:logData0 encoding:NSUTF8StringEncoding];
|
|
421
|
+
[[logLine0 stringByAppendingString:@"\n"] writeToFile:logPath0 atomically:YES encoding:NSUTF8StringEncoding error:nil];
|
|
422
|
+
}
|
|
423
|
+
// #endregion
|
|
372
424
|
|
|
373
425
|
// Enhanced properties
|
|
374
426
|
_enableCaching = YES;
|
|
@@ -440,13 +492,63 @@ using namespace facebook::react;
|
|
|
440
492
|
_changedProps = changedProps;
|
|
441
493
|
|
|
442
494
|
} else {
|
|
495
|
+
// Log all didSetProps calls to understand what's triggering reconfigurations
|
|
496
|
+
RCTLogInfo(@"📥 [iOS Scroll] didSetProps called - changedProps=%@, initialized=%d, currentUsePageVC=%d",
|
|
497
|
+
changedProps, _usePageViewControllerStateInitialized, _currentUsePageViewController);
|
|
443
498
|
|
|
444
|
-
|
|
499
|
+
// Create filtered changedProps array - remove "path" if it hasn't actually changed
|
|
500
|
+
// This prevents unnecessary reconfigurations when path is in changedProps but value unchanged
|
|
501
|
+
NSArray<NSString *> *effectiveChangedProps = changedProps;
|
|
502
|
+
BOOL pathActuallyChanged = NO;
|
|
445
503
|
|
|
504
|
+
if ([changedProps containsObject:@"path"]) {
|
|
505
|
+
// CRITICAL FIX: Only reset state if the path actually changed
|
|
506
|
+
// React Native sometimes includes path in changedProps even when only page changes
|
|
507
|
+
|
|
508
|
+
if (_pdfDocument != Nil && _pdfDocument.documentURL != nil) {
|
|
509
|
+
// Compare new path with existing document's path
|
|
510
|
+
NSString *currentPath = _pdfDocument.documentURL.path;
|
|
511
|
+
NSString *newPath = _path;
|
|
512
|
+
// Normalize paths for comparison (remove trailing slashes, resolve symlinks, etc.)
|
|
513
|
+
if (![currentPath isEqualToString:newPath]) {
|
|
514
|
+
pathActuallyChanged = YES;
|
|
515
|
+
}
|
|
516
|
+
} else {
|
|
517
|
+
// No existing document, so this is a new path (or initial load)
|
|
518
|
+
pathActuallyChanged = YES;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
RCTLogInfo(@"🔄 [iOS Scroll] Path prop in changedProps - hadDocument=%d, pathActuallyChanged=%d",
|
|
522
|
+
(_pdfDocument != Nil), pathActuallyChanged);
|
|
523
|
+
|
|
524
|
+
// Filter out "path" from effectiveChangedProps if it hasn't actually changed
|
|
525
|
+
if (!pathActuallyChanged) {
|
|
526
|
+
RCTLogInfo(@"⏭️ [iOS Scroll] Path value unchanged, filtering out 'path' from effectiveChangedProps");
|
|
527
|
+
NSMutableArray<NSString *> *filtered = [changedProps mutableCopy];
|
|
528
|
+
[filtered removeObject:@"path"];
|
|
529
|
+
effectiveChangedProps = filtered;
|
|
530
|
+
} else {
|
|
531
|
+
// Path actually changed, use changedProps as-is
|
|
532
|
+
effectiveChangedProps = changedProps;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
if (!pathActuallyChanged) {
|
|
536
|
+
RCTLogInfo(@"⏭️ [iOS Scroll] Path value unchanged, skipping document reload");
|
|
537
|
+
// Skip the rest of path handling
|
|
538
|
+
} else {
|
|
539
|
+
// Reset document load state when path actually changes
|
|
540
|
+
_documentLoaded = NO;
|
|
541
|
+
_previousPage = -1;
|
|
542
|
+
_isNavigating = NO;
|
|
446
543
|
|
|
544
|
+
// Release old doc if it exists
|
|
447
545
|
if (_pdfDocument != Nil) {
|
|
448
|
-
//Release old doc
|
|
449
546
|
_pdfDocument = Nil;
|
|
547
|
+
_usePageViewControllerStateInitialized = NO;
|
|
548
|
+
_currentUsePageViewController = NO;
|
|
549
|
+
RCTLogInfo(@"🔄 [iOS Scroll] Reset usePageViewController state - path changed (hadDocument=YES)");
|
|
550
|
+
} else {
|
|
551
|
+
RCTLogInfo(@"⏭️ [iOS Scroll] No previous document to reset");
|
|
450
552
|
}
|
|
451
553
|
|
|
452
554
|
if ([_path hasPrefix:@"blob:"]) {
|
|
@@ -482,16 +584,31 @@ using namespace facebook::react;
|
|
|
482
584
|
}
|
|
483
585
|
|
|
484
586
|
_pdfView.document = _pdfDocument;
|
|
587
|
+
_documentLoaded = YES;
|
|
588
|
+
|
|
589
|
+
// Configure scroll view after document is set
|
|
590
|
+
// PDFKit creates the scroll view lazily, so we need to wait a bit
|
|
591
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
592
|
+
RCTLogInfo(@"🔍 [iOS Scroll] Document set, searching for scroll view");
|
|
593
|
+
[self configureScrollView:self->_pdfView enabled:self->_scrollEnabled depth:0];
|
|
594
|
+
|
|
595
|
+
// Retry after a short delay to catch cases where scroll view is created asynchronously
|
|
596
|
+
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
|
597
|
+
RCTLogInfo(@"🔍 [iOS Scroll] Retry search for scroll view after delay");
|
|
598
|
+
[self configureScrollView:self->_pdfView enabled:self->_scrollEnabled depth:0];
|
|
599
|
+
});
|
|
600
|
+
});
|
|
485
601
|
} else {
|
|
486
602
|
|
|
487
603
|
[self notifyOnChangeWithMessage:[[NSString alloc] initWithString:[NSString stringWithFormat:@"error|Load pdf failed. path=%s",_path.UTF8String]]];
|
|
488
604
|
|
|
489
605
|
_pdfDocument = Nil;
|
|
490
606
|
return;
|
|
607
|
+
}
|
|
491
608
|
}
|
|
492
609
|
}
|
|
493
610
|
|
|
494
|
-
if (_pdfDocument && ([
|
|
611
|
+
if (_pdfDocument && ([effectiveChangedProps containsObject:@"path"] || [changedProps containsObject:@"spacing"])) {
|
|
495
612
|
if (_horizontal) {
|
|
496
613
|
_pdfView.pageBreakMargins = UIEdgeInsetsMake(0,_spacing,0,0);
|
|
497
614
|
if (_spacing==0) {
|
|
@@ -517,11 +634,11 @@ using namespace facebook::react;
|
|
|
517
634
|
}
|
|
518
635
|
}
|
|
519
636
|
|
|
520
|
-
if (_pdfDocument && ([
|
|
637
|
+
if (_pdfDocument && ([effectiveChangedProps containsObject:@"path"] || [changedProps containsObject:@"enableRTL"])) {
|
|
521
638
|
_pdfView.displaysRTL = _enableRTL;
|
|
522
639
|
}
|
|
523
640
|
|
|
524
|
-
if (_pdfDocument && ([
|
|
641
|
+
if (_pdfDocument && ([effectiveChangedProps containsObject:@"path"] || [changedProps containsObject:@"enableAnnotationRendering"])) {
|
|
525
642
|
if (!_enableAnnotationRendering) {
|
|
526
643
|
for (unsigned long i=0; i<_pdfView.document.pageCount; i++) {
|
|
527
644
|
PDFPage *pdfPage = [_pdfView.document pageAtIndex:i];
|
|
@@ -532,7 +649,7 @@ using namespace facebook::react;
|
|
|
532
649
|
}
|
|
533
650
|
}
|
|
534
651
|
|
|
535
|
-
if (_pdfDocument && ([
|
|
652
|
+
if (_pdfDocument && ([effectiveChangedProps containsObject:@"path"] || [changedProps containsObject:@"fitPolicy"] || [changedProps containsObject:@"minScale"] || [changedProps containsObject:@"maxScale"])) {
|
|
536
653
|
|
|
537
654
|
PDFPage *pdfPage = _pdfView.currentPage ? _pdfView.currentPage : [_pdfDocument pageAtIndex:_pdfDocument.pageCount-1];
|
|
538
655
|
CGRect pdfPageRect = [pdfPage boundsForBox:kPDFDisplayBoxCropBox];
|
|
@@ -570,49 +687,94 @@ using namespace facebook::react;
|
|
|
570
687
|
|
|
571
688
|
}
|
|
572
689
|
|
|
573
|
-
if (_pdfDocument && ([
|
|
690
|
+
if (_pdfDocument && ([effectiveChangedProps containsObject:@"path"] || [changedProps containsObject:@"scale"])) {
|
|
574
691
|
_pdfView.scaleFactor = _scale * _fixScaleFactor;
|
|
575
692
|
if (_pdfView.scaleFactor>_pdfView.maxScaleFactor) _pdfView.scaleFactor = _pdfView.maxScaleFactor;
|
|
576
693
|
if (_pdfView.scaleFactor<_pdfView.minScaleFactor) _pdfView.scaleFactor = _pdfView.minScaleFactor;
|
|
577
694
|
}
|
|
578
695
|
|
|
579
|
-
if (_pdfDocument && ([
|
|
696
|
+
if (_pdfDocument && ([effectiveChangedProps containsObject:@"path"] || [changedProps containsObject:@"horizontal"])) {
|
|
580
697
|
if (_horizontal) {
|
|
581
698
|
_pdfView.displayDirection = kPDFDisplayDirectionHorizontal;
|
|
582
699
|
_pdfView.pageBreakMargins = UIEdgeInsetsMake(0,_spacing,0,0);
|
|
700
|
+
RCTLogInfo(@"➡️ [iOS Scroll] Set display direction to HORIZONTAL (spacing=%d)", _spacing);
|
|
583
701
|
} else {
|
|
584
702
|
_pdfView.displayDirection = kPDFDisplayDirectionVertical;
|
|
585
703
|
_pdfView.pageBreakMargins = UIEdgeInsetsMake(0,0,_spacing,0);
|
|
704
|
+
RCTLogInfo(@"⬇️ [iOS Scroll] Set display direction to VERTICAL (spacing=%d)", _spacing);
|
|
586
705
|
}
|
|
587
706
|
}
|
|
588
707
|
|
|
589
|
-
|
|
590
|
-
|
|
708
|
+
// CRITICAL FIX: Only configure usePageViewController when path changes (document loading)
|
|
709
|
+
// This prevents unnecessary reconfigurations during scrolling and layout updates
|
|
710
|
+
// Once configured, usePageViewController doesn't need to be reconfigured unless the document changes
|
|
711
|
+
if (_pdfDocument && [effectiveChangedProps containsObject:@"path"]) {
|
|
712
|
+
// Fix: Disable usePageViewController when horizontal is true, as it conflicts with horizontal scrolling
|
|
713
|
+
// UIPageViewController doesn't work well with horizontal PDFView display direction
|
|
714
|
+
BOOL shouldUsePageViewController = _enablePaging && !_horizontal;
|
|
715
|
+
|
|
716
|
+
RCTLogInfo(@"🔄 [iOS Scroll] Configuring usePageViewController on document load - enablePaging=%d, horizontal=%d, usePageVC=%d",
|
|
717
|
+
_enablePaging, _horizontal, shouldUsePageViewController);
|
|
718
|
+
|
|
719
|
+
// Set state immediately
|
|
720
|
+
_currentUsePageViewController = shouldUsePageViewController;
|
|
721
|
+
_usePageViewControllerStateInitialized = YES;
|
|
722
|
+
|
|
723
|
+
// Configure usePageViewController - this only happens on document load
|
|
724
|
+
if (shouldUsePageViewController) {
|
|
725
|
+
// Only use page view controller for vertical orientation
|
|
591
726
|
[_pdfView usePageViewController:YES withViewOptions:@{UIPageViewControllerOptionSpineLocationKey:@(UIPageViewControllerSpineLocationMin),UIPageViewControllerOptionInterPageSpacingKey:@(_spacing)}];
|
|
727
|
+
RCTLogInfo(@"✅ [iOS Scroll] Enabled UIPageViewController (vertical paging mode)");
|
|
592
728
|
} else {
|
|
729
|
+
// For horizontal or when paging is disabled, use regular scrolling
|
|
593
730
|
[_pdfView usePageViewController:NO withViewOptions:Nil];
|
|
731
|
+
RCTLogInfo(@"✅ [iOS Scroll] Disabled UIPageViewController (using regular scrolling)");
|
|
594
732
|
}
|
|
733
|
+
|
|
734
|
+
// Reconfigure scroll view after usePageViewController changes
|
|
735
|
+
// PDFView's internal scroll view hierarchy changes when usePageViewController is toggled
|
|
736
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
737
|
+
// Reset scroll view references to allow reconfiguration
|
|
738
|
+
RCTLogInfo(@"🔄 [iOS Scroll] Resetting scroll view references for reconfiguration");
|
|
739
|
+
self->_internalScrollView = nil;
|
|
740
|
+
self->_originalScrollDelegate = nil;
|
|
741
|
+
self->_scrollDelegateProxy = nil;
|
|
742
|
+
|
|
743
|
+
// Reconfigure scroll view after view hierarchy updates
|
|
744
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
745
|
+
RCTLogInfo(@"🔧 [iOS Scroll] Reconfiguring scroll view after usePageViewController change (scrollEnabled=%d)", self->_scrollEnabled);
|
|
746
|
+
[self configureScrollView:self->_pdfView enabled:self->_scrollEnabled depth:0];
|
|
747
|
+
});
|
|
748
|
+
});
|
|
595
749
|
}
|
|
596
750
|
|
|
597
|
-
if (_pdfDocument && ([
|
|
751
|
+
if (_pdfDocument && ([effectiveChangedProps containsObject:@"path"] || [changedProps containsObject:@"singlePage"])) {
|
|
598
752
|
if (_singlePage) {
|
|
599
753
|
_pdfView.displayMode = kPDFDisplaySinglePage;
|
|
600
754
|
_pdfView.userInteractionEnabled = NO;
|
|
755
|
+
RCTLogInfo(@"📄 [iOS Scroll] Set to SINGLE PAGE mode (userInteractionEnabled=NO)");
|
|
601
756
|
} else {
|
|
602
757
|
_pdfView.displayMode = kPDFDisplaySinglePageContinuous;
|
|
603
758
|
_pdfView.userInteractionEnabled = YES;
|
|
759
|
+
RCTLogInfo(@"📄 [iOS Scroll] Set to CONTINUOUS PAGE mode (userInteractionEnabled=YES)");
|
|
604
760
|
}
|
|
605
761
|
}
|
|
606
762
|
|
|
607
|
-
if (_pdfDocument && ([
|
|
763
|
+
if (_pdfDocument && ([effectiveChangedProps containsObject:@"path"] || [changedProps containsObject:@"showsHorizontalScrollIndicator"] || [changedProps containsObject:@"showsVerticalScrollIndicator"])) {
|
|
608
764
|
[self setScrollIndicators:self horizontal:_showsHorizontalScrollIndicator vertical:_showsVerticalScrollIndicator depth:0];
|
|
609
765
|
}
|
|
610
766
|
|
|
611
767
|
// Configure scroll view (scrollEnabled)
|
|
612
|
-
if (_pdfDocument && ([
|
|
768
|
+
if (_pdfDocument && ([effectiveChangedProps containsObject:@"path"] ||
|
|
613
769
|
[changedProps containsObject:@"scrollEnabled"])) {
|
|
770
|
+
RCTLogInfo(@"🔧 [iOS Scroll] Configuring scroll enabled=%d (path changed=%d, scrollEnabled changed=%d)",
|
|
771
|
+
_scrollEnabled,
|
|
772
|
+
[effectiveChangedProps containsObject:@"path"],
|
|
773
|
+
[changedProps containsObject:@"scrollEnabled"]);
|
|
774
|
+
|
|
614
775
|
// If path changed, restore original delegate before reconfiguring
|
|
615
|
-
if ([
|
|
776
|
+
if ([effectiveChangedProps containsObject:@"path"] && _internalScrollView) {
|
|
777
|
+
RCTLogInfo(@"🔄 [iOS Scroll] Restoring original scroll delegate (path changed)");
|
|
616
778
|
if (_originalScrollDelegate) {
|
|
617
779
|
_internalScrollView.delegate = _originalScrollDelegate;
|
|
618
780
|
} else {
|
|
@@ -626,34 +788,181 @@ using namespace facebook::react;
|
|
|
626
788
|
// Use dispatch_async to ensure view hierarchy is fully set up after document load
|
|
627
789
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
628
790
|
// Search within _pdfView's hierarchy for scroll views
|
|
791
|
+
RCTLogInfo(@"🔍 [iOS Scroll] Starting scroll view search in PDFView hierarchy");
|
|
629
792
|
[self configureScrollView:self->_pdfView enabled:self->_scrollEnabled depth:0];
|
|
630
793
|
});
|
|
631
794
|
}
|
|
632
795
|
|
|
633
|
-
|
|
634
|
-
|
|
796
|
+
// Separate page navigation logic - only navigate when page prop actually changes
|
|
797
|
+
// Skip navigation on initial load (when path changes) to avoid conflicts
|
|
798
|
+
BOOL shouldNavigateToPage = _documentLoaded &&
|
|
799
|
+
[changedProps containsObject:@"page"] &&
|
|
800
|
+
!_isNavigating &&
|
|
801
|
+
_page != _previousPage &&
|
|
802
|
+
_page > 0 &&
|
|
803
|
+
_page <= (int)_pdfDocument.pageCount;
|
|
804
|
+
|
|
805
|
+
// #region agent log
|
|
806
|
+
if ([changedProps containsObject:@"page"]) {
|
|
807
|
+
NSString *logPath14 = @"/Users/punithmanthri/Documents/github jsi folder /react-native-enhanced-pdf/.cursor/debug.log";
|
|
808
|
+
NSDictionary *logEntry14 = @{
|
|
809
|
+
@"sessionId": @"debug-session",
|
|
810
|
+
@"runId": @"init",
|
|
811
|
+
@"hypothesisId": @"A,C,D",
|
|
812
|
+
@"location": @"RNPDFPdfView.mm:803",
|
|
813
|
+
@"message": @"updateProps: page prop changed - checking shouldNavigateToPage",
|
|
814
|
+
@"data": @{
|
|
815
|
+
@"_page": @(_page),
|
|
816
|
+
@"_previousPage": @(_previousPage),
|
|
817
|
+
@"documentLoaded": @(_documentLoaded),
|
|
818
|
+
@"isNavigating": @(_isNavigating),
|
|
819
|
+
@"shouldNavigateToPage": @(shouldNavigateToPage),
|
|
820
|
+
@"contentOffsetBeforeNav": _internalScrollView ? @{@"x": @(_internalScrollView.contentOffset.x), @"y": @(_internalScrollView.contentOffset.y)} : @"noScrollView"
|
|
821
|
+
},
|
|
822
|
+
@"timestamp": @((long long)([[NSDate date] timeIntervalSince1970] * 1000))
|
|
823
|
+
};
|
|
824
|
+
NSData *logData14 = [NSJSONSerialization dataWithJSONObject:logEntry14 options:0 error:nil];
|
|
825
|
+
NSString *logLine14 = [[NSString alloc] initWithData:logData14 encoding:NSUTF8StringEncoding];
|
|
826
|
+
NSFileHandle *fileHandle14 = [NSFileHandle fileHandleForWritingAtPath:logPath14];
|
|
827
|
+
if (fileHandle14) {
|
|
828
|
+
[fileHandle14 seekToEndOfFile];
|
|
829
|
+
[fileHandle14 writeData:[[logLine14 stringByAppendingString:@"\n"] dataUsingEncoding:NSUTF8StringEncoding]];
|
|
830
|
+
[fileHandle14 closeFile];
|
|
831
|
+
} else {
|
|
832
|
+
[[logLine14 stringByAppendingString:@"\n"] writeToFile:logPath14 atomically:YES encoding:NSUTF8StringEncoding error:nil];
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
// #endregion
|
|
836
|
+
|
|
837
|
+
if (shouldNavigateToPage) {
|
|
838
|
+
_isNavigating = YES;
|
|
839
|
+
PDFPage *pdfPage = [_pdfDocument pageAtIndex:_page-1];
|
|
840
|
+
|
|
841
|
+
if (pdfPage) {
|
|
842
|
+
// Use smooth navigation instead of instant jump to prevent full rerender
|
|
843
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
844
|
+
// #region agent log
|
|
845
|
+
CGPoint contentOffsetBefore = self->_internalScrollView ? self->_internalScrollView.contentOffset : CGPointMake(0, 0);
|
|
846
|
+
NSString *logPath15 = @"/Users/punithmanthri/Documents/github jsi folder /react-native-enhanced-pdf/.cursor/debug.log";
|
|
847
|
+
NSDictionary *logEntry15 = @{
|
|
848
|
+
@"sessionId": @"debug-session",
|
|
849
|
+
@"runId": @"init",
|
|
850
|
+
@"hypothesisId": @"B,C",
|
|
851
|
+
@"location": @"RNPDFPdfView.mm:812",
|
|
852
|
+
@"message": @"goToDestination: BEFORE navigation call",
|
|
853
|
+
@"data": @{
|
|
854
|
+
@"targetPage": @(self->_page),
|
|
855
|
+
@"enablePaging": @(self->_enablePaging),
|
|
856
|
+
@"contentOffsetBefore": @{@"x": @(contentOffsetBefore.x), @"y": @(contentOffsetBefore.y)},
|
|
857
|
+
@"isNavigating": @(self->_isNavigating)
|
|
858
|
+
},
|
|
859
|
+
@"timestamp": @((long long)([[NSDate date] timeIntervalSince1970] * 1000))
|
|
860
|
+
};
|
|
861
|
+
NSData *logData15 = [NSJSONSerialization dataWithJSONObject:logEntry15 options:0 error:nil];
|
|
862
|
+
NSString *logLine15 = [[NSString alloc] initWithData:logData15 encoding:NSUTF8StringEncoding];
|
|
863
|
+
NSFileHandle *fileHandle15 = [NSFileHandle fileHandleForWritingAtPath:logPath15];
|
|
864
|
+
if (fileHandle15) {
|
|
865
|
+
[fileHandle15 seekToEndOfFile];
|
|
866
|
+
[fileHandle15 writeData:[[logLine15 stringByAppendingString:@"\n"] dataUsingEncoding:NSUTF8StringEncoding]];
|
|
867
|
+
[fileHandle15 closeFile];
|
|
868
|
+
} else {
|
|
869
|
+
[[logLine15 stringByAppendingString:@"\n"] writeToFile:logPath15 atomically:YES encoding:NSUTF8StringEncoding error:nil];
|
|
870
|
+
}
|
|
871
|
+
// #endregion
|
|
872
|
+
|
|
873
|
+
if (!self->_enablePaging) {
|
|
874
|
+
// For non-paging mode, use animated navigation
|
|
875
|
+
CGRect pdfPageRect = [pdfPage boundsForBox:kPDFDisplayBoxCropBox];
|
|
876
|
+
|
|
877
|
+
// Handle page rotation
|
|
878
|
+
if (pdfPage.rotation == 90 || pdfPage.rotation == 270) {
|
|
879
|
+
pdfPageRect = CGRectMake(0, 0, pdfPageRect.size.height, pdfPageRect.size.width);
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
CGPoint pointLeftTop = CGPointMake(0, pdfPageRect.size.height);
|
|
883
|
+
PDFDestination *pdfDest = [[PDFDestination alloc] initWithPage:pdfPage atPoint:pointLeftTop];
|
|
884
|
+
|
|
885
|
+
// Use goToDestination for smooth navigation
|
|
886
|
+
[self->_pdfView goToDestination:pdfDest];
|
|
887
|
+
self->_pdfView.scaleFactor = self->_fixScaleFactor * self->_scale;
|
|
888
|
+
} else {
|
|
889
|
+
// For paging mode, use goToRect for better page alignment
|
|
890
|
+
if (self->_page == 1) {
|
|
891
|
+
// Special case for first page
|
|
892
|
+
[self->_pdfView goToRect:CGRectMake(0, NSUIntegerMax, 1, 1) onPage:pdfPage];
|
|
893
|
+
} else {
|
|
894
|
+
CGRect pdfPageRect = [pdfPage boundsForBox:kPDFDisplayBoxCropBox];
|
|
895
|
+
if (pdfPage.rotation == 90 || pdfPage.rotation == 270) {
|
|
896
|
+
pdfPageRect = CGRectMake(0, 0, pdfPageRect.size.height, pdfPageRect.size.width);
|
|
897
|
+
}
|
|
898
|
+
CGPoint pointLeftTop = CGPointMake(0, pdfPageRect.size.height);
|
|
899
|
+
PDFDestination *pdfDest = [[PDFDestination alloc] initWithPage:pdfPage atPoint:pointLeftTop];
|
|
900
|
+
[self->_pdfView goToDestination:pdfDest];
|
|
901
|
+
self->_pdfView.scaleFactor = self->_fixScaleFactor * self->_scale;
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
self->_previousPage = self->_page;
|
|
906
|
+
self->_isNavigating = NO;
|
|
907
|
+
|
|
908
|
+
// #region agent log
|
|
909
|
+
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
|
910
|
+
CGPoint contentOffsetAfter = self->_internalScrollView ? self->_internalScrollView.contentOffset : CGPointMake(0, 0);
|
|
911
|
+
NSString *logPath16 = @"/Users/punithmanthri/Documents/github jsi folder /react-native-enhanced-pdf/.cursor/debug.log";
|
|
912
|
+
NSDictionary *logEntry16 = @{
|
|
913
|
+
@"sessionId": @"debug-session",
|
|
914
|
+
@"runId": @"init",
|
|
915
|
+
@"hypothesisId": @"B,C",
|
|
916
|
+
@"location": @"RNPDFPdfView.mm:845",
|
|
917
|
+
@"message": @"goToDestination: AFTER navigation call (100ms delay)",
|
|
918
|
+
@"data": @{
|
|
919
|
+
@"targetPage": @(self->_page),
|
|
920
|
+
@"previousPage": @(self->_previousPage),
|
|
921
|
+
@"contentOffsetBefore": @{@"x": @(contentOffsetBefore.x), @"y": @(contentOffsetBefore.y)},
|
|
922
|
+
@"contentOffsetAfter": @{@"x": @(contentOffsetAfter.x), @"y": @(contentOffsetAfter.y)},
|
|
923
|
+
@"offsetChanged": @(fabs(contentOffsetBefore.x - contentOffsetAfter.x) > 1 || fabs(contentOffsetBefore.y - contentOffsetAfter.y) > 1)
|
|
924
|
+
},
|
|
925
|
+
@"timestamp": @((long long)([[NSDate date] timeIntervalSince1970] * 1000))
|
|
926
|
+
};
|
|
927
|
+
NSData *logData16 = [NSJSONSerialization dataWithJSONObject:logEntry16 options:0 error:nil];
|
|
928
|
+
NSString *logLine16 = [[NSString alloc] initWithData:logData16 encoding:NSUTF8StringEncoding];
|
|
929
|
+
NSFileHandle *fileHandle16 = [NSFileHandle fileHandleForWritingAtPath:logPath16];
|
|
930
|
+
if (fileHandle16) {
|
|
931
|
+
[fileHandle16 seekToEndOfFile];
|
|
932
|
+
[fileHandle16 writeData:[[logLine16 stringByAppendingString:@"\n"] dataUsingEncoding:NSUTF8StringEncoding]];
|
|
933
|
+
[fileHandle16 closeFile];
|
|
934
|
+
} else {
|
|
935
|
+
[[logLine16 stringByAppendingString:@"\n"] writeToFile:logPath16 atomically:YES encoding:NSUTF8StringEncoding error:nil];
|
|
936
|
+
}
|
|
937
|
+
});
|
|
938
|
+
// #endregion
|
|
939
|
+
});
|
|
940
|
+
} else {
|
|
941
|
+
_isNavigating = NO;
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
// Handle initial page on document load (only when path changes)
|
|
946
|
+
// This handles the case where the document was just loaded and we need to navigate to the initial page
|
|
947
|
+
// Use pathActuallyChanged instead of checking changedProps to ensure we only handle initial page when path actually changed
|
|
948
|
+
if (_pdfDocument && pathActuallyChanged && _documentLoaded) {
|
|
635
949
|
PDFPage *pdfPage = [_pdfDocument pageAtIndex:_page-1];
|
|
636
950
|
if (pdfPage && _page == 1) {
|
|
637
|
-
//
|
|
638
|
-
// error in the pointLeftTop computation that often results in
|
|
639
|
-
// scrolling to the middle of the page.
|
|
640
|
-
// Special case workaround to make starting at the first page
|
|
641
|
-
// align acceptably.
|
|
951
|
+
// Special case workaround for first page alignment
|
|
642
952
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
643
953
|
[self->_pdfView goToRect:CGRectMake(0, NSUIntegerMax, 1, 1) onPage:pdfPage];
|
|
954
|
+
self->_previousPage = self->_page;
|
|
644
955
|
});
|
|
645
956
|
} else if (pdfPage) {
|
|
646
957
|
CGRect pdfPageRect = [pdfPage boundsForBox:kPDFDisplayBoxCropBox];
|
|
647
|
-
|
|
648
|
-
// some pdf with rotation, then adjust it
|
|
649
958
|
if (pdfPage.rotation == 90 || pdfPage.rotation == 270) {
|
|
650
959
|
pdfPageRect = CGRectMake(0, 0, pdfPageRect.size.height, pdfPageRect.size.width);
|
|
651
960
|
}
|
|
652
|
-
|
|
653
961
|
CGPoint pointLeftTop = CGPointMake(0, pdfPageRect.size.height);
|
|
654
962
|
PDFDestination *pdfDest = [[PDFDestination alloc] initWithPage:pdfPage atPoint:pointLeftTop];
|
|
655
963
|
[_pdfView goToDestination:pdfDest];
|
|
656
964
|
_pdfView.scaleFactor = _fixScaleFactor*_scale;
|
|
965
|
+
_previousPage = _page;
|
|
657
966
|
}
|
|
658
967
|
}
|
|
659
968
|
|
|
@@ -676,6 +985,15 @@ using namespace facebook::react;
|
|
|
676
985
|
_initialed = YES;
|
|
677
986
|
|
|
678
987
|
[self didSetProps:mProps];
|
|
988
|
+
|
|
989
|
+
// Configure scroll view after layout to ensure it's found
|
|
990
|
+
// This is important because PDFKit creates the scroll view lazily
|
|
991
|
+
if (_documentLoaded && _pdfDocument) {
|
|
992
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
993
|
+
RCTLogInfo(@"🔍 [iOS Scroll] reactSetFrame called, configuring scroll view after layout");
|
|
994
|
+
[self configureScrollView:self->_pdfView enabled:self->_scrollEnabled depth:0];
|
|
995
|
+
});
|
|
996
|
+
}
|
|
679
997
|
}
|
|
680
998
|
|
|
681
999
|
|
|
@@ -849,7 +1167,21 @@ using namespace facebook::react;
|
|
|
849
1167
|
unsigned long numberOfPages = _pdfDocument.pageCount;
|
|
850
1168
|
|
|
851
1169
|
// Update current page for preloading
|
|
852
|
-
|
|
1170
|
+
int newPage = (int)page + 1;
|
|
1171
|
+
|
|
1172
|
+
// CRITICAL FIX: Update _previousPage to the new page value when page changes from PDFView notifications
|
|
1173
|
+
// This prevents updateProps from triggering programmatic navigation when React Native
|
|
1174
|
+
// receives the pageChanged notification and updates the page prop back to us.
|
|
1175
|
+
// By setting _previousPage = newPage, when updateProps checks _page != _previousPage,
|
|
1176
|
+
// they will be equal (since the page prop will match the new page), and navigation will be skipped.
|
|
1177
|
+
if (newPage != _page) {
|
|
1178
|
+
_previousPage = newPage; // Set to newPage to prevent navigation loop
|
|
1179
|
+
_page = newPage;
|
|
1180
|
+
} else {
|
|
1181
|
+
// If page didn't actually change, just ensure _previousPage matches to prevent navigation
|
|
1182
|
+
_previousPage = _page;
|
|
1183
|
+
}
|
|
1184
|
+
|
|
853
1185
|
_pageCount = (int)numberOfPages;
|
|
854
1186
|
if (_enablePreloading) {
|
|
855
1187
|
[self preloadAdjacentPages:_page];
|
|
@@ -1064,48 +1396,253 @@ using namespace facebook::react;
|
|
|
1064
1396
|
}
|
|
1065
1397
|
|
|
1066
1398
|
- (void)configureScrollView:(UIView *)view enabled:(BOOL)enabled depth:(int)depth {
|
|
1399
|
+
// Log entry to track all calls
|
|
1400
|
+
if (depth == 0) {
|
|
1401
|
+
RCTLogInfo(@"🚀 [iOS Scroll] configureScrollView called - enabled=%d, view=%@", enabled, NSStringFromClass([view class]));
|
|
1402
|
+
}
|
|
1403
|
+
|
|
1067
1404
|
// max depth, prevent infinite loop
|
|
1068
1405
|
if (depth > 10) {
|
|
1406
|
+
RCTLogWarn(@"⚠️ [iOS Scroll] Max depth reached in configureScrollView (depth=%d)", depth);
|
|
1069
1407
|
return;
|
|
1070
1408
|
}
|
|
1071
1409
|
|
|
1072
1410
|
if ([view isKindOfClass:[UIScrollView class]]) {
|
|
1073
1411
|
UIScrollView *scrollView = (UIScrollView *)view;
|
|
1412
|
+
RCTLogInfo(@"📱 [iOS Scroll] Found UIScrollView at depth=%d, frame=%@, contentSize=%@, enabled=%d",
|
|
1413
|
+
depth,
|
|
1414
|
+
NSStringFromCGRect(scrollView.frame),
|
|
1415
|
+
NSStringFromCGSize(scrollView.contentSize),
|
|
1416
|
+
enabled);
|
|
1417
|
+
|
|
1418
|
+
// #region agent log
|
|
1419
|
+
{
|
|
1420
|
+
NSString *logPath1 = @"/Users/punithmanthri/Documents/github jsi folder /react-native-enhanced-pdf/.cursor/debug.log";
|
|
1421
|
+
NSDictionary *logEntry1 = @{
|
|
1422
|
+
@"sessionId": @"debug-session",
|
|
1423
|
+
@"runId": @"init",
|
|
1424
|
+
@"hypothesisId": @"D,F",
|
|
1425
|
+
@"location": @"RNPDFPdfView.mm:1307",
|
|
1426
|
+
@"message": @"Found UIScrollView in hierarchy",
|
|
1427
|
+
@"data": @{
|
|
1428
|
+
@"depth": @(depth),
|
|
1429
|
+
@"contentSize": @{@"width": @(scrollView.contentSize.width), @"height": @(scrollView.contentSize.height)},
|
|
1430
|
+
@"frame": @{@"x": @(scrollView.frame.origin.x), @"y": @(scrollView.frame.origin.y), @"width": @(scrollView.frame.size.width), @"height": @(scrollView.frame.size.height)},
|
|
1431
|
+
@"scrollEnabled": @(scrollView.scrollEnabled),
|
|
1432
|
+
@"alwaysBounceHorizontal": @(scrollView.alwaysBounceHorizontal),
|
|
1433
|
+
@"userInteractionEnabled": @(scrollView.userInteractionEnabled),
|
|
1434
|
+
@"horizontal": @(_horizontal),
|
|
1435
|
+
@"enablePaging": @(_enablePaging)
|
|
1436
|
+
},
|
|
1437
|
+
@"timestamp": @((long long)([[NSDate date] timeIntervalSince1970] * 1000))
|
|
1438
|
+
};
|
|
1439
|
+
NSData *logData1 = [NSJSONSerialization dataWithJSONObject:logEntry1 options:0 error:nil];
|
|
1440
|
+
NSString *logLine1 = [[NSString alloc] initWithData:logData1 encoding:NSUTF8StringEncoding];
|
|
1441
|
+
NSFileHandle *fileHandle1 = [NSFileHandle fileHandleForWritingAtPath:logPath1];
|
|
1442
|
+
if (fileHandle1) {
|
|
1443
|
+
[fileHandle1 seekToEndOfFile];
|
|
1444
|
+
[fileHandle1 writeData:[[logLine1 stringByAppendingString:@"\n"] dataUsingEncoding:NSUTF8StringEncoding]];
|
|
1445
|
+
[fileHandle1 closeFile];
|
|
1446
|
+
} else {
|
|
1447
|
+
[[logLine1 stringByAppendingString:@"\n"] writeToFile:logPath1 atomically:YES encoding:NSUTF8StringEncoding error:nil];
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
// #endregion
|
|
1451
|
+
|
|
1074
1452
|
// Since we're starting the recursion from _pdfView, all scroll views found are within its hierarchy
|
|
1075
1453
|
// Configure scroll properties
|
|
1454
|
+
BOOL previousScrollEnabled = scrollView.scrollEnabled;
|
|
1076
1455
|
scrollView.scrollEnabled = enabled;
|
|
1077
1456
|
|
|
1078
|
-
|
|
1457
|
+
if (previousScrollEnabled != enabled) {
|
|
1458
|
+
RCTLogInfo(@"🔄 [iOS Scroll] Changed scrollEnabled: %d -> %d", previousScrollEnabled, enabled);
|
|
1459
|
+
}
|
|
1460
|
+
|
|
1461
|
+
// Conditionally set horizontal bouncing based on scroll direction
|
|
1462
|
+
// Allow horizontal bounce when horizontal scrolling is enabled
|
|
1463
|
+
BOOL previousAlwaysBounceHorizontal = scrollView.alwaysBounceHorizontal;
|
|
1464
|
+
if (_horizontal) {
|
|
1465
|
+
scrollView.alwaysBounceHorizontal = YES;
|
|
1466
|
+
} else {
|
|
1467
|
+
// Disable horizontal bouncing for vertical scrolling to prevent interference with navigation swipe-back
|
|
1079
1468
|
scrollView.alwaysBounceHorizontal = NO;
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
if (previousAlwaysBounceHorizontal != scrollView.alwaysBounceHorizontal) {
|
|
1472
|
+
RCTLogInfo(@"🔄 [iOS Scroll] Changed alwaysBounceHorizontal: %d -> %d (horizontal=%d)",
|
|
1473
|
+
previousAlwaysBounceHorizontal,
|
|
1474
|
+
scrollView.alwaysBounceHorizontal,
|
|
1475
|
+
_horizontal);
|
|
1476
|
+
}
|
|
1477
|
+
|
|
1080
1478
|
// Keep vertical bounce enabled for natural scrolling feel
|
|
1081
1479
|
scrollView.bounces = YES;
|
|
1480
|
+
|
|
1481
|
+
RCTLogInfo(@"📊 [iOS Scroll] ScrollView config - scrollEnabled=%d, alwaysBounceHorizontal=%d, bounces=%d, delegate=%@",
|
|
1482
|
+
scrollView.scrollEnabled,
|
|
1483
|
+
scrollView.alwaysBounceHorizontal,
|
|
1484
|
+
scrollView.bounces,
|
|
1485
|
+
scrollView.delegate != nil ? @"set" : @"nil");
|
|
1486
|
+
|
|
1487
|
+
// #region agent log
|
|
1488
|
+
{
|
|
1489
|
+
NSString *logPath3 = @"/Users/punithmanthri/Documents/github jsi folder /react-native-enhanced-pdf/.cursor/debug.log";
|
|
1490
|
+
NSDictionary *logEntry3 = @{
|
|
1491
|
+
@"sessionId": @"debug-session",
|
|
1492
|
+
@"runId": @"init",
|
|
1493
|
+
@"hypothesisId": @"A,B,C,D,E",
|
|
1494
|
+
@"location": @"RNPDFPdfView.mm:1374",
|
|
1495
|
+
@"message": @"ScrollView configuration completed",
|
|
1496
|
+
@"data": @{
|
|
1497
|
+
@"scrollEnabled": @(scrollView.scrollEnabled),
|
|
1498
|
+
@"alwaysBounceHorizontal": @(scrollView.alwaysBounceHorizontal),
|
|
1499
|
+
@"bounces": @(scrollView.bounces),
|
|
1500
|
+
@"contentSize": @{@"width": @(scrollView.contentSize.width), @"height": @(scrollView.contentSize.height)},
|
|
1501
|
+
@"userInteractionEnabled": @(scrollView.userInteractionEnabled),
|
|
1502
|
+
@"delegate": scrollView.delegate != nil ? @"set" : @"nil",
|
|
1503
|
+
@"horizontal": @(_horizontal),
|
|
1504
|
+
@"enablePaging": @(_enablePaging)
|
|
1505
|
+
},
|
|
1506
|
+
@"timestamp": @((long long)([[NSDate date] timeIntervalSince1970] * 1000))
|
|
1507
|
+
};
|
|
1508
|
+
NSData *logData3 = [NSJSONSerialization dataWithJSONObject:logEntry3 options:0 error:nil];
|
|
1509
|
+
NSString *logLine3 = [[NSString alloc] initWithData:logData3 encoding:NSUTF8StringEncoding];
|
|
1510
|
+
NSFileHandle *fileHandle3 = [NSFileHandle fileHandleForWritingAtPath:logPath3];
|
|
1511
|
+
if (fileHandle3) {
|
|
1512
|
+
[fileHandle3 seekToEndOfFile];
|
|
1513
|
+
[fileHandle3 writeData:[[logLine3 stringByAppendingString:@"\n"] dataUsingEncoding:NSUTF8StringEncoding]];
|
|
1514
|
+
[fileHandle3 closeFile];
|
|
1515
|
+
} else {
|
|
1516
|
+
[[logLine3 stringByAppendingString:@"\n"] writeToFile:logPath3 atomically:YES encoding:NSUTF8StringEncoding error:nil];
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
// #endregion
|
|
1082
1520
|
|
|
1083
1521
|
// IMPORTANT: PDFKit relies on the scrollView delegate for pinch-zoom (viewForZoomingInScrollView).
|
|
1084
1522
|
// Install a proxy delegate that forwards to the original delegate, while still letting us observe scroll events.
|
|
1085
1523
|
if (!_internalScrollView) {
|
|
1524
|
+
RCTLogInfo(@"✅ [iOS Scroll] Setting internal scroll view reference");
|
|
1086
1525
|
_internalScrollView = scrollView;
|
|
1087
1526
|
if (scrollView.delegate && scrollView.delegate != self) {
|
|
1088
1527
|
_originalScrollDelegate = scrollView.delegate;
|
|
1528
|
+
RCTLogInfo(@"📝 [iOS Scroll] Stored original scroll delegate");
|
|
1089
1529
|
}
|
|
1090
1530
|
if (_originalScrollDelegate) {
|
|
1091
1531
|
_scrollDelegateProxy = [[RNPDFScrollViewDelegateProxy alloc] initWithPrimary:_originalScrollDelegate secondary:(id<UIScrollViewDelegate>)self];
|
|
1092
1532
|
scrollView.delegate = (id<UIScrollViewDelegate>)_scrollDelegateProxy;
|
|
1533
|
+
RCTLogInfo(@"🔗 [iOS Scroll] Installed scroll delegate proxy");
|
|
1093
1534
|
} else {
|
|
1094
1535
|
scrollView.delegate = self;
|
|
1536
|
+
RCTLogInfo(@"🔗 [iOS Scroll] Set self as scroll delegate");
|
|
1095
1537
|
}
|
|
1538
|
+
} else {
|
|
1539
|
+
RCTLogInfo(@"⚠️ [iOS Scroll] Internal scroll view already set, skipping delegate setup");
|
|
1096
1540
|
}
|
|
1097
1541
|
}
|
|
1098
1542
|
|
|
1099
1543
|
for (UIView *subview in view.subviews) {
|
|
1100
1544
|
[self configureScrollView:subview enabled:enabled depth:depth + 1];
|
|
1101
1545
|
}
|
|
1546
|
+
|
|
1547
|
+
// Log at root level if no scroll view was found
|
|
1548
|
+
if (depth == 0 && !_internalScrollView) {
|
|
1549
|
+
RCTLogWarn(@"⚠️ [iOS Scroll] No UIScrollView found in view hierarchy (view=%@, subviewCount=%lu)",
|
|
1550
|
+
NSStringFromClass([view class]),
|
|
1551
|
+
(unsigned long)[view.subviews count]);
|
|
1552
|
+
|
|
1553
|
+
// #region agent log
|
|
1554
|
+
{
|
|
1555
|
+
NSString *logPath6 = @"/Users/punithmanthri/Documents/github jsi folder /react-native-enhanced-pdf/.cursor/debug.log";
|
|
1556
|
+
NSMutableArray *subviewClasses = [NSMutableArray array];
|
|
1557
|
+
for (UIView *subview in view.subviews) {
|
|
1558
|
+
[subviewClasses addObject:NSStringFromClass([subview class])];
|
|
1559
|
+
}
|
|
1560
|
+
NSDictionary *logEntry6 = @{
|
|
1561
|
+
@"sessionId": @"debug-session",
|
|
1562
|
+
@"runId": @"init",
|
|
1563
|
+
@"hypothesisId": @"F",
|
|
1564
|
+
@"location": @"RNPDFPdfView.mm:1446",
|
|
1565
|
+
@"message": @"No UIScrollView found in hierarchy",
|
|
1566
|
+
@"data": @{
|
|
1567
|
+
@"viewClass": NSStringFromClass([view class]),
|
|
1568
|
+
@"subviewCount": @([view.subviews count]),
|
|
1569
|
+
@"subviewClasses": subviewClasses,
|
|
1570
|
+
@"horizontal": @(_horizontal),
|
|
1571
|
+
@"enablePaging": @(_enablePaging)
|
|
1572
|
+
},
|
|
1573
|
+
@"timestamp": @((long long)([[NSDate date] timeIntervalSince1970] * 1000))
|
|
1574
|
+
};
|
|
1575
|
+
NSData *logData6 = [NSJSONSerialization dataWithJSONObject:logEntry6 options:0 error:nil];
|
|
1576
|
+
NSString *logLine6 = [[NSString alloc] initWithData:logData6 encoding:NSUTF8StringEncoding];
|
|
1577
|
+
NSFileHandle *fileHandle6 = [NSFileHandle fileHandleForWritingAtPath:logPath6];
|
|
1578
|
+
if (fileHandle6) {
|
|
1579
|
+
[fileHandle6 seekToEndOfFile];
|
|
1580
|
+
[fileHandle6 writeData:[[logLine6 stringByAppendingString:@"\n"] dataUsingEncoding:NSUTF8StringEncoding]];
|
|
1581
|
+
[fileHandle6 closeFile];
|
|
1582
|
+
} else {
|
|
1583
|
+
[[logLine6 stringByAppendingString:@"\n"] writeToFile:logPath6 atomically:YES encoding:NSUTF8StringEncoding error:nil];
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1586
|
+
// #endregion
|
|
1587
|
+
}
|
|
1102
1588
|
}
|
|
1103
1589
|
|
|
1104
1590
|
#pragma mark - UIScrollViewDelegate
|
|
1105
1591
|
|
|
1106
1592
|
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
|
|
1593
|
+
static int scrollEventCount = 0;
|
|
1594
|
+
scrollEventCount++;
|
|
1595
|
+
|
|
1596
|
+
// Log scroll events periodically (every 10th event to avoid spam)
|
|
1597
|
+
if (scrollEventCount % 10 == 0) {
|
|
1598
|
+
RCTLogInfo(@"📜 [iOS Scroll] scrollViewDidScroll #%d - offset=(%.2f, %.2f), contentSize=(%.2f, %.2f), bounds=(%.2f, %.2f), scrollEnabled=%d",
|
|
1599
|
+
scrollEventCount,
|
|
1600
|
+
scrollView.contentOffset.x,
|
|
1601
|
+
scrollView.contentOffset.y,
|
|
1602
|
+
scrollView.contentSize.width,
|
|
1603
|
+
scrollView.contentSize.height,
|
|
1604
|
+
scrollView.bounds.size.width,
|
|
1605
|
+
scrollView.bounds.size.height,
|
|
1606
|
+
scrollView.scrollEnabled);
|
|
1607
|
+
|
|
1608
|
+
// #region agent log
|
|
1609
|
+
{
|
|
1610
|
+
NSString *logPath2 = @"/Users/punithmanthri/Documents/github jsi folder /react-native-enhanced-pdf/.cursor/debug.log";
|
|
1611
|
+
NSDictionary *logEntry2 = @{
|
|
1612
|
+
@"sessionId": @"debug-session",
|
|
1613
|
+
@"runId": @"init",
|
|
1614
|
+
@"hypothesisId": @"B",
|
|
1615
|
+
@"location": @"RNPDFPdfView.mm:1300",
|
|
1616
|
+
@"message": @"scrollViewDidScroll called",
|
|
1617
|
+
@"data": @{
|
|
1618
|
+
@"eventCount": @(scrollEventCount),
|
|
1619
|
+
@"contentOffset": @{@"x": @(scrollView.contentOffset.x), @"y": @(scrollView.contentOffset.y)},
|
|
1620
|
+
@"contentSize": @{@"width": @(scrollView.contentSize.width), @"height": @(scrollView.contentSize.height)},
|
|
1621
|
+
@"bounds": @{@"width": @(scrollView.bounds.size.width), @"height": @(scrollView.bounds.size.height)},
|
|
1622
|
+
@"scrollEnabled": @(scrollView.scrollEnabled),
|
|
1623
|
+
@"alwaysBounceHorizontal": @(scrollView.alwaysBounceHorizontal)
|
|
1624
|
+
},
|
|
1625
|
+
@"timestamp": @((long long)([[NSDate date] timeIntervalSince1970] * 1000))
|
|
1626
|
+
};
|
|
1627
|
+
NSData *logData2 = [NSJSONSerialization dataWithJSONObject:logEntry2 options:0 error:nil];
|
|
1628
|
+
NSString *logLine2 = [[NSString alloc] initWithData:logData2 encoding:NSUTF8StringEncoding];
|
|
1629
|
+
NSFileHandle *fileHandle2 = [NSFileHandle fileHandleForWritingAtPath:logPath2];
|
|
1630
|
+
if (fileHandle2) {
|
|
1631
|
+
[fileHandle2 seekToEndOfFile];
|
|
1632
|
+
[fileHandle2 writeData:[[logLine2 stringByAppendingString:@"\n"] dataUsingEncoding:NSUTF8StringEncoding]];
|
|
1633
|
+
[fileHandle2 closeFile];
|
|
1634
|
+
} else {
|
|
1635
|
+
[[logLine2 stringByAppendingString:@"\n"] writeToFile:logPath2 atomically:YES encoding:NSUTF8StringEncoding error:nil];
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1638
|
+
// #endregion
|
|
1639
|
+
}
|
|
1107
1640
|
|
|
1108
1641
|
if (!_pdfDocument || _singlePage) {
|
|
1642
|
+
if (scrollEventCount % 10 == 0) {
|
|
1643
|
+
RCTLogInfo(@"⏭️ [iOS Scroll] Skipping scroll handling - pdfDocument=%d, singlePage=%d",
|
|
1644
|
+
_pdfDocument != nil, _singlePage);
|
|
1645
|
+
}
|
|
1109
1646
|
return;
|
|
1110
1647
|
}
|
|
1111
1648
|
|
|
@@ -1126,7 +1663,46 @@ using namespace facebook::react;
|
|
|
1126
1663
|
|
|
1127
1664
|
// Only update if page actually changed and is valid
|
|
1128
1665
|
if (newPage != _page && newPage > 0 && newPage <= (int)_pdfDocument.pageCount) {
|
|
1666
|
+
RCTLogInfo(@"📄 [iOS Scroll] Page changed: %d -> %d (from scroll position)", _page, newPage);
|
|
1667
|
+
// #region agent log
|
|
1668
|
+
{
|
|
1669
|
+
NSString *logPath12 = @"/Users/punithmanthri/Documents/github jsi folder /react-native-enhanced-pdf/.cursor/debug.log";
|
|
1670
|
+
NSDictionary *logEntry12 = @{
|
|
1671
|
+
@"sessionId": @"debug-session",
|
|
1672
|
+
@"runId": @"init",
|
|
1673
|
+
@"hypothesisId": @"A,C,D",
|
|
1674
|
+
@"location": @"RNPDFPdfView.mm:1558",
|
|
1675
|
+
@"message": @"scrollViewDidScroll detected page change - BEFORE updating _page",
|
|
1676
|
+
@"data": @{
|
|
1677
|
+
@"oldPage": @(_page),
|
|
1678
|
+
@"newPage": @(newPage),
|
|
1679
|
+
@"previousPage": @(_previousPage),
|
|
1680
|
+
@"contentOffset": @{@"x": @(scrollView.contentOffset.x), @"y": @(scrollView.contentOffset.y)},
|
|
1681
|
+
@"isNavigating": @(_isNavigating)
|
|
1682
|
+
},
|
|
1683
|
+
@"timestamp": @((long long)([[NSDate date] timeIntervalSince1970] * 1000))
|
|
1684
|
+
};
|
|
1685
|
+
NSData *logData12 = [NSJSONSerialization dataWithJSONObject:logEntry12 options:0 error:nil];
|
|
1686
|
+
NSString *logLine12 = [[NSString alloc] initWithData:logData12 encoding:NSUTF8StringEncoding];
|
|
1687
|
+
NSFileHandle *fileHandle12 = [NSFileHandle fileHandleForWritingAtPath:logPath12];
|
|
1688
|
+
if (fileHandle12) {
|
|
1689
|
+
[fileHandle12 seekToEndOfFile];
|
|
1690
|
+
[fileHandle12 writeData:[[logLine12 stringByAppendingString:@"\n"] dataUsingEncoding:NSUTF8StringEncoding]];
|
|
1691
|
+
[fileHandle12 closeFile];
|
|
1692
|
+
} else {
|
|
1693
|
+
[[logLine12 stringByAppendingString:@"\n"] writeToFile:logPath12 atomically:YES encoding:NSUTF8StringEncoding error:nil];
|
|
1694
|
+
}
|
|
1695
|
+
}
|
|
1696
|
+
// #endregion
|
|
1697
|
+
|
|
1698
|
+
// CRITICAL FIX: Update _previousPage to the new page value when page changes from user scrolling
|
|
1699
|
+
// This prevents updateProps from triggering programmatic navigation when React Native
|
|
1700
|
+
// receives the pageChanged notification and updates the page prop back to us.
|
|
1701
|
+
// By setting _previousPage = newPage, when updateProps checks _page != _previousPage,
|
|
1702
|
+
// they will be equal (since React Native will set _page = newPage), and navigation will be skipped.
|
|
1703
|
+
int oldPage = _page;
|
|
1129
1704
|
_page = newPage;
|
|
1705
|
+
_previousPage = newPage; // Set to newPage to prevent navigation loop
|
|
1130
1706
|
_pageCount = (int)_pdfDocument.pageCount;
|
|
1131
1707
|
|
|
1132
1708
|
// Trigger preloading if enabled
|
|
@@ -1136,6 +1712,39 @@ using namespace facebook::react;
|
|
|
1136
1712
|
|
|
1137
1713
|
// Notify about page change
|
|
1138
1714
|
[self notifyOnChangeWithMessage:[[NSString alloc] initWithString:[NSString stringWithFormat:@"pageChanged|%d|%lu", newPage, _pdfDocument.pageCount]]];
|
|
1715
|
+
// #region agent log
|
|
1716
|
+
{
|
|
1717
|
+
NSString *logPath13 = @"/Users/punithmanthri/Documents/github jsi folder /react-native-enhanced-pdf/.cursor/debug.log";
|
|
1718
|
+
NSDictionary *logEntry13 = @{
|
|
1719
|
+
@"sessionId": @"debug-session",
|
|
1720
|
+
@"runId": @"init",
|
|
1721
|
+
@"hypothesisId": @"A,C,D",
|
|
1722
|
+
@"location": @"RNPDFPdfView.mm:1570",
|
|
1723
|
+
@"message": @"scrollViewDidScroll detected page change - AFTER updating _page and _previousPage (to prevent navigation loop)",
|
|
1724
|
+
@"data": @{
|
|
1725
|
+
@"_page": @(_page),
|
|
1726
|
+
@"_previousPage": @(_previousPage),
|
|
1727
|
+
@"notificationSent": @YES
|
|
1728
|
+
},
|
|
1729
|
+
@"timestamp": @((long long)([[NSDate date] timeIntervalSince1970] * 1000))
|
|
1730
|
+
};
|
|
1731
|
+
NSData *logData13 = [NSJSONSerialization dataWithJSONObject:logEntry13 options:0 error:nil];
|
|
1732
|
+
NSString *logLine13 = [[NSString alloc] initWithData:logData13 encoding:NSUTF8StringEncoding];
|
|
1733
|
+
NSFileHandle *fileHandle13 = [NSFileHandle fileHandleForWritingAtPath:logPath13];
|
|
1734
|
+
if (fileHandle13) {
|
|
1735
|
+
[fileHandle13 seekToEndOfFile];
|
|
1736
|
+
[fileHandle13 writeData:[[logLine13 stringByAppendingString:@"\n"] dataUsingEncoding:NSUTF8StringEncoding]];
|
|
1737
|
+
[fileHandle13 closeFile];
|
|
1738
|
+
} else {
|
|
1739
|
+
[[logLine13 stringByAppendingString:@"\n"] writeToFile:logPath13 atomically:YES encoding:NSUTF8StringEncoding error:nil];
|
|
1740
|
+
}
|
|
1741
|
+
}
|
|
1742
|
+
// #endregion
|
|
1743
|
+
}
|
|
1744
|
+
} else {
|
|
1745
|
+
if (scrollEventCount % 50 == 0) {
|
|
1746
|
+
RCTLogWarn(@"⚠️ [iOS Scroll] No visible page found for scroll position (%.2f, %.2f)",
|
|
1747
|
+
pdfPoint.x, pdfPoint.y);
|
|
1139
1748
|
}
|
|
1140
1749
|
}
|
|
1141
1750
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-pdf-jsi",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.2.0",
|
|
4
4
|
"summary": "High-performance React Native PDF viewer with JSI acceleration - up to 80x faster than traditional bridge",
|
|
5
5
|
"description": "🚀 Ultra-fast React Native PDF viewer with JSI (JavaScript Interface) integration for maximum performance. Features lazy loading, smart caching, progressive loading, and zero-bridge overhead operations. Perfect for large PDF files with 30-day persistent cache and advanced memory optimization. Google Play 16KB page size compliant for Android 15+. Supports iOS, Android, and Windows platforms.",
|
|
6
6
|
"main": "index.js",
|