TigerCow.Door


이전글

리액트 네이티브 #4_ 인스타그램(Instagram) UI 무작정 따라하기 (1)

리액트 네이티브 #5_ 인스타그램(Instagram) UI 무작정 따라하기 (2)

리액트 네이티브 #6_ 인스타그램(Instagram) UI 무작정 따라하기 (3)

리액트 네이티브 #7_ 인스타그램(Instagram) UI 무작정 따라하기 (4)


Github

https://github.com/doorBW/INSTA-by-react-native



안녕하세요. 문범우입니다.

지난 포스팅을 통해 우리는 인스타그램에서 프로필화면의 상단부까지 UI를 따라해보았습니다.


이번 포스팅에서는, 프로필화면에서 하단 네개의 버튼을 구성하고, 네개의 버튼중 두개에 대해서 화면구성을 완성해보도록 하겠습니다.


제가 1편에서 소개해드렸던 영상의 강의는 해당 포스팅까지 입니다.

이후로 지속되는 인스타그램 UI따라하기는, 제가 개인적으로 공부하면서 정리하여 올릴 생각입니다.

영상을 참고했던 이전의 내용들보다 구성이나, 완성도가 떨어질 수 있으나 최선을 다해보도록 하겠습니다 :)


오늘 포스팅을 통해 완성되는 화면은 다음과 같습니다.




그럼 먼저 프로필 화면의 하단을 구현하기전에 간단히 살펴보면, 4가지 버튼이 있는 View와 그 아래 각 버튼에 따라 내용이 나타나는 View로 나누어볼 수 있습니다.

그럼 먼저 버튼을 구성하는 View를 만들어 보도록 하겠습니다.


기존의 Content 태그 내에서 지난번에 작성이 완료된 View 밖에 새로운 View를 만들고, 그 내부에 2개의 View를 만듭니다. (하나는 버튼이 있는 View, 하나는 내용이 있는 View)

먼저 첫번째 View를 아래와 같이 채워줍니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
import React, { Component } from 'react';
import { 
    View,
    Text,
    StyleSheet,
    Image
    } from 'react-native';
import { Icon, Container, Content, Header, Left, Body, Right, Button } from 'native-base';
class ProfileTab extends Component{
    static navigationOptions = {
        tabBarIcon: ({ tintColor }) => (
            < Icon name='person' style={{color:tintColor}} />
        )
    }
 
    render(){
        return (
            <Container style={{flex:1, backgroundColor:'white'}}>
                <Header>
                    <Left style={{flexDirection:'row', alignItems:'center'}}>
                        <Text style={{fontWeight:'bold', fontSize:17}}>tigercow.door</Text>
                        <Icon name='caret-down' type='FontAwesome' style={{paddingLeft:10, fontSize:14}}/>
                    </Left>
                    <Right style={{flexDirection:'row', alignItems:'center'}}>
                        <Icon name='back-in-time' type='Entypo' style={{paddingRight:10, fontSize:23}}/>
                        <Icon name='user-plus' type='Feather' style={{paddingRight:10, fontSize:23}}/>
                        <Icon name='dots-vertical' type='MaterialCommunityIcons' style={{fontSize:23}}/>
                    </Right>
                </Header>
                <Content>
                    <View style={{paddingTop:10}}>
                        <View style={{flexDirection:'row'}}>
                            <View style={{flex:1, alignItems:'center'}}>
                                <Image source={require('../../assets/beomwoo.jpeg')}
                                style={{width:75, height:75, borderRadius:37.5}}/>
                            </View>
                            <View style={{flex:3}}>
                                <View style={{flexDirection:'row', justifyContent:'space-around'}}>
                                    <View style={{alignItems:'center'}}>
                                        <Text style={{fontSize:17, fontWeight:'bold'}}>167</Text>
                                        <Text style={{fontSize:12, color:'gray'}}>게시물</Text>
                                    </View>
                                    <View style={{alignItems:'center'}}>
                                        <Text style={{fontSize:17, fontWeight:'bold'}}>346</Text>
                                        <Text style={{fontSize:12, color:'gray'}}>팔로워</Text>
                                    </View>
                                    <View style={{alignItems:'center'}}>
                                        <Text style={{fontSize:17, fontWeight:'bold'}}>192</Text>
                                        <Text style={{fontSize:12, color:'gray'}}>팔로잉</Text>
                                    </View>
                                </View>
                                <View style={{flexDirection:'row'}}>
                                    <Button bordered dark
                                    style={{flex:1, justifyContent:'center', height:30, marginHorizontal:10, marginTop:10}}>
                                        <Text>프로필 수정</Text>
                                    </Button>
                                </View>
                            </View>
                        </View>
                        <View style={{paddingHorizontal:10, paddingVertical:10}}>
                            <Text style={{fontWeight:'bold'}}>범우[25:?]</Text>
                            <Text> React-Native로</Text>
                            <Text> Instagram UI 따라하기!!!</Text>
                        </View>
                    </View>
 
                    {/* 하단 */}
                    <View>
                        <View style={{flexDirection:'row', justifyContent:'space-around', borderTopWidth:1,borderTopColor:'#eae5e5'}}>
                            <Button transparent><Icon name='ios-apps-outline'/></Button>
                            <Button transparent><Icon name='ios-list-outline'/></Button>
                            <Button transparent><Icon name='ios-people-outline'/></Button>
                            <Button transparent><Icon name='ios-bookmark-outline'/></Button>
                        </View>
                        <View>
 
                        </View>
                    </View>
                </Content>
            </Container>
        );
    }
}
export default ProfileTab;
 
const style = StyleSheet.create({
    container: {
        flex: 1,
        alignItems: 'center',
        justifyContent: 'center',
    }
})
cs


69번줄 ~ 74번줄을 보시면 첫번째 View태그 내부에 4개의 버튼이 들어간 것을 볼 수 있습니다. 또한 해당 View 태그에 대해 몇가지 속성을 추가하였습니다.

추가적으로 각 버튼을 눌렀을때, 눌린 버튼이 하이라이팅 되는 효과를 추가해보도록 하겠습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
import React, { Component } from 'react';
import { 
    View,
    Text,
    StyleSheet,
    Image
    } from 'react-native';
import { Icon, Container, Content, Header, Left, Body, Right, Button } from 'native-base';
class ProfileTab extends Component{
    static navigationOptions = {
        tabBarIcon: ({ tintColor }) => (
            < Icon name='person' style={{color:tintColor}} />
        )
    }
    constructor(props){
        super(props)
 
        this.state = {
            activeIndex: 0
        };
    }
 
    segmentClicked=(index)=>{
        this.setState({
            activeIndex: index
        })
    }
    
    render(){
        return (
            <Container style={{flex:1, backgroundColor:'white'}}>
                <Header>
                    <Left style={{flexDirection:'row', alignItems:'center'}}>
                        <Text style={{fontWeight:'bold', fontSize:17}}>tigercow.door</Text>
                        <Icon name='caret-down' type='FontAwesome' style={{paddingLeft:10, fontSize:14}}/>
                    </Left>
                    <Right style={{flexDirection:'row', alignItems:'center'}}>
                        <Icon name='back-in-time' type='Entypo' style={{paddingRight:10, fontSize:23}}/>
                        <Icon name='user-plus' type='Feather' style={{paddingRight:10, fontSize:23}}/>
                        <Icon name='dots-vertical' type='MaterialCommunityIcons' style={{fontSize:23}}/>
                    </Right>
                </Header>
                <Content>
                    <View style={{paddingTop:10}}>
                        <View style={{flexDirection:'row'}}>
                            <View style={{flex:1, alignItems:'center'}}>
                                <Image source={require('../../assets/beomwoo.jpeg')}
                                style={{width:75, height:75, borderRadius:37.5}}/>
                            </View>
                            <View style={{flex:3}}>
                                <View style={{flexDirection:'row', justifyContent:'space-around'}}>
                                    <View style={{alignItems:'center'}}>
                                        <Text style={{fontSize:17, fontWeight:'bold'}}>167</Text>
                                        <Text style={{fontSize:12, color:'gray'}}>게시물</Text>
                                    </View>
                                    <View style={{alignItems:'center'}}>
                                        <Text style={{fontSize:17, fontWeight:'bold'}}>346</Text>
                                        <Text style={{fontSize:12, color:'gray'}}>팔로워</Text>
                                    </View>
                                    <View style={{alignItems:'center'}}>
                                        <Text style={{fontSize:17, fontWeight:'bold'}}>192</Text>
                                        <Text style={{fontSize:12, color:'gray'}}>팔로잉</Text>
                                    </View>
                                </View>
                                <View style={{flexDirection:'row'}}>
                                    <Button bordered dark
                                    style={{flex:1, justifyContent:'center', height:30, marginHorizontal:10, marginTop:10}}>
                                        <Text>프로필 수정</Text>
                                    </Button>
                                </View>
                            </View>
                        </View>
                        <View style={{paddingHorizontal:10, paddingVertical:10}}>
                            <Text style={{fontWeight:'bold'}}>범우[25:?]</Text>
                            <Text> React-Native로</Text>
                            <Text> Instagram UI 따라하기!!!</Text>
                        </View>
                    </View>
 
                    {/* 하단 */}
                    <View>
                        <View style={{flexDirection:'row', justifyContent:'space-around', borderTopWidth:1,borderTopColor:'#eae5e5'}}>
                            <Button transparent onPress={()=>this.segmentClicked(0)} active={this.state.activeIndex == 0}>
                                <Icon name='ios-apps-outline' style={[this.state.activeIndex == 0 ? {} : {color:'grey'}]}/>
                            </Button>
                            <Button transparent onPress={()=>this.segmentClicked(1)} active={this.state.activeIndex == 1}>
                                <Icon name='ios-list-outline' style={[this.state.activeIndex == 1 ? {} : {color:'grey'}]}/>
                            </Button>
                            <Button transparent onPress={()=>this.segmentClicked(2)} active={this.state.activeIndex == 2}>
                                <Icon name='ios-people-outline' style={[this.state.activeIndex == 2 ? {} : {color:'grey'}]}/>
                            </Button>
                            <Button transparent onPress={()=>this.segmentClicked(3)} active={this.state.activeIndex == 3}>
                                <Icon name='ios-bookmark-outline' style={[this.state.activeIndex == 3 ? {} : {color:'grey'}]}/>
                            </Button>
                        </View>
                        <View>
 
                        </View>
                    </View>
                </Content>
            </Container>
        );
    }
}
export default ProfileTab;
 
const style = StyleSheet.create({
    container: {
        flex: 1,
        alignItems: 'center',
        justifyContent: 'center',
    }
})
cs


react 자체적인 문법이 약간 사용되는데, 크게 어렵지 않으니 이해하실 수 있을 것 입니다. 먼저 15번줄 ~ 27번줄을 보시면 constructor와 하나의 함수가 정의되어 있습니다. 먼저 constructor에서 state에 activeIndex라는 요소를 지정해주었고 그 초기값은 0으로 두었습니다. 그리고 segmentClicked 함수에서는 클릭되는 요소의 인덱스에 따라서 activeIndex를 바꾸어주는 함수입니다.


그리고 각 버튼을 다시 확인해보면, onPress와 active 속성을 추가해주었습니다. 해당 버튼이 눌렸을때 해당 인덱스를 함수로 전달하여 activeIndex를 변하게끔하고 해당 버튼이 눌려있을때 그 activeIndex값을 유지하도록 합니다.

그리고 내부의 아이콘에 style을 추가해주는데, 현재의 activeIndex와 일치할때는 본래의 색을 내지만 그것이 아닐때는 회색을 갖도록 하였습니다.


이제 4개의 버튼 하단에 있는 내용에 대해서 구성해보도록 하겠습니다.

해당 내용은 단순히 고정된 정보를 보여주는 것이 아니라, 방금 저희가 구현한 4개의 버튼에서 어떤 것이 선택되어 있는지에 따라 보여지는 것이 다릅니다.

따라서 이 또한 함수를 이용해서 내용을 보여주도록 합니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
import React, { Component } from 'react';
import { 
    View,
    Text,
    StyleSheet,
    Image
    } from 'react-native';
import { Icon, Container, Content, Header, Left, Body, Right, Button } from 'native-base';
class ProfileTab extends Component{
    static navigationOptions = {
        tabBarIcon: ({ tintColor }) => (
            < Icon name='person' style={{color:tintColor}} />
        )
    }
    constructor(props){
        super(props)
 
        this.state = {
            activeIndex: 0
        };
    }
 
    segmentClicked=(index)=>{
        this.setState({
            activeIndex: index
        })
    }
 
    renderSection = () =>{
        if (this.state.activeIndex == 0){
            return(
                <View><Text>this is first section</Text></View>
            )
        }
    }
    
    render(){
        return (
            <Container style={{flex:1, backgroundColor:'white'}}>
                <Header>
                    <Left style={{flexDirection:'row', alignItems:'center'}}>
                        <Text style={{fontWeight:'bold', fontSize:17}}>tigercow.door</Text>
                        <Icon name='caret-down' type='FontAwesome' style={{paddingLeft:10, fontSize:14}}/>
                    </Left>
                    <Right style={{flexDirection:'row', alignItems:'center'}}>
                        <Icon name='back-in-time' type='Entypo' style={{paddingRight:10, fontSize:23}}/>
                        <Icon name='user-plus' type='Feather' style={{paddingRight:10, fontSize:23}}/>
                        <Icon name='dots-vertical' type='MaterialCommunityIcons' style={{fontSize:23}}/>
                    </Right>
                </Header>
                <Content>
                    <View style={{paddingTop:10}}>
                        <View style={{flexDirection:'row'}}>
                            <View style={{flex:1, alignItems:'center'}}>
                                <Image source={require('../../assets/beomwoo.jpeg')}
                                style={{width:75, height:75, borderRadius:37.5}}/>
                            </View>
                            <View style={{flex:3}}>
                                <View style={{flexDirection:'row', justifyContent:'space-around'}}>
                                    <View style={{alignItems:'center'}}>
                                        <Text style={{fontSize:17, fontWeight:'bold'}}>167</Text>
                                        <Text style={{fontSize:12, color:'gray'}}>게시물</Text>
                                    </View>
                                    <View style={{alignItems:'center'}}>
                                        <Text style={{fontSize:17, fontWeight:'bold'}}>346</Text>
                                        <Text style={{fontSize:12, color:'gray'}}>팔로워</Text>
                                    </View>
                                    <View style={{alignItems:'center'}}>
                                        <Text style={{fontSize:17, fontWeight:'bold'}}>192</Text>
                                        <Text style={{fontSize:12, color:'gray'}}>팔로잉</Text>
                                    </View>
                                </View>
                                <View style={{flexDirection:'row'}}>
                                    <Button bordered dark
                                    style={{flex:1, justifyContent:'center', height:30, marginHorizontal:10, marginTop:10}}>
                                        <Text>프로필 수정</Text>
                                    </Button>
                                </View>
                            </View>
                        </View>
                        <View style={{paddingHorizontal:10, paddingVertical:10}}>
                            <Text style={{fontWeight:'bold'}}>범우[25:?]</Text>
                            <Text> React-Native로</Text>
                            <Text> Instagram UI 따라하기!!!</Text>
                        </View>
                    </View>
 
                    {/* 하단 */}
                    <View>
                        <View style={{flexDirection:'row', justifyContent:'space-around', borderTopWidth:1,borderTopColor:'#eae5e5'}}>
                            <Button transparent onPress={()=>this.segmentClicked(0)} active={this.state.activeIndex == 0}>
                                <Icon name='ios-apps-outline' style={[this.state.activeIndex == 0 ? {} : {color:'grey'}]}/>
                            </Button>
                            <Button transparent onPress={()=>this.segmentClicked(1)} active={this.state.activeIndex == 1}>
                                <Icon name='ios-list-outline' style={[this.state.activeIndex == 1 ? {} : {color:'grey'}]}/>
                            </Button>
                            <Button transparent onPress={()=>this.segmentClicked(2)} active={this.state.activeIndex == 2}>
                                <Icon name='ios-people-outline' style={[this.state.activeIndex == 2 ? {} : {color:'grey'}]}/>
                            </Button>
                            <Button transparent onPress={()=>this.segmentClicked(3)} active={this.state.activeIndex == 3}>
                                <Icon name='ios-bookmark-outline' style={[this.state.activeIndex == 3 ? {} : {color:'grey'}]}/>
                            </Button>
                        </View>
                        <View>
                            {this.renderSection()}
                        </View>
                    </View>
                </Content>
            </Container>
        );
    }
}
export default ProfileTab;
 
const style = StyleSheet.create({
    container: {
        flex: 1,
        alignItems: 'center',
        justifyContent: 'center',
    }
})
cs


먼저, 105번줄을 보시면 해당 내용은 renderSection()이라는 함수를 통해서 보여지도록 하고 있습니다.

그리고 29번줄 ~ 35번줄을 보시면 renderSection() 함수에 대해서 나와 있습니다.

우리가 위에서 어떤 버튼이 눌려있는지 activeIndex를 통해서 확인했기 때문에, 동일하게 해당 값을 통해서 어떤 버튼이 눌려있는지 확인하고 그에 맞는 내용을 반환하도록 할 것 입니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
import React, { Component } from 'react';
import { 
    View,
    Text,
    StyleSheet,
    Image,
    Dimensions
    } from 'react-native';
import { Icon, Container, Content, Header, Left, Body, Right, Button } from 'native-base';
 
var images=[
    require('../../assets/1.jpg'),
    require('../../assets/2.jpg'),
    require('../../assets/3.jpg'),
    require('../../assets/4.jpg'),
    require('../../assets/5.jpg'),
    require('../../assets/6.jpeg'),
    require('../../assets/7.jpg'),
    require('../../assets/beomwoo.jpeg'),
    require('../../assets/deep_learning.png'),
    require('../../assets/python.jpg')
]
 
var {width,height} = Dimensions.get('window')
class ProfileTab extends Component{
    static navigationOptions = {
        tabBarIcon: ({ tintColor }) => (
            < Icon name='person' style={{color:tintColor}} />
        )
    }
    constructor(props){
        super(props)
 
        this.state = {
            activeIndex: 0
        };
    }
 
    segmentClicked=(index)=>{
        this.setState({
            activeIndex: index
        })
    }
 
    renderSectionOne = () => {
        return images.map((image,index) => {
            return (
                <View key={index} style={[{width:(width)/3}, {height:(width)/3}]}>
                    <Image style={{flex:1, width:undefined, height:undefined}}
                    source = {image}
                    />
                </View>
            )
        })
    }
 
    renderSection = () =>{
        if (this.state.activeIndex == 0){
            return(
                <View style={{flexDirection:'row', flexWrap:'wrap'}}>
                    {this.renderSectionOne()}
                </View>
            )
        }
    }
 
    
    
    render(){
        return (
            <Container style={{flex:1, backgroundColor:'white'}}>
                <Header>
                    <Left style={{flexDirection:'row', alignItems:'center'}}>
                        <Text style={{fontWeight:'bold', fontSize:17}}>tigercow.door</Text>
                        <Icon name='caret-down' type='FontAwesome' style={{paddingLeft:10, fontSize:14}}/>
                    </Left>
                    <Right style={{flexDirection:'row', alignItems:'center'}}>
                        <Icon name='back-in-time' type='Entypo' style={{paddingRight:10, fontSize:23}}/>
                        <Icon name='user-plus' type='Feather' style={{paddingRight:10, fontSize:23}}/>
                        <Icon name='dots-vertical' type='MaterialCommunityIcons' style={{fontSize:23}}/>
                    </Right>
                </Header>
                <Content>
                    <View style={{paddingTop:10}}>
                        <View style={{flexDirection:'row'}}>
                            <View style={{flex:1, alignItems:'center'}}>
                                <Image source={require('../../assets/beomwoo.jpeg')}
                                style={{width:75, height:75, borderRadius:37.5}}/>
                            </View>
                            <View style={{flex:3}}>
                                <View style={{flexDirection:'row', justifyContent:'space-around'}}>
                                    <View style={{alignItems:'center'}}>
                                        <Text style={{fontSize:17, fontWeight:'bold'}}>167</Text>
                                        <Text style={{fontSize:12, color:'gray'}}>게시물</Text>
                                    </View>
                                    <View style={{alignItems:'center'}}>
                                        <Text style={{fontSize:17, fontWeight:'bold'}}>346</Text>
                                        <Text style={{fontSize:12, color:'gray'}}>팔로워</Text>
                                    </View>
                                    <View style={{alignItems:'center'}}>
                                        <Text style={{fontSize:17, fontWeight:'bold'}}>192</Text>
                                        <Text style={{fontSize:12, color:'gray'}}>팔로잉</Text>
                                    </View>
                                </View>
                                <View style={{flexDirection:'row'}}>
                                    <Button bordered dark
                                    style={{flex:1, justifyContent:'center', height:30, marginHorizontal:10, marginTop:10}}>
                                        <Text>프로필 수정</Text>
                                    </Button>
                                </View>
                            </View>
                        </View>
                        <View style={{paddingHorizontal:10, paddingVertical:10}}>
                            <Text style={{fontWeight:'bold'}}>범우[25:?]</Text>
                            <Text> React-Native로</Text>
                            <Text> Instagram UI 따라하기!!!</Text>
                        </View>
                    </View>
 
                    {/* 하단 */}
                    <View>
                        <View style={{flexDirection:'row', justifyContent:'space-around', borderTopWidth:1,borderTopColor:'#eae5e5'}}>
                            <Button transparent onPress={()=>this.segmentClicked(0)} active={this.state.activeIndex == 0}>
                                <Icon name='ios-apps-outline' style={[this.state.activeIndex == 0 ? {} : {color:'grey'}]}/>
                            </Button>
                            <Button transparent onPress={()=>this.segmentClicked(1)} active={this.state.activeIndex == 1}>
                                <Icon name='ios-list-outline' style={[this.state.activeIndex == 1 ? {} : {color:'grey'}]}/>
                            </Button>
                            <Button transparent onPress={()=>this.segmentClicked(2)} active={this.state.activeIndex == 2}>
                                <Icon name='ios-people-outline' style={[this.state.activeIndex == 2 ? {} : {color:'grey'}]}/>
                            </Button>
                            <Button transparent onPress={()=>this.segmentClicked(3)} active={this.state.activeIndex == 3}>
                                <Icon name='ios-bookmark-outline' style={[this.state.activeIndex == 3 ? {} : {color:'grey'}]}/>
                            </Button>
                        </View>
                        <View>
                            {this.renderSection()}
                        </View>
                    </View>
                </Content>
            </Container>
        );
    }
}
export default ProfileTab;
 
const style = StyleSheet.create({
    container: {
        flex: 1,
        alignItems: 'center',
        justifyContent: 'center',
    }
})
cs


renderSection에서 첫번째 버튼이 눌렸을때 보여주고자 하는 것은 다수의 이미지 입니다. 먼저 이러한 이미지를 위해 11번줄 ~ 22번줄과 같이 여러개의 이미지를 불러왔습니다. 그리고 각각의 이미지를 알맞게 보여주도록하기 위해 새로운 renderSectionOne 함수를 만들었습니다.

45번줄 ~ 55번줄을 보시면 해당 함수에 대해서 코드가 작성되어 있습니다.

map함수를 통해 images에 있는 각각의 이미지에 대해 모두 처리하도록 하여 많은 이미지를 보다 쉽게 설정할 수 있습니다.

각각의 이미지의 크기는 사용자의 핸드폰 화면의 1/3 정도의 크기를 가지는데 이를 위해서 최상단에 Dimensions라는 것을 Import하여 24번줄과 같이 사용자의 화면 크기를 받아왔고, 이를 통해 각 이미지의 가로, 세로를 설정하였습니다.


마지막으로, 각 이미지간에 존재하는 약간의 경계를 설정하고, 두번째 버튼을 눌렀을때 나오는 화면을 구성해보도록 합니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
import React, { Component } from 'react';
import { 
    View,
    Text,
    StyleSheet,
    Image,
    Dimensions
    } from 'react-native';
import { Icon, Container, Content, Header, Left, Body, Right, Button } from 'native-base';
import CardCompnent from '../CardComponent';
 
var images=[
    require('../../assets/1.jpg'),
    require('../../assets/2.jpg'),
    require('../../assets/3.jpg'),
    require('../../assets/4.jpg'),
    require('../../assets/5.jpg'),
    require('../../assets/6.jpeg'),
    require('../../assets/7.jpg'),
    require('../../assets/beomwoo.jpeg'),
    require('../../assets/deep_learning.png'),
    require('../../assets/python.jpg')
]
 
var {width,height} = Dimensions.get('window')
class ProfileTab extends Component{
    static navigationOptions = {
        tabBarIcon: ({ tintColor }) => (
            < Icon name='person' style={{color:tintColor}} />
        )
    }
    constructor(props){
        super(props)
 
        this.state = {
            activeIndex: 0
        };
    }
 
    segmentClicked=(index)=>{
        this.setState({
            activeIndex: index
        })
    }
 
    renderSectionOne = () => {
        return images.map((image,index) => {
            return (
                <View key={index} style={[{width:(width)/3}, {height:(width)/3}, index % 3 !== 0 ? {paddingLeft:2}:{paddingLeft:0},{marginBottom:2}]}>
                    <Image style={{flex:1, width:undefined, height:undefined}}
                    source = {image}
                    />
                </View>
            )
        })
    }
 
    renderSection = () =>{
        if (this.state.activeIndex == 0){
            return(
                <View style={{flexDirection:'row', flexWrap:'wrap'}}>
                    {this.renderSectionOne()}
                </View>
            )
        }
        else if(this.state.activeIndex == 1){
            return(
                <View>
                <CardCompnent imageSource='1' likes='100'/>
                <CardCompnent imageSource='2' likes='36'/>
                <CardCompnent imageSource='3' likes='240'/>
                </View>
            )
        }
    }
 
    
    
    render(){
        return (
            <Container style={{flex:1, backgroundColor:'white'}}>
                <Header>
                    <Left style={{flexDirection:'row', alignItems:'center'}}>
                        <Text style={{fontWeight:'bold', fontSize:17}}>tigercow.door</Text>
                        <Icon name='caret-down' type='FontAwesome' style={{paddingLeft:10, fontSize:14}}/>
                    </Left>
                    <Right style={{flexDirection:'row', alignItems:'center'}}>
                        <Icon name='back-in-time' type='Entypo' style={{paddingRight:10, fontSize:23}}/>
                        <Icon name='user-plus' type='Feather' style={{paddingRight:10, fontSize:23}}/>
                        <Icon name='dots-vertical' type='MaterialCommunityIcons' style={{fontSize:23}}/>
                    </Right>
                </Header>
                <Content>
                    <View style={{paddingTop:10}}>
                        <View style={{flexDirection:'row'}}>
                            <View style={{flex:1, alignItems:'center'}}>
                                <Image source={require('../../assets/beomwoo.jpeg')}
                                style={{width:75, height:75, borderRadius:37.5}}/>
                            </View>
                            <View style={{flex:3}}>
                                <View style={{flexDirection:'row', justifyContent:'space-around'}}>
                                    <View style={{alignItems:'center'}}>
                                        <Text style={{fontSize:17, fontWeight:'bold'}}>167</Text>
                                        <Text style={{fontSize:12, color:'gray'}}>게시물</Text>
                                    </View>
                                    <View style={{alignItems:'center'}}>
                                        <Text style={{fontSize:17, fontWeight:'bold'}}>346</Text>
                                        <Text style={{fontSize:12, color:'gray'}}>팔로워</Text>
                                    </View>
                                    <View style={{alignItems:'center'}}>
                                        <Text style={{fontSize:17, fontWeight:'bold'}}>192</Text>
                                        <Text style={{fontSize:12, color:'gray'}}>팔로잉</Text>
                                    </View>
                                </View>
                                <View style={{flexDirection:'row'}}>
                                    <Button bordered dark
                                    style={{flex:1, justifyContent:'center', height:30, marginHorizontal:10, marginTop:10}}>
                                        <Text>프로필 수정</Text>
                                    </Button>
                                </View>
                            </View>
                        </View>
                        <View style={{paddingHorizontal:10, paddingVertical:10}}>
                            <Text style={{fontWeight:'bold'}}>범우[25:?]</Text>
                            <Text> React-Native로</Text>
                            <Text> Instagram UI 따라하기!!!</Text>
                        </View>
                    </View>
 
                    {/* 하단 */}
                    <View>
                        <View style={{flexDirection:'row', justifyContent:'space-around', borderTopWidth:1,borderTopColor:'#eae5e5'}}>
                            <Button transparent onPress={()=>this.segmentClicked(0)} active={this.state.activeIndex == 0}>
                                <Icon name='ios-apps-outline' style={[this.state.activeIndex == 0 ? {} : {color:'grey'}]}/>
                            </Button>
                            <Button transparent onPress={()=>this.segmentClicked(1)} active={this.state.activeIndex == 1}>
                                <Icon name='ios-list-outline' style={[this.state.activeIndex == 1 ? {} : {color:'grey'}]}/>
                            </Button>
                            <Button transparent onPress={()=>this.segmentClicked(2)} active={this.state.activeIndex == 2}>
                                <Icon name='ios-people-outline' style={[this.state.activeIndex == 2 ? {} : {color:'grey'}]}/>
                            </Button>
                            <Button transparent onPress={()=>this.segmentClicked(3)} active={this.state.activeIndex == 3}>
                                <Icon name='ios-bookmark-outline' style={[this.state.activeIndex == 3 ? {} : {color:'grey'}]}/>
                            </Button>
                        </View>
                        <View>
                            {this.renderSection()}
                        </View>
                    </View>
                </Content>
            </Container>
        );
    }
}
export default ProfileTab;
 
const style = StyleSheet.create({
    container: {
        flex: 1,
        alignItems: 'center',
        justifyContent: 'center',
    }
})
cs


49번줄의 style을 보시면, paddingLeft는 요소가 몇번째에 위치해있는지에 따라 값을 주거나, 안주는 것으로 설정하였고 marginBottom은 모든 이미지에게 2라는 값을 주었습니다.


이렇게되면 첫번째 버튼을 눌렀을때의 화면이 잘 나오고, 두번째 버튼은 우리가 지난번에 만든 CardComponent를 이용하여 66번줄 ~ 74번줄과 같이 작성하면 완성됩니다.


블로그 이미지

Tigercow.Door

Web Programming / Back-end / Database / AI / Algorithm / DeepLearning / etc


지난글

리액트 네이티브 #4_ 인스타그램(Instagram) UI 무작정 따라하기 (1)

리액트 네이티브 #5_ 인스타그램(Instagram) UI 무작정 따라하기 (2)

리액트 네이티브 #6_ 인스타그램(Instagram) UI 무작정 따라하기 (3)


Github

https://github.com/doorBW/INSTA-by-react-native



안녕하세요. 문범우입니다.

지난 포스팅에서는 인스타그램 메인 화면 상단에 있는 스토리바를 추가해보았습니다.

이번 포스팅에서는 프로필창의 상단부분을 완성시켜보도록 합니다.

이번 포스팅을 통해 나오는 결과물은 다음과 같습니다.

(강의에서 진행되는 내용과 약간의 차이가 존재합니다.)



1. 상단 네비게이션바 수정


먼저 현재의 프로필 탭을 눌러보면 다음과 같은 화면이 나옵니다.


첫번째로 수정해야 하는 항목은 상단 네비게이션입니다.

우리가 HomeTab에서의 기준으로 상단바를 만들었는데, 사실상 프로필탭에서는 상단바가 다릅니다.

따라서, MainScreen.js에 있는 상단 네비게이션 바를 없애고 아래와 같이 수정합니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
import React, { Component } from 'react';
import { 
    View,
    Text,
    StyleSheet,
    Platform
    } from 'react-native';
 
import { Icon } from 'native-base';
import { createTabNavigator } from 'react-navigation';
 
import HomeTab from './AppTabNavigator/HomeTab';
import SearchTab from './AppTabNavigator/SearchTab';
import AddMediaTab from './AppTabNavigator/AddMediaTab';
import LikesTab from './AppTabNavigator/LikesTab';
import ProfileTab from './AppTabNavigator/ProfileTab';
 
class MainScreen extends Component{
 
    static navigationOptions = {
        header: null
    }
 
    render() {
        return (
            <AppTabNavigator />
        );
    }
}
export default MainScreen;
 
const AppTabNavigator = createTabNavigator({
    HomeTab:{
        screen: HomeTab
    },
    Search:{
        screen: SearchTab
    },
    AddMedia:{
        screen: AddMediaTab
    },
    Likes:{
        screen: LikesTab
    },
    Profile:{
        screen: ProfileTab
    }
}, {
    animationEnabled: true,
    swipeEnabled: true,
    tabBarPosition: "bottom",
    tabBarOptions: {
        style: {
            ...Platform.select({
                android:{
                    backgroundColor:'white'
                }
            })
        },
        activeTintColor: '#000',
        inactiveTintColor: '#d1cece',
        showLabel: false,
        showIcon: true,
    }
})
 
const style = StyleSheet.create({
    container: {
        flex: 1,
        alignItems: 'center',
        justifyContent: 'center',
    }
})
cs


21번줄을 보시면 이전에 있던 헤더를 모두 없애고 단순히 null 값을 주었습니다.


이제 HomeTab.js에서 헤더를 추가해야합니다.

헤더를 만들기 위해 필요한, Header, Left, Right, Body를 native base에서 import하고 아래와 같이 코드를 작성합니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
import React, { Component } from 'react';
import { 
    View,
    Text,
    StyleSheet,
    ScrollView
    } from 'react-native';
import { Icon, Container, Content, Thumbnail, Header, Left, Right, Body } from 'native-base';
 
import CardComponent from '../CardComponent';
 
class HomeTab extends Component{
 
    static navigationOptions = {
        tabBarIcon: ({ tintColor }) => (
            < Icon name='ios-home' style={{color:tintColor}} />
        )
    }
 
    render(){
        return (
            <Container style = {style.container}>
                <Header>
                    <Left><Icon name='ios-camera-outline' style={{paddingLeft:10}}/></Left>
                    <Body><Text style={{fontWeight:'900'}}>Instagram</Text></Body>
                    <Right><Icon name='ios-send-outline' style={{paddingRight:10}}/></Right>
                </Header>
                <Content>
                    <View style={{height:100}}>
                        <View style={{flex:1, flexDirection:'row', justifyContent:'space-between', alignItems:'center', paddingHorizontal:7}}>
                            <Text style={{fontWeight:'bold'}}>스토리</Text>
                            <View style={{flexDirection:'row', alignItems:'center'}}>
                                <Icon name='md-play' style={{fontSize:14}}/>
                                <Text style={{fontWeight:'bold'}}>모두 보기</Text>
                            </View>
                        </View>
                        <View style={{flex:3}}>
                            <ScrollView 
                                horizontal={true}
                                showsHorizontalScrollIndicator={false}
                                contentContainerStyle={{
                                    alignItems:'center',
                                    paddingStart:5,
                                    paddingEnd:5
                                }}
                            >
                                <Thumbnail style={{marginHorizontal:5, borderColor:'red', borderWidth:2}} source={require('../../assets/beomwoo.jpeg')}/>
                                <Thumbnail style={{marginHorizontal:5, borderColor:'red', borderWidth:2}} source={require('../../assets/1.jpg')}/>
                                <Thumbnail style={{marginHorizontal:5, borderColor:'red', borderWidth:2}} source={require('../../assets/2.jpg')}/>
                                <Thumbnail style={{marginHorizontal:5, borderColor:'red', borderWidth:2}} source={require('../../assets/3.jpg')}/>
                                <Thumbnail style={{marginHorizontal:5, borderColor:'red', borderWidth:2}} source={require('../../assets/4.jpg')}/>
                                <Thumbnail style={{marginHorizontal:5, borderColor:'red', borderWidth:2}} source={require('../../assets/5.jpg')}/>
                                <Thumbnail style={{marginHorizontal:5, borderColor:'red', borderWidth:2}} source={require('../../assets/6.jpeg')}/>
                                <Thumbnail style={{marginHorizontal:5, borderColor:'red', borderWidth:2}} source={require('../../assets/7.jpg')}/>
                                
                            </ScrollView>
                        </View>
                    </View>
                    <CardComponent imageSource='1' likes='2324'/>
                    <CardComponent imageSource='2' likes='46'/>
                    <CardComponent imageSource='3' likes='208'/>
                </Content>
            </Container>
        );
    }
}
export default HomeTab;
 
const style = StyleSheet.create({
    container: {
        flex: 1,
        backgroundColor: 'white'
    }
})
cs


23번줄 ~ 27번줄을 보시면 헤더가 추가된 코드를 확인하실 수 있습니다.

이렇게 하면 HomeTab에서만 상단 바가 나타나고 다른 화면에서는 나타나지 않는 것을 확인할 수 있습니다.



2. 프로필창 상단 구성하기


그럼 이제 프로필창으로 넘어와서 상단 네비게이션 바와, 프로필 사진, 팔로워 수 등을 구성해보도록 하겠습니다.

먼저 전체적인 구성에 필요할 요소들을 import 합니다.

native base로 부터, Container, Content, Header, Left, Body, Right, Button을 import 합니다.


이제 프로필창 상단에 있는 네비게이션 바를 구성합니다.

(해당 부분은 강의 영상과 약간의 차이가 있습니다.)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import React, { Component } from 'react';
import { 
    View,
    Text,
    StyleSheet
    } from 'react-native';
import { Icon, Container, Content, Header, Left, Body, Right, Button } from 'native-base';
class ProfileTab extends Component{
    static navigationOptions = {
        tabBarIcon: ({ tintColor }) => (
            < Icon name='person' style={{color:tintColor}} />
        )
    }
 
    render(){
        return (
            <Container>
                <Header>
                    <Left style={{flexDirection:'row', alignItems:'center'}}>
                        <Text style={{fontWeight:'bold', fontSize:17}}>tigercow.door</Text>
                        <Icon name='caret-down' type='FontAwesome' style={{paddingLeft:10, fontSize:14}}/>
                    </Left>
                    <Right style={{flexDirection:'row', alignItems:'center'}}>
                        <Icon name='back-in-time' type='Entypo' style={{paddingRight:10, fontSize:23}}/>
                        <Icon name='user-plus' type='Feather' style={{paddingRight:10, fontSize:23}}/>
                        <Icon name='dots-vertical' type='MaterialCommunityIcons' style={{fontSize:23}}/>
                    </Right>
                </Header>
            </Container>
        );
    }
}
export default ProfileTab;
 
const style = StyleSheet.create({
    container: {
        flex: 1,
        alignItems: 'center',
        justifyContent: 'center',
    }
})
cs


18번줄 ~ 28번줄까지 프로필창의 헤더를 구성하였습니다. HomeTab에서의 구성과 거의 비슷하지만 Body가 없고 Left와 Right에 2개이상의 요소가 들어갑니다.


이제 이어서 프로필 사진과 그 옆의 요소들을 구성해보도록 하겠습니다.


먼저, 사진과 게시물, 팔로워, 팔로잉 수를 나타내는 요소를 구성하였습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
import React, { Component } from 'react';
import { 
    View,
    Text,
    StyleSheet,
    Image
    } from 'react-native';
import { Icon, Container, Content, Header, Left, Body, Right, Button } from 'native-base';
class ProfileTab extends Component{
    static navigationOptions = {
        tabBarIcon: ({ tintColor }) => (
            < Icon name='person' style={{color:tintColor}} />
        )
    }
 
    render(){
        return (
            <Container>
                <Header>
                    <Left style={{flexDirection:'row', alignItems:'center'}}>
                        <Text style={{fontWeight:'bold', fontSize:17}}>tigercow.door</Text>
                        <Icon name='caret-down' type='FontAwesome' style={{paddingLeft:10, fontSize:14}}/>
                    </Left>
                    <Right style={{flexDirection:'row', alignItems:'center'}}>
                        <Icon name='back-in-time' type='Entypo' style={{paddingRight:10, fontSize:23}}/>
                        <Icon name='user-plus' type='Feather' style={{paddingRight:10, fontSize:23}}/>
                        <Icon name='dots-vertical' type='MaterialCommunityIcons' style={{fontSize:23}}/>
                    </Right>
                </Header>
                <Content>
                    <View>
                        <View style={{flexDirection:'row'}}>
                            <View style={{flex:1}}>
                                <Image source={require('../../assets/beomwoo.jpeg')}
                                style={{width:75, height:75, borderRadius:37.5}}/>
                            </View>
                            <View style={{flex:3}}>
                                <View style={{flexDirection:'row', justifyContent:'space-around'}}>
                                    <View style={{alignItems:'center'}}>
                                        <Text style={{fontSize:17, fontWeight:'bold'}}>167</Text>
                                        <Text style={{fontSize:12, color:'gray'}}>게시물</Text>
                                    </View>
                                    <View style={{alignItems:'center'}}>
                                        <Text style={{fontSize:17, fontWeight:'bold'}}>346</Text>
                                        <Text style={{fontSize:12, color:'gray'}}>팔로워</Text>
                                    </View>
                                    <View style={{alignItems:'center'}}>
                                        <Text style={{fontSize:17, fontWeight:'bold'}}>192</Text>
                                        <Text style={{fontSize:12, color:'gray'}}>팔로잉</Text>
                                    </View>
                                </View>
                            </View>
                        </View>
                    </View>
                </Content>
            </Container>
        );
    }
}
export default ProfileTab;
 
const style = StyleSheet.create({
    container: {
        flex: 1,
        alignItems: 'center',
        justifyContent: 'center',
    }
})
cs


많은 View태그로 묶여있어서 헷갈릴 수도 있지만 하나씩 살펴보도록 하겠습니다.

31번줄에서 첫번째 View태그로 시작되어 프로필창을 전체적으로 묶어주고 있습니다.

이후 다음 32번줄의 View태그에서는 상단 태그를 묶어주는 것으로써 상단의 요소들이 가로로 정렬되기 때문에 flexDirection도 함께 설정하였습니다.

그리고 33번의 View태그에서는 사진을 담고 있으며, 37번의 View태그에서는 사진 우측의 요소들을 담고 있습니다.

그리고 우측 요소들 중에, 게시글, 팔로워, 팔로잉을 나타내는 요소들을 각각 View태그로 묶어서 구성하였습니다.


이제 그 요소들 아래에 버튼을 추가하고, 그 하단에 이름과 내용을 작성해봅니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
import React, { Component } from 'react';
import { 
    View,
    Text,
    StyleSheet,
    Image
    } from 'react-native';
import { Icon, Container, Content, Header, Left, Body, Right, Button } from 'native-base';
class ProfileTab extends Component{
    static navigationOptions = {
        tabBarIcon: ({ tintColor }) => (
            < Icon name='person' style={{color:tintColor}} />
        )
    }
 
    render(){
        return (
            <Container style={{flex:1, backgroundColor:'white'}}>
                <Header>
                    <Left style={{flexDirection:'row', alignItems:'center'}}>
                        <Text style={{fontWeight:'bold', fontSize:17}}>tigercow.door</Text>
                        <Icon name='caret-down' type='FontAwesome' style={{paddingLeft:10, fontSize:14}}/>
                    </Left>
                    <Right style={{flexDirection:'row', alignItems:'center'}}>
                        <Icon name='back-in-time' type='Entypo' style={{paddingRight:10, fontSize:23}}/>
                        <Icon name='user-plus' type='Feather' style={{paddingRight:10, fontSize:23}}/>
                        <Icon name='dots-vertical' type='MaterialCommunityIcons' style={{fontSize:23}}/>
                    </Right>
                </Header>
                <Content>
                    <View style={{paddingTop:10}}>
                        <View style={{flexDirection:'row'}}>
                            <View style={{flex:1, alignItems:'center'}}>
                                <Image source={require('../../assets/beomwoo.jpeg')}
                                style={{width:75, height:75, borderRadius:37.5}}/>
                            </View>
                            <View style={{flex:3}}>
                                <View style={{flexDirection:'row', justifyContent:'space-around'}}>
                                    <View style={{alignItems:'center'}}>
                                        <Text style={{fontSize:17, fontWeight:'bold'}}>167</Text>
                                        <Text style={{fontSize:12, color:'gray'}}>게시물</Text>
                                    </View>
                                    <View style={{alignItems:'center'}}>
                                        <Text style={{fontSize:17, fontWeight:'bold'}}>346</Text>
                                        <Text style={{fontSize:12, color:'gray'}}>팔로워</Text>
                                    </View>
                                    <View style={{alignItems:'center'}}>
                                        <Text style={{fontSize:17, fontWeight:'bold'}}>192</Text>
                                        <Text style={{fontSize:12, color:'gray'}}>팔로잉</Text>
                                    </View>
                                </View>
                                <View style={{flexDirection:'row'}}>
                                    <Button bordered dark
                                    style={{flex:1, justifyContent:'center', height:30, marginHorizontal:10, marginTop:10}}>
                                        <Text>프로필 수정</Text>
                                    </Button>
                                </View>
                            </View>
                        </View>
                        <View style={{paddingHorizontal:10, paddingVertical:10}}>
                            <Text style={{fontWeight:'bold'}}>범우[25:?]</Text>
                            <Text> React-Native로</Text>
                            <Text> Instagram UI 따라하기!!!</Text>
                        </View>
                    </View>
                </Content>
            </Container>
        );
    }
}
export default ProfileTab;
 
const style = StyleSheet.create({
    container: {
        flex: 1,
        alignItems: 'center',
        justifyContent: 'center',
    }
})
cs


53번 ~ 56번줄에서 버튼을 추가했습니다.

그리고 지금까지 구성한 요소들을 묶는 View태그에서 벗어나 60번 줄에서 새로운 View태그를 만들어 그 안에 사용자의 이름과 내용을 넣어주었습니다.


이렇게 하여 나온 결과는 다음과 같습니다.



블로그 이미지

Tigercow.Door

Web Programming / Back-end / Database / AI / Algorithm / DeepLearning / etc


지난글

리액트 네이티브 #4_ 인스타그램(Instagram) UI 무작정 따라하기 (1)


Github

https://github.com/doorBW/INSTA-by-react-native


안녕하세요. 문범우입니다.

지난 포스팅에서는 인스타그램을 만들기 위해 기본적인 네비게이션 구성을 하고 각 화면으로 넘어가는 단계까지 진행하였습니다.


이번 포스팅에서는 메인화면에 피드를 추가해보는 과정을 진행해보도록 하겠습니다.

이번 포스팅을 통한 결과물은 다음과 같습니다.



1. CardComponent 추가하기.


먼저 각 피드가 될, 새로운 js 파일을 만듭니다.

저는 Components 폴더 내부에 CardComponent.js 라는 이름으로 파일을 만들었습니다.


그리고 CardComponent.js 의 내용으로는 기본 틀만 잡아주었습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import React, { Component } from 'react';
import { 
    View,
    Text,
    StyleSheet
} from 'react-native';
 
class CardCompnent extends Component{
    render(){
        return (
            <View style={style.container}>
                <Text>CardCompnent</Text>
            </View>
        );
    }
}
export default CardCompnent;
 
const style = StyleSheet.create({
    container: {
        flex: 1,
        alignItems: 'center',
        justifyContent: 'center',
    }
})
cs


이제 HomeTab 에서 해당 CardComponent를 불러와 피드처럼 수정해주어야 합니다. 실질적인 피드의 사진이나 기타 내용은 CardComponent.js 에서 작성해 줄테지만, 우리가 인스타그램에서 보면 하나의 피드만 존재하는게 아니잖아요?

즉, CardComponent.js 는 하나의 피드가 될 것이고, 여러개의 피드를 추가하는건 HomeTab.js 에서 진행하기 때문에, 각 피드의 형태를 HomeTab.js 에서 잡아주어야 합니다.


먼저 HomeTab.js 에서 아래 코드를 통해 CardComponent.js 를 불러옵니다.


import CardComponent from '../CardComponent';


그리고 HomeTab.js 의 본문 내용을 수정하여 CardComponet를 보여주도록 합니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import React, { Component } from 'react';
import { 
    View,
    Text,
    StyleSheet
    } from 'react-native';
import { Icon, Container, Content } from 'native-base';
 
import CardComponent from '../CardComponent';
 
class HomeTab extends Component{
 
    static navigationOptions = {
        tabBarIcon: ({ tintColor }) => (
            < Icon name='ios-home' style={{color:tintColor}} />
        )
    }
 
    render(){
        return (
            <Container style = {style.container}>
                <Content>
                    <CardComponent/>
                </Content>
            </Container>
        );
    }
}
export default HomeTab;
 
const style = StyleSheet.create({
    container: {
        flex: 1,
        backgroundColor: 'white'
    }
})
cs


위의 코드가 현재까지 진행된 HomeTab.js 의 코드입니다.

먼저 9번 줄에서 CardComponent를 import 하였고, 추가적으로 native-base 라이브러리에서 Icon 뿐만 아니라, Container와 Content를 불러왔습니다.

그리고 21번 ~ 25번 줄을 보시면 기존에 있던 HomeTab의 내용을 없애고 Container > Content > CardComponent 형태로 작성하였습니다.

또한 Container 태그에 스타일을 지정해주었고, 32번줄에 작성되어 있는 container 스타일을 보면, 기존에 있던 것에서 flex를 남겨두고 지웠으며, 추가로 backgroundColor를 지정해 주었습니다.


이렇게 하여 시뮬레이터를 확인하면 HomeTab에서 CardComponent가 뜨는 것을 볼 수 있습니다.



2. CardComponent 수정하기


이제 CardComponent.js 로 들어와 수정해보도록 하겠습니다.

먼저, CardComponent에서 사용할 native-base의 요소를 몇가지 추가해보도록 하겠습니다.

우리가 사용할 요소들은 Card, CardItem, Thumnail, Body, Left, Right, Button, Icon 이므로 아래와 같은 코드로 추가합니다.


import { Card, CardItem, Thumnail, Body, Left, Right, Button, Icon} from 'native-base';


이제 각각의 요소들을 활용하여 CardComponent의 내용을 꾸며봅니다.

이전에 먼저 여러분의 프로젝트 폴더에 있는 assets 폴더 안에, 프로필사진으로 쓸 이미지 하나와, 피드 이미지로 사용할 사진 약 3개를 넣어두시면 편합니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import React, { Component } from 'react';
import { 
    View,
    Text,
    StyleSheet
} from 'react-native';
 
import { Card, CardItem, Thumbnail, Body, Left, Right, Button, Icon } from 'native-base';
 
class CardCompnent extends Component{
    render(){
        return (
            <Card>
                <CardItem>
                    <Left>
                        <Thumbnail source={require('../assets/beomwoo.jpeg')} />
                        <Body>
                            <Text>Beomwoo</Text>
                            <Text note>2018년 5월 22일</Text>
                        </Body>
                    </Left>
                </CardItem>
            </Card>
        );
    }
}
export default CardCompnent;
 
const style = StyleSheet.create({
    container: {
        flex: 1,
        alignItems: 'center',
        justifyContent: 'center',
    }
})
cs


위의 CardComponents.js 의 코드를 보시면, 내부에 Card > CardItem > Left > Thumbnail, Body 가 있는 것을 볼 수 있습니다.

먼저 여기서 Thumbnail은 프로필 사진을 의미합니다. 해당 태그의 source 속성에 여러분들의 이미지를 추가시켜 주시면 되고, 해당 이미지는 인스타그램의 프로필처럼 동그라미 형태로 나오게 됩니다.

그리고 Body 내부에 적어준 Text 두개는 사용자 계정과 요일을 나타냅니다.


이제 피드의 상단부를 제작하였으니, 본문, 피드 이미지를 추가해야 합니다.

그러기 위해서 CardItem을 추가하여 코드를 작성합니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import React, { Component } from 'react';
import { 
    View,
    Text,
    StyleSheet,
    Image
} from 'react-native';
 
import { Card, CardItem, Thumbnail, Body, Left, Right, Button, Icon } from 'native-base';
 
class CardCompnent extends Component{
    render(){
        return (
            <Card>
                <CardItem>
                    <Left>
                        <Thumbnail source={require('../assets/beomwoo.jpeg')} />
                        <Body>
                            <Text>Beomwoo</Text>
                            <Text note>2018년 5월 22일</Text>
                        </Body>
                    </Left>
                </CardItem>
                <CardItem>
                    <Image source={require('../assets/react_native.jpg')} style={{height:200, width:null, flex:1}}/>
                </CardItem>
            </Card>
        );
    }
}
export default CardCompnent;
 
const style = StyleSheet.create({
    container: {
        flex: 1,
        alignItems: 'center',
        justifyContent: 'center',
    }
})
cs


24번 ~ 26번 줄을 보시면 새로운 CardItem 을 추가하여 그 내부에 Image 태그를 통해 피드이미지가 될 사진을 추가하였습니다.

이때, Image 태그를 사용하기 위해 6번 줄과 같이 import 요소를 추가해주어야 합니다.


이제, 피드의 마지막으로 좋아요, 댓글 버튼 등과 좋아요 개수, 본문을 추가해보도록 하겠습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
import React, { Component } from 'react';
import { 
    View,
    Text,
    StyleSheet,
    Image
} from 'react-native';
 
import { Card, CardItem, Thumbnail, Body, Left, Right, Button, Icon } from 'native-base';
 
class CardCompnent extends Component{
    render(){
        return (
            <Card>
                <CardItem>
                    <Left>
                        <Thumbnail source={require('../assets/beomwoo.jpeg')} />
                        <Body>
                            <Text>Beomwoo</Text>
                            <Text note>2018년 5월 22일</Text>
                        </Body>
                    </Left>
                </CardItem>
                <CardItem>
                    <Image source={require('../assets/react_native.jpg')} style={{height:200, width:null, flex:1}}/>
                </CardItem>
                <CardItem style={{height:45}}>
                    <Left>
                        <Button transparent>
                            <Icon name='ios-heart-outline' style={{color:'black'}}/>
                        </Button>
                        <Button transparent>
                            <Icon name='ios-chatbubbles-outline' style={{color:'black'}}/>
                        </Button>
                        <Button transparent>
                            <Icon name='ios-send-outline' style={{color:'black'}}/>
                        </Button>
                    </Left>
                </CardItem>
                <CardItem style={{ height:40 }}>
                    <Text>좋아요 101개</Text>
                </CardItem>
                <CardItem>
                    <Text>
                        <Text style={{fontWeight:'900'}}>Beomwoo </Text>
                        #인스타그램 #따라하기 #리액트네이티브</Text>
                </CardItem>
            </Card>
        );
    }
}
export default CardCompnent;
 
const style = StyleSheet.create({
    container: {
        flex: 1,
        alignItems: 'center',
        justifyContent: 'center',
    }
})
cs


갑작스레 추가된 코드가 좀 길게 보일 수 있지만, 27번 ~ 47번까지, 3개의 CardItem을 추가시키며, 버튼을 포함한 CardItem, 좋아요 개수를 알려주는 CardItem, 본문내용을 보여주는 CardItem 을 추가했을 뿐입니다.

크게 어려운 내용은 아니니 따라오실 수 있을 겁니다. :)


이렇게까지 하고 시뮬레이터를 확인해보면, 



위와 같이 인스타그램의 하나의 피드처럼 완성이 되었습니다.


그런데, 각 피드마다 이미지나, 좋아요 개수는 같지 않습니다.

(물론 아이디나 날짜, 댓글도 서로 다르게 구현되어야 하지만 일단은 따라하기니까... 이미지와 좋아요 개수만 컨드롤 해보겠습니다.)

즉, 여러개의 피드를 추가할텐데, 이때 각각의 피드마다 이미지와 좋아요 개수가 다르게 설정될 수 있도록 코드를 수정해보도록 하겠습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
import React, { Component } from 'react';
import { 
    View,
    Text,
    StyleSheet,
    Image
} from 'react-native';
 
import { Card, CardItem, Thumbnail, Body, Left, Right, Button, Icon } from 'native-base';
 
class CardCompnent extends Component{
    render(){
        const images = {
            '1': require('../assets/react_native.jpg'),
            '2': require('../assets/python.jpg'),
            '3': require('../assets/deep_learning.png')
        }
 
        return (
            <Card>
                <CardItem>
                    <Left>
                        <Thumbnail source={require('../assets/beomwoo.jpeg')} />
                        <Body>
                            <Text>Beomwoo</Text>
                            <Text note>2018년 5월 22일</Text>
                        </Body>
                    </Left>
                </CardItem>
                <CardItem>
                    {/* <Image source={require('../assets/react_native.jpg')} style={{height:200, width:null, flex:1}}/> */}
                    <Image source={images[this.props.imageSource]} style={{height:200, width:null, flex:1}}/>
                </CardItem>
                <CardItem style={{height:45}}>
                    <Left>
                        <Button transparent>
                            <Icon name='ios-heart-outline' style={{color:'black'}}/>
                        </Button>
                        <Button transparent>
                            <Icon name='ios-chatbubbles-outline' style={{color:'black'}}/>
                        </Button>
                        <Button transparent>
                            <Icon name='ios-send-outline' style={{color:'black'}}/>
                        </Button>
                    </Left>
                </CardItem>
                <CardItem style={{ height:40 }}>
                    {/* <Text>좋아요 101개</Text> */}
                    <Text>좋아요 {this.props.likes}개</Text>
                </CardItem>
                <CardItem>
                    <Text>
                        <Text style={{fontWeight:'900'}}>Beomwoo </Text>
                        #인스타그램 #따라하기 #리액트네이티브</Text>
                </CardItem>
            </Card>
        );
    }
}
export default CardCompnent;
 
const style = StyleSheet.create({
    container: {
        flex: 1,
        alignItems: 'center',
        justifyContent: 'center',
    }
})
cs


위의 코드를 보시면, 31번줄을 32번 줄과 같이 수정했고, 48번줄을 49번 줄과 같이 수정하였습니다.

수정된 두줄의 코드 모두 어떤 이미지를 넣거나, 숫자를 넣는대신에 {this.props.~} 형태로 입력해주었습니다.

그리고 이미지는 13번 ~ 17번 줄에 집합형태로 추가시켜줌으로써 우리가 어떤 숫자를 넘겨주는지에 따라 이미지가 설정되도록 하였습니다.

이것은 우리가 HomeTab.js 에서 각 피드를 추가할때 ~에 쓰여진 이름으로 데이터를 전달해주면 그 값을 받는 곳을 지정해 준 것입니다.

즉, 우리가 HomeTab.js 에서 CardComponent 를 보여줄때 추가로 imageSource에 진짜 이미지의 경로를 적어주고, likes 에 좋아요의 개수를 적어주면 그 값이 전달되어 보여지게 됩니다.


그럼 HomeTab.js 로 돌아가서 총 3개의 피드를 추가해보도록 하겠습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import React, { Component } from 'react';
import { 
    View,
    Text,
    StyleSheet
    } from 'react-native';
import { Icon, Container, Content } from 'native-base';
 
import CardComponent from '../CardComponent';
 
class HomeTab extends Component{
 
    static navigationOptions = {
        tabBarIcon: ({ tintColor }) => (
            < Icon name='ios-home' style={{color:tintColor}} />
        )
    }
 
    render(){
        return (
            <Container style = {style.container}>
                <Content>
                    <CardComponent imageSource='1' likes='2324'/>
                    <CardComponent imageSource='2' likes='46'/>
                    <CardComponent imageSource='3' likes='208'/>
                </Content>
            </Container>
        );
    }
}
export default HomeTab;
 
const style = StyleSheet.create({
    container: {
        flex: 1,
        backgroundColor: 'white'
    }
})
cs


위와 같이 HomeTab.js 에서 CardComponent 를 3개 추가하고 각각에 따라 설정값을 다르게 주었습니다.


이렇게까지 하면 다음과 같이 어느정도 인스타그램의 첫 화면이 완성되었습니다.



이렇게 하여 인스타그램 UI 따라하기 2번째 포스팅을 마무리 하겠습니다.


오류가 생기거나 추가적으로 궁금한 사항이 있으신 분들은 댓글 또는 공지사항을 통해 이메일이나 카카오톡으로 말씀해주세요 :)

블로그 이미지

Tigercow.Door

Web Programming / Back-end / Database / AI / Algorithm / DeepLearning / etc

Github

https://github.com/doorBW/INSTA-by-react-native


안녕하세요. 문범우입니다.

오랜만에 리액트 네이티브에 대해 포스팅하게 되었습니다.

이전 포스트까지 해서 리액트 네이티브 환경구성하고, 기본적인 flex에 대해서 알아보았는데, 이후에 어떤 것을 하면서 연습하면 좋을지를 고민하느라 늦어지게 되었습니다.

그리고 결정을 내린건 인스타그램 UI를 만들어보기로 하였습니다.

저도 공부를 시작하고 있는 터라.. 처음부터 완벽하게 하지는 못하겠더라구요.

그래서 마침 유튜브에 좋은 강의가 올라와있길래 이를 참고하면서 포스팅을 진행해보도록 하겠습니다.


https://www.youtube.com/watch?v=cgg1HidN4mQ&t=5s


위의 링크를 들어가보시면 인스타그램 UI 따라하기 영상이 총 5편으로 제작되어 있습니다.

저는 해당 영상을 그대로 따라해보면서 정리하는 정도로 포스팅을 할 예정이에요.

영어가 상관없거나 영상으로 만나보고 싶으신 분들은 위의 영상을 추천드립니다 :)


먼저, 이번 포스팅에서는 아래의 내용정도까지 제작이 가능합니다.


- 인스타그램의 상단, 하단 네비게이션 구현 및 각 화면으로 넘어가기



1. 필요한 라이브러리 설치


먼저 우리가 인스타그램을 만들어보기 위해서 필요한 라이브러리 몇개를 설치해야 합니다.

첫번째로는 아이콘등의 UI를 위해 native base를 설치합니다.

VS code 또는 터미널에서 아래 명령어를 실행하여 설치합니다.


npm install native-base



native base는 리액트 네이티브를 위한 오픈소스 UI 컴포넌트를 제공해주는 라이브러리 입니다.

공식 사이트는 아래 링크를 통해 확인하실 수 있습니다.

https://nativebase.io/


다음으로 설치할 라이브러리는 react navigation 입니다.

이전과 같이 아래 명령어를 통해 설치합니다.


npm install react-navigation



react navigation은 이름에서도 알 수 있듯이 우리가 네비게이션을 사용하기 위해서 필요한 라이브러리 입니다.


네비게이션이 무엇인가요?


이렇게 두개의 라이브러리 설치를 완료하셨으면 마지막으로 다음 명령어를 입력하여 dependency를 추가합니다.


npm install




2. Stack 네비게이션 추가하기


그럼 이제 실제로 네비게이션을 추가해보도록 하겠습니다.

먼저 Expo를 통해 프로젝트를 생성합니다.

프로젝트를 생성할때 '네비게이션이 없는' 빈 프로젝트로 생성합니다.



VS code를 통해 새로 생성한 프로젝트를 열면 위와 같이 기본코드만 작성되어 있습니다.


먼저, StackNavigator를 추가해보도록 하겠습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
// react-natvigation 라이브러리 에서 StackNavigator 추가하기
import { createStackNavigator } from 'react-navigation';
 
export default class App extends React.Component {
  render() {
    return (
      <View style={styles.container}>
        <Text>Open up App.js to start working on your!</Text>
      </View>
    );
  }
}
 
// StackNavigator 를 App에 추가하기
const AppStackNavigator = createStackNavigator({
  Main:{
    screen: MainScreen
  }
})
 
const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
});
cs


먼저 사용을 위해 4번줄과 같이 react navigation에서 StackNavigator를 추가하였습니다.

그리고 16~21번째 줄과 같이 StackNavigator를 추가하여 MainScreen을 띄어주도록 하였습니다.


이렇게 코드를 작성하고 저장하여 시뮬레이터를 확인해보면 에러화면이 뜰텐데, 지금은 이게 정상입니다. 기본적으로 리액트 네이티브에서 App.js를 실행시켜주는데, 우리가 App.js에서 메인으로 MainScreen을 띄어주라고 했지만, 우리는 MainScreen이란 것을 만든적이 없기 때문입니다.


그럼 바로 MainScreen을 만들어 보도록 하겠습니다.


먼저, 이후 우리가 만들 화면들도 추가적으로 있기 때문에 이들을 보기좋게 관리하기 위해 현재 프로젝트의 폴더 안에 Components 라는 폴더를 만들고, Components 폴더 안에 MainScreen.js 를 만듭니다.



그리고 해당 MainScreen.js 파일에 기본적으로 아래와 같은 형태로 코드를 구현했습니다.

(영상에서는 user snippet을 설정하여 빠르게 작성하였는데 따로 snippet을 설정하지 않으신 분들은 그냥 그대로 코드를 작성해주시면 됩니다.)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import React, { Component } from 'react';
import { 
    View,
    Text,
    StyleSheet
    } from 'react-natvie';
 
class MainScreen extends Component{
    render(){
        return (
            <View style={style.container}>
                <Text>MainScreen</Text>
            </View>
        );
    }
}
export default MainScreen;
 
const style = StyleSheet.create({
    container: {
        flex: 1,
        alignItems: 'center',
        justifyContent: 'center',
    }
})
cs


이렇게 MainScreen.js 파일을 구성하셨으면, 다시 App.js로 돌아와서 방금 만들어준 MainScreen.js 를 import 합니다.

자신이 MainScreen.js를 만든 위치에 따라서 경로를 잡아줘야 합니다.

저는 현재 폴더안에 있는 Components 폴더 안에 만들었기에 다음과 같이 import를 하였습니다.


import MainScreen from './Components/MainScreen.js';


그리고 App.js의 본문내용을 우리가 만든, AppStackNavigator로 띄워줘야 합니다.

이를 수정한 App.js의 전체 코드는 아래와 같습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
// react-natvigation 라이브러리 에서 StackNavigator 추가하기
import { createStackNavigator } from 'react-navigation';
// MainScreen 추가
import MainScreen from './Components/MainScreen';
 
export default class App extends React.Component {
  render() {
    return (
      <AppStackNavigator />
    );
  }
}
 
// StackNavigator 를 App에 추가하기
const AppStackNavigator = createStackNavigator({
  Main:{
    screen: MainScreen
  }
})
 
const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
});
cs


위 코드 11번째 줄에서 AppStackNavigator를 보여주고 있는 것을 확인하세요.



3. 상단 네비게이션 추가하기


이제 우리가 인스타그램을 볼때 상단에 있는 네비게이션 바를 추가해보도록 하겠습니다.

상단바를 보시면 왼쪽에는 카메라, 가운데(제목)으로는 Instagram, 오른쪽에는종이비행기 아이콘이 있습니다.


이를 구현하기 위해서 MainScreen.js 에 들어가 네비게이션 옵션의 속성을 수정합니다.

물론, 아이콘을 사용하기 위해 우리가 설치했던 라이브러리를 import 해야합니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import React, { Component } from 'react';
import { 
    View,
    Text,
    StyleSheet
    } from 'react-native';
 
import { Icon } from 'native-base';
 
class MainScreen extends Component{
 
    static navigationOptions = {
        headerLeft: <Icon name='ios-camera-outline' style={{paddingLeft:10}}/>,
        title: 'Instagram',
        headerRight: <Icon name='ios-send-outline' style={{paddingRight:10}}/>,
    }
 
    render(){
        return (
            <View style={style.container}>
                <Text>MainScreen</Text>
            </View>
        );
    }
}
export default MainScreen;
 
const style = StyleSheet.create({
    container: {
        flex: 1,
        alignItems: 'center',
        justifyContent: 'center',
    }
})
cs


8번째 줄에서 아이콘을 사용하기 위한 native-base 라이브러리를 추가했습니다.

12번 ~ 16번 줄을 보시면 네비게이션 옵션에서 세개의 속성을 수정한것을 확인할 수 있습니다. padding 값은 아이콘이 화면 모서리에 너무 딱 붙지 않게 추가해준 스타일 입니다.



4. Tab 네비게이션 추가하기


이젠 인스타그램의 하단에 있는, 각 페이지로 이동하는 Tab 네비게이션을 추가해보도록 하겠습니다.


하단에 추가되는 네비게이션은 버튼을 누르면 그에 해당하는 위치로 이동되는 것으로써 일반적으로 TabNavigation이라고 합니다.

이를 이용하기 위해 react-navigation 라이브러리에서 TabNavigator를 추가합니다.

그리고 기본적인 TabNavigator를 구성합니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import React, { Component } from 'react';
import { 
    View,
    Text,
    StyleSheet
    } from 'react-native';
 
import { Icon } from 'native-base';
import { createTabNavigator } from 'react-navigation';
 
class MainScreen extends Component{
 
    static navigationOptions = {
        headerLeft: <Icon name='ios-camera-outline' style={{paddingLeft:10}}/>,
        title: 'Instagram',
        headerRight: <Icon name='ios-send-outline' style={{paddingRight:10}}/>,
    }
 
    render(){
        return (
            <View style={style.container}>
                <Text>MainScreen</Text>
            </View>
        );
    }
}
export default MainScreen;
 
const AppTabNavigator = createTabNavigator({
    HomeTab:{
        screen:HomeTab
    },
    Search:{
        screen:SearchTab
    },
    AddMedia:{
        screen:AddMediaTab
    },
    Likes:{
        screen:LikesTab
    },
    Profile:{
        screen:ProfileTab
    }
})
 
const style = StyleSheet.create({
    container: {
        flex: 1,
        alignItems: 'center',
        justifyContent: 'center',
    }
})
cs


9번 줄에서 TabNavigator를 추가하였습니다.

그리고 29번 ~ 45번줄에 기본적인 TabNavigator를 구성하였습니다.

우리는 총 5개의 탭을 만들것이고 이에 해당하는 각각의 screen을 연결한 것입니다.

물론 이렇게 코드를 작성하고 시뮬레이터를 확인하면 오류가 납니다.

좀 전에 위에서 났던 오류와 같이 HomeTab을 찾을 수 없다는 등의 오류가 발생하는데 우리가 현재 HomeTab을 연결시켜놓고 HomeTab 화면을 만들지 않았기 때문입니다.


각각의 화면을 만들어주기 위해, 저는 Components 폴더 안에 AppTabNavigator라는 폴더를 새로 만들어서 5개의 파일을 다음과 같이 추가하였습니다.



그리고 각 파일에 대해 MainScreen.js 에서 기본 형태를 작성해주었던 것 처럼 코드를 작성합니다.

예시로 HomeTab.js 의 코드는 아래와 같습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import React, { Component } from 'react';
import { 
    View,
    Text,
    StyleSheet
    } from 'react-native';
 
class HomeTab extends Component{
    render(){
        return (
            <View style={style.container}>
                <Text>HomeTab</Text>
            </View>
        );
    }
}
export default HomeTab;
 
const style = StyleSheet.create({
    container: {
        flex: 1,
        alignItems: 'center',
        justifyContent: 'center',
    }
})
cs


그리고 MainScreen.js 에 5개의 js 파일을 아래 코드와 같이 import 합니다.


1
2
3
4
5
import HomeTab from './AppTabNavigator/HomeTab';
import SearchTab from './AppTabNavigator/SearchTab';
import AddMediaTab from './AppTabNavigator/AddMediaTab';
import LikesTab from './AppTabNavigator/LikesTab';
import ProfileTab from './AppTabNavigator/ProfileTab';
cs


이렇게 코드를 작성하고 시뮬레이터를 확인하면 이제 오류는 뜨지 않습니다.

하지만 노란색 경고창이 뜰텐데, 이는 현재 react-navigation에서 createTabNavigator를 더이상 사용하지 않기 때문입니다.

이 대신에 createBottomTabNavigator 등을 사용할 수 있으나, 뒤에서 진행되는 네비게이션의 속성들에서 약간의 차이가 존재합니다.

일단은 영상을 그대로 따라가기 위해 그대로 진행하도록 하겠습니다.

경고창은 한번클릭하여 Dismiss를 클릭해주시면 사라집니다.

이제 실제로 하단 Tab 네비게이션을 추가해보도록 하겠습니다.


MainScreen.js 의 render>return 부분에 있는 내용을 수정합니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
import React, { Component } from 'react';
import { 
    View,
    Text,
    StyleSheet
    } from 'react-native';
 
import { Icon } from 'native-base';
import { createTabNavigator } from 'react-navigation';
 
import HomeTab from './AppTabNavigator/HomeTab';
import SearchTab from './AppTabNavigator/SearchTab';
import AddMediaTab from './AppTabNavigator/AddMediaTab';
import LikesTab from './AppTabNavigator/LikesTab';
import ProfileTab from './AppTabNavigator/ProfileTab';
 
class MainScreen extends Component{
 
    static navigationOptions = {
        headerLeft: <Icon name='ios-camera-outline' style={{paddingLeft:10}} />,
        title: 'Instagram',
        headerRight: <Icon name='ios-send-outline' style={{paddingRight:10}} />,
    }
 
    render() {
        return (
            <AppTabNavigator />
        );
    }
}
export default MainScreen;
 
const AppTabNavigator = createTabNavigator({
    HomeTab:{
        screen: HomeTab
    },
    Search:{
        screen: SearchTab
    },
    AddMedia:{
        screen: AddMediaTab
    },
    Likes:{
        screen: LikesTab
    },
    Profile:{
        screen: ProfileTab
    }
})
 
const style = StyleSheet.create({
    container: {
        flex: 1,
        alignItems: 'center',
        justifyContent: 'center',
    }
})
cs


위의 코드가 현재 MainScreen.js 의 전체코드입니다. 27번 줄과 같이 AppTabNavigator를 추가합니다.



이렇게 하여 시뮬레이터에서 확인하면 오류가 발생합니다.

강의에서는 별다른 오류없이 제대로 화면이 뜨지만, 저는 하나의 네비게이터만 사용해야 한다는 등의 오류가 발생합니다.

알아보니 다른 사람들도 비슷한 오류를 겪고 있는 듯 한데 정확히 어떤 점이 잘못된 건지는 아직 잘 모르겠습니다 :'(

https://github.com/react-navigation/react-navigation/issues/4196


위의 링크에서 이에 대한 문제를 이야기 중인데, 일단 중간에 나와있는 것 처럼 임시방편으로 아예 해당 오류가 발생했을 때 무시하도록 하는 방법을 사용하였습니다.

무시해도 일단은 작동되기 때문에 임시방편으로 놓아두도록 하겠습니다.

방법은 다음과 같습니다.

프로젝트의 폴더에서, node_modules 폴더 내부의, react-navigation 폴더 내부의 src 폴더 내부로 들어갑니다. 그럼 createNavigationContainer.js 파일의 코드를 수정합니다.

아래 사진과 같이 약 200번줄에서 시작되는 곳에 내부를 주석처리합니다.


이렇게 주석처리를 하면 우리가 위에서 겪은 오류가 발생했을때 아무 처리도 안하고 넘어가게 하는 것이므로 무시하게 됩니다.




이렇게 까지 하고 시뮬레이터를 확인해보면 하단에 네비게이션이 생긴것을 확인할 수 있습니다. 하지만 인스타그램의 네비게이션은 아이콘도 있고, 좀 더 시각적으로 잘 표현되어있죠?


그럼, 이번 포스팅의 마지막으로 해당 네비게이션을 수정해보도록 하겠습니다.


각각의 Tab 페이지에서 navigationOptions에 아이콘을 먼저 추가해야 합니다.

아이콘을 사용하기 위해서 native-base 라이브러리를 추가하고, navigationOptions를 수정합니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import React, { Component } from 'react';
import { 
    View,
    Text,
    StyleSheet
    } from 'react-native';
import { Icon } from 'native-base';
 
class HomeTab extends Component{
 
    static navigationOptions = {
        tabBarIcon: ({ tintColor }) => (
            < Icon name='ios-home' style={{color:tintColor}} />
        )
    }
 
    render(){
        return (
            <View style={style.container}>
                <Text>HomeTab</Text>
            </View>
        );
    }
}
export default HomeTab;
 
const style = StyleSheet.create({
    container: {
        flex: 1,
        alignItems: 'center',
        justifyContent: 'center',
    }
})
cs


위의 코드는 HomeTab.js 의 전체 코드입니다. 

이제 위와 같이 나머지 Tab 페이지에도 아이콘을 추가합니다.

아이콘을 추가하는 코드는 모두 동일하며 아이콘의 name 만 서로 다릅니다.

SearchTab.js 에는 'ios-search' 아이콘,

AddMediaTab.js 에는 'ios-add-circle' 아이콘,

LikesTab.js 에는 'ios-heart' 아이콘,

ProfileTab.js 에는 'person' 아이콘으로 추가합니다.


그리고 MainScreen.js 로 돌아와서 createTabNavigator의 속성을 수정해보도록 하겠습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
import React, { Component } from 'react';
import { 
    View,
    Text,
    StyleSheet,
    Platform
    } from 'react-native';
 
import { Icon } from 'native-base';
import { createTabNavigator } from 'react-navigation';
 
import HomeTab from './AppTabNavigator/HomeTab';
import SearchTab from './AppTabNavigator/SearchTab';
import AddMediaTab from './AppTabNavigator/AddMediaTab';
import LikesTab from './AppTabNavigator/LikesTab';
import ProfileTab from './AppTabNavigator/ProfileTab';
 
class MainScreen extends Component{
 
    static navigationOptions = {
        headerLeft: <Icon name='ios-camera-outline' style={{paddingLeft:10}} />,
        title: 'Instagram',
        headerRight: <Icon name='ios-send-outline' style={{paddingRight:10}} />,
    }
 
    render() {
        return (
            <AppTabNavigator />
        );
    }
}
export default MainScreen;
 
const AppTabNavigator = createTabNavigator({
    HomeTab:{
        screen: HomeTab
    },
    Search:{
        screen: SearchTab
    },
    AddMedia:{
        screen: AddMediaTab
    },
    Likes:{
        screen: LikesTab
    },
    Profile:{
        screen: ProfileTab
    }
}, {
    animationEnabled: true,
    swipeEnabled: true,
    tabBarPosition: "bottom",
    tabBarOptions: {
        style: {
            ...Platform.select({
                android:{
                    backgroundColor:'white'
                }
            })
        },
        activeTintColor: '#000',
        inactiveTintColor: '#d1cece',
        showLabel: false,
        showIcon: true,
    }
})
 
const style = StyleSheet.create({
    container: {
        flex: 1,
        alignItems: 'center',
        justifyContent: 'center',
    }
})
cs


MainScreen.js의 전체적인 코드는 위와 같습니다.

추가된 부분은 50번 ~ 66번의 코드입니다.

우리가 각 탭에 대한 Screen 지정을 하였고 그 뒤에서 추가적인 속성을 설정합니다.

전체적인 속성은 react-navigation 공식문서에서 확인하실 수 있습니다.

속성들의 이름을 보면 간단히 무엇을 하는지 이해할 수 있을 것이며, 추가적으로 54번에 추가된 속성, tabBarOptions 에서 style에 Platform 을 사용하기 위해 코드 상단에 react-native 에서 Platform 을 추가로 import 하신것을 확인하실 수 있습니다.

이는 안드로이드 환경에서 바탕색이 흰색으로 되게끔 설정한 속성입니다.


이렇게 네비게이션의 속성까지 수정하면 아래와 같은 화면이 나오게 됩니다.



이렇게 하여, 인스타그램 UI 무작정 따라하기 1편을 마무리하도록 하겠습니다.


오류가 발생하거나 추가적인 질문은 언제나 댓글 및 이메일 또는 공지사항을 통한 1:1 카카오톡을 이용해주세요 :)

블로그 이미지

Tigercow.Door

Web Programming / Back-end / Database / AI / Algorithm / DeepLearning / etc