# 🏞️ View Functions ### Navigation - [🏡 Native modules made easy with Expo](/gYH9xz-oR2ai0Yih8if50w) - [👶 First Steps](/ANE6NSUlTSimTIrN-gMsBw) - [⚙️ Native Module](/mAIt0ctDTvSL5xV4uE9nWQ) - [📈 Passing data to view](/_gWWp8uoQwGkqKcEyfk8Tg) - [📚 View Props](/96IjlLNDRvydILdkbfaA5A) - [🔥 View Events](/PQWXYmxLRCebmLfx3Fh_gg) - [👉 🏞️ View Functions](/DcjStCFdT6euzWqnJqCL6w) - [👷‍♂️ Classes and Shared Objects](/__42gVw8RqiIgfSbVseIvw) - [📊 Views and shared object](/IgHyIAHQQPCbeBD74Ri4BA) --- ### Task 1 Export a `moveToStart` view function that scrolls the chart view to the fist data point. :::spoiler :robot_face: Android :open_file_folder: File `android/src/main/java/expo/modules/workshopscharts/LinearChartModule.kt` ```kotlin AsyncFunction("moveToStart") { view: LinearChartView -> view.moveToStart() } ``` :open_file_folder: File `android/src/main/java/expo/modules/workshopscharts/LinearChartView.kt` ```kotlin fun moveToStart() { val dataSet = chartView.data ?: return chartView.moveTo(dataSet.xMin - 1f) } ``` ::: :::spoiler :apple: iOS :open_file_folder: File `ios/LinearChartModule.swift` ```swift AsyncFunction("moveToStart") { (view: LinearChartView) in view.moveToStart() } ``` :open_file_folder: File `ios/LinearChartView.swift` ```swift func moveToStart() { if let dataSet = chartView.data { chartView.moveViewToX(dataSet.xMin - 1) } } ``` ::: <br /> 📝 Full changelog: [open GitHub Commit](https://github.com/software-mansion-labs/appjs-2023-workshop-expo-modules/commit/be4f5f66832269a66c9a5d79790028d43f40d579) --- ### Task 2 Similar to the previous example, export two additional functions that can scroll the view to the last data point (`moveToEnd`) or to a provided point (`moveToPoint`). :::spoiler :robot_face: Android :open_file_folder: File `android/src/main/java/expo/modules/workshopscharts/LinearChartModule.kt` ```kotlin AsyncFunction("moveToEnd") { view: LinearChartView -> view.moveToEnd() } AsyncFunction("moveToPoint") { view: LinearChartView, x: Float, y: Float -> view.moveToPoint(x, y) } ``` :open_file_folder: File `android/src/main/java/expo/modules/workshopscharts/LinearChartView.kt` ```kotlin fun moveToEnd() { val dataSet = chartView.data ?: return chartView.moveTo(dataSet.xMax + 1f) } fun moveToPoint(x: Float, y: Float) { chartView.moveTo(x, y) } ``` ::: :::spoiler :apple: iOS :open_file_folder: File `ios/LinearChartModule.swift` ```swift AsyncFunction("moveToEnd") { (view: LinearChartView) in view.moveToEnd() } AsyncFunction("moveToPoint") { (view: LinearChartView, x: Double, y: Double) in view.moveToPoint(x, y) } ``` :open_file_folder: File `ios/LinearChartView.swift` ```swift func moveToEnd() { if let dataSet = chartView.data { chartView.moveViewToX(dataSet.xMax - 1) } } func moveToPoint(_ x: Double, _ y: Double) { chartView.moveViewTo(xValue: x, yValue: y, axis: .left) } ``` ::: <br /> 📝 Full changelog: [open GitHub Commit](https://github.com/software-mansion-labs/appjs-2023-workshop-expo-modules/commit/d5d09847ef49780236d53975dd0b5459ada0b9eb) --- ### Task 3 Export the `saveToGallery` view function which can save the visible portion of the view as an image into the camera roll. :::spoiler :robot_face: Android :open_file_folder: File `android/src/main/java/expo/modules/workshopscharts/LinearChartModule.kt` ```kotlin class UserRejectedPermissionsException : CodedException( message = "User rejected permissions" ) private var wasScopedInitialized = false internal val moduleScope by lazy { wasScopedInitialized = true CoroutineScope( Dispatchers.IO + SupervisorJob() + CoroutineName("LinearChartModuleScope") ) } // ... override fun definition() = ModuleDefinition { OnDestroy { if (wasScopedInitialized) { moduleScope.cancel(ContextDestroyedException()) } } // ... AsyncFunction("saveToGallery") { view: LinearChartView, promise: Promise -> val permissionManager = appContext.permissions ?: throw Exceptions.MissingPermissions() permissionManager.askForPermissions({ result -> if (result[Manifest.permission.WRITE_EXTERNAL_STORAGE]?.status != PermissionsStatus.GRANTED) { promise.reject(UserRejectedPermissionsException()) return@askForPermissions } view.saveToGallery(promise) }, Manifest.permission.WRITE_EXTERNAL_STORAGE) } } ``` :open_file_folder: File `android/src/main/java/expo/modules/workshopscharts/LinearChartView.kt` ```kotlin fun saveToGallery(promise: Promise) { appContext .registry .getModule<LinearChartModule>() ?.moduleScope ?.launch { val bitmap = chartView.chartBitmap Utils.saveImage(bitmap, context, "MyApp") promise.resolve(null) } } ``` ::: :::spoiler :apple: iOS :open_file_folder: File `ios/LinearChartModule.swift` ```swift AsyncFunction("saveToGallery") { (view: LinearChartView, promise: Promise) in view.saveToGallery(promise) } ``` :open_file_folder: File `ios/LinearChartView.swift` ```swift class ImageSaver: NSObject { private let promise: Promise init(promise: Promise) { self.promise = promise } func writeToPhotoAlbum(image: UIImage) { UIImageWriteToSavedPhotosAlbum(image, self, #selector(saveCompleted), nil) } @objc func saveCompleted(_ image: UIImage, didFinishSavingWithError error: Error?, contextInfo: UnsafeRawPointer) { promise.resolve(error == nil) } } // ... func saveToGallery(_ promise: Promise) { if let imageData = chartView.getChartImage(transparent: true) { ImageSaver(promise: promise).writeToPhotoAlbum(image: imageData) return } promise.resolve(false) } ``` ::: <br /> 📝 Full changelog: [open GitHub Commit](https://github.com/software-mansion-labs/appjs-2023-workshop-expo-modules/commit/e76d324d18f1b8b758b3ff1d33ce2e8f366c73a8) --- ### [👷‍♂️ Classes and Shared Objects](/__42gVw8RqiIgfSbVseIvw)