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

3
我正在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
4个回答

8
您需要在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. */
    }
}

4
首先,您需要创建一个 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和view。
#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)
    }
}

然后找到绑定到您自定义UI视图的shadowview,并更新intrinsicContentSize。

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

可能会对您有所帮助。


1
这很适合作为使用原始视图的“intrinsicContentSize”的解决方案!谢谢! - phsource
请注意,由于所有子视图共享相同的“delegate”,因此仅当您一次实例化此RNGuessLikeContainerView的单个实例时才起作用。我将致力于解决适用于多个视图的方案。 - phsource

0
@implementation SNBFundCardViewManager

RCT_EXPORT_MODULE(FundCard)

- (UIView *)view
{
    return [[SNBFundHomeFundCardCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@""];
}

- (dispatch_queue_t)methodQueue
{
    return dispatch_get_main_queue();
}

RCT_CUSTOM_VIEW_PROPERTY(data, NSDictionary, SNBFundHomeFundCardCell)
{
    view.rnData = json;
    // 自己撑起高度
    CGFloat height = [view.vm getCellHeight];
    [self.bridge.uiManager setIntrinsicContentSize:CGSizeMake(UIViewNoIntrinsicMetric, height) forView:view];
}

目前您的回答不清楚。请[编辑]以添加更多细节,帮助他人了解这如何解决所提出的问题。您可以在帮助中心找到有关如何编写好答案的更多信息。 - Community

-3

很抱歉没有找到类似于“自动”行为的功能,但是可以将组件设置为:

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

根据父元素进行扩展(和收缩),这对我的用例来说已经足够好了。遗憾的是,设置“flex: 1”没有同样的效果。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接