728x90
반응형
ios에서 노치디자인에서 헤더를 투명화 하기위해
헤더의 높이를 조절해야 할 일이 생겼다.
animated view로 헤더의 position을
transform: [
{
translateY: safeAreaTopPosition.interpolate({
inputRange: [0, 1],
outputRange: [0, insets.top],
}),
},
],
로 옮겨주는데,
그렇게되면 status bar 에 겹치는 현상이 발생하기때문에, height 값조정과 내용들의 정렬화 애니메이션이 필요하게되었다.
height 값은 state값으로 조절해주고
setState에서 값을 변경 한뒤
LayoutAnimation.configureNext(LayoutAnimation.Presets.linear);
를 통하면 자연스럽게 늘어나는 UI를 입혀준다.
최종 소스는
import { useCallback, useRef, useState } from 'react';
import {
Animated,
Dimensions,
FlatList,
LayoutAnimation,
NativeScrollEvent,
NativeSyntheticEvent,
Platform,
View,
} from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { heightScale } from '../../util/Layout';
const DUMMY = [
{
index: 0,
},
{
index: 1,
},
{
index: 2,
},
{
index: 3,
},
{
index: 4,
},
{
index: 5,
},
{
index: 6,
},
{
index: 7,
},
];
const deviceWidth = Dimensions.get('window').width;
const bannerHeight = (deviceWidth * 600) / 480;
const ListHeader = () => {
return (
<View style={{ flexDirection: 'column' }}>
<View
style={{
width: deviceWidth,
height: bannerHeight,
backgroundColor: 'orange',
}}
></View>
<View
style={{
width: '100%',
height: heightScale(90),
backgroundColor: 'blue',
}}
></View>
<View
style={{
width: '100%',
height: heightScale(90),
backgroundColor: 'red',
}}
></View>
</View>
);
};
export const HomeScreen = () => {
const viewRef = useRef(null);
var currentOffset = 0;
const backgroundColor = useRef(new Animated.Value(0)).current;
const safeAreaTopPosition = useRef(new Animated.Value(1)).current;
const safeAreaTopPadding = useRef(new Animated.Value(0)).current;
const [animatedHeaderHeight, setAnimatedHeaderHeight] = useState(
heightScale(75),
);
const insets = useSafeAreaInsets();
const fadingIn = useCallback(() => {
Animated.timing(backgroundColor, {
toValue: 1,
duration: 300,
useNativeDriver: true,
}).start();
if (Platform.OS === 'ios') {
Animated.timing(safeAreaTopPosition, {
toValue: 0,
duration: 300,
useNativeDriver: true,
}).start();
Animated.timing(safeAreaTopPadding, {
toValue: 1,
duration: 300,
useNativeDriver: true,
}).start();
setAnimatedHeaderHeight(heightScale(75) + insets.top);
LayoutAnimation.configureNext(LayoutAnimation.Presets.linear);
}
}, []);
const fadingOut = useCallback(() => {
Animated.timing(backgroundColor, {
toValue: 0,
duration: 300,
useNativeDriver: true,
}).start();
if (Platform.OS === 'ios') {
Animated.timing(safeAreaTopPosition, {
toValue: 1,
duration: 300,
useNativeDriver: true,
}).start();
Animated.timing(safeAreaTopPadding, {
toValue: 0,
duration: 300,
useNativeDriver: true,
}).start();
setAnimatedHeaderHeight(heightScale(75));
LayoutAnimation.configureNext(LayoutAnimation.Presets.linear);
}
}, []);
const animationStart = useCallback(
(event: NativeSyntheticEvent<NativeScrollEvent>) => {
let direction =
event.nativeEvent.contentOffset.y > currentOffset ? 'down' : 'up';
currentOffset = event.nativeEvent.contentOffset.y;
if (
direction === 'down' &&
event.nativeEvent.contentOffset.y > bannerHeight / 2
) {
fadingIn();
}
if (
direction === 'up' &&
event.nativeEvent.contentOffset.y < bannerHeight / 2
) {
fadingOut();
}
},
[],
);
return (
<View
style={{
flex: 1,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: 'white',
}}
>
<Animated.View
ref={viewRef}
style={{
width: '100%',
borderWidth: 1,
height: animatedHeaderHeight,
position: 'absolute',
top: 0,
zIndex: 1,
backgroundColor: backgroundColor.interpolate({
inputRange: [0, 1],
outputRange: ['#FFFFFF00', '#FFFFFF'],
}),
transform: [
{
translateY: safeAreaTopPosition.interpolate({
inputRange: [0, 1],
outputRange: [0, insets.top],
}),
},
],
}}
>
<Animated.Text
style={[
{ color: 'black' },
{
transform: [
{
translateY: safeAreaTopPosition.interpolate({
inputRange: [0, 1],
outputRange: [insets.top, 0],
}),
},
],
},
]}
>
headerheaderheaderheaderheader
</Animated.Text>
</Animated.View>
<FlatList
style={{ flex: 1 }}
data={DUMMY}
ListHeaderComponent={ListHeader}
onScroll={(event) => animationStart(event)}
renderItem={() => {
return (
<View
style={{
width: 100,
height: 100,
borderWidth: 1,
backgroundColor: 'pink',
}}
></View>
);
}}
/>
</View>
);
};
728x90
반응형