CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-jetbrains-compose-material--material-uikitarm64

Material Design components for Compose Multiplatform, specifically optimized for iOS devices running on ARM64 architecture

Pending
Overview
Eval results
Files

ios-integration.mddocs/

iOS Platform Integration

Seamless integration with native iOS UIKit components and platform-specific behaviors, enabling Material Design components to work harmoniously with iOS native development patterns.

Capabilities

UIKit View Integration

Embed native iOS UIKit views directly within Compose Material layouts.

/**
 * Composes a UIView provided by `factory` into a Compose UI hierarchy
 * @param factory A function which is called to create the UIView
 * @param modifier Modifier to be applied to this UIKitView
 * @param update A lambda which is called whenever the UIKitView is recomposed
 * @param onResize Called when the UIView's size changes
 * @param onRelease Called when the UIKitView is removed from the composition
 */
@Composable
fun <T : UIView> UIKitView(
    factory: () -> T,
    modifier: Modifier = Modifier,
    update: (T) -> Unit = {},
    onResize: ((T, CGRect) -> Unit)? = null,
    onRelease: (T) -> Unit = {}
)

Usage Examples:

// Embed a native iOS text field
@Composable
fun NativeTextField(
    text: String,
    onTextChange: (String) -> Unit,
    placeholder: String = ""
) {
    UIKitView(
        factory = {
            UITextField().apply {
                this.placeholder = placeholder
                borderStyle = UITextBorderStyle.UITextBorderStyleRoundedRect
                
                // Set up text change listener
                addTarget(
                    target = object : NSObject() {
                        @ObjCAction
                        fun textChanged() {
                            onTextChange(text ?: "")
                        }
                    },
                    action = NSSelectorFromString("textChanged"),
                    forControlEvents = UIControlEvents.UIControlEventEditingChanged
                )
            }
        },
        update = { textField ->
            textField.text = text
        },
        modifier = Modifier
            .fillMaxWidth()
            .height(44.dp)
    )
}

// Embed a native iOS map view
@Composable
fun NativeMapView(
    latitude: Double,
    longitude: Double,
    modifier: Modifier = Modifier
) {
    UIKitView(
        factory = {
            MKMapView().apply {
                showsUserLocation = true
                userInteractionEnabled = true
            }
        },
        update = { mapView ->
            val coordinate = CLLocationCoordinate2DMake(latitude, longitude)
            val region = MKCoordinateRegionMake(
                coordinate,
                MKCoordinateSpanMake(0.01, 0.01)
            )
            mapView.setRegion(region, animated = true)
        },
        modifier = modifier
    )
}

// Embed iOS camera preview
@Composable
fun CameraPreview(
    modifier: Modifier = Modifier,
    onCapturePhoto: () -> Unit = {}
) {
    UIKitView(
        factory = {
            UIView().apply {
                backgroundColor = UIColor.blackColor
                // Set up AVCaptureSession here
            }
        },
        modifier = modifier
    )
}

UIKit View Controller Integration

Embed complete UIKit view controllers within Compose layouts.

/**
 * Composes a UIViewController provided by `factory` into a Compose UI hierarchy
 * @param factory A function which is called to create the UIViewController
 * @param modifier Modifier to be applied to this UIKitViewController
 * @param update A lambda which is called whenever the UIKitViewController is recomposed
 * @param onRelease Called when the UIKitViewController is removed from the composition
 */
@Composable
fun <T : UIViewController> UIKitViewController(
    factory: () -> T,
    modifier: Modifier = Modifier,
    update: (T) -> Unit = {},
    onRelease: (T) -> Unit = {}
)

Usage Examples:

// Embed iOS document picker
@Composable
fun DocumentPicker(
    onDocumentSelected: (URL) -> Unit,
    modifier: Modifier = Modifier
) {
    var showPicker by remember { mutableStateOf(false) }
    
    if (showPicker) {
        UIKitViewController(
            factory = {
                UIDocumentPickerViewController(
                    documentTypes = listOf("public.data"),
                    mode = UIDocumentPickerMode.UIDocumentPickerModeImport
                ).apply {
                    delegate = object : UIDocumentPickerDelegate {
                        override fun documentPicker(
                            controller: UIDocumentPickerViewController,
                            didPickDocumentsAt: List<URL>
                        ) {
                            didPickDocumentsAt.firstOrNull()?.let { url ->
                                onDocumentSelected(url)
                            }
                            showPicker = false
                        }
                        
                        override fun documentPickerWasCancelled(
                            controller: UIDocumentPickerViewController
                        ) {
                            showPicker = false
                        }
                    }
                }
            },
            modifier = modifier
        )
    }
    
    // Trigger button
    Button(onClick = { showPicker = true }) {
        Text("Select Document")
    }
}

// Embed iOS image picker
@Composable
fun ImagePicker(
    onImageSelected: (UIImage) -> Unit,
    modifier: Modifier = Modifier
) {
    var showImagePicker by remember { mutableStateOf(false) }
    
    if (showImagePicker) {
        UIKitViewController(
            factory = {
                UIImagePickerController().apply {
                    sourceType = UIImagePickerControllerSourceType.UIImagePickerControllerSourceTypePhotoLibrary
                    delegate = object : UIImagePickerControllerDelegate, UINavigationControllerDelegate {
                        override fun imagePickerController(
                            picker: UIImagePickerController,
                            didFinishPickingMediaWithInfo: Map<Any?, Any?>
                        ) {
                            val image = didFinishPickingMediaWithInfo[UIImagePickerControllerOriginalImage] as? UIImage
                            image?.let { onImageSelected(it) }
                            showImagePicker = false
                        }
                        
                        override fun imagePickerControllerDidCancel(picker: UIImagePickerController) {
                            showImagePicker = false
                        }
                    }
                }
            },
            modifier = modifier
        )
    }
}

Native iOS Navigation Integration

Integrate Material Design components with iOS navigation patterns.

/**
 * iOS-specific navigation utilities for Material components
 */
object iOSNavigation {
    /**
     * Push a new screen onto the iOS navigation stack
     */
    expect fun pushViewController(viewController: UIViewController, animated: Boolean = true)
    
    /**
     * Pop the current screen from the iOS navigation stack
     */
    expect fun popViewController(animated: Boolean = true)
    
    /**
     * Present a modal screen
     */
    expect fun presentViewController(
        viewController: UIViewController,
        animated: Boolean = true,
        completion: (() -> Unit)? = null
    )
    
    /**
     * Dismiss a modal screen
     */
    expect fun dismissViewController(
        animated: Boolean = true,
        completion: (() -> Unit)? = null
    )
}

Usage Examples:

// Material components with iOS navigation
@Composable
fun MaterialScreenWithiOSNavigation() {
    Scaffold(
        topBar = {
            TopAppBar(
                title = { Text("Material + iOS") },
                navigationIcon = {
                    IconButton(onClick = {
                        // Use iOS navigation
                        iOSNavigation.popViewController()
                    }) {
                        Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Back")
                    }
                },
                actions = {
                    IconButton(onClick = {
                        // Present iOS modal
                        val settingsVC = SettingsViewController()
                        iOSNavigation.presentViewController(settingsVC)
                    }) {
                        Icon(Icons.Default.Settings, contentDescription = "Settings")
                    }
                }
            )
        }
    ) { paddingValues ->
        LazyColumn(
            modifier = Modifier.padding(paddingValues),
            contentPadding = PaddingValues(16.dp)
        ) {
            items(dataItems) { item ->
                Card(
                    modifier = Modifier
                        .fillMaxWidth()
                        .padding(vertical = 4.dp)
                        .clickable {
                            // Navigate to detail screen
                            val detailVC = DetailViewController(item)
                            iOSNavigation.pushViewController(detailVC)
                        }
                ) {
                    // Card content
                }
            }
        }
    }
}

Safe Area and Layout Integration

Handle iOS safe areas and layout constraints within Material components.

/**
 * iOS safe area insets integration
 */
@Composable
expect fun WindowInsets.Companion.safeArea(): WindowInsets

/**
 * Apply iOS safe area padding to Material components
 */
@Composable
fun Modifier.iOSSafeArea(): Modifier = this.then(
    windowInsetsPadding(WindowInsets.safeArea())
)

/**
 * iOS status bar integration
 */
object iOSStatusBar {
    expect fun setStatusBarStyle(style: StatusBarStyle)
    expect fun setStatusBarHidden(hidden: Boolean, animated: Boolean = true)
}

enum class StatusBarStyle {
    Default, LightContent, DarkContent
}

Usage Examples:

// Material app with iOS safe area handling
@Composable
fun iOSMaterialApp() {
    MaterialTheme(
        colors = if (isSystemInDarkTheme()) darkColors() else lightColors()
    ) {
        // Update status bar based on theme
        LaunchedEffect(MaterialTheme.colors.isLight) {
            iOSStatusBar.setStatusBarStyle(
                if (MaterialTheme.colors.isLight) StatusBarStyle.DarkContent
                else StatusBarStyle.LightContent
            )
        }
        
        Scaffold(
            modifier = Modifier.iOSSafeArea(),
            topBar = {
                TopAppBar(
                    title = { Text("iOS Safe Area") },
                    backgroundColor = Color.Transparent,
                    elevation = 0.dp
                )
            }
        ) { paddingValues ->
            // Content automatically respects safe areas
            Content(modifier = Modifier.padding(paddingValues))
        }
    }
}

// Handle keyboard avoidance
@Composable
fun KeyboardAwareContent() {
    val keyboardHeight by keyboardAsState()
    
    Column(
        modifier = Modifier
            .fillMaxSize()
            .padding(bottom = keyboardHeight)
    ) {
        // Content that moves up when keyboard appears
        TextField(
            value = text,
            onValueChange = { text = it },
            modifier = Modifier.fillMaxWidth()
        )
    }
}

@Composable
expect fun keyboardAsState(): State<Dp>

Haptic Feedback Integration

Integrate iOS haptic feedback with Material component interactions.

/**
 * iOS haptic feedback integration
 */
object iOSHaptics {
    /**
     * Trigger light impact haptic feedback
     */
    expect fun lightImpact()
    
    /**
     * Trigger medium impact haptic feedback
     */
    expect fun mediumImpact()
    
    /**
     * Trigger heavy impact haptic feedback
     */
    expect fun heavyImpact()
    
    /**
     * Trigger selection haptic feedback
     */
    expect fun selectionChanged()
    
    /**
     * Trigger notification haptic feedback
     */
    expect fun notification(type: NotificationHapticType)
}

enum class NotificationHapticType {
    Success, Warning, Error
}

Usage Examples:

// Material buttons with iOS haptic feedback
@Composable
fun HapticButton(
    onClick: () -> Unit,
    modifier: Modifier = Modifier,
    content: @Composable RowScope.() -> Unit
) {
    Button(
        onClick = {
            iOSHaptics.lightImpact()
            onClick()
        },
        modifier = modifier,
        content = content
    )
}

// Switch with haptic feedback
@Composable
fun HapticSwitch(
    checked: Boolean,
    onCheckedChange: (Boolean) -> Unit,
    modifier: Modifier = Modifier
) {
    Switch(
        checked = checked,
        onCheckedChange = { newValue ->
            iOSHaptics.selectionChanged()
            onCheckedChange(newValue)
        },
        modifier = modifier
    )
}

// Slider with haptic steps
@Composable
fun HapticSlider(
    value: Float,
    onValueChange: (Float) -> Unit,
    steps: Int = 0,
    modifier: Modifier = Modifier
) {
    var lastStepValue by remember { mutableStateOf(value) }
    
    Slider(
        value = value,
        onValueChange = { newValue ->
            if (steps > 0) {
                val stepValue = (newValue * steps).roundToInt() / steps.toFloat()
                if (stepValue != lastStepValue) {
                    iOSHaptics.selectionChanged()
                    lastStepValue = stepValue
                }
            }
            onValueChange(newValue)
        },
        steps = steps,
        modifier = modifier
    )
}

Native iOS Alerts and Dialogs

Present native iOS alerts while maintaining Material theming consistency.

/**
 * Present native iOS alert dialogs
 */
object iOSAlerts {
    /**
     * Show a native iOS alert
     */
    expect fun showAlert(
        title: String,
        message: String? = null,
        primaryButton: AlertButton,
        secondaryButton: AlertButton? = null
    )
    
    /**
     * Show a native iOS action sheet
     */
    expect fun showActionSheet(
        title: String? = null,
        message: String? = null,
        actions: List<AlertAction>,
        anchor: CGRect? = null
    )
}

data class AlertButton(
    val title: String,
    val style: AlertButtonStyle = AlertButtonStyle.Default,
    val action: () -> Unit
)

data class AlertAction(
    val title: String,
    val style: AlertActionStyle = AlertActionStyle.Default,
    val action: () -> Unit
)

enum class AlertButtonStyle {
    Default, Cancel, Destructive
}

enum class AlertActionStyle {
    Default, Cancel, Destructive
}

Usage Examples:

// Material components triggering iOS alerts
@Composable
fun MaterialWithiOSAlerts() {
    var showDeleteDialog by remember { mutableStateOf(false) }
    
    LazyColumn {
        items(items) { item ->
            Card(
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(8.dp)
            ) {
                Row(
                    modifier = Modifier.padding(16.dp),
                    horizontalArrangement = Arrangement.SpaceBetween,
                    verticalAlignment = Alignment.CenterVertically
                ) {
                    Text(item.title)
                    
                    IconButton(onClick = {
                        // Show native iOS alert
                        iOSAlerts.showAlert(
                            title = "Delete Item",
                            message = "Are you sure you want to delete '${item.title}'?",
                            primaryButton = AlertButton(
                                title = "Delete",
                                style = AlertButtonStyle.Destructive
                            ) {
                                deleteItem(item)
                            },
                            secondaryButton = AlertButton(
                                title = "Cancel",
                                style = AlertButtonStyle.Cancel
                            ) {
                                // Do nothing
                            }
                        )
                    }) {
                        Icon(
                            Icons.Default.Delete,
                            contentDescription = "Delete",
                            tint = MaterialTheme.colors.error
                        )
                    }
                }
            }
        }
    }
}

// Action sheet from Material FAB
@Composable
fun FabWithActionSheet() {
    FloatingActionButton(
        onClick = {
            iOSAlerts.showActionSheet(
                title = "Choose Action",
                actions = listOf(
                    AlertAction("Take Photo") { /* camera action */ },
                    AlertAction("Choose from Library") { /* gallery action */ },
                    AlertAction("Cancel", AlertActionStyle.Cancel) { }
                )
            )
        }
    ) {
        Icon(Icons.Default.Add, contentDescription = "Add")
    }
}

Platform-Specific Utilities

/**
 * iOS system integration utilities
 */
object iOSSystem {
    /**
     * Get current iOS version
     */
    expect val systemVersion: String
    
    /**
     * Check if device supports specific features
     */
    expect fun supportsHaptics(): Boolean
    expect fun supportsCamera(): Boolean
    expect fun supportsFaceID(): Boolean
    expect fun supportsTouchID(): Boolean
    
    /**
     * Device information
     */
    expect val deviceModel: String
    expect val isIPad: Boolean
    expect val isIPhone: Boolean
    
    /**
     * Open iOS system settings
     */
    expect fun openSettings()
    
    /**
     * Share content using iOS share sheet
     */
    expect fun share(
        items: List<Any>,
        excludedActivityTypes: List<String> = emptyList()
    )
}

Install with Tessl CLI

npx tessl i tessl/maven-org-jetbrains-compose-material--material-uikitarm64

docs

core-components.md

icons.md

index.md

input-components.md

ios-integration.md

navigation.md

theming.md

tile.json