diff --git a/Examples/UIExplorer/SegmentedControlIOSExample.js b/Examples/UIExplorer/SegmentedControlIOSExample.js
new file mode 100644
index 000000000..119196d8f
--- /dev/null
+++ b/Examples/UIExplorer/SegmentedControlIOSExample.js
@@ -0,0 +1,169 @@
+/**
+ * The examples provided by Facebook are for non-commercial testing and
+ * evaluation purposes only.
+ *
+ * Facebook reserves all rights not expressly granted.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
+ * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * @flow
+ */
+'use strict';
+
+var React = require('react-native');
+var {
+ SegmentedControlIOS,
+ Text,
+ View,
+ StyleSheet
+} = React;
+
+var BasicSegmentedControlExample = React.createClass({
+ render() {
+ return (
+
+
+
+
+
+
+
+
+ );
+ }
+});
+
+var PreSelectedSegmentedControlExample = React.createClass({
+ render() {
+ return (
+
+
+
+
+
+ );
+ }
+});
+
+var MomentarySegmentedControlExample = React.createClass({
+ render() {
+ return (
+
+
+
+
+
+ );
+ }
+});
+
+var DisabledSegmentedControlExample = React.createClass({
+ render() {
+ return (
+
+
+
+
+
+ );
+ },
+});
+
+var ColorSegmentedControlExample = React.createClass({
+ render() {
+ return (
+
+
+
+
+
+
+
+
+ );
+ },
+});
+
+var EventSegmentedControlExample = React.createClass({
+ getInitialState() {
+ return {
+ values: ['One', 'Two', 'Three'],
+ value: 'Not selected',
+ selectedIndex: undefined
+ };
+ },
+
+ render() {
+ return (
+
+
+ Value: {this.state.value}
+
+
+ Index: {this.state.selectedIndex}
+
+
+
+ );
+ },
+
+ _onChange(event) {
+ this.setState({
+ selectedIndex: event.nativeEvent.selectedIndex,
+ });
+ },
+
+ _onValueChange(value) {
+ this.setState({
+ value: value,
+ });
+ }
+});
+
+var styles = StyleSheet.create({
+ text: {
+ fontSize: 14,
+ textAlign: 'center',
+ fontWeight: '500',
+ margin: 10,
+ },
+});
+
+exports.title = '';
+exports.displayName = 'SegmentedControlExample';
+exports.description = 'Native segmented control';
+exports.examples = [
+ {
+ title: 'Segmented controls can have values',
+ render(): ReactElement { return ; }
+ },
+ {
+ title: 'Segmented controls can have a pre-selected value',
+ render(): ReactElement { return ; }
+ },
+ {
+ title: 'Segmented controls can be momentary',
+ render(): ReactElement { return ; }
+ },
+ {
+ title: 'Segmented controls can be disabled',
+ render(): ReactElement { return ; }
+ },
+ {
+ title: 'Custom colors can be provided',
+ render(): ReactElement { return ; }
+ },
+ {
+ title: 'Change events can be detected',
+ render(): ReactElement { return ; }
+ }
+];
diff --git a/Examples/UIExplorer/UIExplorerList.js b/Examples/UIExplorer/UIExplorerList.js
index 730106177..a10d291bd 100644
--- a/Examples/UIExplorer/UIExplorerList.js
+++ b/Examples/UIExplorer/UIExplorerList.js
@@ -43,6 +43,7 @@ var COMPONENTS = [
require('./NavigatorIOSExample'),
require('./PickerIOSExample'),
require('./ScrollViewExample'),
+ require('./SegmentedControlIOSExample'),
require('./SliderIOSExample'),
require('./SwitchIOSExample'),
require('./TabBarIOSExample'),
diff --git a/Libraries/Components/SegmentedControlIOS/SegmentedControlIOS.js b/Libraries/Components/SegmentedControlIOS/SegmentedControlIOS.js
new file mode 100644
index 000000000..23d952776
--- /dev/null
+++ b/Libraries/Components/SegmentedControlIOS/SegmentedControlIOS.js
@@ -0,0 +1,120 @@
+/**
+ * Copyright (c) 2015-present, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ *
+ * @providesModule SegmentedControlIOS
+ * @flow
+ */
+'use strict';
+
+var NativeMethodsMixin = require('NativeMethodsMixin');
+var NativeModules = require('NativeModules');
+var PropTypes = require('ReactPropTypes');
+var React = require('React');
+var StyleSheet = require('StyleSheet');
+
+var requireNativeComponent = require('requireNativeComponent');
+var verifyPropTypes = require('verifyPropTypes');
+
+type DefaultProps = {
+ values: Array;
+ enabled: boolean;
+};
+
+var SEGMENTED_CONTROL_REFERENCE = 'segmentedcontrol';
+
+type Event = Object;
+
+/**
+ * Use `SegmentedControlIOS` to render a UISegmentedControl iOS.
+ */
+var SegmentedControlIOS = React.createClass({
+ mixins: [NativeMethodsMixin],
+
+ propTypes: {
+ /**
+ * The labels for the control's segment buttons, in order.
+ */
+ values: PropTypes.arrayOf(PropTypes.string),
+
+ /**
+ * The index in `props.values` of the segment to be pre-selected
+ */
+ selectedIndex: PropTypes.number,
+
+ /**
+ * Callback that is called when the user taps a segment;
+ * passes the segment's value as an argument
+ */
+ onValueChange: PropTypes.func,
+
+ /**
+ * Callback that is called when the user taps a segment;
+ * passes the event as an argument
+ */
+ onChange: PropTypes.func,
+
+ /**
+ * If false the user won't be able to interact with the control.
+ * Default value is true.
+ */
+ enabled: PropTypes.bool,
+
+ /**
+ * Accent color of the control.
+ */
+ tintColor: PropTypes.string,
+
+ /**
+ * If true, then selecting a segment won't persist visually.
+ * The `onValueChange` callback will still work as expected.
+ */
+ momentary: PropTypes.bool
+ },
+
+ getDefaultProps: function(): DefaultProps {
+ return {
+ values: [],
+ enabled: true
+ };
+ },
+
+ _onChange: function(event: Event) {
+ this.props.onChange && this.props.onChange(event);
+ this.props.onValueChange && this.props.onValueChange(event.nativeEvent.value);
+ },
+
+ render: function() {
+ return (
+
+ );
+ }
+});
+
+var styles = StyleSheet.create({
+ segmentedControl: {
+ height: NativeModules.SegmentedControlManager.ComponentHeight
+ },
+});
+
+var RCTSegmentedControl = requireNativeComponent(
+ 'RCTSegmentedControl',
+ null
+);
+if (__DEV__) {
+ verifyPropTypes(
+ RCTSegmentedControl,
+ RCTSegmentedControl.viewConfig
+ );
+}
+
+module.exports = SegmentedControlIOS;
diff --git a/Libraries/react-native/react-native.js b/Libraries/react-native/react-native.js
index 01bff7eae..b94b172f3 100644
--- a/Libraries/react-native/react-native.js
+++ b/Libraries/react-native/react-native.js
@@ -27,6 +27,7 @@ var ReactNative = Object.assign(Object.create(require('React')), {
NavigatorIOS: require('NavigatorIOS'),
PickerIOS: require('PickerIOS'),
Navigator: require('Navigator'),
+ SegmentedControlIOS: require('SegmentedControlIOS'),
ScrollView: require('ScrollView'),
SliderIOS: require('SliderIOS'),
SwitchIOS: require('SwitchIOS'),
diff --git a/React/React.xcodeproj/project.pbxproj b/React/React.xcodeproj/project.pbxproj
index fce2aae42..10867e9cb 100644
--- a/React/React.xcodeproj/project.pbxproj
+++ b/React/React.xcodeproj/project.pbxproj
@@ -9,6 +9,8 @@
/* Begin PBXBuildFile section */
000E6CEB1AB0E980000CDF4D /* RCTSourceCode.m in Sources */ = {isa = PBXBuildFile; fileRef = 000E6CEA1AB0E980000CDF4D /* RCTSourceCode.m */; };
00C1A2B31AC0B7E000E89A1C /* RCTDevMenu.m in Sources */ = {isa = PBXBuildFile; fileRef = 00C1A2B21AC0B7E000E89A1C /* RCTDevMenu.m */; };
+ 131B6AF41AF1093D00FFC3E0 /* RCTSegmentedControl.m in Sources */ = {isa = PBXBuildFile; fileRef = 131B6AF11AF1093D00FFC3E0 /* RCTSegmentedControl.m */; };
+ 131B6AF51AF1093D00FFC3E0 /* RCTSegmentedControlManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 131B6AF31AF1093D00FFC3E0 /* RCTSegmentedControlManager.m */; };
13456E931ADAD2DE009F94A7 /* RCTConvert+CoreLocation.m in Sources */ = {isa = PBXBuildFile; fileRef = 13456E921ADAD2DE009F94A7 /* RCTConvert+CoreLocation.m */; };
13456E961ADAD482009F94A7 /* RCTConvert+MapKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 13456E951ADAD482009F94A7 /* RCTConvert+MapKit.m */; };
134FCB361A6D42D900051CC8 /* RCTSparseArray.m in Sources */ = {isa = PBXBuildFile; fileRef = 83BEE46D1A6D19BC00B5863B /* RCTSparseArray.m */; };
@@ -84,6 +86,10 @@
000E6CEA1AB0E980000CDF4D /* RCTSourceCode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTSourceCode.m; sourceTree = ""; };
00C1A2B11AC0B7E000E89A1C /* RCTDevMenu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTDevMenu.h; sourceTree = ""; };
00C1A2B21AC0B7E000E89A1C /* RCTDevMenu.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTDevMenu.m; sourceTree = ""; };
+ 131B6AF01AF1093D00FFC3E0 /* RCTSegmentedControl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTSegmentedControl.h; sourceTree = ""; };
+ 131B6AF11AF1093D00FFC3E0 /* RCTSegmentedControl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTSegmentedControl.m; sourceTree = ""; };
+ 131B6AF21AF1093D00FFC3E0 /* RCTSegmentedControlManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTSegmentedControlManager.h; sourceTree = ""; };
+ 131B6AF31AF1093D00FFC3E0 /* RCTSegmentedControlManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTSegmentedControlManager.m; sourceTree = ""; };
13442BF21AA90E0B0037E5B0 /* RCTAnimationType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAnimationType.h; sourceTree = ""; };
13442BF31AA90E0B0037E5B0 /* RCTPointerEvents.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTPointerEvents.h; sourceTree = ""; };
13442BF41AA90E0B0037E5B0 /* RCTViewControllerProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTViewControllerProtocol.h; sourceTree = ""; };
@@ -290,6 +296,10 @@
58114A141AAE854800E7D092 /* RCTPickerManager.h */,
58114A151AAE854800E7D092 /* RCTPickerManager.m */,
13442BF31AA90E0B0037E5B0 /* RCTPointerEvents.h */,
+ 131B6AF01AF1093D00FFC3E0 /* RCTSegmentedControl.h */,
+ 131B6AF11AF1093D00FFC3E0 /* RCTSegmentedControl.m */,
+ 131B6AF21AF1093D00FFC3E0 /* RCTSegmentedControlManager.h */,
+ 131B6AF31AF1093D00FFC3E0 /* RCTSegmentedControlManager.m */,
13B07FF61A6947C200A75B9A /* RCTScrollView.h */,
13B07FF71A6947C200A75B9A /* RCTScrollView.m */,
13B07FF81A6947C200A75B9A /* RCTScrollViewManager.h */,
@@ -489,6 +499,7 @@
13B0801E1A69489C00A75B9A /* RCTTextField.m in Sources */,
13B07FEF1A69327A00A75B9A /* RCTAlertManager.m in Sources */,
83CBBACC1A6023D300E9B192 /* RCTConvert.m in Sources */,
+ 131B6AF41AF1093D00FFC3E0 /* RCTSegmentedControl.m in Sources */,
830A229E1A66C68A008503DA /* RCTRootView.m in Sources */,
13B07FF01A69327A00A75B9A /* RCTExceptionsManager.m in Sources */,
83CBBA5A1A601E9000E9B192 /* RCTRedBox.m in Sources */,
@@ -529,6 +540,7 @@
58114A161AAE854800E7D092 /* RCTPicker.m in Sources */,
137327E81AA5CF210034F82E /* RCTTabBarItem.m in Sources */,
13E067551A70F44B002CDEE1 /* RCTShadowView.m in Sources */,
+ 131B6AF51AF1093D00FFC3E0 /* RCTSegmentedControlManager.m in Sources */,
58114A171AAE854800E7D092 /* RCTPickerManager.m in Sources */,
13B0801A1A69489C00A75B9A /* RCTNavigator.m in Sources */,
830BA4551A8E3BDA00D53203 /* RCTCache.m in Sources */,
diff --git a/React/Views/RCTSegmentedControl.h b/React/Views/RCTSegmentedControl.h
new file mode 100644
index 000000000..8e6e1255e
--- /dev/null
+++ b/React/Views/RCTSegmentedControl.h
@@ -0,0 +1,20 @@
+//
+// RCTSegmentedControl.h
+// React
+//
+// Created by Clay Allsopp on 3/31/15.
+// Copyright (c) 2015 Facebook. All rights reserved.
+//
+
+#import
+
+@class RCTEventDispatcher;
+
+@interface RCTSegmentedControl : UISegmentedControl
+
+- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER;
+
+@property (nonatomic, copy) NSArray *values;
+@property (nonatomic, assign) NSInteger selectedIndex;
+
+@end
diff --git a/React/Views/RCTSegmentedControl.m b/React/Views/RCTSegmentedControl.m
new file mode 100644
index 000000000..59e4cfb86
--- /dev/null
+++ b/React/Views/RCTSegmentedControl.m
@@ -0,0 +1,57 @@
+//
+// RCTSegmentedControl.m
+// React
+//
+// Created by Clay Allsopp on 3/31/15.
+// Copyright (c) 2015 Facebook. All rights reserved.
+//
+
+#import "RCTSegmentedControl.h"
+
+#import "RCTConvert.h"
+#import "RCTEventDispatcher.h"
+#import "UIView+React.h"
+
+@implementation RCTSegmentedControl
+{
+ RCTEventDispatcher *_eventDispatcher;
+}
+
+- (id)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
+{
+ if ((self = [super initWithFrame:CGRectZero])) {
+ _eventDispatcher = eventDispatcher;
+ _selectedIndex = self.selectedSegmentIndex;
+ [self addTarget:self action:@selector(onChange:)
+ forControlEvents:UIControlEventValueChanged];
+ }
+ return self;
+}
+
+- (void)setValues:(NSArray *)values
+{
+ _values = [values copy];
+ [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)onChange:(UISegmentedControl *)sender
+{
+ NSDictionary *event = @{
+ @"target": self.reactTag,
+ @"value": [self titleForSegmentAtIndex:sender.selectedSegmentIndex],
+ @"selectedSegmentIndex": @(sender.selectedSegmentIndex)
+ };
+ [_eventDispatcher sendInputEventWithName:@"topChange" body:event];
+}
+
+@end
diff --git a/React/Views/RCTSegmentedControlManager.h b/React/Views/RCTSegmentedControlManager.h
new file mode 100644
index 000000000..03647c72e
--- /dev/null
+++ b/React/Views/RCTSegmentedControlManager.h
@@ -0,0 +1,14 @@
+/**
+ * Copyright (c) 2015-present, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ */
+
+#import "RCTViewManager.h"
+
+@interface RCTSegmentedControlManager : RCTViewManager
+
+@end
diff --git a/React/Views/RCTSegmentedControlManager.m b/React/Views/RCTSegmentedControlManager.m
new file mode 100644
index 000000000..d7e1156ff
--- /dev/null
+++ b/React/Views/RCTSegmentedControlManager.m
@@ -0,0 +1,39 @@
+/**
+ * Copyright (c) 2015-present, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ */
+
+#import "RCTSegmentedControlManager.h"
+
+#import "RCTBridge.h"
+#import "RCTConvert.h"
+#import "RCTSegmentedControl.h"
+
+@implementation RCTSegmentedControlManager
+
+RCT_EXPORT_MODULE()
+
+- (UIView *)view
+{
+ return [[RCTSegmentedControl alloc] initWithEventDispatcher:self.bridge.eventDispatcher];
+}
+
+RCT_EXPORT_VIEW_PROPERTY(values, NSStringArray)
+RCT_EXPORT_VIEW_PROPERTY(selectedIndex, NSInteger)
+RCT_EXPORT_VIEW_PROPERTY(tintColor, UIColor)
+RCT_EXPORT_VIEW_PROPERTY(momentary, BOOL)
+RCT_EXPORT_VIEW_PROPERTY(enabled, BOOL)
+
+- (NSDictionary *)constantsToExport
+{
+ RCTSegmentedControl *view = [[RCTSegmentedControl alloc] init];
+ return @{
+ @"ComponentHeight": @(view.intrinsicContentSize.height),
+ };
+}
+
+@end