Skip to content
This repository has been archived by the owner on Nov 27, 2022. It is now read-only.

Height in different scene #290

Closed
patrikmasiar opened this issue Jul 3, 2017 · 28 comments
Closed

Height in different scene #290

patrikmasiar opened this issue Jul 3, 2017 · 28 comments

Comments

@patrikmasiar
Copy link

How to fix differences in height from one tab to another?

@melihberberolu
Copy link

+1

1 similar comment
@pj0579
Copy link

pj0579 commented Jul 24, 2017

+1

@satya164
Copy link
Owner

You need to handle it yourself. Listen to layout changes in individual tabs and set tab view's height. There are no plans to implement it.

@eduardojunio
Copy link

"There are no plans to implement it." - @satya164
Why?

@satya164
Copy link
Owner

Because you can implement it yourself and I don't want to work on or maintain a feature I'll never use.

@thijssmudde
Copy link

Can you help us achieve this (dont need the entire code, just the methods and values that need to change)? I'm just lost on how to limit the height of one particular tab-view. Now its possible to scroll the entire animated screen based on the height of the largest tab-view

@satya164
Copy link
Owner

satya164 commented Jan 17, 2018

What's your use case? I'm don't know what you're planning to do, but I'm pretty sure that dynamic height for tab view is not the best solution.

Regarding methods and values, there's nothing special, you measure the layout of components with onLayout and set height with style.

And whoever is downvoting the comment that I won't do it, I am building this library for free and I'm not required to do whatever you want me to do. Thanks.

@yangnana11
Copy link

I can't make it re-render for now so I need to set multiple parameters for every tab height.
Here is the code if you are interested

Parent:
Remind: the init height can't be <=0, you can set it any number cause it will be changed later

  constructor (props) {
    super(props)
    this.state = {
      index: 0,
      routes: [
        { key: 'first', title: 'tab1' },
        { key: 'second', title: 'tab2' }
      ],
      tab1Height: Metrics.HEIGHT,
      tab2Height: Metrics.HEIGHT
    }
    this.setHeight = this.setHeight.bind(this)
  }
  setHeight (idx, height) {
    switch (idx) {
      case 1:
        this.setState({tab1Height: height})
        break
      case 2:
        this.setState({tab2Height: height})
        break
      default:
        break
    }
  }
  renderScene = ({ route }) => {
    switch (route.key) {
      case 'first':
        return <WalletTab1Screen setHeight={this.setHeight} />
      case 'second':
        return <WalletTab2Screen setHeight={this.setHeight} />
      default:
        return null
    }
  }

and render function:

<TabView
            style={{height: index === 0 ? this.state.tab1Height : this.state.tab2Height}}
            navigationState={this.state}
            renderScene={this.renderScene}
            renderPager={props => (
              <PagerPan {...props} />
            )}
            renderTabBar={props =>
              <TabBar
                {...props}
                style={{marginTop: HEADER_MAX_HEIGHT}}
                indicatorStyle={{ backgroundColor: 'pink' }}
              />
            }
            onIndexChange={index => this.setState({ index })}
            initialLayout={{ width: Metrics.WIDTH }}
          />

Children tab 1:

<View onLayout={(event) => this.props.setHeight(1, event.nativeEvent.layout.height + Metrics.header + 80)}>...</View

Children tab 2:

<View onLayout={(event) => this.props.setHeight(2, event.nativeEvent.layout.height + Metrics.header + 80)}>...</View>

@jeongsd
Copy link

jeongsd commented Nov 21, 2018

@yangnana11 thank you
it work for me

@ttkien95
Copy link

ttkien95 commented Dec 7, 2018

@yangnana11 thank you
it work for me

@dseawel
Copy link

dseawel commented Dec 17, 2018

I ended up removing the tabs content from the tab control altogether. It's hacky but worked for me...


  render() {
    const {index} = this.state;
    return (
      <ScrollView>
        <TabView
          renderPager={this._renderPager}
          renderScene={() => null}
          onIndexChange={index => this.setState({index})}
          initialLayout={{height: 0, width: Dimensions.get('window').width}}
        />
        {index === 0 && <Tab1Content />}
        {index === 1 && <Tab2Content />}
      </ScrollView>
    );
  }

@nicoleanater
Copy link

Based on @dseawel solution I came with my own aswell. I was using SceneMap to render my components. I changed to my own switch case so I could send a isActive prop to tell the component whether it should render all its content or an empty View.
Example, i changed this SceneMap:

renderScene = SceneMap({
        details: DetailsTab,
        gallery: GalleryTab,
        ratings: RatingsTab
});

To this switch case showed in the reference example, but sending a isActive prop:

renderScene = ({ route, jumpTo }) => {
        switch (route.key) {
            case 'details':
                return <DetailsTab navigation={this.props.navigation} jumpTo={jumpTo} isActive={this.state.tabState.index === 0}/>;
            case 'albums':
                return <GalleryTab navigation={this.props.navigation} jumpTo={jumpTo} isActive={this.state.tabState.index === 1}/>;
            case 'ratings': 
                return <RatingsTab navigation={this.props.navigation} jumpTo={jumpTo} isActive={this.state.tabState.index === 2}/>;
            default:
                return <View/>;
        }
};

And on the component just put a simple validation:

 render() {
        return this.props.isActive ? (
            <View>
                {/* contents */}
             </View>
        ) : <View/>

@tomzaku
Copy link

tomzaku commented Jun 23, 2019

@dseawel @nicoleanater There are good, but it will force rerendering when click any tab.

@saniagh
Copy link

saniagh commented Aug 7, 2019

One way to change TabView's height is:

import { Dimensions } from "react-native";
const SCREEN_HEIGHT = Dimensions.get('window').height

(...)

constructor(props) {
	super(props);

	this.state = {
		tabView: {
			index: 1,
			routes: [
				{ key: 'Media', title: 'Media' },
				{ key: 'About', title: 'About' },
			],
		},
		tabViewHeight: 522
	}
}

setTabViewHeight = (currentIndex, tabIndex) => event => {
	if (currentIndex === tabIndex) {
		const height = event.nativeEvent.layout.height + SCREEN_HEIGHT * 0.08;
		if (height < 487) {
			this.setState({ tabViewHeight: 487 + SCREEN_HEIGHT * 0.08 });
		} else {
			this.setState({ tabViewHeight: height })
		}
	}
};

(...)

<TabView
	style={ { height: tabViewHeight } }
	navigationState={ tabView }
	lazy={ true }
	renderLazyPlaceholder={ () => <ActivityIndicator/> }
	renderScene={ SceneMap({
		Media: () => (
			<View onLayout={ this.setTabViewHeight(tabView.index, 0) }>
				<Media/>
			</View>
		),
		About: () => (
			<View onLayout={ this.setTabViewHeight(tabView.index, 1) }>
				<About />
			</View>
		)
	}) }
	renderTabBar={ props => (
		<TabBar
			{ ...props }
			style={ { backgroundColor: 'white' } }
			labelStyle={ { color: 'black', fontSize: 12 } }
			tabStyle={ { padding: 0 } }
			indicatorStyle={ { backgroundColor: '#7DA89B' } }/>
	) }
	onIndexChange={ index => this.setState({
		tabView: {
			...this.state.tabView,
			index: index
		}
	}) }/>

We make use of onLayout to get the height of the wrapping View of each rendered component in a tab, save it in this.state, and pass it to TabView via style.

We added + SCREEN_HEIGHT * 0.08 due to the measurements coming a bit short ( might be because of the Tabs component height ) and everything works as expected.

We used onLayout={ event => console.log(event) } to get the values 522 and 487 but I believe those are different for every device or layout you have.

@Usamaliaquat123
Copy link

@saniagh that work but you shoud need to type event.nativeEvent.layout.height to get the actual value of View

<View onLayout={event => console.log(event.nativeEvent.layout.height)}> /* Something happen */ </View>

@amruu
Copy link

amruu commented Nov 9, 2019

I ended up removing the tabs content from the tab control altogether. It's hacky but worked for me...


  render() {
    const {index} = this.state;
    return (
      <ScrollView>
        <TabView
          renderPager={this._renderPager}
          renderScene={() => null}
          onIndexChange={index => this.setState({index})}
          initialLayout={{height: 0, width: Dimensions.get('window').width}}
        />
        {index === 0 && <Tab1Content />}
        {index === 1 && <Tab2Content />}
      </ScrollView>
    );
  }

This works for me, thanks.

@anuragdwivedi29
Copy link

anuragdwivedi29 commented Nov 19, 2019

@amruu but the swipe is not working when we follow your solution.Can you please give an insight about this?

@azhararmar
Copy link

@anuragdwivedi29 true that swipe gesture gets disabled, however you can implement custom gesture using this https://www.npmjs.com/package/react-native-swipe-gestures

@pragupnewzera
Copy link

Try something like this,
Fix the height of the parent widget to the maximum screen height you want that Tab Navigator to have.

<ListView or ScrollView>
 <View style={{height:  (width)/(0.8)}} >
    <Tab.Navigator tabBar={(props) => <TabBar {...props} />}>
      <Tab.Screen name="T1" component={T1} />
      <Tab.Screen name="T2" component={T2} />
      <Tab.Screen name="T3" component={T3} />
    </Tab.Navigator>
   </View>
</ ListView or ScrollView>

And for tabs do Something like this

T1 ->

<View style={styles.container}>
  <ScrollView nestedScrollEnabled={true}>
    <FlatList
      numColumns={3}
      data={allMedia}
      keyExtractor={(item) => item.id}
      listKey={(post) => `${post.id}D`}
      renderItem={({ item }) => (Anything)}
      scrollEnabled ={false}
    />
  </ScrollView>
</View>

Remember to disable the scroll view Inside the FlatList of tabs and wrap with a ScrollView with nestedScrollEnabled={true}

@bhargava-aman
Copy link

I found this useful in my case

https://www.gitmemory.com/issue/react-native-community/react-native-tab-view/290/552070362

it says to hide the inactive tab content so that max height of tabview will always be the active one's height

@azhararmar
Copy link

I found this useful in my case

https://www.gitmemory.com/issue/react-native-community/react-native-tab-view/290/552070362

it says to hide the inactive tab content so that max height of tabview will always be the active one's height

I had been using the same, unfortunately gesture does not work with this approach.

@VictorioMolina
Copy link

Any other way to solve other than bypassing the renderScene method or cleaning the content of each inactive scene?

@VictorioMolina
Copy link

I have written a solution that is based on the comment @satya164 said above: "Listen to layout changes in individual tabs and set tab view's height"

Here you can see it: https://stackoverflow.com/questions/63606479/react-native-tab-view-always-has-the-height-equal-to-height-of-the-highest-tab/63611264?noredirect=1#comment112488635_63611264

Maybe it can help someone in the future.

Pd: I have no idea how to do this with an inifinite scroll tab that uses pagination, but in my use case this works setting an undefined height to it.

@zhlee1997
Copy link

I have written a solution that is based on the comment @satya164 said above: "Listen to layout changes in individual tabs and set tab view's height"

Here you can see it: https://stackoverflow.com/questions/63606479/react-native-tab-view-always-has-the-height-equal-to-height-of-the-highest-tab/63611264?noredirect=1#comment112488635_63611264

Maybe it can help someone in the future.

Pd: I have no idea how to do this with an inifinite scroll tab that uses pagination, but in my use case this works setting an undefined height to it.

In my case, I have set the height for the tab which uses infinite scroll with pagination in TabView to undefined, but it fails.

Here is the error:
Error: Invariant Violation: [56745,"RCTView",{"height":"<<-Infinity>>"}] is not usable as a native method argument

@gilad7
Copy link

gilad7 commented Dec 17, 2020

in the solution @amruu provided I get that all my content is starting at the bottom for some reason. Anyone else experienced this issue?

@201724468
Copy link

Based on @dseawel solution I came with my own aswell. I was using SceneMap to render my components. I changed to my own switch case so I could send a isActive prop to tell the component whether it should render all its content or an empty View.
Example, i changed this SceneMap:

renderScene = SceneMap({
        details: DetailsTab,
        gallery: GalleryTab,
        ratings: RatingsTab
});

To this switch case showed in the reference example, but sending a isActive prop:

renderScene = ({ route, jumpTo }) => {
        switch (route.key) {
            case 'details':
                return <DetailsTab navigation={this.props.navigation} jumpTo={jumpTo} isActive={this.state.tabState.index === 0}/>;
            case 'albums':
                return <GalleryTab navigation={this.props.navigation} jumpTo={jumpTo} isActive={this.state.tabState.index === 1}/>;
            case 'ratings': 
                return <RatingsTab navigation={this.props.navigation} jumpTo={jumpTo} isActive={this.state.tabState.index === 2}/>;
            default:
                return <View/>;
        }
};

And on the component just put a simple validation:

 render() {
        return this.props.isActive ? (
            <View>
                {/* contents */}
             </View>
        ) : <View/>

this works for me. Thx for posting

@SiSa68
Copy link

SiSa68 commented Jun 10, 2021

I have fixed this with the preserve of lazy loading and swipe action
Here is what I did
In TabView I created two function

const [isSwiping, setIsSwiping] = React.useState(false);

const swipeStart = () => {
  setIsSwiping(true);
  onSwipeStart?.();
}

const swipeEnd = () => {
  setIsSwiping(false);
  onSwipeEnd?.();
}

To detect and save flag when user swipe between the tabs (there was two props onSwipeStart and onSwipeEnd which I used for this)
And then I send this flag (isSwiping) down to SceneView.
Inside SceneView I used this isSwiping and focused (which is already available inside render function) to check if this scene is currently invisible, then hide it:

<View
    //...other props
    style={[
      //...other styles
      focused || isSwiping? null: {height: 0},
    ]}
>

I created a PR for this #1191

@azhararmar
Copy link

What's your use case? I'm don't know what you're planning to do, but I'm pretty sure that dynamic height for tab view is not the best solution.
Regarding methods and values, there's nothing special, you measure the layout of components with onLayout and set height with style.
And whoever is downvoting the comment that I won't do it, I am building this library for free and I'm not required to do whatever you want me to do. Thanks.

madarjhat chup reh

Not cool to use abusive tone 😠. He developed the library for Free. It is his wish wether we like it or not.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.