Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React Native native UI component width and height

I'm creating a native UI component on iOS and I want its size to expand according to the content size. It seems like I must set a fixed width and height in order for the view to be rendered. Any idea how to solve it?

// JS

import React from 'react';
import { View, requireNativeComponent } from 'react-native';

class StyledText extends React.Component {
render() {
    return (
        <View style={this.props.style}>
// without the height and width the compnent won't show up
            <StyledLabelReactBridge styledText={'some text'} style={{height: 100, width: 100, backgroundColor: 'red'}}/>
        </View>
    );
}
}

StyledText.propTypes = {
styledText: React.PropTypes.string,
style: View.propTypes.style
};

const StyledLabelReactBridge = requireNativeComponent('StyledLabelReactBridge', StyledText);

module.exports = StyledText;

// objective-C

@implementation StyledLabelReactBridgeManager

RCT_EXPORT_MODULE()

- (UIView *)view
{
return [[NewStyledLabel alloc] init];
}

RCT_CUSTOM_VIEW_PROPERTY(styledText, NSString, NewStyledLabel)
{
if (![json isKindOfClass:[NSString class]])
     return;

[view setStyledText:[NewStyledText textFromXHTML:json]];
}

@end
like image 876
Rom Shiri Avatar asked Oct 14 '25 14:10

Rom Shiri


2 Answers

You need to override reactSetFrame in xcode to receive content size change.

#import "React/UIView+React.h"

@implementation YourView {
    - (void)reactSetFrame:(CGRect)frame {
        [super reactSetFrame: frame];
        /* everytime content size changes, you will get its frame here. */
    }
}
like image 155
Val Avatar answered Oct 17 '25 13:10

Val


first you should create a subclass of RCTShadowView like

#import <React/RCTShadowView.h>

@interface RNGuessLikeContainerShadowView : RCTShadowView

@end

@implementation RNGuessLikeContainerShadowView

- (void)setLocalData:(NSObject *)localData {
    if ([localData isKindOfClass:[NSNumber class]]) {
        [self setIntrinsicContentSize:CGSizeMake(UIScreen.mainScreen.bounds.size.width, ((NSNumber *)localData).floatValue)];
    }
}

@end

then create subclass of RCTViewManager and return shadowview and view of you custom class instance

#import <React/RCTUIManager.h>
#import <React/RCTUIManagerUtils.h>

@interface RNGuessLikeModule: RCTViewManager <RNGuessLikeContainerViewHeightUpdater>

@end

@implementation RNGuessLikeModule

RCT_EXPORT_MODULE(RNGuessLikeModule)

RCT_EXPORT_VIEW_PROPERTY(objects, NSString);

- (UIView *)view {
    RNGuessLikeContainerView *_view = [RNGuessLikeContainerView new];
    _view.delegate = self;
    return _view;
}

- (RCTShadowView *)shadowView {
    return [RNGuessLikeContainerShadowView new];
}

- (void)didUpdateWithHeight:(CGFloat)height view:(RNGuessLikeContainerView *)view {
    RCTExecuteOnUIManagerQueue(^{
        RCTShadowView *shadowView = [self.bridge.uiManager shadowViewForReactTag:view.reactTag];
        [shadowView setLocalData:@(height)];
        [self.bridge.uiManager setNeedsLayout];
    });
}

@end

and in mine code i set custom native ui view delegate to RNGuessLikeModule which is subclass of RCTViewManager, and you can caculate size in you custom view when data from rn module passed

@objc
public protocol RNGuessLikeContainerViewHeightUpdater {
    func didUpdate(height: CGFloat, view: RNGuessLikeContainerView)
}

public final class RNGuessLikeContainerView: UIView, GuessLikeItemsComponentContainer {
    
    @objc
    public var objects: String? {
        didSet {
            if let _objects = objects,
                let _data = _objects.data(using: .utf8, allowLossyConversion: true) {
                reload(objects: _data)
            }
        }
    }
    
    @objc
    public weak var delegate: RNGuessLikeContainerViewHeightUpdater?
    
    public var controller: UIViewController {
        return reactViewController()
    }
    public var guessLikeSceneType: GuessLikeSceneType = .邀好友赚现金红包
    public var guessLikeTitle: String?
    public var guessLikeItems: [GuessLikeItemsSectionSubItem] = []
    public var routerInfo: String?
    @objc
    public private(set) var guessLikeHeight: CGFloat = 0
    
    lazy var backend = GuessLikeItemsComponentContainerBackend(parent: self)
    
    public lazy var guessLikeContainer: UICollectionView = {
        let _container = createGuessLikeContainer()
        _container.dataSource = backend
        _container.delegate = backend
        addSubview(_container)
        return _container
    }()
    
    override public func layoutSubviews() {
        super.layoutSubviews()
        guessLikeContainer.frame = bounds
    }
    
    public func reload(objects: Data) {
        precondition(pthread_main_np() != 0, "RNGuessLikeContainerView reload method should be called on main thread")
        do {
            let _items = try JSONDecoder().decode([ItemListModel].self, from: objects)
            guessLikeItems = GuessLikeItemsSectionItem(list: _items).items
            guessLikeContainer.reloadData()
            updateHeight()
        } catch {
            debugPrint(error)
        }
    }
    
    public func append(objects: Data) {
        precondition(pthread_main_np() != 0, "RNGuessLikeContainerView append method should be called on main thread")
        if let _list = try? JSONDecoder().decode([ItemListModel].self, from: objects) {
            let _items = GuessLikeItemsSectionItem(list: _list).items
            guessLikeItems.append(contentsOf: _items)
            guessLikeContainer.reloadData()
            updateHeight()
        }
    }
    
    func updateHeight() {
        if guessLikeItems.isEmpty {
            guessLikeHeight = 0
        } else {
            var leftHeight: CGFloat = 0
            var rightHeight: CGFloat = 0
            for (index, item) in guessLikeItems.enumerated() {
                if index % 2 == 0 {
                    leftHeight += item.height + 10.0
                } else {
                    rightHeight += item.height + 10.0
                }
            }
            
            let sectionHeaderHeight: CGFloat = 50.0
            guessLikeHeight = max(leftHeight, rightHeight) + sectionHeaderHeight
        }
        if let _delegate = delegate {
            _delegate.didUpdate(height: guessLikeHeight, view: self)
        }
    }
    
    public override var intrinsicContentSize: CGSize {
        return CGSize(width: UIScreen.main.bounds.width, height: guessLikeHeight)
    }
}

then find shadowview binded to your custom ui view and update intrinsicContentSize

finaly call [self.bridge.uiManager setNeedsLayout]

may help you

like image 39
hasayakey Avatar answered Oct 17 '25 13:10

hasayakey



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!