Variable not passed to UIView - ios

I am trying to pass a variable from within my main UIViewController to a UIView.
CGRect frame = CGRectMake(0,0,347, 308);
XBMCPopUpView * xbmcpop = [[XBMCPopUpView alloc]initWithFrame:frame];
xbmcpop.DevIP = #"2222";
[self.view addSubview:xbmcpop];
the Object xbmcpop gets created and its string property "DevIP" gets assigned correctly. But when the UIVIEW is actually viewed it seems like the instance that is viewed is different from the one I created and the variable #"2222" is not passed to the string property "DevIP"
Note: My Main UIViewController is in a storyboard with a navigation controller
If you are interested to see the "XBMCPopUpView class, here it is:
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#class XBMCPopUpView;
#interface XBMCPopUpView : UIView
{
NSString * DevIP;
}
#property (strong,nonatomic) NSString *DevIP;
- (id)initWithFrame:(CGRect)frame;
-(IBAction)sendPlay:(id)sender;
#end

Under
#interface XBMCPopUpView : UIView
remove
{
NSString * DevIP;
}

Since you're using a storyboard, you need to use your view controller's awakeFromNib method for this.
When you add the awakeFromNib method, don't forget to call [super awakeFromNib] there.
- (void)awakeFromNib
{
[super awakeFromNib];
CGRect frame = CGRectMake(0,0,347, 308);
XBMCPopUpView * xbmcpop = [[XBMCPopUpView alloc]initWithFrame:frame];
xbmcpop.DevIP = #"2222";
[self.view addSubview:xbmcpop];
}

Related

IBOutlets in subview are nil

I have a UIViewController,which is associated with custom class MAViewControllerMenu and loads right after the splash screen. In that UIViewController, I have an UIScrollView, which belongs to another class, MASlideShowView, in which the IBOutlet of the UIScrollView is defined and is connected to.
The class for the UIViewController has, among others, the field:
#property MASlideShowView* slideShow;
as a private property for the class that holds the UIScrollView inside it.
Also in the UIViewController,
- (void)viewDidLoad
{
[super viewDidLoad];
//TODO [_slideShow initializeImages];
_slideShow = [[MASlideShowView alloc] initWithModel];
_slideShow.delegate = imageViewController;
}
- (void)viewDidAppear{
[super viewDidAppear:(YES)];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
// Set up the content size of the scroll view
//HERE, self.slideShow is allocated, but all the fields it has, including the IBOutlet to the UIScrollView is still nil
CGSize pagesScrollViewSize = self.slideShow.frame.size;
_slideShow.contentSize = CGSizeMake(pagesScrollViewSize.width * self.pageViews.count, pagesScrollViewSize.height);
//Delegate
_slideShow.scrollView.delegate = self;
// Load the initial set of pages that are on screen
[_slideShow loadVisiblePages:YES page_index:0 image:_last_image_taken];
}
Note the error I saw in the comments in the above class
The MASlideShowView file looks like:
h:
#class MASlideShowView;
#protocol slideShowDelegate <NSObject>
-(void)imageViewSelected:(MASlideShowView*)slideShow image:(UIImage*)image;
#end
#interface MASlideShowView : UIScrollView
#property (nonatomic,weak) id<slideShowDelegate> delegate;//delegate to next controller to notify upon picture centered
#property (nonatomic, strong) IBOutlet UIScrollView *scrollView;
#property (nonatomic, strong) IBOutlet UIPageControl *pageControl;
#property (weak, nonatomic) IBOutlet UIButton *rotateImageButton;
#property UIImageView* centered_image_view;
- (IBAction)PageThroughPageControl;
- (IBAction)rotateImageButtonClicked;
- (id)initWithModel;
- (void)pageThroughPageControl;
- (void)addImageToSlideshow:(UIImage*)toAdd;
- (void)loadVisiblePages:(BOOL)use_page_number page_index:(NSInteger)page image:(UIImage*)image;
#end
m:
- (id)initWithModel{
[self initializeImages];
return self;
}
-(void)initializeImages{
// Set up the image you want to scroll & zoom and add it to the scroll view
self.pageViews = [NSMutableArray arrayWithObjects:nil];
NSInteger pageCount = 0;
_imageViewCount = 0;
// Set up the page control
self.pageControl.currentPage = 0;
self.pageControl.numberOfPages = pageCount;
// Set up the array to hold the views for each page
self.pageViews = [[NSMutableArray alloc] init];
for (NSInteger i = 0; i < pageCount; ++i) {
[self.pageViews addObject:[NSNull null]];
}
}
My question is simple:
How can I make the UIScrollView initialize?
I know that there's no viewDidAppear as it inherits from UIScrollView.
Thanks
As you are using Interface Builder, I would recommend calling initializeImages inside awakeFromNib:
An awakeFromNib message is sent to each object loaded from the
archive, but only if it can respond to the message, and only after all
the objects in the archive have been loaded and initialized. When an
object receives an awakeFromNib message, it is guaranteed to have all
its outlet instance variables set.
More details here.
Other observations:
As for your code, you have slideShow correctly set by Interface Builder when entering viewDidLoad but you're replacing that instance by assigning _slideShow = [[MASlideShowView alloc] initWithModel], which results in a completely different object.
Moreover your initWithModel doesn't look at all like a correct init method as it doesn't call any of its super's init methods. You should start with Apple's snippet by writing init in an empty line and press escape:
Again the first paragraph of the answer should be enough for your problem.
There's a few ways you could go about fixing this.
One way is like #HoanNguyen mentioned to use awakeFromNib. Personally I don't use this but it's a valid lifecycle event for setup.
Another option is to override initWithCoder: which is the standard initializer storyboards use
- (id)initWithCoder:(NSCoder *)aDecoder{
self = [super initWithCoder:aDecoder];
if (self) {
[self initializeImages];
}
return self;
}
You could then remove your initWithModel call and the storyboard should handle everything.

Custom UIView variable / property Access

I've managed to setup a custom UIView class with a nib.
My .h looks like
#interface MyView : UIView <UITextFieldDelegate>
#property (nonatomic, weak) IBOutlet UITextField *textField;
#property (nonatomic, strong) MyView *topView;
And .m
#implementation MyView
NSString *_detail;
-(id)initWithCoder:(NSCoder *)aDecoder{
if ((self = [super initWithCoder:aDecoder])&&self.subviews.count==0){
MyView *v = [[[NSBundle mainBundle] loadNibNamed:#"MyView" owner:self options:nil] objectAtIndex:0];
self.textField = v.textField;
if (self.topView == nil)self.topView = self;
v.topView = self.topView;
[self addSubview:v];
}
return self;
}
-(NSString *)topDetail{
return _detail;
}
-(NSString *)detail{
return [self.topView topDetail];
}
-(void)setTopDetail:(NSString *)detail{
_detail = detail;
}
-(void)setDetail:(NSString *)detail{
[self.topView setTopDetail:detail];
}
- (BOOL)textFieldShouldReturn{
//here I show an UIAlertView using self.detail for the message
}
Note: The setup I have works exactly how I want it to.
The problem
What I would like to do is remove my manual detail methods and turn NSString *_detail into #property (...)NSString *detail
When I try it with the #property, then within my ViewController if i call
myView.detail = someString, myView will be referring to the top most view. Then if textFieldShouldReturn gets called because of user interaction, then it calls the nested MyViews _detail which has not been set.
What I want:
To not have to write extra code for access to _detail regardless of where I'm accessing it from. I want to merely declare the property and go on with my usual coding.
Your problem is that you're trying to keep the a class reference, topView, with an object property.
In other words every objects' topView is the object itself, which makes no sense.
Your definition should be:
#interface MyView : UIView <UITextFieldDelegate>
// Class "properties"
+ (instancetype)topview;
+ (void)setTopView:(UIView *)topView;
// Object properties
#property (nonatomic, weak) IBOutlet UITextField *textField;
#property (nonatomic, strong) NSString *detail;
Now you can keep track of the topView:
static MyView * _topView;
#implementation MyView
+ (instancetype)topView {return _topView}; // You could also create one here lazily
+ (void)setTopView:(UIView *)topView { _topView = topView };
-(id)initWithCoder:(NSCoder *)aDecoder{
if ((self = [super initWithCoder:aDecoder])&&self.subviews.count==0){
JUITextFieldHint *v = [[[NSBundle mainBundle] loadNibNamed:#"JUITextFieldHint" owner:self options:nil] objectAtIndex:0];
self.textField = v.textField;
if ([MyView topView] == nil)[MyView setTopView:self];
v.topView = self.topView;
[self addSubview:v];
}
return self;
}
No more need for manual setters and getters. Now you can use your detail property, either with anyInstance.detail or [MyView topView].detail, or even MyView.topView.detail if you like dots like me ;)
You're init method still looks weird but should work. Check Apples init template.
Lastly, textField can be weak as long as it has a superview, otherwise make it strong.
My xib contained one UIView (no controller). I had the UIView set to MyView for the class.
I changed the UIView back to just UIView then set File's Owner to MyView. This solved issues of recursion (which is why I had such a weird setup in the first place) and caused my variables and IBOutlets to be linked up properly.
Credit goes to How do I create a custom iOS view class and instantiate multiple copies of it (in IB)? and some of the comments which I missed the first couple times I read through it.

Is it okay to store a casted reference (to my subclass of UIView) in my UIViewController?

Are there any dangers in storing a casted reference to MyView in my subclass of UIViewController? I am trying to avoid casting self.view every time I want to use an instance variable/method because it looks ugly. For example, let's avoid:
[((MyView *)self.view) instancedView]
castedView is defined at the same time as self.view
castedView.instancedView is a subview of castedView stored as an instance variable (a UIButton that I want to attach a tap event listener to, for example)
MyViewController.h
#interface MyViewController : UIViewController
#property MyView *castedView;
#end
MyViewController.m
#implementation MyViewController
#synthesize castedView;
- (void)viewDidLoad {
[super viewDidLoad];
castedView = [[MyView alloc] init];
self.view = castedView;
// ... create gestureRecognizer named "onTap" that calls "whenTapped" ...
[castedView.instancedView addGestureRecognizer:onTap];
}
- (IBAction)whenTapped {
castedView.instancedView.backgroundColor = [UIColor whiteColor];
}
#end
Any help is appreciated.

ViewController is not updating UIView with custom class

I'm not sure if I have my view controller in my storyboard file connected to my view correctly but here's a breakdown of the setup - I'm using xcode 5:
One view controller using custom class CICBoostGaugeViewController (configured in identity inspector).
One view using custom class CICGaugeBuilder (configured in identity inspector in storyboard file).
The CICGaugeBuilder class draws a gauge and sets the properties of the gauge in an init method. When I run this setup in the simulator the gauge appears correctly based on the initialized gauge parameters in CICGaugeBuilder. No problems so far.
My issue is when I create an instance of the CICGaugeBuilder class in my view controller and set the properties, they do not show up when I run it in the sim, only the values initialized in the CICGaugeBuilder class are applied.
CICBoostGaugeViewController.h
#import <UIKit/UIKit.h>
#import "CICGaugeBuilder.h"
#class CICGaugeBuilder;
#interface CICBoostGaugeViewController : UIViewController{
CICGaugeBuilder *boostGauge;
}
#property (nonatomic, retain) IBOutlet CICGaugeBuilder *boostGauge;
#end
CICBoostGaugeViewController.m
#import "CICBoostGaugeViewController.h"
#import "CICGaugeBuilder.h"
#interface CICBoostGaugeViewController ()
#end
#implementation CICBoostGaugeViewController
#synthesize boostGauge;
- (void)viewDidLoad
{
[super viewDidLoad];
self.boostGauge.minGaugeNumber = 0;
self.boostGauge.maxGaugeNumber = 25;
self.boostGauge.gaugeLabel = #"Boost/Vac";
self.boostGauge.incrementPerLargeTick = 10;
self.boostGauge.tickStartAngleDegrees = 135;
self.boostGauge.tickDistance = 270;
self.boostGauge.menuItemsFont = [UIFont fontWithName:#"Helvetica" size:14];
self.boostGauge.value = 10;
}
- (void)viewDidUnload
{
//self.boostGauge = nil;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
This is the initialization method in the CICGaugeBuilder class:
self.minGaugeNumber = 0;
self.maxGaugeNumber = 100;
self.gaugeType = 2;
self.gaugeLabel = #"Boost/Vac";
self.incrementPerLargeTick = 10;
self.tickStartAngleDegrees = 135;
self.tickDistance = 270;
self.menuItemsFont = [UIFont fontWithName:#"Helvetica" size:14];
Note that the maxGaugeNumber of 100 is retained when run in the simulator. The gauge draws correctly, but the problem is the object instance in the view controller is not used. I've tried [self.boostGauge SetNeedsDisplay] and it did not work.
In my storyboard file the view controller does not have any referencing outlets. I think this needs to be connected to the app delegate but I'm not sure. Dragging the view controller to the app delegate does not allow a connection to be made.
Here is the CICAppDelegate.h file:
#import <UIKit/UIKit.h>
#class CICBoostGaugeViewController;
#interface CICAppDelegate : UIResponder <UIApplicationDelegate>{
UIWindow *window;
CICBoostGaugeViewController *viewController;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet CICBoostGaugeViewController *viewController;
#end
As I go through you App,I found.
After calling [self.boostGauge SetNeedsDisplay] will not make any kind of effect because:
SetNeedsDisplay calls the - (void)drawRect:(CGRect)rect method of CICGaugeBuilder Class
and
- (void)drawRect:(CGRect)rect
{
CGRect innerFrame;
// self.value = 0;
[self initializeGauge];
}
here you again calling initialize method and initialise method will again set value to initial value.
ULTIMATELY NO EFFECT.
Most importantly you call value using
CICAppDelegate *ap=(CICAppDelegate *)[[UIApplication sharedApplication]delegate];
ap.guage.value=50;
Take a object reference of CICGaugeBuilder Class in appDel.
Add following to initialiseGuageMethod
CICAppDelegate *ap=(CICAppDelegate *)[[UIApplication sharedApplication]delegate];
ap.guage=self;

How can we access values of an array added in a viewcontroller class, inside a different UIView class..?

viewcontroller.m has the following code
- (void)viewDidLoad
{
[super viewDidLoad];
self.array=[[NSArray alloc]initWithObjects:#"hi",#"hello", nil];
NSLog(#"%#",self.array);
view *view1=[[view alloc]init];
[view1 addSubview:self.view];
view1.viewController=self;
}
and there is another UIView class where I am trying to access the array :
the .h file :
#import <UIKit/UIKit.h>
#import "ViewController.h"
#class ViewController;
#interface view : UIView{
ViewController *viewController;
}
#property (nonatomic,retain)ViewController *viewController;
#end
and the .m file :
#import "view.h"
#import "ViewController.h"
#implementation view
#synthesize viewController;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
NSLog(#"%#",[viewController array]);
}
return self;
}
I checked in other posts of stackoverflow, and the passing of values was mentioned only between viewcontrollers; or the array was declared in the appdelegate and used in the classes(which I want to avoid).
The NSLog in the last code segment above gives null; so can you please help out in accessing the values of this array.
Thanks in advance..!!
You can achieve using this code in your ViewController
#import "view.h"
- (void)viewDidLoad
{
[super viewDidLoad];
NSArray *ary = [NSArray arrayWithObjects:#"7",#"5",#"3",#"2", nil];
view *v=[[view alloc] init];
[v initView:ary];
}
And in your view.h file :
#import <UIKit/UIKit.h>
#interface view : UIView
-(void)initView:(NSArray *)ary;
#end
And in your .m file :
#import "view.h"
#import "ViewController.h"
#implementation view
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
}
return self;
}
-(void)initView:(NSArray *)ary
{
NSLog(#"%#",ary);
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
// Drawing code
}
*/
#end
Log value will display this :
2013-02-20 20:11:52.731 SampleProject[9414:f803] (
7,
5,
3,
2
)
This line
view *view1=[[view alloc]init];
calls the desired initializer initWithFrame: before you set view1.viewController, so what's happening is that
NSLog(#"%#",[viewController array]);
actually calls
NSLog(#"%#",[null array]);
or (note that is pseudocode)
NSLog(#"%#",null);
What you'll want to do is to use view1.viewController after it is assigned. The best practice would be to make a custom constructor taking UIViewController* as a parameter and use it.
First thing you are calling the init method on the view and checking for viewController in the initWithFrame method which is never called. (But maybe you are calling the initWithFrame: from inside your init method with a default frame. :) ). Second, you are setting the viewcontroller property after you have called the init method, so your viewcontroller is still uninitialized in your initWithFrame method.
Third, instead of passing the whole of viewcontroller to your view to access the array (which kind of goes against MVC pattern), you could probably use just create an instance variable in your UIView subclass and pass just the array.
Then you could follow the answer given by Dilip, preferably using the setter method for setting the array. IMO.

Resources