0
# iOS Platform Integration
1
2
Seamless integration with native iOS UIKit components and platform-specific behaviors, enabling Material Design components to work harmoniously with iOS native development patterns.
3
4
## Capabilities
5
6
### UIKit View Integration
7
8
Embed native iOS UIKit views directly within Compose Material layouts.
9
10
```kotlin { .api }
11
/**
12
* Composes a UIView provided by `factory` into a Compose UI hierarchy
13
* @param factory A function which is called to create the UIView
14
* @param modifier Modifier to be applied to this UIKitView
15
* @param update A lambda which is called whenever the UIKitView is recomposed
16
* @param onResize Called when the UIView's size changes
17
* @param onRelease Called when the UIKitView is removed from the composition
18
*/
19
@Composable
20
fun <T : UIView> UIKitView(
21
factory: () -> T,
22
modifier: Modifier = Modifier,
23
update: (T) -> Unit = {},
24
onResize: ((T, CGRect) -> Unit)? = null,
25
onRelease: (T) -> Unit = {}
26
)
27
```
28
29
**Usage Examples:**
30
31
```kotlin
32
// Embed a native iOS text field
33
@Composable
34
fun NativeTextField(
35
text: String,
36
onTextChange: (String) -> Unit,
37
placeholder: String = ""
38
) {
39
UIKitView(
40
factory = {
41
UITextField().apply {
42
this.placeholder = placeholder
43
borderStyle = UITextBorderStyle.UITextBorderStyleRoundedRect
44
45
// Set up text change listener
46
addTarget(
47
target = object : NSObject() {
48
@ObjCAction
49
fun textChanged() {
50
onTextChange(text ?: "")
51
}
52
},
53
action = NSSelectorFromString("textChanged"),
54
forControlEvents = UIControlEvents.UIControlEventEditingChanged
55
)
56
}
57
},
58
update = { textField ->
59
textField.text = text
60
},
61
modifier = Modifier
62
.fillMaxWidth()
63
.height(44.dp)
64
)
65
}
66
67
// Embed a native iOS map view
68
@Composable
69
fun NativeMapView(
70
latitude: Double,
71
longitude: Double,
72
modifier: Modifier = Modifier
73
) {
74
UIKitView(
75
factory = {
76
MKMapView().apply {
77
showsUserLocation = true
78
userInteractionEnabled = true
79
}
80
},
81
update = { mapView ->
82
val coordinate = CLLocationCoordinate2DMake(latitude, longitude)
83
val region = MKCoordinateRegionMake(
84
coordinate,
85
MKCoordinateSpanMake(0.01, 0.01)
86
)
87
mapView.setRegion(region, animated = true)
88
},
89
modifier = modifier
90
)
91
}
92
93
// Embed iOS camera preview
94
@Composable
95
fun CameraPreview(
96
modifier: Modifier = Modifier,
97
onCapturePhoto: () -> Unit = {}
98
) {
99
UIKitView(
100
factory = {
101
UIView().apply {
102
backgroundColor = UIColor.blackColor
103
// Set up AVCaptureSession here
104
}
105
},
106
modifier = modifier
107
)
108
}
109
```
110
111
### UIKit View Controller Integration
112
113
Embed complete UIKit view controllers within Compose layouts.
114
115
```kotlin { .api }
116
/**
117
* Composes a UIViewController provided by `factory` into a Compose UI hierarchy
118
* @param factory A function which is called to create the UIViewController
119
* @param modifier Modifier to be applied to this UIKitViewController
120
* @param update A lambda which is called whenever the UIKitViewController is recomposed
121
* @param onRelease Called when the UIKitViewController is removed from the composition
122
*/
123
@Composable
124
fun <T : UIViewController> UIKitViewController(
125
factory: () -> T,
126
modifier: Modifier = Modifier,
127
update: (T) -> Unit = {},
128
onRelease: (T) -> Unit = {}
129
)
130
```
131
132
**Usage Examples:**
133
134
```kotlin
135
// Embed iOS document picker
136
@Composable
137
fun DocumentPicker(
138
onDocumentSelected: (URL) -> Unit,
139
modifier: Modifier = Modifier
140
) {
141
var showPicker by remember { mutableStateOf(false) }
142
143
if (showPicker) {
144
UIKitViewController(
145
factory = {
146
UIDocumentPickerViewController(
147
documentTypes = listOf("public.data"),
148
mode = UIDocumentPickerMode.UIDocumentPickerModeImport
149
).apply {
150
delegate = object : UIDocumentPickerDelegate {
151
override fun documentPicker(
152
controller: UIDocumentPickerViewController,
153
didPickDocumentsAt: List<URL>
154
) {
155
didPickDocumentsAt.firstOrNull()?.let { url ->
156
onDocumentSelected(url)
157
}
158
showPicker = false
159
}
160
161
override fun documentPickerWasCancelled(
162
controller: UIDocumentPickerViewController
163
) {
164
showPicker = false
165
}
166
}
167
}
168
},
169
modifier = modifier
170
)
171
}
172
173
// Trigger button
174
Button(onClick = { showPicker = true }) {
175
Text("Select Document")
176
}
177
}
178
179
// Embed iOS image picker
180
@Composable
181
fun ImagePicker(
182
onImageSelected: (UIImage) -> Unit,
183
modifier: Modifier = Modifier
184
) {
185
var showImagePicker by remember { mutableStateOf(false) }
186
187
if (showImagePicker) {
188
UIKitViewController(
189
factory = {
190
UIImagePickerController().apply {
191
sourceType = UIImagePickerControllerSourceType.UIImagePickerControllerSourceTypePhotoLibrary
192
delegate = object : UIImagePickerControllerDelegate, UINavigationControllerDelegate {
193
override fun imagePickerController(
194
picker: UIImagePickerController,
195
didFinishPickingMediaWithInfo: Map<Any?, Any?>
196
) {
197
val image = didFinishPickingMediaWithInfo[UIImagePickerControllerOriginalImage] as? UIImage
198
image?.let { onImageSelected(it) }
199
showImagePicker = false
200
}
201
202
override fun imagePickerControllerDidCancel(picker: UIImagePickerController) {
203
showImagePicker = false
204
}
205
}
206
}
207
},
208
modifier = modifier
209
)
210
}
211
}
212
```
213
214
### Native iOS Navigation Integration
215
216
Integrate Material Design components with iOS navigation patterns.
217
218
```kotlin { .api }
219
/**
220
* iOS-specific navigation utilities for Material components
221
*/
222
object iOSNavigation {
223
/**
224
* Push a new screen onto the iOS navigation stack
225
*/
226
expect fun pushViewController(viewController: UIViewController, animated: Boolean = true)
227
228
/**
229
* Pop the current screen from the iOS navigation stack
230
*/
231
expect fun popViewController(animated: Boolean = true)
232
233
/**
234
* Present a modal screen
235
*/
236
expect fun presentViewController(
237
viewController: UIViewController,
238
animated: Boolean = true,
239
completion: (() -> Unit)? = null
240
)
241
242
/**
243
* Dismiss a modal screen
244
*/
245
expect fun dismissViewController(
246
animated: Boolean = true,
247
completion: (() -> Unit)? = null
248
)
249
}
250
```
251
252
**Usage Examples:**
253
254
```kotlin
255
// Material components with iOS navigation
256
@Composable
257
fun MaterialScreenWithiOSNavigation() {
258
Scaffold(
259
topBar = {
260
TopAppBar(
261
title = { Text("Material + iOS") },
262
navigationIcon = {
263
IconButton(onClick = {
264
// Use iOS navigation
265
iOSNavigation.popViewController()
266
}) {
267
Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Back")
268
}
269
},
270
actions = {
271
IconButton(onClick = {
272
// Present iOS modal
273
val settingsVC = SettingsViewController()
274
iOSNavigation.presentViewController(settingsVC)
275
}) {
276
Icon(Icons.Default.Settings, contentDescription = "Settings")
277
}
278
}
279
)
280
}
281
) { paddingValues ->
282
LazyColumn(
283
modifier = Modifier.padding(paddingValues),
284
contentPadding = PaddingValues(16.dp)
285
) {
286
items(dataItems) { item ->
287
Card(
288
modifier = Modifier
289
.fillMaxWidth()
290
.padding(vertical = 4.dp)
291
.clickable {
292
// Navigate to detail screen
293
val detailVC = DetailViewController(item)
294
iOSNavigation.pushViewController(detailVC)
295
}
296
) {
297
// Card content
298
}
299
}
300
}
301
}
302
}
303
```
304
305
### Safe Area and Layout Integration
306
307
Handle iOS safe areas and layout constraints within Material components.
308
309
```kotlin { .api }
310
/**
311
* iOS safe area insets integration
312
*/
313
@Composable
314
expect fun WindowInsets.Companion.safeArea(): WindowInsets
315
316
/**
317
* Apply iOS safe area padding to Material components
318
*/
319
@Composable
320
fun Modifier.iOSSafeArea(): Modifier = this.then(
321
windowInsetsPadding(WindowInsets.safeArea())
322
)
323
324
/**
325
* iOS status bar integration
326
*/
327
object iOSStatusBar {
328
expect fun setStatusBarStyle(style: StatusBarStyle)
329
expect fun setStatusBarHidden(hidden: Boolean, animated: Boolean = true)
330
}
331
332
enum class StatusBarStyle {
333
Default, LightContent, DarkContent
334
}
335
```
336
337
**Usage Examples:**
338
339
```kotlin
340
// Material app with iOS safe area handling
341
@Composable
342
fun iOSMaterialApp() {
343
MaterialTheme(
344
colors = if (isSystemInDarkTheme()) darkColors() else lightColors()
345
) {
346
// Update status bar based on theme
347
LaunchedEffect(MaterialTheme.colors.isLight) {
348
iOSStatusBar.setStatusBarStyle(
349
if (MaterialTheme.colors.isLight) StatusBarStyle.DarkContent
350
else StatusBarStyle.LightContent
351
)
352
}
353
354
Scaffold(
355
modifier = Modifier.iOSSafeArea(),
356
topBar = {
357
TopAppBar(
358
title = { Text("iOS Safe Area") },
359
backgroundColor = Color.Transparent,
360
elevation = 0.dp
361
)
362
}
363
) { paddingValues ->
364
// Content automatically respects safe areas
365
Content(modifier = Modifier.padding(paddingValues))
366
}
367
}
368
}
369
370
// Handle keyboard avoidance
371
@Composable
372
fun KeyboardAwareContent() {
373
val keyboardHeight by keyboardAsState()
374
375
Column(
376
modifier = Modifier
377
.fillMaxSize()
378
.padding(bottom = keyboardHeight)
379
) {
380
// Content that moves up when keyboard appears
381
TextField(
382
value = text,
383
onValueChange = { text = it },
384
modifier = Modifier.fillMaxWidth()
385
)
386
}
387
}
388
389
@Composable
390
expect fun keyboardAsState(): State<Dp>
391
```
392
393
### Haptic Feedback Integration
394
395
Integrate iOS haptic feedback with Material component interactions.
396
397
```kotlin { .api }
398
/**
399
* iOS haptic feedback integration
400
*/
401
object iOSHaptics {
402
/**
403
* Trigger light impact haptic feedback
404
*/
405
expect fun lightImpact()
406
407
/**
408
* Trigger medium impact haptic feedback
409
*/
410
expect fun mediumImpact()
411
412
/**
413
* Trigger heavy impact haptic feedback
414
*/
415
expect fun heavyImpact()
416
417
/**
418
* Trigger selection haptic feedback
419
*/
420
expect fun selectionChanged()
421
422
/**
423
* Trigger notification haptic feedback
424
*/
425
expect fun notification(type: NotificationHapticType)
426
}
427
428
enum class NotificationHapticType {
429
Success, Warning, Error
430
}
431
```
432
433
**Usage Examples:**
434
435
```kotlin
436
// Material buttons with iOS haptic feedback
437
@Composable
438
fun HapticButton(
439
onClick: () -> Unit,
440
modifier: Modifier = Modifier,
441
content: @Composable RowScope.() -> Unit
442
) {
443
Button(
444
onClick = {
445
iOSHaptics.lightImpact()
446
onClick()
447
},
448
modifier = modifier,
449
content = content
450
)
451
}
452
453
// Switch with haptic feedback
454
@Composable
455
fun HapticSwitch(
456
checked: Boolean,
457
onCheckedChange: (Boolean) -> Unit,
458
modifier: Modifier = Modifier
459
) {
460
Switch(
461
checked = checked,
462
onCheckedChange = { newValue ->
463
iOSHaptics.selectionChanged()
464
onCheckedChange(newValue)
465
},
466
modifier = modifier
467
)
468
}
469
470
// Slider with haptic steps
471
@Composable
472
fun HapticSlider(
473
value: Float,
474
onValueChange: (Float) -> Unit,
475
steps: Int = 0,
476
modifier: Modifier = Modifier
477
) {
478
var lastStepValue by remember { mutableStateOf(value) }
479
480
Slider(
481
value = value,
482
onValueChange = { newValue ->
483
if (steps > 0) {
484
val stepValue = (newValue * steps).roundToInt() / steps.toFloat()
485
if (stepValue != lastStepValue) {
486
iOSHaptics.selectionChanged()
487
lastStepValue = stepValue
488
}
489
}
490
onValueChange(newValue)
491
},
492
steps = steps,
493
modifier = modifier
494
)
495
}
496
```
497
498
### Native iOS Alerts and Dialogs
499
500
Present native iOS alerts while maintaining Material theming consistency.
501
502
```kotlin { .api }
503
/**
504
* Present native iOS alert dialogs
505
*/
506
object iOSAlerts {
507
/**
508
* Show a native iOS alert
509
*/
510
expect fun showAlert(
511
title: String,
512
message: String? = null,
513
primaryButton: AlertButton,
514
secondaryButton: AlertButton? = null
515
)
516
517
/**
518
* Show a native iOS action sheet
519
*/
520
expect fun showActionSheet(
521
title: String? = null,
522
message: String? = null,
523
actions: List<AlertAction>,
524
anchor: CGRect? = null
525
)
526
}
527
528
data class AlertButton(
529
val title: String,
530
val style: AlertButtonStyle = AlertButtonStyle.Default,
531
val action: () -> Unit
532
)
533
534
data class AlertAction(
535
val title: String,
536
val style: AlertActionStyle = AlertActionStyle.Default,
537
val action: () -> Unit
538
)
539
540
enum class AlertButtonStyle {
541
Default, Cancel, Destructive
542
}
543
544
enum class AlertActionStyle {
545
Default, Cancel, Destructive
546
}
547
```
548
549
**Usage Examples:**
550
551
```kotlin
552
// Material components triggering iOS alerts
553
@Composable
554
fun MaterialWithiOSAlerts() {
555
var showDeleteDialog by remember { mutableStateOf(false) }
556
557
LazyColumn {
558
items(items) { item ->
559
Card(
560
modifier = Modifier
561
.fillMaxWidth()
562
.padding(8.dp)
563
) {
564
Row(
565
modifier = Modifier.padding(16.dp),
566
horizontalArrangement = Arrangement.SpaceBetween,
567
verticalAlignment = Alignment.CenterVertically
568
) {
569
Text(item.title)
570
571
IconButton(onClick = {
572
// Show native iOS alert
573
iOSAlerts.showAlert(
574
title = "Delete Item",
575
message = "Are you sure you want to delete '${item.title}'?",
576
primaryButton = AlertButton(
577
title = "Delete",
578
style = AlertButtonStyle.Destructive
579
) {
580
deleteItem(item)
581
},
582
secondaryButton = AlertButton(
583
title = "Cancel",
584
style = AlertButtonStyle.Cancel
585
) {
586
// Do nothing
587
}
588
)
589
}) {
590
Icon(
591
Icons.Default.Delete,
592
contentDescription = "Delete",
593
tint = MaterialTheme.colors.error
594
)
595
}
596
}
597
}
598
}
599
}
600
}
601
602
// Action sheet from Material FAB
603
@Composable
604
fun FabWithActionSheet() {
605
FloatingActionButton(
606
onClick = {
607
iOSAlerts.showActionSheet(
608
title = "Choose Action",
609
actions = listOf(
610
AlertAction("Take Photo") { /* camera action */ },
611
AlertAction("Choose from Library") { /* gallery action */ },
612
AlertAction("Cancel", AlertActionStyle.Cancel) { }
613
)
614
)
615
}
616
) {
617
Icon(Icons.Default.Add, contentDescription = "Add")
618
}
619
}
620
```
621
622
## Platform-Specific Utilities
623
624
```kotlin { .api }
625
/**
626
* iOS system integration utilities
627
*/
628
object iOSSystem {
629
/**
630
* Get current iOS version
631
*/
632
expect val systemVersion: String
633
634
/**
635
* Check if device supports specific features
636
*/
637
expect fun supportsHaptics(): Boolean
638
expect fun supportsCamera(): Boolean
639
expect fun supportsFaceID(): Boolean
640
expect fun supportsTouchID(): Boolean
641
642
/**
643
* Device information
644
*/
645
expect val deviceModel: String
646
expect val isIPad: Boolean
647
expect val isIPhone: Boolean
648
649
/**
650
* Open iOS system settings
651
*/
652
expect fun openSettings()
653
654
/**
655
* Share content using iOS share sheet
656
*/
657
expect fun share(
658
items: List<Any>,
659
excludedActivityTypes: List<String> = emptyList()
660
)
661
}
662
```