App更新注意

1.android 代码不更新
react-native bundle --platform android --dev false --entry-file index.js --bundle-output android/app/src/main/assets/index.android.bundle --assets-dest android/app/src/main/res

2.iphone 12 底部适配问题
https://www.jianshu.com/p/e1da502fac49

  1. "react-native-share": "1.2.1",
    崩溃问题

UIViewController *controller = RCTPresentedViewController();
shareController.completionWithItemsHandler = ^(NSString *activityType, BOOL completed, __unused NSArray *returnedItems, NSError *activityError) {
if (activityError) {
failureCallback(activityError);
} else if (completed){
successCallback(@[@(completed), RCTNullIfNil(activityType)]);
}
};

分享Telegram app 分享失败

RCTActionSheetManager.m // 145行

// [items addObject:data];

  UIImage *image = [UIImage imageWithData: data];
  if(image) {
        [items addObject:image];
      }

android中 TargetChosenReceiver.java 56行

  Intent intent = new Intent(sTargetChosenReceiveAction);
        intent.setPackage(reactContext.getPackageName());
        intent.putExtra(EXTRA_RECEIVER_TOKEN, sLastRegisteredReceiver.hashCode());

        final PendingIntent callback = PendingIntent.getBroadcast(reactContext, 0, intent,
                Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ?
                        PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_IMMUTABLE :
                        PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);

//        final PendingIntent callback = PendingIntent.getBroadcast(reactContext, 0, intent,
//                PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);

4.ios OPPOSans 字体修复

RCTFont.mm 335行修改

if (![familyName isEqualToString:@"OPPOSans"]) {
// Get the closest font that matches the given weight for the fontFamily
CGFloat closestWeight = INFINITY;
for (NSString *name in [UIFont fontNamesForFamilyName:familyName]) {
UIFont *match = [UIFont fontWithName:name size:fontSize];
if (isItalic == isItalicFont(match) &&
isCondensed == isCondensedFont(match)) {
CGFloat testWeight = weightOfFont(match);
if (ABS(testWeight - fontWeight) < ABS(closestWeight - fontWeight)) {
font = match;
closestWeight = testWeight;
}
}
}
}

5.android 编译需要 升级到 支持androidX


image.png

6.ios 运行
(一)


image.png

替换 parseIOSDevicesList.js runIOS.js

image.png
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;

/**
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @format
 *  strict
 */

/**
 * Parses the output of `xcrun simctl list devices` command
 */
function parseIOSDevicesList(text) {
  const devices = [];
  text.stdout.split('\n').forEach(line => {
    const device = line.match(
        /(.*?) (\(([0-9.]+)\) )?\(([0-9A-F-]+)\)( \(Simulator\))?/i,
    )
    if (device) {
      const [, name,, version, udid, isSimulator] = device;
      const metadata = {
        name,
        udid
      };

      if (version) {
        metadata.version = version;
        metadata.type = isSimulator ? 'simulator' : 'device';
      } else {
        metadata.type = 'catalyst';
      }

      devices.push(metadata);
    }
  });
  return devices;
}

var _default = parseIOSDevicesList;
exports.default = _default;
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;

function _child_process() {
  const data = _interopRequireDefault(require("child_process"));

  _child_process = function () {
    return data;
  };

  return data;
}

function _execa() {
  const data = _interopRequireDefault(require("execa"));

  _execa = function () {
    return data;
  };

  return data;
}

function _fs() {
  const data = _interopRequireDefault(require("fs"));

  _fs = function () {
    return data;
  };

  return data;
}

function _path() {
  const data = _interopRequireDefault(require("path"));

  _path = function () {
    return data;
  };

  return data;
}

var _findXcodeProject = _interopRequireDefault(require("./findXcodeProject"));

var _parseIOSDevicesList = _interopRequireDefault(require("./parseIOSDevicesList"));

var _findMatchingSimulator = _interopRequireDefault(require("./findMatchingSimulator"));

var _errors = require("../../tools/errors");

var _logger = _interopRequireDefault(require("../../tools/logger"));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; }

function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }

function runIOS(_, ctx, args) {
  if (!_fs().default.existsSync(args.projectPath)) {
    throw new Error('iOS project folder not found. Are you sure this is a React Native project?');
  }

  process.chdir(args.projectPath);
  const xcodeProject = (0, _findXcodeProject.default)(_fs().default.readdirSync('.'));

  if (!xcodeProject) {
    throw new Error(`Could not find Xcode project files in "${args.projectPath}" folder`);
  }

  const inferredSchemeName = _path().default.basename(xcodeProject.name, _path().default.extname(xcodeProject.name));

  const scheme = args.scheme || inferredSchemeName;

  _logger.default.info(`Found Xcode ${xcodeProject.isWorkspace ? 'workspace' : 'project'} ${xcodeProject.name}`);

  // const devices = (0, _parseIOSDevicesList.default)( // $FlowExpectedError https://github.com/facebook/flow/issues/5675
  // _child_process().default.execFileSync('xcrun', ['instruments', '-s'], {
  //   encoding: 'utf8'
  // }));

  const devices = (0, _parseIOSDevicesList.default)(_execa().default.sync('xcrun', ['xctrace', 'list', 'devices']));


  if (args.device) {
    const selectedDevice = matchingDevice(devices, args.device);

    if (selectedDevice) {
      return runOnDevice(selectedDevice, scheme, xcodeProject, args.configuration, args.packager, args.verbose, args.port);
    }

    if (devices && devices.length > 0) {
      // $FlowIssue: args.device is defined in this context
      _logger.default.error(`Could not find device with the name: "${args.device}".
Choose one of the following:${printFoundDevices(devices)}`);
    } else {
      _logger.default.error('No iOS devices connected.');
    }
  } else if (args.udid) {
    // $FlowIssue: args.udid is defined in this context
    return runOnDeviceByUdid(args, scheme, xcodeProject, devices);
  }

  return runOnSimulator(xcodeProject, args, scheme);
}

function runOnDeviceByUdid(args, scheme, xcodeProject, devices) {
  const selectedDevice = matchingDeviceByUdid(devices, args.udid);

  if (selectedDevice) {
    runOnDevice(selectedDevice, scheme, xcodeProject, args.configuration, args.packager, args.verbose, args.port);
    return;
  }

  if (devices && devices.length > 0) {
    // $FlowIssue: args.udid is defined in this context
    _logger.default.error(`Could not find device with the udid: "${args.udid}".
Choose one of the following:\n${printFoundDevices(devices)}`);
  } else {
    _logger.default.error('No iOS devices connected.');
  }
}

async function runOnSimulator(xcodeProject, args, scheme) {
  let simulators;

  try {
    simulators = JSON.parse( // $FlowIssue: https://github.com/facebook/flow/issues/5675
    _child_process().default.execFileSync('xcrun', ['simctl', 'list', '--json', 'devices'], {
      encoding: 'utf8'
    }));
  } catch (e) {
    throw new Error('Could not parse the simulator list output');
  }

  const selectedSimulator = (0, _findMatchingSimulator.default)(simulators, args.simulator);

  if (!selectedSimulator) {
    throw new Error(`Could not find ${args.simulator} simulator`);
  }
  /**
   * Booting simulator through `xcrun simctl boot` will boot it in the `headless` mode
   * (running in the background).
   *
   * In order for user to see the app and the simulator itself, we have to make sure
   * that the Simulator.app is running.
   *
   * We also pass it `-CurrentDeviceUDID` so that when we launch it for the first time,
   * it will not boot the "default" device, but the one we set. If the app is already running,
   * this flag has no effect.
   */


  const activeDeveloperDir = _child_process().default.execFileSync('xcode-select', ['-p'], {
    encoding: 'utf8'
  }) // $FlowExpectedError https://github.com/facebook/flow/issues/5675
  .trim();

  _child_process().default.execFileSync('open', [`${activeDeveloperDir}/Applications/Simulator.app`, '--args', '-CurrentDeviceUDID', selectedSimulator.udid]);

  if (!selectedSimulator.booted) {
    bootSimulator(selectedSimulator);
  }

  const appName = await buildProject(xcodeProject, selectedSimulator.udid, scheme, args.configuration, args.packager, args.verbose, args.port);
  const appPath = getBuildPath(args.configuration, appName, false, scheme);

  _logger.default.info(`Installing ${appPath}`);

  _child_process().default.spawnSync('xcrun', ['simctl', 'install', selectedSimulator.udid, appPath], {
    stdio: 'inherit'
  });

  const bundleID = _child_process().default.execFileSync('/usr/libexec/PlistBuddy', ['-c', 'Print:CFBundleIdentifier', _path().default.join(appPath, 'Info.plist')], {
    encoding: 'utf8'
  }) // $FlowExpectedError https://github.com/facebook/flow/issues/5675
  .trim();

  _logger.default.info(`Launching ${bundleID}`);

  _child_process().default.spawnSync('xcrun', ['simctl', 'launch', selectedSimulator.udid, bundleID], {
    stdio: 'inherit'
  });
}

async function runOnDevice(selectedDevice, scheme, xcodeProject, configuration, launchPackager, verbose, port) {
  const appName = await buildProject(xcodeProject, selectedDevice.udid, scheme, configuration, launchPackager, verbose, port);
  const iosDeployInstallArgs = ['--bundle', getBuildPath(configuration, appName, true, scheme), '--id', selectedDevice.udid, '--justlaunch'];

  _logger.default.info(`installing and launching your app on ${selectedDevice.name}...`);

  const iosDeployOutput = _child_process().default.spawnSync('ios-deploy', iosDeployInstallArgs, {
    encoding: 'utf8'
  });

  if (iosDeployOutput.error) {
    _logger.default.error('** INSTALLATION FAILED **\nMake sure you have ios-deploy installed globally.\n(e.g "npm install -g ios-deploy")');
  } else {
    _logger.default.info('** INSTALLATION SUCCEEDED **');
  }
}

function buildProject(xcodeProject, udid, scheme, configuration, launchPackager = false, verbose, port) {
  return new Promise((resolve, reject) => {
    const xcodebuildArgs = [xcodeProject.isWorkspace ? '-workspace' : '-project', xcodeProject.name, '-configuration', configuration, '-scheme', scheme, '-destination', `id=${udid}`, '-derivedDataPath', `build/${scheme}`];

    _logger.default.info(`Building using "xcodebuild ${xcodebuildArgs.join(' ')}"`);

    let xcpretty;

    if (!verbose) {
      xcpretty = xcprettyAvailable() && _child_process().default.spawn('xcpretty', [], {
        stdio: ['pipe', process.stdout, process.stderr]
      });
    }

    const buildProcess = _child_process().default.spawn('xcodebuild', xcodebuildArgs, getProcessOptions(launchPackager, port));

    let buildOutput = '';
    let errorOutput = '';
    buildProcess.stdout.on('data', data => {
      buildOutput += data.toString();

      if (xcpretty) {
        xcpretty.stdin.write(data);
      } else {
        _logger.default.info(data.toString());
      }
    });
    buildProcess.stderr.on('data', data => {
      errorOutput += data;
    });
    buildProcess.on('close', code => {
      if (xcpretty) {
        xcpretty.stdin.end();
      }

      if (code !== 0) {
        reject(new _errors.ProcessError(['Failed to build iOS project.', `We ran "xcodebuild" command but it exited with error code ${code}.`, `To debug build logs further, consider building your app with Xcode.app, by opening ${xcodeProject.name}`].join(' '), errorOutput));
        return;
      }

      resolve(getProductName(buildOutput) || scheme);
    });
  });
}

function bootSimulator(selectedSimulator) {
  const simulatorFullName = formattedDeviceName(selectedSimulator);

  _logger.default.info(`Launching ${simulatorFullName}...`);

  try {
    _child_process().default.spawnSync('xcrun', ['instruments', '-w', selectedSimulator.udid]);
  } catch (_ignored) {// instruments always fail with 255 because it expects more arguments,
    // but we want it to only launch the simulator
  }
}

function getBuildPath(configuration, appName, isDevice, scheme) {
  let device;

  if (isDevice) {
    device = 'iphoneos';
  } else if (appName.toLowerCase().includes('tvos')) {
    device = 'appletvsimulator';
  } else {
    device = 'iphonesimulator';
  }

  let buildPath = `build/${scheme}/Build/Products/${configuration}-${device}/${appName}.app`; // Check wether app file exist, sometimes `-derivedDataPath` option of `xcodebuild` not works as expected.

  if (!_fs().default.existsSync(_path().default.join(buildPath))) {
    return `DerivedData/Build/Products/${configuration}-${device}/${appName}.app`;
  }

  return buildPath;
}

function getProductName(buildOutput) {
  const productNameMatch = /export FULL_PRODUCT_NAME="?(.+).app"?$/m.exec(buildOutput);
  return productNameMatch ? productNameMatch[1] : null;
}

function xcprettyAvailable() {
  try {
    _child_process().default.execSync('xcpretty --version', {
      stdio: [0, 'pipe', 'ignore']
    });
  } catch (error) {
    return false;
  }

  return true;
}

function matchingDevice(devices, deviceName) {
  if (deviceName === true && devices.length === 1) {
    _logger.default.info(`Using first available device ${devices[0].name} due to lack of name supplied.`);

    return devices[0];
  }

  for (let i = devices.length - 1; i >= 0; i--) {
    if (devices[i].name === deviceName || formattedDeviceName(devices[i]) === deviceName) {
      return devices[i];
    }
  }

  return null;
}

function matchingDeviceByUdid(devices, udid) {
  for (let i = devices.length - 1; i >= 0; i--) {
    if (devices[i].udid === udid) {
      return devices[i];
    }
  }

  return null;
}

function formattedDeviceName(simulator) {
  return `${simulator.name} (${simulator.version})`;
}

function printFoundDevices(devices) {
  let output = '';

  for (let i = devices.length - 1; i >= 0; i--) {
    output += `${devices[i].name} Udid: ${devices[i].udid}\n`;
  }

  return output;
}

function getProcessOptions(launchPackager, port) {
  if (launchPackager) {
    return {
      env: _objectSpread({}, process.env, {
        RCT_METRO_PORT: port
      })
    };
  }

  return {
    env: _objectSpread({}, process.env, {
      RCT_NO_LAUNCH_PACKAGER: true
    })
  };
}

var _default = {
  name: 'run-ios',
  description: 'builds your app and starts it on iOS simulator',
  func: runIOS,
  examples: [{
    desc: 'Run on a different simulator, e.g. iPhone 5',
    cmd: 'react-native run-ios --simulator "iPhone 5"'
  }, {
    desc: 'Pass a non-standard location of iOS directory',
    cmd: 'react-native run-ios --project-path "./app/ios"'
  }, {
    desc: "Run on a connected device, e.g. Max's iPhone",
    cmd: 'react-native run-ios --device "Max\'s iPhone"'
  }, {
    desc: 'Run on the AppleTV simulator',
    cmd: 'react-native run-ios --simulator "Apple TV"  --scheme "helloworld-tvOS"'
  }],
  options: [{
    command: '--simulator [string]',
    description: 'Explicitly set simulator to use. Optionally include iOS version between' + 'parenthesis at the end to match an exact version: "iPhone 6 (10.0)"',
    default: 'iPhone X'
  }, {
    command: '--configuration [string]',
    description: 'Explicitly set the scheme configuration to use',
    default: 'Debug'
  }, {
    command: '--scheme [string]',
    description: 'Explicitly set Xcode scheme to use'
  }, {
    command: '--project-path [string]',
    description: 'Path relative to project root where the Xcode project ' + '(.xcodeproj) lives.',
    default: 'ios'
  }, {
    command: '--device [string]',
    description: 'Explicitly set device to use by name.  The value is not required if you have a single device connected.'
  }, {
    command: '--udid [string]',
    description: 'Explicitly set device to use by udid'
  }, {
    command: '--no-packager',
    description: 'Do not launch packager while building'
  }, {
    command: '--verbose',
    description: 'Do not use xcpretty even if installed'
  }, {
    command: '--port [number]',
    default: process.env.RCT_METRO_PORT || 8081,
    parse: val => Number(val)
  }]
};
exports.default = _default;

(二)
替换


image.png

fishhook.c

// Copyright (c) 2013, Facebook, Inc.
// All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//   * Redistributions of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//   * Neither the name Facebook nor the names of its contributors may be used to
//     endorse or promote products derived from this software without specific
//     prior written permission.
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include "fishhook.h"

#include <dlfcn.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <mach/mach.h>
#include <mach/vm_map.h>
#include <mach/vm_region.h>
#include <mach-o/dyld.h>
#include <mach-o/loader.h>
#include <mach-o/nlist.h>

#ifdef __LP64__
typedef struct mach_header_64 mach_header_t;
typedef struct segment_command_64 segment_command_t;
typedef struct section_64 section_t;
typedef struct nlist_64 nlist_t;
#define LC_SEGMENT_ARCH_DEPENDENT LC_SEGMENT_64
#else
typedef struct mach_header mach_header_t;
typedef struct segment_command segment_command_t;
typedef struct section section_t;
typedef struct nlist nlist_t;
#define LC_SEGMENT_ARCH_DEPENDENT LC_SEGMENT
#endif

#ifndef SEG_DATA_CONST
#define SEG_DATA_CONST  "__DATA_CONST"
#endif

struct rebindings_entry {
  struct rebinding *rebindings;
  size_t rebindings_nel;
  struct rebindings_entry *next;
};

static struct rebindings_entry *_rebindings_head;

static int prepend_rebindings(struct rebindings_entry **rebindings_head,
                              struct rebinding rebindings[],
                              size_t nel) {
  struct rebindings_entry *new_entry = (struct rebindings_entry *) malloc(sizeof(struct rebindings_entry));
  if (!new_entry) {
    return -1;
  }
  new_entry->rebindings = (struct rebinding *) malloc(sizeof(struct rebinding) * nel);
  if (!new_entry->rebindings) {
    free(new_entry);
    return -1;
  }
  memcpy(new_entry->rebindings, rebindings, sizeof(struct rebinding) * nel);
  new_entry->rebindings_nel = nel;
  new_entry->next = *rebindings_head;
  *rebindings_head = new_entry;
  return 0;
}

#if 0
static int get_protection(void *addr, vm_prot_t *prot, vm_prot_t *max_prot) {
  mach_port_t task = mach_task_self();
  vm_size_t size = 0;
  vm_address_t address = (vm_address_t)addr;
  memory_object_name_t object;
#ifdef __LP64__
  mach_msg_type_number_t count = VM_REGION_BASIC_INFO_COUNT_64;
  vm_region_basic_info_data_64_t info;
  kern_return_t info_ret = vm_region_64(
      task, &address, &size, VM_REGION_BASIC_INFO_64, (vm_region_info_64_t)&info, &count, &object);
#else
  mach_msg_type_number_t count = VM_REGION_BASIC_INFO_COUNT;
  vm_region_basic_info_data_t info;
  kern_return_t info_ret = vm_region(task, &address, &size, VM_REGION_BASIC_INFO, (vm_region_info_t)&info, &count, &object);
#endif
  if (info_ret == KERN_SUCCESS) {
    if (prot != NULL)
      *prot = info.protection;

    if (max_prot != NULL)
      *max_prot = info.max_protection;

    return 0;
  }

  return -1;
}
#endif

static void perform_rebinding_with_section(struct rebindings_entry *rebindings,
                                           section_t *section,
                                           intptr_t slide,
                                           nlist_t *symtab,
                                           char *strtab,
                                           uint32_t *indirect_symtab) {
  uint32_t *indirect_symbol_indices = indirect_symtab + section->reserved1;
  void **indirect_symbol_bindings = (void **)((uintptr_t)slide + section->addr);

  for (uint i = 0; i < section->size / sizeof(void *); i++) {
    uint32_t symtab_index = indirect_symbol_indices[i];
    if (symtab_index == INDIRECT_SYMBOL_ABS || symtab_index == INDIRECT_SYMBOL_LOCAL ||
        symtab_index == (INDIRECT_SYMBOL_LOCAL   | INDIRECT_SYMBOL_ABS)) {
      continue;
    }
    uint32_t strtab_offset = symtab[symtab_index].n_un.n_strx;
    char *symbol_name = strtab + strtab_offset;
    bool symbol_name_longer_than_1 = symbol_name[0] && symbol_name[1];
    struct rebindings_entry *cur = rebindings;
    while (cur) {
      for (uint j = 0; j < cur->rebindings_nel; j++) {
        if (symbol_name_longer_than_1 && strcmp(&symbol_name[1], cur->rebindings[j].name) == 0) {
          kern_return_t err;

          if (cur->rebindings[j].replaced != NULL && indirect_symbol_bindings[i] != cur->rebindings[j].replacement)
            *(cur->rebindings[j].replaced) = indirect_symbol_bindings[i];

          /**
           * 1. Moved the vm protection modifying codes to here to reduce the
           *    changing scope.
           * 2. Adding VM_PROT_WRITE mode unconditionally because vm_region
           *    API on some iOS/Mac reports mismatch vm protection attributes.
           * -- Lianfu Hao Jun 16th, 2021
           **/
          err = vm_protect (mach_task_self (), (uintptr_t)indirect_symbol_bindings, section->size, 0, VM_PROT_READ | VM_PROT_WRITE | VM_PROT_COPY);
          if (err == KERN_SUCCESS) {
            /**
             * Once we failed to change the vm protection, we
             * MUST NOT continue the following write actions!
             * iOS 15 has corrected the const segments prot.
             * -- Lionfore Hao Jun 11th, 2021
             **/
            indirect_symbol_bindings[i] = cur->rebindings[j].replacement;
          }
          goto symbol_loop;
        }
      }
      cur = cur->next;
    }
  symbol_loop:;
  }
}

static void rebind_symbols_for_image(struct rebindings_entry *rebindings,
                                     const struct mach_header *header,
                                     intptr_t slide) {
  Dl_info info;
  if (dladdr(header, &info) == 0) {
    return;
  }

  segment_command_t *cur_seg_cmd;
  segment_command_t *linkedit_segment = NULL;
  struct symtab_command* symtab_cmd = NULL;
  struct dysymtab_command* dysymtab_cmd = NULL;

  uintptr_t cur = (uintptr_t)header + sizeof(mach_header_t);
  for (uint i = 0; i < header->ncmds; i++, cur += cur_seg_cmd->cmdsize) {
    cur_seg_cmd = (segment_command_t *)cur;
    if (cur_seg_cmd->cmd == LC_SEGMENT_ARCH_DEPENDENT) {
      if (strcmp(cur_seg_cmd->segname, SEG_LINKEDIT) == 0) {
        linkedit_segment = cur_seg_cmd;
      }
    } else if (cur_seg_cmd->cmd == LC_SYMTAB) {
      symtab_cmd = (struct symtab_command*)cur_seg_cmd;
    } else if (cur_seg_cmd->cmd == LC_DYSYMTAB) {
      dysymtab_cmd = (struct dysymtab_command*)cur_seg_cmd;
    }
  }

  if (!symtab_cmd || !dysymtab_cmd || !linkedit_segment ||
      !dysymtab_cmd->nindirectsyms) {
    return;
  }

  // Find base symbol/string table addresses
  uintptr_t linkedit_base = (uintptr_t)slide + linkedit_segment->vmaddr - linkedit_segment->fileoff;
  nlist_t *symtab = (nlist_t *)(linkedit_base + symtab_cmd->symoff);
  char *strtab = (char *)(linkedit_base + symtab_cmd->stroff);

  // Get indirect symbol table (array of uint32_t indices into symbol table)
  uint32_t *indirect_symtab = (uint32_t *)(linkedit_base + dysymtab_cmd->indirectsymoff);

  cur = (uintptr_t)header + sizeof(mach_header_t);
  for (uint i = 0; i < header->ncmds; i++, cur += cur_seg_cmd->cmdsize) {
    cur_seg_cmd = (segment_command_t *)cur;
    if (cur_seg_cmd->cmd == LC_SEGMENT_ARCH_DEPENDENT) {
      if (strcmp(cur_seg_cmd->segname, SEG_DATA) != 0 &&
          strcmp(cur_seg_cmd->segname, SEG_DATA_CONST) != 0) {
        continue;
      }
      for (uint j = 0; j < cur_seg_cmd->nsects; j++) {
        section_t *sect =
          (section_t *)(cur + sizeof(segment_command_t)) + j;
        if ((sect->flags & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS) {
          perform_rebinding_with_section(rebindings, sect, slide, symtab, strtab, indirect_symtab);
        }
        if ((sect->flags & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS) {
          perform_rebinding_with_section(rebindings, sect, slide, symtab, strtab, indirect_symtab);
        }
      }
    }
  }
}

static void _rebind_symbols_for_image(const struct mach_header *header,
                                      intptr_t slide) {
    rebind_symbols_for_image(_rebindings_head, header, slide);
}

int rebind_symbols_image(void *header,
                         intptr_t slide,
                         struct rebinding rebindings[],
                         size_t rebindings_nel) {
    struct rebindings_entry *rebindings_head = NULL;
    int retval = prepend_rebindings(&rebindings_head, rebindings, rebindings_nel);
    rebind_symbols_for_image(rebindings_head, (const struct mach_header *) header, slide);
    if (rebindings_head) {
      free(rebindings_head->rebindings);
    }
    free(rebindings_head);
    return retval;
}

int rebind_symbols(struct rebinding rebindings[], size_t rebindings_nel) {
  int retval = prepend_rebindings(&_rebindings_head, rebindings, rebindings_nel);
  if (retval < 0) {
    return retval;
  }
  // If this was the first call, register callback for image additions (which is also invoked for
  // existing images, otherwise, just run on existing images
  if (!_rebindings_head->next) {
    _dyld_register_func_for_add_image(_rebind_symbols_for_image);
  } else {
    uint32_t c = _dyld_image_count();
    for (uint32_t i = 0; i < c; i++) {
      _rebind_symbols_for_image(_dyld_get_image_header(i), _dyld_get_image_vmaddr_slide(i));
    }
  }
  return retval;
}

fishhook.h

// Copyright (c) 2013, Facebook, Inc.
// All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//   * Redistributions of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//   * Neither the name Facebook nor the names of its contributors may be used to
//     endorse or promote products derived from this software without specific
//     prior written permission.
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#ifndef fishhook_h
#define fishhook_h

#include <stddef.h>
#include <stdint.h>

#if !defined(FISHHOOK_EXPORT)
#define FISHHOOK_VISIBILITY __attribute__((visibility("hidden")))
#else
#define FISHHOOK_VISIBILITY __attribute__((visibility("default")))
#endif

#ifdef __cplusplus
extern "C" {
#endif //__cplusplus

/*
 * A structure representing a particular intended rebinding from a symbol
 * name to its replacement
 */
struct rebinding {
  const char *name;
  void *replacement;
  void **replaced;
};

/*
 * For each rebinding in rebindings, rebinds references to external, indirect
 * symbols with the specified name to instead point at replacement for each
 * image in the calling process as well as for all future images that are loaded
 * by the process. If rebind_functions is called more than once, the symbols to
 * rebind are added to the existing list of rebindings, and if a given symbol
 * is rebound more than once, the later rebinding will take precedence.
 */
FISHHOOK_VISIBILITY
int rebind_symbols(struct rebinding rebindings[], size_t rebindings_nel);

/*
 * Rebinds as above, but only in the specified image. The header should point
 * to the mach-o header, the slide should be the slide offset. Others as above.
 */
FISHHOOK_VISIBILITY
int rebind_symbols_image(void *header,
                         intptr_t slide,
                         struct rebinding rebindings[],
                         size_t rebindings_nel);

#ifdef __cplusplus
}
#endif //__cplusplus

#endif //fishhook_h
  1. "react-native-snap-carousel": "^3.9.1",
    修改android 无限滑动问题
    Carousel.js
import React, { Component } from 'react';
import { Animated, Easing, FlatList, I18nManager, Platform, ScrollView, View, ViewPropTypes } from 'react-native';
import PropTypes from 'prop-types';
import shallowCompare from 'react-addons-shallow-compare';
import {
    defaultScrollInterpolator,
    stackScrollInterpolator,
    tinderScrollInterpolator,
    defaultAnimatedStyles,
    shiftAnimatedStyles,
    stackAnimatedStyles,
    tinderAnimatedStyles
} from '../utils/animations';

const IS_IOS = Platform.OS === 'ios';

// Native driver for scroll events
// See: https://facebook.github.io/react-native/blog/2017/02/14/using-native-driver-for-animated.html
const AnimatedFlatList = FlatList ? Animated.createAnimatedComponent(FlatList) : null;
const AnimatedScrollView = Animated.createAnimatedComponent(ScrollView);

// React Native automatically handles RTL layouts; unfortunately, it's buggy with horizontal ScrollView
// See https://github.com/facebook/react-native/issues/11960
// NOTE: the following variable is not declared in the constructor
// otherwise it is undefined at init, which messes with custom indexes
const IS_RTL = I18nManager.isRTL;

export default class Carousel extends Component {

    static propTypes = {
        data: PropTypes.array.isRequired,
        renderItem: PropTypes.func.isRequired,
        itemWidth: PropTypes.number, // required for horizontal carousel
        itemHeight: PropTypes.number, // required for vertical carousel
        sliderWidth: PropTypes.number, // required for horizontal carousel
        sliderHeight: PropTypes.number, // required for vertical carousel
        activeAnimationType: PropTypes.string,
        activeAnimationOptions: PropTypes.object,
        activeSlideAlignment: PropTypes.oneOf(['center', 'end', 'start']),
        activeSlideOffset: PropTypes.number,
        apparitionDelay: PropTypes.number,
        autoplay: PropTypes.bool,
        autoplayDelay: PropTypes.number,
        autoplayInterval: PropTypes.number,
        callbackOffsetMargin: PropTypes.number,
        containerCustomStyle: ViewPropTypes ? ViewPropTypes.style : View.propTypes.style,
        contentContainerCustomStyle: ViewPropTypes ? ViewPropTypes.style : View.propTypes.style,
        enableMomentum: PropTypes.bool,
        enableSnap: PropTypes.bool,
        firstItem: PropTypes.number,
        hasParallaxImages: PropTypes.bool,
        inactiveSlideOpacity: PropTypes.number,
        inactiveSlideScale: PropTypes.number,
        inactiveSlideShift: PropTypes.number,
        layout: PropTypes.oneOf(['default', 'stack', 'tinder']),
        layoutCardOffset: PropTypes.number,
        lockScrollTimeoutDuration: PropTypes.number,
        lockScrollWhileSnapping: PropTypes.bool,
        loop: PropTypes.bool,
        loopClonesPerSide: PropTypes.number,
        scrollEnabled: PropTypes.bool,
        scrollInterpolator: PropTypes.func,
        slideInterpolatedStyle: PropTypes.func,
        slideStyle: ViewPropTypes ? ViewPropTypes.style : View.propTypes.style,
        shouldOptimizeUpdates: PropTypes.bool,
        swipeThreshold: PropTypes.number,
        useScrollView: PropTypes.oneOfType([PropTypes.bool, PropTypes.func]),
        vertical: PropTypes.bool,
        onBeforeSnapToItem: PropTypes.func,
        onSnapToItem: PropTypes.func
    };

    static defaultProps = {
        activeAnimationType: 'timing',
        activeAnimationOptions: null,
        activeSlideAlignment: 'center',
        activeSlideOffset: 20,
        apparitionDelay: 0,
        autoplay: false,
        autoplayDelay: 1000,
        autoplayInterval: 3000,
        callbackOffsetMargin: 5,
        containerCustomStyle: {},
        contentContainerCustomStyle: {},
        enableMomentum: false,
        enableSnap: true,
        firstItem: 0,
        hasParallaxImages: false,
        inactiveSlideOpacity: 0.7,
        inactiveSlideScale: 0.9,
        inactiveSlideShift: 0,
        layout: 'default',
        lockScrollTimeoutDuration: 1000,
        lockScrollWhileSnapping: false,
        loop: false,
        loopClonesPerSide: 3,
        scrollEnabled: true,
        slideStyle: {},
        shouldOptimizeUpdates: true,
        swipeThreshold: 20,
        useScrollView: !AnimatedFlatList,
        vertical: false
    }

    constructor (props) {
        super(props);

        this.state = {
            hideCarousel: true,
            interpolators: []
        };

        // The following values are not stored in the state because 'setState()' is asynchronous
        // and this results in an absolutely crappy behavior on Android while swiping (see #156)
        const initialActiveItem = this._getFirstItem(props.firstItem);
        this._activeItem = initialActiveItem;
        this._previousActiveItem = initialActiveItem;
        this._previousFirstItem = initialActiveItem;
        this._previousItemsLength = initialActiveItem;

        this._mounted = false;
        this._positions = [];
        this._currentContentOffset = 0; // store ScrollView's scroll position
        this._canFireBeforeCallback = false;
        this._canFireCallback = false;
        this._scrollOffsetRef = null;
        this._onScrollTriggered = true; // used when momentum is enabled to prevent an issue with edges items
        this._lastScrollDate = 0; // used to work around a FlatList bug
        this._scrollEnabled = props.scrollEnabled !== false;

        this._initPositionsAndInterpolators = this._initPositionsAndInterpolators.bind(this);
        this._renderItem = this._renderItem.bind(this);
        this._onSnap = this._onSnap.bind(this);

        this._onLayout = this._onLayout.bind(this);
        this._onScroll = this._onScroll.bind(this);
        this._onScrollBeginDrag = props.enableSnap ? this._onScrollBeginDrag.bind(this) : undefined;
        this._onScrollEnd = props.enableSnap || props.autoplay ? this._onScrollEnd.bind(this) : undefined;
        this._onScrollEndDrag = !props.enableMomentum ? this._onScrollEndDrag.bind(this) : undefined;
        this._onMomentumScrollEnd = props.enableMomentum ? this._onMomentumScrollEnd.bind(this) : undefined;
        this._onTouchStart = this._onTouchStart.bind(this);
        this._onTouchEnd = this._onTouchEnd.bind(this);
        this._onTouchRelease = this._onTouchRelease.bind(this);
        this._animated = false;
        this._getKeyExtractor = this._getKeyExtractor.bind(this);

        this._setScrollHandler(props);

        // This bool aims at fixing an iOS bug due to scrollTo that triggers onMomentumScrollEnd.
        // onMomentumScrollEnd fires this._snapScroll, thus creating an infinite loop.
        this._ignoreNextMomentum = false;

        // Warnings
        if (!ViewPropTypes) {
            console.warn('react-native-snap-carousel: It is recommended to use at least version 0.44 of React Native with the plugin');
        }
        if (!props.vertical && (!props.sliderWidth || !props.itemWidth)) {
            console.error('react-native-snap-carousel: You need to specify both `sliderWidth` and `itemWidth` for horizontal carousels');
        }
        if (props.vertical && (!props.sliderHeight || !props.itemHeight)) {
            console.error('react-native-snap-carousel: You need to specify both `sliderHeight` and `itemHeight` for vertical carousels');
        }
        if (props.apparitionDelay && !IS_IOS && !props.useScrollView) {
            console.warn('react-native-snap-carousel: Using `apparitionDelay` on Android is not recommended since it can lead to rendering issues');
        }
        if (props.customAnimationType || props.customAnimationOptions) {
            console.warn('react-native-snap-carousel: Props `customAnimationType` and `customAnimationOptions` have been renamed to `activeAnimationType` and `activeAnimationOptions`');
        }
        if (props.onScrollViewScroll) {
            console.error('react-native-snap-carousel: Prop `onScrollViewScroll` has been removed. Use `onScroll` instead');
        }
    }

    componentDidMount () {
        const { apparitionDelay, autoplay, firstItem } = this.props;
        const _firstItem = this._getFirstItem(firstItem);
        const apparitionCallback = () => {
            this.setState({ hideCarousel: false });
            if (autoplay) {
                this.startAutoplay();
            }
        };

        this._mounted = true;
        this._initPositionsAndInterpolators();

        // Without 'requestAnimationFrame' or a `0` timeout, images will randomly not be rendered on Android...
        requestAnimationFrame(() => {
            if (!this._mounted) {
                return;
            }

            this._snapToItem(_firstItem, false, false, true, false);
            this._hackActiveSlideAnimation(_firstItem, 'start', true);

            if (apparitionDelay) {
                this._apparitionTimeout = setTimeout(() => {
                    apparitionCallback();
                }, apparitionDelay);
            } else {
                apparitionCallback();
            }
        });
    }

    shouldComponentUpdate (nextProps, nextState) {
        if (this.props.shouldOptimizeUpdates === false) {
            return true;
        } else {
            return shallowCompare(this, nextProps, nextState);
        }
    }

    componentDidUpdate (prevProps) {
        const { interpolators } = this.state;
        const { firstItem, itemHeight, itemWidth, scrollEnabled, sliderHeight, sliderWidth } = this.props;
        const itemsLength = this._getCustomDataLength(this.props);

        if (!itemsLength) {
            return;
        }

        const nextFirstItem = this._getFirstItem(firstItem, this.props);
        let nextActiveItem = this._activeItem || this._activeItem === 0 ? this._activeItem : nextFirstItem;

        const hasNewSliderWidth = sliderWidth && sliderWidth !== prevProps.sliderWidth;
        const hasNewSliderHeight = sliderHeight && sliderHeight !== prevProps.sliderHeight;
        const hasNewItemWidth = itemWidth && itemWidth !== prevProps.itemWidth;
        const hasNewItemHeight = itemHeight && itemHeight !== prevProps.itemHeight;
        const hasNewScrollEnabled = scrollEnabled !== prevProps.scrollEnabled;

        // Prevent issues with dynamically removed items
        if (nextActiveItem > itemsLength - 1) {
            nextActiveItem = itemsLength - 1;
        }

        // Handle changing scrollEnabled independent of user -> carousel interaction
        if (hasNewScrollEnabled) {
            this._setScrollEnabled(scrollEnabled);
        }

        if (interpolators.length !== itemsLength || hasNewSliderWidth ||
            hasNewSliderHeight || hasNewItemWidth || hasNewItemHeight) {
            this._activeItem = nextActiveItem;
            this._previousItemsLength = itemsLength;

            this._initPositionsAndInterpolators(this.props);

            // Handle scroll issue when dynamically removing items (see #133)
            // This also fixes first item's active state on Android
            // Because 'initialScrollIndex' apparently doesn't trigger scroll
            if (this._previousItemsLength > itemsLength) {
                this._hackActiveSlideAnimation(nextActiveItem, null, true);
            }

            if (hasNewSliderWidth || hasNewSliderHeight || hasNewItemWidth || hasNewItemHeight) {
                this._snapToItem(nextActiveItem, false, false, false, false);
            }
        } else if (nextFirstItem !== this._previousFirstItem && nextFirstItem !== this._activeItem) {
            this._activeItem = nextFirstItem;
            this._previousFirstItem = nextFirstItem;
            this._snapToItem(nextFirstItem, false, true, false, false);
        }

        if (this.props.onScroll !== prevProps.onScroll) {
          this._setScrollHandler(this.props);
        }
    }

    componentWillUnmount () {
        this._mounted = false;
        this.stopAutoplay();
        clearTimeout(this._apparitionTimeout);
        clearTimeout(this._hackSlideAnimationTimeout);
        clearTimeout(this._enableAutoplayTimeout);
        clearTimeout(this._autoplayTimeout);
        clearTimeout(this._snapNoMomentumTimeout);
        clearTimeout(this._edgeItemTimeout);
        clearTimeout(this._lockScrollTimeout);
    }

    get realIndex () {
        return this._activeItem;
    }

    get currentIndex () {
        return this._getDataIndex(this._activeItem);
    }

    get currentScrollPosition () {
        return this._currentContentOffset;
    }

    _setScrollHandler(props) {
      // Native driver for scroll events
      const scrollEventConfig = {
        listener: this._onScroll,
        useNativeDriver: true,
      };
      this._scrollPos = new Animated.Value(0);
      const argMapping = props.vertical
        ? [{ nativeEvent: { contentOffset: { y: this._scrollPos } } }]
        : [{ nativeEvent: { contentOffset: { x: this._scrollPos } } }];

      if (props.onScroll && Array.isArray(props.onScroll._argMapping)) {
        // Because of a react-native issue https://github.com/facebook/react-native/issues/13294
        argMapping.pop();
        const [ argMap ] = props.onScroll._argMapping;
        if (argMap && argMap.nativeEvent && argMap.nativeEvent.contentOffset) {
          // Shares the same animated value passed in props
          this._scrollPos =
            argMap.nativeEvent.contentOffset.x ||
            argMap.nativeEvent.contentOffset.y ||
            this._scrollPos;
        }
        argMapping.push(...props.onScroll._argMapping);
      }
      this._onScrollHandler = Animated.event(
        argMapping,
        scrollEventConfig
      );
    }

    _needsScrollView () {
        const { useScrollView } = this.props;
        return useScrollView || !AnimatedFlatList || this._shouldUseStackLayout() || this._shouldUseTinderLayout();
    }

    _needsRTLAdaptations () {
        const { vertical } = this.props;
        return IS_RTL && !IS_IOS && !vertical;
    }

    _canLockScroll () {
        const { scrollEnabled, enableMomentum, lockScrollWhileSnapping } = this.props;
        return scrollEnabled && !enableMomentum && lockScrollWhileSnapping;
    }

    _enableLoop () {
        const { data, enableSnap, loop } = this.props;
        return enableSnap && loop && data && data.length && data.length > 1;
    }

    _shouldAnimateSlides (props = this.props) {
        const { inactiveSlideOpacity, inactiveSlideScale, scrollInterpolator, slideInterpolatedStyle } = props;
        return inactiveSlideOpacity < 1 ||
            inactiveSlideScale < 1 ||
            !!scrollInterpolator ||
            !!slideInterpolatedStyle ||
            this._shouldUseShiftLayout() ||
            this._shouldUseStackLayout() ||
            this._shouldUseTinderLayout();
    }

    _shouldUseCustomAnimation () {
        const { activeAnimationOptions } = this.props;
        return !!activeAnimationOptions && !this._shouldUseStackLayout() && !this._shouldUseTinderLayout();
    }

    _shouldUseShiftLayout () {
        const { inactiveSlideShift, layout } = this.props;
        return layout === 'default' && inactiveSlideShift !== 0;
    }

    _shouldUseStackLayout () {
        return this.props.layout === 'stack';
    }

    _shouldUseTinderLayout () {
        return this.props.layout === 'tinder';
    }

    _getCustomData (props = this.props) {
        const { data, loopClonesPerSide } = props;
        const dataLength = data && data.length;

        if (!dataLength) {
            return [];
        }

        if (!this._enableLoop()) {
            return data;
        }

        let previousItems = [];
        let nextItems = [];

        if (loopClonesPerSide > dataLength) {
            const dataMultiplier = Math.floor(loopClonesPerSide / dataLength);
            const remainder = loopClonesPerSide % dataLength;

            for (let i = 0; i < dataMultiplier; i++) {
                previousItems.push(...data);
                nextItems.push(...data);
            }

            previousItems.unshift(...data.slice(-remainder));
            nextItems.push(...data.slice(0, remainder));
        } else {
            previousItems = data.slice(-loopClonesPerSide);
            nextItems = data.slice(0, loopClonesPerSide);
        }

        return previousItems.concat(data, nextItems);
    }

    _getCustomDataLength (props = this.props) {
        const { data, loopClonesPerSide } = props;
        const dataLength = data && data.length;

        if (!dataLength) {
            return 0;
        }

        return this._enableLoop() ? dataLength + (2 * loopClonesPerSide) : dataLength;
    }

    _getCustomIndex (index, props = this.props) {
        const itemsLength = this._getCustomDataLength(props);

        if (!itemsLength || (!index && index !== 0)) {
            return 0;
        }

        return this._needsRTLAdaptations() ? itemsLength - index - 1 : index;
    }

    _getDataIndex (index) {
        const { data, loopClonesPerSide } = this.props;
        const dataLength = data && data.length;

        if (!this._enableLoop() || !dataLength) {
            return index;
        }

        if (index >= dataLength + loopClonesPerSide) {
            return loopClonesPerSide > dataLength ?
                (index - loopClonesPerSide) % dataLength :
                index - dataLength - loopClonesPerSide;
        } else if (index < loopClonesPerSide) {
            // TODO: is there a simpler way of determining the interpolated index?
            if (loopClonesPerSide > dataLength) {
                const baseDataIndexes = [];
                const dataIndexes = [];
                const dataMultiplier = Math.floor(loopClonesPerSide / dataLength);
                const remainder = loopClonesPerSide % dataLength;

                for (let i = 0; i < dataLength; i++) {
                    baseDataIndexes.push(i);
                }

                for (let j = 0; j < dataMultiplier; j++) {
                    dataIndexes.push(...baseDataIndexes);
                }

                dataIndexes.unshift(...baseDataIndexes.slice(-remainder));
                return dataIndexes[index];
            } else {
                return index + dataLength - loopClonesPerSide;
            }
        } else {
            return index - loopClonesPerSide;
        }
    }

    // Used with `snapToItem()` and 'PaginationDot'
    _getPositionIndex (index) {
        const { loop, loopClonesPerSide } = this.props;
        return loop ? index + loopClonesPerSide : index;
    }

    _getFirstItem (index, props = this.props) {
        const { loopClonesPerSide } = props;
        const itemsLength = this._getCustomDataLength(props);

        if (!itemsLength || index > itemsLength - 1 || index < 0) {
            return 0;
        }

        return this._enableLoop() ? index + loopClonesPerSide : index;
    }

    _getWrappedRef () {
        if (this._carouselRef && (
            (this._needsScrollView() && this._carouselRef.scrollTo) ||
            (!this._needsScrollView() && this._carouselRef.scrollToOffset)
        )) {
            return this._carouselRef;
        }
        // https://github.com/facebook/react-native/issues/10635
        // https://stackoverflow.com/a/48786374/8412141
        return this._carouselRef && this._carouselRef.getNode && this._carouselRef.getNode();
    }

    _getScrollEnabled () {
        return this._scrollEnabled;
    }

    _setScrollEnabled (scrollEnabled = true) {
        const wrappedRef = this._getWrappedRef();

        if (!wrappedRef || !wrappedRef.setNativeProps) {
            return;
        }

        // 'setNativeProps()' is used instead of 'setState()' because the latter
        // really takes a toll on Android behavior when momentum is disabled
        wrappedRef.setNativeProps({ scrollEnabled });
        this._scrollEnabled = scrollEnabled;
    }

    _getKeyExtractor (item, index) {
        return this._needsScrollView() ? `scrollview-item-${index}` : `flatlist-item-${index}`;
    }

    _getScrollOffset (event) {
        const { vertical } = this.props;
        return (event && event.nativeEvent && event.nativeEvent.contentOffset &&
            event.nativeEvent.contentOffset[vertical ? 'y' : 'x']) || 0;
    }

    _getContainerInnerMargin (opposite = false) {
        const { sliderWidth, sliderHeight, itemWidth, itemHeight, vertical, activeSlideAlignment } = this.props;

        if ((activeSlideAlignment === 'start' && !opposite) ||
            (activeSlideAlignment === 'end' && opposite)) {
            return 0;
        } else if ((activeSlideAlignment === 'end' && !opposite) ||
            (activeSlideAlignment === 'start' && opposite)) {
            return vertical ? sliderHeight - itemHeight : sliderWidth - itemWidth;
        } else {
            return vertical ? (sliderHeight - itemHeight) / 2 : (sliderWidth - itemWidth) / 2;
        }
    }

    _getViewportOffset () {
        const { sliderWidth, sliderHeight, itemWidth, itemHeight, vertical, activeSlideAlignment } = this.props;

        if (activeSlideAlignment === 'start') {
            return vertical ? itemHeight / 2 : itemWidth / 2;
        } else if (activeSlideAlignment === 'end') {
            return vertical ?
                sliderHeight - (itemHeight / 2) :
                sliderWidth - (itemWidth / 2);
        } else {
            return vertical ? sliderHeight / 2 : sliderWidth / 2;
        }
    }

    _getCenter (offset) {
        return offset + this._getViewportOffset() - this._getContainerInnerMargin();
    }

    _getActiveItem (offset) {
        const { activeSlideOffset, swipeThreshold } = this.props;
        const center = this._getCenter(offset);
        const centerOffset = activeSlideOffset || swipeThreshold;

        for (let i = 0; i < this._positions.length; i++) {
            const { start, end } = this._positions[i];
            if (center + centerOffset >= start && center - centerOffset <= end) {
                return i;
            }
        }

        const lastIndex = this._positions.length - 1;
        if (this._positions[lastIndex] && center - centerOffset > this._positions[lastIndex].end) {
            return lastIndex;
        }

        return 0;
    }

    _initPositionsAndInterpolators (props = this.props) {
        const { data, itemWidth, itemHeight, scrollInterpolator, vertical } = props;
        const sizeRef = vertical ? itemHeight : itemWidth;

        if (!data || !data.length) {
            return;
        }

        let interpolators = [];
        this._positions = [];

        this._getCustomData(props).forEach((itemData, index) => {
            const _index = this._getCustomIndex(index, props);
            let animatedValue;

            this._positions[index] = {
                start: index * sizeRef,
                end: index * sizeRef + sizeRef
            };

            if (!this._shouldAnimateSlides(props)) {
                animatedValue = new Animated.Value(1);
            } else if (this._shouldUseCustomAnimation()) {
                animatedValue = new Animated.Value(_index === this._activeItem ? 1 : 0);
            } else {
                let interpolator;

                if (scrollInterpolator) {
                    interpolator = scrollInterpolator(_index, props);
                } else if (this._shouldUseStackLayout()) {
                    interpolator = stackScrollInterpolator(_index, props);
                } else if (this._shouldUseTinderLayout()) {
                    interpolator = tinderScrollInterpolator(_index, props);
                }

                if (!interpolator || !interpolator.inputRange || !interpolator.outputRange) {
                    interpolator = defaultScrollInterpolator(_index, props);
                }

                animatedValue = this._scrollPos.interpolate({
                    ...interpolator,
                    extrapolate: 'clamp'
                });
            }

            interpolators.push(animatedValue);
        });

        this.setState({ interpolators });
    }

    _getSlideAnimation (index, toValue) {
        const { interpolators } = this.state;
        const { activeAnimationType, activeAnimationOptions } = this.props;

        const animatedValue = interpolators && interpolators[index];

        if (!animatedValue && animatedValue !== 0) {
            return null;
        }

        const animationCommonOptions = {
            isInteraction: false,
            useNativeDriver: true,
            ...activeAnimationOptions,
            toValue: toValue
        };

        return Animated.parallel([
            Animated['timing'](
                animatedValue,
                { ...animationCommonOptions, easing: Easing.linear }
            ),
            Animated[activeAnimationType](
                animatedValue,
                { ...animationCommonOptions }
            )
        ]);
    }

    _playCustomSlideAnimation (current, next) {
        const { interpolators } = this.state;
        const itemsLength = this._getCustomDataLength();
        const _currentIndex = this._getCustomIndex(current);
        const _currentDataIndex = this._getDataIndex(_currentIndex);
        const _nextIndex = this._getCustomIndex(next);
        const _nextDataIndex = this._getDataIndex(_nextIndex);
        let animations = [];

        // Keep animations in sync when looping
        if (this._enableLoop()) {
            for (let i = 0; i < itemsLength; i++) {
                if (this._getDataIndex(i) === _currentDataIndex && interpolators[i]) {
                    animations.push(this._getSlideAnimation(i, 0));
                } else if (this._getDataIndex(i) === _nextDataIndex && interpolators[i]) {
                    animations.push(this._getSlideAnimation(i, 1));
                }
            }
        } else {
            if (interpolators[current]) {
                animations.push(this._getSlideAnimation(current, 0));
            }
            if (interpolators[next]) {
                animations.push(this._getSlideAnimation(next, 1));
            }
        }

        Animated.parallel(animations, { stopTogether: false }).start();
    }

    _hackActiveSlideAnimation (index, goTo, force = false) {
        const { data } = this.props;

        if (!this._mounted || !this._carouselRef || !this._positions[index] || (!force && this._enableLoop())) {
            return;
        }

        const offset = this._positions[index] && this._positions[index].start;

        if (!offset && offset !== 0) {
            return;
        }

        const itemsLength = data && data.length;
        const direction = goTo || itemsLength === 1 ? 'start' : 'end';

        this._scrollTo(offset + (direction === 'start' ? -1 : 1), false);

        clearTimeout(this._hackSlideAnimationTimeout);
        this._hackSlideAnimationTimeout = setTimeout(() => {
            this._scrollTo(offset, false);
        }, 50); // works randomly when set to '0'
    }

    _lockScroll () {
        const { lockScrollTimeoutDuration } = this.props;
        clearTimeout(this._lockScrollTimeout);
        this._lockScrollTimeout = setTimeout(() => {
            this._releaseScroll();
        }, lockScrollTimeoutDuration);
        this._setScrollEnabled(false);
    }

    _releaseScroll () {
        clearTimeout(this._lockScrollTimeout);
        this._setScrollEnabled(true);
    }

    _repositionScroll (index) {
        const { data, loopClonesPerSide } = this.props;
        const dataLength = data && data.length;

        if (!this._enableLoop() || !dataLength ||
            (index >= loopClonesPerSide && index < dataLength + loopClonesPerSide)) {
            return;
        }

        let repositionTo = index;

        if (index >= dataLength + loopClonesPerSide) {
            repositionTo = index - dataLength;
        } else if (index < loopClonesPerSide) {
            repositionTo = index + dataLength;
        }

        this._snapToItem(repositionTo, false, false, false, false);
    }

    _scrollTo (offset, animated = true) {
        const { vertical } = this.props;
        const wrappedRef = this._getWrappedRef();

        if (!this._mounted || !wrappedRef) {
            return;
        }

        const specificOptions = this._needsScrollView() ? {
            x: vertical ? 0 : offset,
            y: vertical ? offset : 0
        } : {
            offset
        };
        const options = {
            ...specificOptions,
            animated
        };

        // if (this._needsScrollView()) {
        //     wrappedRef.scrollTo(options);
        // } else {
        //     wrappedRef.scrollToOffset(options);
        // }

        setTimeout(() => {
            if (this._needsScrollView()) {
                wrappedRef.scrollTo(options);
            } else {
                wrappedRef.scrollToOffset(options);
            }
        }, (!animated && this._animated) ? 200 : 0);

        this._animated = animated;
    }

    _onScroll (event) {
        const { callbackOffsetMargin, enableMomentum, onScroll } = this.props;

        const scrollOffset = event ? this._getScrollOffset(event) : this._currentContentOffset;
        const nextActiveItem = this._getActiveItem(scrollOffset);
        const itemReached = nextActiveItem === this._itemToSnapTo;
        const scrollConditions =
            scrollOffset >= this._scrollOffsetRef - callbackOffsetMargin &&
            scrollOffset <= this._scrollOffsetRef + callbackOffsetMargin;

        this._currentContentOffset = scrollOffset;
        this._onScrollTriggered = true;
        this._lastScrollDate = Date.now();

        if (this._activeItem !== nextActiveItem && this._shouldUseCustomAnimation()) {
            this._playCustomSlideAnimation(this._activeItem, nextActiveItem);
        }

        if (enableMomentum) {
            clearTimeout(this._snapNoMomentumTimeout);

            if (this._activeItem !== nextActiveItem) {
                this._activeItem = nextActiveItem;
            }

            if (itemReached) {
                if (this._canFireBeforeCallback) {
                    this._onBeforeSnap(this._getDataIndex(nextActiveItem));
                }

                if (scrollConditions && this._canFireCallback) {
                    this._onSnap(this._getDataIndex(nextActiveItem));
                }
            }
        } else if (this._activeItem !== nextActiveItem && itemReached) {
            if (this._canFireBeforeCallback) {
                this._onBeforeSnap(this._getDataIndex(nextActiveItem));
            }

            if (scrollConditions) {
                this._activeItem = nextActiveItem;

                if (this._canLockScroll()) {
                    this._releaseScroll();
                }

                if (this._canFireCallback) {
                    this._onSnap(this._getDataIndex(nextActiveItem));
                }
            }
        }

        if (nextActiveItem === this._itemToSnapTo &&
            // scrollOffset === this._scrollOffsetRef) {
          Math.abs(scrollOffset - this._scrollOffsetRef) <= 1.0) {
            this._repositionScroll(nextActiveItem);
        }

        if (typeof onScroll === "function" && event) {
            onScroll(event);
        }
    }

    _onStartShouldSetResponderCapture (event) {
        const { onStartShouldSetResponderCapture } = this.props;

        if (onStartShouldSetResponderCapture) {
            onStartShouldSetResponderCapture(event);
        }

        return this._getScrollEnabled();
    }

    _onTouchStart () {
        const { onTouchStart } = this.props

        // `onTouchStart` is fired even when `scrollEnabled` is set to `false`
        if (this._getScrollEnabled() !== false && this._autoplaying) {
            this.pauseAutoPlay();
        }

        if (onTouchStart) {
            onTouchStart()
        }
    }

    _onTouchEnd () {
        const { onTouchEnd } = this.props

        if (this._getScrollEnabled() !== false && this._autoplay && !this._autoplaying) {
            // This event is buggy on Android, so a fallback is provided in _onScrollEnd()
            this.startAutoplay();
        }

        if (onTouchEnd) {
            onTouchEnd()
        }
    }

    // Used when `enableSnap` is ENABLED
    _onScrollBeginDrag (event) {
        const { onScrollBeginDrag } = this.props;

        if (!this._getScrollEnabled()) {
            return;
        }

        this._scrollStartOffset = this._getScrollOffset(event);
        this._scrollStartActive = this._getActiveItem(this._scrollStartOffset);
        this._ignoreNextMomentum = false;
        // this._canFireCallback = false;

        if (onScrollBeginDrag) {
            onScrollBeginDrag(event);
        }
    }

    // Used when `enableMomentum` is DISABLED
    _onScrollEndDrag (event) {
        const { onScrollEndDrag } = this.props;

        if (this._carouselRef) {
            this._onScrollEnd && this._onScrollEnd();
        }

        if (onScrollEndDrag) {
            onScrollEndDrag(event);
        }
    }

    // Used when `enableMomentum` is ENABLED
    _onMomentumScrollEnd (event) {
        const { onMomentumScrollEnd } = this.props;

        if (this._carouselRef) {
            this._onScrollEnd && this._onScrollEnd();
        }

        if (onMomentumScrollEnd) {
            onMomentumScrollEnd(event);
        }
    }

    _onScrollEnd (event) {
        const { autoplayDelay, enableSnap } = this.props;

        if (this._ignoreNextMomentum) {
            // iOS fix
            this._ignoreNextMomentum = false;
            return;
        }

        if (this._currentContentOffset === this._scrollEndOffset) {
            return;
        }

        this._scrollEndOffset = this._currentContentOffset;
        this._scrollEndActive = this._getActiveItem(this._scrollEndOffset);

        if (enableSnap) {
            this._snapScroll(this._scrollEndOffset - this._scrollStartOffset);
        }

        // The touchEnd event is buggy on Android, so this will serve as a fallback whenever needed
        // https://github.com/facebook/react-native/issues/9439
        if (this._autoplay && !this._autoplaying) {
            clearTimeout(this._enableAutoplayTimeout);
            this._enableAutoplayTimeout = setTimeout(() => {
                this.startAutoplay();
            }, autoplayDelay + 50);
        }
    }

    // Due to a bug, this event is only fired on iOS
    // https://github.com/facebook/react-native/issues/6791
    // it's fine since we're only fixing an iOS bug in it, so ...
    _onTouchRelease (event) {
        const { enableMomentum } = this.props;

        if (enableMomentum && IS_IOS) {
            clearTimeout(this._snapNoMomentumTimeout);
            this._snapNoMomentumTimeout = setTimeout(() => {
                this._snapToItem(this._activeItem);
            }, 100);
        }
    }

    _onLayout (event) {
        const { onLayout } = this.props;

        // Prevent unneeded actions during the first 'onLayout' (triggered on init)
        if (this._onLayoutInitDone) {
            this._initPositionsAndInterpolators();
            this._snapToItem(this._activeItem, false, false, false, false);
        } else {
            this._onLayoutInitDone = true;
        }

        if (onLayout) {
            onLayout(event);
        }
    }

    _snapScroll (delta) {
        const { swipeThreshold } = this.props;

        // When using momentum and releasing the touch with
        // no velocity, scrollEndActive will be undefined (iOS)
        if (!this._scrollEndActive && this._scrollEndActive !== 0 && IS_IOS) {
            this._scrollEndActive = this._scrollStartActive;
        }

        if (this._scrollStartActive !== this._scrollEndActive) {
            // Snap to the new active item
            this._snapToItem(this._scrollEndActive);
        } else {
            // Snap depending on delta
            if (delta > 0) {
                if (delta > swipeThreshold) {
                    this._snapToItem(this._scrollStartActive + 1);
                } else {
                    this._snapToItem(this._scrollEndActive);
                }
            } else if (delta < 0) {
                if (delta < -swipeThreshold) {
                    this._snapToItem(this._scrollStartActive - 1);
                } else {
                    this._snapToItem(this._scrollEndActive);
                }
            } else {
                // Snap to current
                this._snapToItem(this._scrollEndActive);
            }
        }
    }

    _snapToItem (index, animated = true, fireCallback = true, initial = false, lockScroll = true) {
        const { enableMomentum, onSnapToItem, onBeforeSnapToItem } = this.props;
        const itemsLength = this._getCustomDataLength();
        const wrappedRef = this._getWrappedRef();

        if (!itemsLength || !wrappedRef) {
            return;
        }

        if (!index || index < 0) {
            index = 0;
        } else if (itemsLength > 0 && index >= itemsLength) {
            index = itemsLength - 1;
        }

        if (index !== this._previousActiveItem) {
            this._previousActiveItem = index;

            // Placed here to allow overscrolling for edges items
            if (lockScroll && this._canLockScroll()) {
                this._lockScroll();
            }

            if (fireCallback) {
                if (onBeforeSnapToItem) {
                    this._canFireBeforeCallback = true;
                }

                if (onSnapToItem) {
                    this._canFireCallback = true;
                }
            }
        }

        this._itemToSnapTo = index;
        this._scrollOffsetRef = this._positions[index] && this._positions[index].start;
        this._onScrollTriggered = false;

        if (!this._scrollOffsetRef && this._scrollOffsetRef !== 0) {
            return;
        }

        this._scrollTo(this._scrollOffsetRef, animated);

        this._scrollEndOffset = this._currentContentOffset;

        if (enableMomentum) {
            // iOS fix, check the note in the constructor
            if (!initial) {
                this._ignoreNextMomentum = true;
            }

            // When momentum is enabled and the user is overscrolling or swiping very quickly,
            // 'onScroll' is not going to be triggered for edge items. Then callback won't be
            // fired and loop won't work since the scrollview is not going to be repositioned.
            // As a workaround, '_onScroll()' will be called manually for these items if a given
            // condition hasn't been met after a small delay.
            // WARNING: this is ok only when relying on 'momentumScrollEnd', not with 'scrollEndDrag'
            if (index === 0 || index === itemsLength - 1) {
                clearTimeout(this._edgeItemTimeout);
                this._edgeItemTimeout = setTimeout(() => {
                    if (!initial && index === this._activeItem && !this._onScrollTriggered) {
                        this._onScroll();
                    }
                }, 250);
            }
        }
    }

    _onBeforeSnap (index) {
        const { onBeforeSnapToItem } = this.props;

        if (!this._carouselRef) {
            return;
        }

        this._canFireBeforeCallback = false;
        onBeforeSnapToItem && onBeforeSnapToItem(index);
    }

    _onSnap (index) {
        const { onSnapToItem } = this.props;

        if (!this._carouselRef) {
            return;
        }

        this._canFireCallback = false;
        onSnapToItem && onSnapToItem(index);
    }

    startAutoplay () {
        const { autoplayInterval, autoplayDelay } = this.props;
        this._autoplay = true;

        if (this._autoplaying) {
            return;
        }

        clearTimeout(this._autoplayTimeout);
        this._autoplayTimeout = setTimeout(() => {
            this._autoplaying = true;
            this._autoplayInterval = setInterval(() => {
                if (this._autoplaying) {
                    this.snapToNext();
                }
            }, autoplayInterval);
        }, autoplayDelay);
    }

    pauseAutoPlay () {
        this._autoplaying = false;
        clearTimeout(this._autoplayTimeout);
        clearTimeout(this._enableAutoplayTimeout);
        clearInterval(this._autoplayInterval);
    }

    stopAutoplay () {
        this._autoplay = false;
        this.pauseAutoPlay();
    }

    snapToItem (index, animated = true, fireCallback = true) {
        if (!index || index < 0) {
            index = 0;
        }

        const positionIndex = this._getPositionIndex(index);

        if (positionIndex === this._activeItem) {
            return;
        }

        this._snapToItem(positionIndex, animated, fireCallback);
    }

    snapToNext (animated = true, fireCallback = true) {
        const itemsLength = this._getCustomDataLength();

        let newIndex = this._activeItem + 1;
        if (newIndex > itemsLength - 1) {
            if (!this._enableLoop()) {
                return;
            }
            newIndex = 0;
        }
        this._snapToItem(newIndex, animated, fireCallback);
    }

    snapToPrev (animated = true, fireCallback = true) {
        const itemsLength = this._getCustomDataLength();

        let newIndex = this._activeItem - 1;
        if (newIndex < 0) {
            if (!this._enableLoop()) {
                return;
            }
            newIndex = itemsLength - 1;
        }
        this._snapToItem(newIndex, animated, fireCallback);
    }

    // https://github.com/facebook/react-native/issues/1831#issuecomment-231069668
    triggerRenderingHack (offset) {
        // Avoid messing with user scroll
        if (Date.now() - this._lastScrollDate < 500) {
            return;
        }

        const scrollPosition = this._currentContentOffset;
        if (!scrollPosition && scrollPosition !== 0) {
            return;
        }

        const scrollOffset = offset || (scrollPosition === 0 ? 1 : -1);
        this._scrollTo(scrollPosition + scrollOffset, false);
    }

    _getSlideInterpolatedStyle (index, animatedValue) {
        const { layoutCardOffset, slideInterpolatedStyle } = this.props;

        if (slideInterpolatedStyle) {
            return slideInterpolatedStyle(index, animatedValue, this.props);
        } else if (this._shouldUseTinderLayout()) {
            return tinderAnimatedStyles(index, animatedValue, this.props, layoutCardOffset);
        } else if (this._shouldUseStackLayout()) {
            return stackAnimatedStyles(index, animatedValue, this.props, layoutCardOffset);
        } else if (this._shouldUseShiftLayout()) {
            return shiftAnimatedStyles(index, animatedValue, this.props);
        } else {
            return defaultAnimatedStyles(index, animatedValue, this.props);
        }
    }

    _renderItem ({ item, index }) {
        const { interpolators } = this.state;
        const {
            hasParallaxImages,
            itemWidth,
            itemHeight,
            keyExtractor,
            renderItem,
            sliderHeight,
            sliderWidth,
            slideStyle,
            vertical
        } = this.props;

        const animatedValue = interpolators && interpolators[index];

        if (!animatedValue && animatedValue !== 0) {
            return null;
        }

        const animate = this._shouldAnimateSlides();
        const Component = animate ? Animated.View : View;
        const animatedStyle = animate ? this._getSlideInterpolatedStyle(index, animatedValue) : {};

        const parallaxProps = hasParallaxImages ? {
            scrollPosition: this._scrollPos,
            carouselRef: this._carouselRef,
            vertical,
            sliderWidth,
            sliderHeight,
            itemWidth,
            itemHeight
        } : undefined;

        const mainDimension = vertical ? { height: itemHeight } : { width: itemWidth };
        const specificProps = this._needsScrollView() ? {
            key: keyExtractor ? keyExtractor(item, index) : this._getKeyExtractor(item, index)
        } : {};

        return (
            <Component style={[mainDimension, slideStyle, animatedStyle]} pointerEvents={'box-none'} {...specificProps}>
                { renderItem({ item, index }, parallaxProps) }
            </Component>
        );
    }

    _getComponentOverridableProps () {
        const {
            enableMomentum,
            itemWidth,
            itemHeight,
            loopClonesPerSide,
            sliderWidth,
            sliderHeight,
            vertical
        } = this.props;

        const visibleItems = Math.ceil(vertical ?
            sliderHeight / itemHeight :
            sliderWidth / itemWidth) + 1;
        const initialNumPerSide = this._enableLoop() ? loopClonesPerSide : 2;
        const initialNumToRender = visibleItems + (initialNumPerSide * 2);
        const maxToRenderPerBatch = 1 + (initialNumToRender * 2);
        const windowSize = maxToRenderPerBatch;

        const specificProps = !this._needsScrollView() ? {
            initialNumToRender: initialNumToRender,
            maxToRenderPerBatch: maxToRenderPerBatch,
            windowSize: windowSize
            // updateCellsBatchingPeriod
        } : {};

        return {
            decelerationRate: enableMomentum ? 0.9 : 'fast',
            showsHorizontalScrollIndicator: false,
            showsVerticalScrollIndicator: false,
            overScrollMode: 'never',
            automaticallyAdjustContentInsets: false,
            directionalLockEnabled: true,
            pinchGestureEnabled: false,
            scrollsToTop: false,
            removeClippedSubviews: !this._needsScrollView(),
            inverted: this._needsRTLAdaptations(),
            // renderToHardwareTextureAndroid: true,
            ...specificProps
        };
    }

    _getComponentStaticProps () {
        const { hideCarousel } = this.state;
        const {
            containerCustomStyle,
            contentContainerCustomStyle,
            keyExtractor,
            sliderWidth,
            sliderHeight,
            style,
            vertical
        } = this.props;

        const containerStyle = [
            containerCustomStyle || style || {},
            hideCarousel ? { opacity: 0 } : {},
            vertical ?
                { height: sliderHeight, flexDirection: 'column' } :
                // LTR hack; see https://github.com/facebook/react-native/issues/11960
                // and https://github.com/facebook/react-native/issues/13100#issuecomment-328986423
                { width: sliderWidth, flexDirection: this._needsRTLAdaptations() ? 'row-reverse' : 'row' }
        ];
        const contentContainerStyle = [
            vertical ? {
                paddingTop: this._getContainerInnerMargin(),
                paddingBottom: this._getContainerInnerMargin(true)
            } : {
                paddingLeft: this._getContainerInnerMargin(),
                paddingRight: this._getContainerInnerMargin(true)
            },
            contentContainerCustomStyle || {}
        ];

        const specificProps = !this._needsScrollView() ? {
            // extraData: this.state,
            renderItem: this._renderItem,
            numColumns: 1,
            keyExtractor: keyExtractor || this._getKeyExtractor
        } : {};

        return {
            ref: c => this._carouselRef = c,
            data: this._getCustomData(),
            style: containerStyle,
            contentContainerStyle: contentContainerStyle,
            horizontal: !vertical,
            scrollEventThrottle: 1,
            onScroll: this._onScrollHandler,
            onScrollBeginDrag: this._onScrollBeginDrag,
            onScrollEndDrag: this._onScrollEndDrag,
            onMomentumScrollEnd: this._onMomentumScrollEnd,
            onResponderRelease: this._onTouchRelease,
            onStartShouldSetResponderCapture: this._onStartShouldSetResponderCapture,
            onTouchStart: this._onTouchStart,
            onTouchEnd: this._onScrollEnd,
            onLayout: this._onLayout,
            ...specificProps
        };
    }

    render () {
        const { data, renderItem, useScrollView } = this.props;

        if (!data || !renderItem) {
            return null;
        }

        const props = {
            ...this._getComponentOverridableProps(),
            ...this.props,
            ...this._getComponentStaticProps()
        };

        const ScrollViewComponent = typeof useScrollView === 'function' ? useScrollView : AnimatedScrollView

        return this._needsScrollView() ? (
            <ScrollViewComponent {...props}>
                {
                    this._getCustomData().map((item, index) => {
                        return this._renderItem({ item, index });
                    })
                }
            </ScrollViewComponent>
        ) : (
            <AnimatedFlatList {...props} />
        );
    }
}

  1. ios 打包
    UIWebView 解决苹果上架问题
    https://blog.csdn.net/sinat_30949835/article/details/107491719

9.android 二维码识别
@cubeking/react-native-qr-scanner 修改scanningImage方法

  public Result scanningImage(String path) {
        if (path == null || path.length() == 0) {
            return null;
        }
        Hashtable<DecodeHintType, String> hints = new Hashtable<>();
        hints.put(DecodeHintType.CHARACTER_SET, "UTF8"); //设置二维码内容的编码


        String replaceCode = path.replace("data:image/png;base64,", "");
        byte[] bitmapByte = Base64.decode(replaceCode, Base64.DEFAULT);
        Bitmap  scanBitmap = BitmapFactory.decodeByteArray(bitmapByte, 0, bitmapByte.length);

//        BitmapFactory.Options options = new BitmapFactory.Options();
//        options.inJustDecodeBounds = true; // 先获取原大小
//        Bitmap scanBitmap = BitmapFactory.decodeFile(path, options);
//        options.inJustDecodeBounds = false; // 获取新的大小
//        int sampleSize = (int) (options.outHeight / (float) 200);
//        if (sampleSize <= 0)
//            sampleSize = 1;
//        options.inSampleSize = sampleSize;
//        scanBitmap = BitmapFactory.decodeFile(path, options);

        int width=scanBitmap.getWidth();
        int height=scanBitmap.getHeight();
        int[] pixels=new int[width*height];
        scanBitmap.getPixels(pixels,0,width,0,0,width,height);//获取图片像素点
        RGBLuminanceSource source = new RGBLuminanceSource(scanBitmap.getWidth(),scanBitmap.getHeight(),pixels);
        BinaryBitmap bitmap1 = new BinaryBitmap(new HybridBinarizer(source));
        QRCodeReader reader = new QRCodeReader();
        try {
            return reader.decode(bitmap1, hints);
        } catch (NotFoundException e) {
            e.printStackTrace();
        } catch (ChecksumException e) {
            e.printStackTrace();
        } catch (FormatException e) {
            e.printStackTrace();
        }
        return null;
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,014评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,796评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,484评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,830评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,946评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,114评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,182评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,927评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,369评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,678评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,832评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,533评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,166评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,885评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,128评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,659评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,738评论 2 351

推荐阅读更多精彩内容