I've created a view with a variable and loaded it in the loadView method of a view controller. How do I pass a value to the view variable from the view controller after the loadView method has been called?
LocationView.m
#import "LocationView.h"
#implementation LocationView
#synthesize locationTitle;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
locationTitle = [[UILabel alloc]initWithFrame:CGRectMake(10, 10, 300, 20)];
[self addSubview:locationTitle];
}
return self;
}
LocationViewController.m
#import "LocationViewController.h"
#import "LocationView.h"
#interface LocationViewController ()
#end
#implementation LocationViewController
- (void)loadView
{
CGRect frame = [[UIScreen mainScreen] bounds];
LocationView *locationView = [[LocationView alloc] initWithFrame:frame];
[self setView:locationView];
}
- (void)viewDidLoad
{
[super viewDidLoad];
How do I pass a value to locationTitle here?
}
You've already set up the locationTitle property for LocationView objects, so you can just access that. The only thing you need to do beforehand is keep a reference to the LocationView object around in an instance variable so you can access it from any instance method.
#implementation LocationViewController {
LocationView *_locationView;
}
- (void)loadView
{
CGRect frame = [[UIScreen mainScreen] bounds];
_locationView = [[LocationView alloc] initWithFrame:frame];
[self setView:_locationView];
}
- (void)viewDidLoad
{
[super viewDidLoad];
_locationView.locationTitle = #"Test";
}
#end
Alternatively, since you are assigning the custom view to the view controllers main view, you could cast that:
- (void)viewDidLoad
{
[super viewDidLoad];
((LocationView *)self.view).locationTitle = #"Test";
}
Related
I'm trying to get my custom UIView to respond to width and height styles, e.g.
<MyCustomView style={{width: 200, height: 300}}/>
I've closely followed the simple example at https://github.com/brentvatne/react-native-dashed-border-example
Currently my View and ViewManager look like:
#import "MyCustomView.h"
#import "RCTViewManager.h"
#implementation MyCustomView
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Create the component view
}
return self;
}
#end
and the ViewManager:
#implementation MyCustomViewManager
RCT_EXPORT_MODULE();
- (UIView *)view
{
return [[MyCustomView alloc] initWithFrame: [[UIScreen mainScreen] bounds]];
}
#end
However this of course always fills the screen. I want to be able to set the height on this component, and ideally using the RN method of passing it via style.
If I follow the simple border example, I end up with
#implementation MyCustomView
- (instancetype)init
{
self = [super init];
if (self) {
// Create the component view
}
return self;
}
#end
and
#implementation MyCustomViewManager
RCT_EXPORT_MODULE();
- (UIView *)view
{
return [[MyCustomView alloc] init];
}
#end
But the view does not show, and reports self.bounds.size.height to be 0.00.
Is there something I've missed to get this working? Do I need to employ RCTBridge? I did previously have the line
#synthesize bridge = _bridge;
as in https://github.com/brentvatne/react-native-dashed-border-example/blob/master/BVDashedBorderViewManager.m#L9 but I got an error saying "Bridge not set"
You can override reactSetFrame in your view and you'll get access to the frame whenever it's updated by a React Native layout:
- (void)reactSetFrame:(CGRect)frame
{
CGFloat height = frame.size.height;
// Do something with the height here
[super reactSetFrame: frame];
}
You'll need to import the React category for UIView if you're not already for that file:
#import "UIView+React.h"
I have an UICollectionView which uses - (void)scrollViewDidScroll:(UIScrollView *)scrollViewto determine when the user has scrolled to the bottom.
The owner of my UICollectionView has a method which sends a command to a server and asks for more data.
What I want to do is to perform a selector on the owner (my viewcontroller) from my UICollectionView, and specifically from within scrollViewDidScroll.
I try to do it using the following code:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
CGFloat offsetY = scrollView.contentOffset.y;
CGFloat contentHeight = scrollView.contentSize.height;
if (offsetY > contentHeight - scrollView.frame.size.height)
{
[[self superView] performSelector:#selector(onScrolledToBottom) withObject:nil];
}
}
Note: the selector onScrolledToBottom is a SEL property of my UICollectionView.
The error I am getting says:
-[UIView onScrolledToBottom]: unrecognized selector sent to instance 0x7fc641e76e00
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UIView onScrolledToBottom]: unrecognized selector sent to instance 0x7fc641e76e00'
EDIT
I've stripped my code down to fit this question.
I have the followin in my ViewController.m
....
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self initCollectionView];
}
....
- (void) getMoreInfo{
NSLog(#"Getting more info");
}
- (void) initCollectionView{
UICollectionViewFlowLayout* flowLayout = [[UICollectionViewFlowLayout alloc]init];
flowLayout.itemSize = CGSizeMake(50.0, 60.0);
flowLayout.sectionInset = UIEdgeInsetsMake(20, 0, 20, 0);
[flowLayout setScrollDirection:UICollectionViewScrollDirectionVertical];
flowLayout.headerReferenceSize = CGSizeMake(320.f, 30.f);
myCollectionView *mVC = [myCollectionView alloc] init: self.view: flowLayout: #selector(getMoreInfo)];
}
And for my myCollectionView the .hand .mfiles look as following:
#import <UIKit/UIKit.h>
#interface myCollectionView : UICollectionView<UIScrollViewDelegate, UICollectionViewDelegate, UICollectionViewDataSource>{
UIView *parentView;
SEL onScrolledToBottom;
}
#property UIView *parentView;
#property SEL onScrolledToBottom;
- (id)init: (UIView*) parent: (UICollectionViewLayout *)layout: (SEL)onScrolledToBottomSEL;
#end
and
#import "myCollectionView.h"
#implementation myCollectionView
#synthesize parentView = parentView;
#synthesize onScrolledToBottom = onScrollToBotton;
- (id)init: (UIView*) parent: (UICollectionViewLayout *)layout: (SEL)onScrolledToBottomSEL{
self = [super initWithFrame: CGRectMake(0.0f, 0.0f, parent.frame.size.width, parent.frame.size.height) collectionViewLayout: layout];
if (self) {
parentView = parent;
self.delegate = self;
self.dataSource = self;
onScrolledToBottom = onScrolledToBottomSEL;
}
return self;
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
CGFloat offsetY = scrollView.contentOffset.y;
CGFloat contentHeight = scrollView.contentSize.height;
if (offsetY > contentHeight - scrollView.frame.size.height)
{
[parentView performSelector:#selector(onScrolledToBottom) withObject:nil];
}
}
You're getting the superview instead of the parent controller, that's one. Secondly, you shouldn't try to do it this way. Instead use a protocol and pass your controller as a delegate to your custom collectionview.
#protocol MyCollectionViewScrollProtocol <NSObject>
- (void)scrolledToBottom;
#end
Then in your ViewController implement this protocol and in your CollectionView
create a weak property delegate like this:
#property(weak, nonatomic) id<MyCollectionViewScrollProtocol> delegate;
Then you'll be able to call it
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
CGFloat offsetY = scrollView.contentOffset.y;
CGFloat contentHeight = scrollView.contentSize.height;
if (offsetY > contentHeight - scrollView.frame.size.height)
{
[self.delegate scrolledToBottom];
}
}
and don't forget to set the delegate property from the controller.
Edit by OP:
The controller should contain the following code in the .hfile:
#import <UIKit/UIKit.h>
#protocol ViewControllerProtocol <NSObject>
- (void) Pong;
#end
#interface ViewController : UIViewController <ViewControllerProtocol>
#end
and the following code somewhere in the .m file:
#import "ViewController.h"
#import UIKit;
#import "myUnit.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
myUnit mU = [[myUnit alloc] init];
mU.linkToController = self;
[mU Ping];
}
......
- (void) Pong{
NSLog(#"Pong");
}
......
The unit that wants to access the controller needs to have the following code in the .hfile:
#import <Foundation/Foundation.h>
#import "ViewController.h"
#interface myUnit : NSObject {
}
#property (weak, nonatomic) id <ViewControllerProtocol> linkToController;
- (void)Ping;
#end
And the unit that wants to access the controller needs to have the following code in the .mfile:
#import "myUnit.h"
#import "ViewController.h"
#implementation myUnit
- (void) Ping {
NSLog(#"Ping");
[self.linkToController Pong];
}
#end
The collectionView.parentView will be ViewController.view, which is totally a UIView, doesn't have a method called onScrolledToBottom.
Here is a approach if I right about what you want, but IT'S REALLY REALLY A BAD WAY to do this.
in ViewController.m
....
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self initCollectionView];
}
....
- (void) getMoreInfo{
NSLog(#"Getting more info");
}
- (void) initCollectionView{
UICollectionViewFlowLayout* flowLayout = [[UICollectionViewFlowLayout alloc]init];
flowLayout.itemSize = CGSizeMake(50.0, 60.0);
flowLayout.sectionInset = UIEdgeInsetsMake(20, 0, 20, 0);
[flowLayout setScrollDirection:UICollectionViewScrollDirectionVertical];
flowLayout.headerReferenceSize = CGSizeMake(320.f, 30.f);
myCollectionView *mVC = [myCollectionView alloc] initWithParentViewController:self layout:flowLayout selector:#selector(getMoreInfo)];
}
in myCollectionView.h
#import <UIKit/UIKit.h>
#interface myCollectionView : UICollectionView<UIScrollViewDelegate, UICollectionViewDelegate, UICollectionViewDataSource>{
UIViewController *_parentViewController;
SEL _onScrolledToBottom;
}
#property(weak) UIViewController *parentViewController;
#property SEL onScrolledToBottom;
- (id)initWithParentViewController:(UIView *)parentViewController layout:(UICollectionViewLayout *)layout selector:(SEL)onScrolledToBottomSEL;
#end
in myCollectionView.m
#import "myCollectionView.h"
#implementation myCollectionView
#synthesize parentViewController = _parentViewController;
#synthesize onScrolledToBottom = _onScrollToBotton;
- (id)initWithParentViewController:(UIView *)parentViewController layout:(UICollectionViewLayout *)layout selector:(SEL)onScrolledToBottomSEL{
self = [super initWithFrame: CGRectMake(0.0f, 0.0f, parent.frame.size.width, parent.frame.size.height) collectionViewLayout: layout];
if (self) {
_parentViewController = parentViewController;
self.delegate = self;
self.dataSource = self;
_onScrolledToBottom = onScrolledToBottomSEL;
}
return self;
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
CGFloat offsetY = scrollView.contentOffset.y;
CGFloat contentHeight = scrollView.contentSize.height;
if (offsetY > contentHeight - scrollView.frame.size.height)
{
[_parentViewController performSelector:#selector(onScrolledToBottom) withObject:nil];
}
}
WARNING:
In MVC there should be a Controller to implement
the protocol, not a view.
Class name's first character must be capitalized, you should use MyCollectionView instead of myCollectionView as a class name.
When use property, the LLVM compiler will automatically generate a member variable, no need to declare it again.
In this case use protocol-delegate is the best way.
Your "myCollectionView" should have a delegate (you will have to create a protocol for that) with some method along the line:
- (void)myCollectionViewDidScrollToBottom:(myCollectionView *)collectionView
By convention view will pass itself to this method (just like any tableView delegate methods). Then in the initCollectionView after myCollectionView *mVC = [myCollectionView alloc] init... you will have to set this delegate to the current view controller and implement that method.
mVC.delegate = self;
...
some other place in code
...
- (void)myCollectionViewDidScrollToBottom:(myCollectionView *)collectionView {
//do what's need to be done ;)
}
Now your "myCollectionView" does not care where it's just and by "who". Anyone who would like to use it can implement this method and be a delegate for that view with any custom logic. That is in this method you would call any required methods and any other things.
How this works:
View when scrolls down notifies it's delegate about this event
Delegate handles all the logic what's need to be done
:)
First I want to say thank you to all of you.
I read through everything you guys wrote and I was inspired.
Especially Stanislav Ageev's answer where he said
You're getting the superview instead of the parent controller...
.
I tried to pass the controller itself as a UIViewController instead of view of the controller.
And I also removed the #selector() from within the scrollViewDidScroll and passed the selector directly.
EDIT I missed zy.lius answer. Credit goes to him/her for posting an answer which basically explains what I've done here even though I figured this out on my own. Sorry.
My code now looks like this (.h/.m):
Controller
....
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self initCollectionView];
}
....
- (void) getMoreInfo{
NSLog(#"Getting more info");
}
- (void) initCollectionView{
UICollectionViewFlowLayout* flowLayout = [[UICollectionViewFlowLayout alloc]init];
flowLayout.itemSize = CGSizeMake(50.0, 60.0);
flowLayout.sectionInset = UIEdgeInsetsMake(20, 0, 20, 0);
[flowLayout setScrollDirection:UICollectionViewScrollDirectionVertical];
flowLayout.headerReferenceSize = CGSizeMake(320.f, 30.f);
myCollectionView *mVC = [myCollectionView alloc] init: self: flowLayout: #selector(getMoreInfo)];
}
myCollectionView.h file:
#import <UIKit/UIKit.h>
#interface myCollectionView : UICollectionView<UIScrollViewDelegate, UICollectionViewDelegate, UICollectionViewDataSource>{
UICollectionView *parentController;
SEL onScrolledToBottom;
}
#property UICollectionView *parentController;
#property SEL onScrolledToBottom;
- (id)init: (UICollectionView*) parent: (UICollectionViewLayout *)layout: (SEL)onScrolledToBottomSEL;
#end
and the .m file:
#import "myCollectionView.h"
#implementation myCollectionView
#synthesize parentController = parentController;
#synthesize onScrolledToBottom = onScrollToBotton;
- (id)init: (UIView*) parent: (UICollectionViewLayout *)layout: (SEL)onScrolledToBottomSEL{
self = [super initWithFrame: CGRectMake(0.0f, 0.0f, parent.view.frame.size.width, parent.view.frame.size.height) collectionViewLayout: layout];
if (self) {
parentController = parent;
self.delegate = self;
self.dataSource = self;
onScrolledToBottom = onScrolledToBottomSEL;
}
return self;
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
CGFloat offsetY = scrollView.contentOffset.y;
CGFloat contentHeight = scrollView.contentSize.height;
if (offsetY > contentHeight - scrollView.frame.size.height)
{
[parentController performSelector:#onScrolledToBottom withObject:nil];
}
}
This works perfectly fine and this could be used to communicate with the main controller in various ways.
And it is very simple.
Thanks again for inspiring me to find the most efficient solution.
I have tried modify my code many times, but still can't pass variable from UIViewController to UIView, variable always return (null).
// BPGraphView.h
#interface BPGraphView : UIView
#property (nonatomic, retain) NSString *test;
#end
// BPGraphView.m
#import "BPGraphView.h"
#implementation BPGraphView
#synthesize test;
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
NSLog(#"test %#",test);
if (self) {
// Initialization code
}
return self;
}
- (void)drawRect:(CGRect)rect
{
NSLog(#"draw %#", test); // always return (null)
if ([test isEqual:#"something"]) {
[self drawOutLine];
}
}
#end
// BloodPressureViewController.m
- (void)viewDidLoad
{
BPGraphView * graphview=[[BPGraphView alloc] init];
graphview.test = #"something";
}
In your code:
- (void)viewDidLoad
{
BPGraphView * graphview=[[BPGraphView alloc] init];
graphview.test = #"dd";
}
The variable graphview is basically destroyed once the viewDidLoad is run to finish. It will never get a chance to run drawRect.
Now the question is how should you define a instance variable of BPGraphView in your UIViewController. The easiest way should be adding a BPGraphView onto your view's xib file and link to an IBOutlet in your UIViewController. In this way, you should be able to assign to test
#IBOutlet BPGraphView graphview;
- (void)viewDidLoad
{
graphview.test = #"dd";
}
Without setting a frame, drawRect will not be called.
BPGraphView * graphview=[[BPGraphView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
graphview.test = #"something";
[self.view addSubview:graphview];
I tried this,it works well,in your code,drawRect is not called
BPGraphView * graphview=[[BPGraphView alloc] init];
graphview.test = #"dd";
[graphview drawRect:CGRectMake(0, 0, 0, 0)];
I can't intercept the init function that's getting called when it's getting created inside of the xib file.
I want to add borderline to it when it gets created so that I won't need to add it manually.
.h:
#import <UIKit/UIKit.h>
#interface UITextView (FITAddBorderline)
- (id) init;
- (id) initWithFrame:(CGRect)frame;
#end
.m:
#import "UITextView+FITAddBorderline.h"
#implementation UITextView (FITAddBorderline)
- (id) init
{
self = [super init];
if (self) {
[self addBorderline];
}
return self;
}
- (id) initWithFrame:(CGRect)frame {
self = [super init];
if (self) {
self.frame = frame;
[self addBorderline];
}
return self;
}
- (void) addBorderline {
//To make the border look very close to a UITextField
[self.layer setBorderColor:[[[UIColor grayColor] colorWithAlphaComponent:0.5] CGColor]];
[self.layer setBorderWidth:2.0];
//The rounded corner part, where you specify your view's corner radius:
self.layer.cornerRadius = 5;
self.clipsToBounds = YES;
}
#end
Views that come from NIBs are initialized with initWithCoder:
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self)
{
[self addBorderline];
}
return self;
}
As a side note, I would recommend changing what you are doing and use a subclass instead of a category. You can get yourself into some trouble overriding methods in a category. See more info here.
You just need to implement the awakeFromNib method:
-(void)awakeFromNib
{
[super awakeFromNib];
[self addBorderline];
}
I have a view with a property that should take an NSString from my view controller and display an image. If I hard code locationImageFile with "320x213-1.png" in my view, the image displays properly, but if I try to set locationImageFile from my view controller, the image doesn't appear. I feel like I'm missing something pretty basic. Side note: I seem to be able to set the locationTitle property from my view controller without any problems.
LocationViewController.m
#import "LocationViewController.h"
#import "LocationView.h"
#implementation LocationViewController
{
LocationView *_locationView;
}
- (void)loadView
{
CGRect frame = [[UIScreen mainScreen] bounds];
_locationView = [[LocationView alloc] initWithFrame:frame];
[self setView:_locationView];
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
_locationView.locationTitle.text = aLocation.name;
_locationView.locationImageFile = #"320x213-1.png";
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
LocationView.m
#import "LocationView.h"
#implementation LocationView
#synthesize locationTitle;
#synthesize locationImageFile;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
[self setBackgroundColor: [UIColor blueColor]];
// Image container
UIImage *locationImage = [UIImage imageNamed:locationImageFile];
UIImageView *locationImageContainer = [[UIImageView alloc] initWithImage:locationImage];
[locationImageContainer setTranslatesAutoresizingMaskIntoConstraints:NO];
locationImageContainer.backgroundColor = [UIColor yellowColor];
[self addSubview:locationImageContainer];
// Text line
locationTitle = [[UILabel alloc]init];
[locationTitle setTranslatesAutoresizingMaskIntoConstraints:NO];
locationTitle.backgroundColor = [UIColor whiteColor];
[self addSubview:locationTitle];
}
return self;
}
#end
I think this is because the loadView method are invoke earlier than viewDidLoad method.
so when you set _locationView.locationImageFile = #"320x213-1.png";,the containerView's image are not being set.
You can consider declare locationImageContainer as a property of locationView , and set it's image directly.like
_locationView.locationImageContainer.image = [UIImage imageNamed:#"320x213-1.png"];
instead of setting locationImageFile as a property.
When LocationView is initialized, the value of the instance variable locationImageFile equals nil. Override the setter of the locationImageFile property inside LocationView.m like this:
- (void)setLocationImageFile:(NSString *)aLocationImageFile
{
locationImageFile = aLocationImageFile;
locationImageContainer.image = [UIImage imageNamed:aLocationImageFile];
}
You can try, doing it like this:
_locationView.locationImageFile = [UIImage imageNamed:locationImageFile];
if you did it like this:
_locationView.locationImageFile = locationImageFile;