Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[expo-updates][ios] Add segmented control for iOS 13 #8038

Merged
merged 5 commits into from May 22, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/bare-expo/package.json
Expand Up @@ -79,6 +79,7 @@
},
"dependencies": {
"@babel/runtime": "^7.5.5",
"@react-native-community/segmented-control": "^1.4.0",
"@react-navigation/web": "2.0.0-alpha.0",
"expo": "~37.0.6",
"expo-image": "~1.0.0-alpha.0",
Expand Down
1 change: 1 addition & 0 deletions apps/native-component-list/package.json
Expand Up @@ -16,6 +16,7 @@
"@react-native-community/datetimepicker": "2.2.2",
"@react-native-community/masked-view": "0.1.6",
"@react-native-community/netinfo": "5.5.0",
"@react-native-community/segmented-control": "^1.4.0",
"@react-native-community/viewpager": "3.3.0",
"@react-navigation/web": "2.0.0-alpha.0",
"date-format": "^2.0.0",
Expand Down
2 changes: 2 additions & 0 deletions apps/native-component-list/src/navigation/ExpoComponents.ts
Expand Up @@ -36,6 +36,7 @@ const ReanimatedImagePreview = optionalRequire(() =>
const ReanimatedProgress = optionalRequire(() =>
require('../screens/Reanimated/ReanimatedProgressScreen')
);
const SegmentedControl = optionalRequire(() => require('../screens/SegmentedControlScreen'));
const SVGExample = optionalRequire(() => require('../screens/SVG/SVGExampleScreen'));
const SVG = optionalRequire(() => require('../screens/SVG/SVGScreen'));
const SharedElement = optionalRequire(() => require('../screens/SharedElementScreen'));
Expand Down Expand Up @@ -67,6 +68,7 @@ const optionalScreens: { [key: string]: React.ComponentType | null } = {
ReanimatedProgress,
Gif,
FacebookAds,
SegmentedControl,
SVG,
SVGExample,
LinearGradient,
Expand Down
Expand Up @@ -36,6 +36,7 @@ export default class ExpoComponentsScreen extends React.Component {
'MaskedView',
'ReanimatedImagePreview',
'ReanimatedProgress',
'SegmentedControl',
'Screens',
'SharedElement',
'SVG',
Expand Down
104 changes: 104 additions & 0 deletions apps/native-component-list/src/screens/SegmentedControlScreen.tsx
@@ -0,0 +1,104 @@
import SegmentedControl from '@react-native-community/segmented-control';
import React, { useState } from 'react';
import { StyleSheet, ScrollView, Text, View } from 'react-native';

// This example is a copy from https://github.com/react-native-community/segmented-control/blob/master/example

const SegmentedControlScreen = () => {
const [values] = useState(['One', 'Two', 'Three']);
const [value, setValue] = useState('Unselected');
const [selectedIndex, setIndex] = useState(undefined);

const _onChange = event => {
setIndex(event.nativeEvent.selectedSegmentIndex);
};

const _onValueChange = (value: string) => {
setValue(value);
};

return (
<ScrollView contentContainerStyle={styles.container}>
<View style={styles.segmentContainer}>
<Text style={styles.text}>Segmented controls can have values</Text>
<SegmentedControl values={['One', 'Two']} />
</View>

<View style={styles.segmentSection}>
<SegmentedControl values={['One', 'Two', 'Three', 'Four', 'Five']} />
</View>

<View style={styles.segmentSection}>
<Text style={styles.text}>Segmented controls can have pre-selected values</Text>
<SegmentedControl values={['One', 'Two']} selectedIndex={0} />
</View>

<View style={styles.segmentSection}>
<Text style={styles.text}>Segmented controls can be momentary</Text>
<SegmentedControl values={['One', 'Two']} momentary />
</View>

<View style={styles.segmentSection}>
<Text style={styles.text}>Segmented controls can be disabled</Text>
<SegmentedControl enabled={false} values={['One', 'Two']} selectedIndex={1} />
</View>

<View style={styles.segmentContainer}>
<Text style={styles.text}>Custom colors can be provided</Text>
<SegmentedControl
tintColor="#ff0000"
values={['One', 'Two', 'Three', 'Four']}
selectedIndex={0}
backgroundColor="#0000ff"
activeTextColor="white"
/>
</View>
<View style={styles.segmentContainer}>
<SegmentedControl
tintColor="#00ff00"
values={['One', 'Two', 'Three']}
selectedIndex={1}
activeTextColor="black"
/>
</View>
<View style={styles.segmentSection}>
<SegmentedControl textColor="#ff00ff" values={['One', 'Two']} selectedIndex={1} />
</View>

<View>
<Text style={styles.text}>Custom colors can be provided</Text>
<View style={styles.segmentContainer}>
<SegmentedControl
values={values}
selectedIndex={selectedIndex}
onChange={_onChange}
onValueChange={_onValueChange}
/>
</View>
<Text style={[styles.text]}>
Value: {value} Index: {selectedIndex}
</Text>
</View>
</ScrollView>
);
};

const styles = StyleSheet.create({
text: {
fontSize: 14,
textAlign: 'center',
fontWeight: '500',
margin: 10,
},
segmentContainer: {
marginBottom: 10,
},
segmentSection: {
marginBottom: 25,
},
container: {
paddingTop: 80,
},
});

export default SegmentedControlScreen;
1 change: 1 addition & 0 deletions docs/common/navigation.js
Expand Up @@ -195,6 +195,7 @@ const sections = [
'SafeAreaView',
'ScrollView',
'SectionList',
'SegmentedControl',
'SegmentedControlIOS',
'Slider',
'SnapshotViewIOS',
Expand Down
@@ -0,0 +1,17 @@
/**
* 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.
*/

#import <UIKit/UIKit.h>

#import <React/RCTComponent.h>

@interface RNCSegmentedControl : UISegmentedControl

@property (nonatomic, assign) NSInteger selectedIndex;
@property (nonatomic, copy) RCTBubblingEventBlock onChange;

@end
@@ -0,0 +1,111 @@
/**
* 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.
*/

#import "RNCSegmentedControl.h"

#import <React/RCTConvert.h>
#import <React/RCTEventDispatcher.h>
#import <React/UIView+React.h>

@implementation RNCSegmentedControl

- (instancetype)initWithFrame:(CGRect)frame
{
if ((self = [super initWithFrame:frame])) {
_selectedIndex = self.selectedSegmentIndex;
[self addTarget:self action:@selector(didChange)
forControlEvents:UIControlEventValueChanged];
}
return self;
}

- (void)setValues:(NSArray<NSString *> *)values
{
[self removeAllSegments];
for (NSString *value in values) {
[self insertSegmentWithTitle:value atIndex:self.numberOfSegments animated:NO];
}
super.selectedSegmentIndex = _selectedIndex;
}

- (void)setSelectedIndex:(NSInteger)selectedIndex
{
_selectedIndex = selectedIndex;
super.selectedSegmentIndex = selectedIndex;
}

- (void)setBackgroundColor:(UIColor *)backgroundColor
{
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && defined(__IPHONE_13_0) && \
__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0
if (@available(iOS 13.0, *)) {
[super setBackgroundColor:backgroundColor];
}
#endif
}

- (void)setTextColor:(UIColor *)textColor
{
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && defined(__IPHONE_13_0) && \
__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0
if (@available(iOS 13.0, *)) {
[self setTitleTextAttributes:@{NSForegroundColorAttributeName: textColor}
forState:UIControlStateNormal];
}
#endif
}

- (void)setActiveTextColor:(UIColor *)textColor
{
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && defined(__IPHONE_13_0) && \
__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0
if (@available(iOS 13.0, *)) {
[self setTitleTextAttributes:@{NSForegroundColorAttributeName: textColor}
forState:UIControlStateSelected];
}
#endif
}

- (void)setTintColor:(UIColor *)tintColor
{
[super setTintColor:tintColor];
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && defined(__IPHONE_13_0) && \
__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0
if (@available(iOS 13.0, *)) {
[self setSelectedSegmentTintColor:tintColor];
[self setTitleTextAttributes:@{NSForegroundColorAttributeName: tintColor}
forState:UIControlStateNormal];
}
#endif
}

- (void)didChange
{
_selectedIndex = self.selectedSegmentIndex;
if (_onChange) {
_onChange(@{
@"value": [self titleForSegmentAtIndex:_selectedIndex],
@"selectedSegmentIndex": @(_selectedIndex)
});
}
}

- (void)setAppearance:(NSString *)appearanceString
{
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && defined(__IPHONE_13_0) && \
__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0
if (@available(iOS 13.0, *)) {
if ([appearanceString isEqual: @"dark"]) {
[self setOverrideUserInterfaceStyle:UIUserInterfaceStyleDark];
} else if ([appearanceString isEqual: @"light"]) {
[self setOverrideUserInterfaceStyle:UIUserInterfaceStyleLight];
}
}
#endif
}

@end
@@ -0,0 +1,12 @@
/**
* 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.
*/

#import <React/RCTViewManager.h>

@interface RNCSegmentedControlManager : RCTViewManager

@end
@@ -0,0 +1,34 @@
/**
* 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.
*/

#import "RNCSegmentedControlManager.h"

#import <React/RCTBridge.h>
#import <React/RCTConvert.h>
#import "RNCSegmentedControl.h"

@implementation RNCSegmentedControlManager

RCT_EXPORT_MODULE()

- (UIView *)view
{
return [RNCSegmentedControl new];
}

RCT_EXPORT_VIEW_PROPERTY(values, NSArray<NSString *>)
RCT_EXPORT_VIEW_PROPERTY(selectedIndex, NSInteger)
RCT_EXPORT_VIEW_PROPERTY(tintColor, UIColor)
RCT_EXPORT_VIEW_PROPERTY(backgroundColor, UIColor)
RCT_EXPORT_VIEW_PROPERTY(textColor, UIColor)
RCT_EXPORT_VIEW_PROPERTY(activeTextColor, UIColor)
RCT_EXPORT_VIEW_PROPERTY(momentary, BOOL)
RCT_EXPORT_VIEW_PROPERTY(enabled, BOOL)
RCT_EXPORT_VIEW_PROPERTY(onChange, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(appearance, NSString)

@end
1 change: 1 addition & 0 deletions packages/expo/bundledNativeModules.json
Expand Up @@ -90,6 +90,7 @@
"@react-native-community/datetimepicker": "2.2.2",
"@react-native-community/masked-view": "0.1.6",
"@react-native-community/viewpager": "3.3.0",
"@react-native-community/segmented-control": "1.4.0",
"expo-error-recovery": "~1.1.0",
"expo-module-template": "~8.2.0",
"expo-image-loader": "~1.0.1",
Expand Down
10 changes: 10 additions & 0 deletions tools/expotools/src/commands/UpdateVendoredModule.ts
Expand Up @@ -333,6 +333,16 @@ const vendoredModulesConfig: { [key: string]: VendoredModuleConfig } = {
},
],
},
'@react-native-community/segmented-control': {
repoUrl: 'https://github.com/react-native-community/segmented-control',
installableInManagedApps: true,
steps: [
{
sourceIosPath: 'ios',
targetIosPath: 'Api/Components/SegmentedControl',
},
],
},
};

async function getBundledNativeModulesAsync(): Promise<{ [key: string]: string }> {
Expand Down