React Native原生UI组件的宽度和高度

时间:2017-08-01 09:23:54

标签: react-native react-native-ios

我在iOS上创建本机UI组件,我希望其大小根据内容大小进行扩展。 看起来我必须设置固定的宽度和高度才能呈现视图。知道怎么解决吗?

// 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

3 个答案:

答案 0 :(得分:2)

首先,您应该像这样创建RCTShadowView的子类

#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

然后创建RCTViewManager的子类并返回shadowview和自定义类实例的视图

#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

,在我的代码中,我将自定义本机ui视图委托设置为RNGuessLikeModule,它是RCTViewManager的子类, 并且当rn模块的数据通过时,您可以在自定义视图中计算尺寸

@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)
    }
}

然后找到shadowview绑定到您的自定义ui视图并更新internalContentSize

最后调用[self.bridge.uiManager setNeedsLayout]

可以帮助您

答案 1 :(得分:1)

您需要覆盖xcode中的reactSetFrame以接收内容大小更改。

#import "React/UIView+React.h"

@implementation YourView {
    - (void)reactSetFrame:(CGRect)frame {
        [super reactSetFrame: frame];
        /* everytime content size changes, you will get its frame here. */
    }
}

答案 2 :(得分:-1)

好吧,无法找到'auto'之类的行为,但是,将组件设置为:

{{ width: '100%', height: '100%}}

根据父级扩展(缩小),这对我的用例来说已经足够了。设置'flex:1'不会产生同样的效果,这是一种遗憾。