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
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. */
}
}
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With