I have a project in Xcode 5 that I'm migrating to Xcode 7. I have an array of custom UIViewControllers that come with XIBs. I take each object in the array and pass it through, like this
- (void) createViewForObject:(NSObject *)object
{
if ([object isKindOfClass:[CustomViewController class]])
{
CustomViewController *cvc = (CustomViewController*)object;
//If I create a view controller from within the function,
//it successfully adds, as demonstrated below.
//But I'm not able to add the view of the object
//that was passed in from outside
OtherCustomViewController *otherCvc = //initialization here
UITableViewCell* cell= [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"CustomViewControllerCell"];
[cvc.view frameResizeToHeight:[gtf getHeight]];
[cvc.view frameMoveToPosition:CGPointMake(340, 0)];
cell.frame=cvc.view.frame;
otherCvc.view.frame = cvc.view.frame;
[cell frameResizeToHeight:MAX(MAX(50, textSize.height+10),cell.frame.size.height)+11]; //just a resizing helper method
//This is code that successfully adds the other custom view controller that was created within this method to the cell, but I want to use the other custom view controller (the one that was passed in from outside) so I can keep references to certain properties.
// [cell addSubview:otherCvc.view];
// [self addChildViewController:otherCvc];
// [otherCvc didMoveToParentViewController:self];
//This is the code that doesn't work that I want to work
[self addChildViewController:cvc];
cvc.view.frame = cell.frame;
[cell.contentView addSubview:cvc.view];
[cvc didMoveToParentViewController:self];
//I'm also able to add the custom view that was passed in from outside directly to the main view with the code below, so the views do exist.
//They're just not getting added to the cell.
//[self.view addSubview:cvc.view];
}
}
I'm able to access all the properties of this custom view controller, but it won't let me add the view as a subview to the cell, even though it did allow this in Xcode 5. (the Xcode 5 was post ARC, BTW). What's also interesting is that I'm able to add the view controller subviews to the main view (to self.view)
Any ideas on what I can do?
Related
I will start off by describing what I'm trying to accomplish and then follow by describing what I've tried already. I am pretty sure what I've tried is NOT the best approach so please correct my approach as needed!
I have a ViewController.m and a Custom View which is laid out in CustomView.xib. The custom view has UIButtons and UILabels which are populated from an Array of Custom Objects.
The user flow should go as such: ViewController starts off showing CustomView with its labels populated by
CustomObjectArray[0] -> User presses button -> Another "copy" of CustomView slides into the view, over the previous version.
It's labels and buttons are populated by CustomObjectArray[1] -> User presses button -> repeat until end of Array.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
So far I made a ViewController.m/h, a CustomView.m/h AND a CustomView.xib file. I used interface builder to do the layout.
On the "Custom Class" tab for the top-level View in XIB file, I type in "CustomView". I also drag IBOutlets from the XIB file to the CustomView HEADER (.h) file.
In the ViewController, under -(instancetype) init method, I create a custom view using the normal initWithNib method. And then I do:
self.view = CustomViewVariableName;
When I run the program, the view show's up fine. However, when I try to selector's, nothing's getting recognized by the buttons:
[currentCustomView.continueButton addTarget:self action:#selector(continueButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
What did I do wrong here?
More importantly, given my described goals up top, am I even doing this right? Do I need to have CustomView.m/h files? Or can I do the same thing with ONLY the XIB and the ViewController file. Remember that I need to have "multiple copies" and slide them on top of each other until the end of my custom objects array.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Some more code as requested.
In ViewController:
- (instancetype)init
{
currentCustomView = [[[NSBundle mainBundle] loadNibNamed:#"CustomView" owner:self options:nil] objectAtIndex:0];
self.view = currentQuizQuestionView;
.......
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
[currentCustomView.continueButton addTarget:self action:#selector(continuePressed:) forControlEvents:UIControlEventTouchUpInside];
}
-(void)continuePressed:(id)sender{
NSLog(#"Current position");
//[self moveInQuestion];
}
In CustomView.h:
#import <UIKit/UIKit.h>
#interface CustomView : UIView
#property (weak, nonatomic) IBOutlet UIButton *continueButton;
#end
In CustomView.m:
this is just the default page, I added nothing in this file.
CustomView.xib:
I'm not sure if I fully understand your requirements. Let's assume you would like to have several CustomViews as subviews of your ViewController and those subviews can be display each by each after pressing its own button.
First thing about adding subview:
self.view = CustomViewVariableName;
Since CustomsViews will be subviews according to my assumption, above line is an error. You probably would like to have something like this.
for (NSInteger i = 0; i < 5; i++) {
CustomView *v = [[CustomView alloc] initWithNib];
[v.button addTarget:self
action:#selector(continuePressed:)
forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:v];
}
Second, I noticed you would like to have an array as a handler to refer all the subviews. We can add above CustomView into a mutable array by insert one more line into above for-loop.
[_customObjectArray addObject:v.button];
Finally, I created a simple project and tried to implement things you mentioned. Maybe you can take it for a reference. https://db.tt/Och2tzyG
My Views Hierarchy is next:
My root View Controller that contains custom view
My custom UIView that contain custom UITableView
My custom UITableView
And i want to use delegate methods of my custom UITableView, but i don't really know how to access it's delegates because UITableView is added in my custom UIView class. Please clear me up, i'm little confused about this question.
Here's my code:
Adding my custom UITableView to UIView(i'm using CollapseClick):
-(void) initMehod{
DetailsView *testView = [[[NSBundle mainBundle] loadNibNamed:#"DetailsView" owner:self options:nil] lastObject];
test1View = testView;
//collapse click init
CGRect viewRect = [test1View frame];
viewRect.origin.x = 10;
test1View.frame = viewRect;
[test1View setBackgroundColor:[UIColor magentaColor]];
self.myCollapseClick.CollapseClickDelegate = self;
[self.myCollapseClick reloadCollapseClick];
// If you want a cell open on load, run this method:
[self.myCollapseClick openCollapseClickCellAtIndex:0 animated:NO];
}
...
And i'm using PagedFlowView in my root View Controller to add CustomView:
- (UIView *)flowView:(PagedFlowView *)flowView cellForPageAtIndex:(NSInteger)index{
mainDetailV = (MainDetailView *)[flowView dequeueReusableCell];
if (!mainDetailV) {
mainDetailV = [[[NSBundle mainBundle] loadNibNamed:#"MainDetailView" owner:self options:nil] lastObject];
//mainDetailV.layer.cornerRadius = 6;
mainDetailV.layer.masksToBounds = YES;
}
[usedViewControllers insertObject:mainDetailV atIndex:index];
return mainDetailV;
}
Everything works perfectly, except i can't use my custom UITableView delegate (for example didSelectRowAtIndexPath: method).
EDIT: I have fixed my problem, PagedFlowView actually handles tag gesture recogniser and overrides my custom tableView delegate methods.
Similar question:
UITableView as a subview?
First of all your collapsable tableview abstracts tableview delegates (i.e its a wrapper).
It provides you with a method didClickCollapseClickCellAtIndex instead. Use that.
It seems to be that you have some problem in understanding delegation pattern.
Avoid breaking the cascading effect of delegation pattern. It makes debugging easy
and helps to understand the flow of the code. Below diagram might help you to understand what I mean
Implement you didSelectRowAtIndexPath in custom view instead of rootViewcontroller.
// This is your custom view
-(id)init{
----
self.myCollapseClick.CollapseClickDelegate = self;
----
}
//collapsable delegate
-(void)didClickCollapseClickCellAtIndex:(int)index isNowOpen:(BOOL)open {
// you need to create your own custom view delegate with mehthod didSelectIndex:index
[self.customViewDelegate didSelectIndex:index];
}
In your rootviewcontroller you need to do this
customView.customViewDelegate = self;
-(void)didSelectIndex:(int)index{
// do your stuff here
}
I have custom UIViewController class called MSPageViewController with and associated nib file. I have an IBOutlet which is a UIImageView called pageImage.
Now, I want to use this view controller in another UIViewController which will display a series of my custom MSPageViewController in a UIPageViewController. So, I use the following code:
// alloc and init my custom view controller
MSPageViewController *page1 = [[MSPageViewController alloc] initWithNibName:#"MSPageViewController" bundle:nil];
// I must call this or, the image that I set below will always be null
// why? I guess it's because the view hasn't been drawn yet because it hasn't been displayed, so I need to force the redraw - but this is my question. Is this is the right approach?
[page1.view setNeedsDisplay];
// set the image
page1.pageImage.image = [UIImage imageNamed:#"tutorialPage1.png"];
// make my array of view controllers, it expects an array because could be double-sided
NSArray *viewController = [NSArray page1];
// pass the array that contains my custom view controller
[self.pageController setViewControllers:viewController direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
So am I doing this right? I have to force the redraw so that my outlets exist when I try to assign to them?
It's not the setNeedsDisplay part that you "need", it's the self.view part. By accessing the view property you are forcing the view controller to actually load the NIB. I guess that as a side effect of this, the pageImage property is populated as well (and was nil before you called self.view).
So, just calling self.view; instead of [self.view setNeedsDisplay]; should be enough.
As others have noted, pageImage (a UIImageView?) is likely not loaded from the nib yet when you're accessing it.
If you have a custom getter for pageImage, you could do the following:
- (UIImageView*) pageImage
{
[self view];
return _pageImage; // assuming the property backing ivar is synthesized as _pageImage.
}
But my personal preference would be to not expose the imageview itself and just expose a property for image. Then you can set the image to the viewcontroller regardless of it's loaded state, and internally set it to the imageView once the view loads:
- (void) setImage: (UIImage*) image
{
_image = image;
if ( self.isViewLoaded )
{
self.pageImage.image = image;
}
}
- (void) viewDidLoad
{
[super viewDidLoad];
self.pageImage.image = self.image;
}
If you moved some code to the viewDidLoad method you would be guaranteed that the view had been drawn.
I am designing a custom view controller in interface builder with a XIB file and I have a custom UIView subclass that I want to add to my view controller. Here is how I've put my custom view into the view controller:
My TonerEffectButtonView class is a subclass of UIView (and has nothing to do with UIButton) and here is my code for it:
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
-(void)prepareWithSelector:(SEL)selector onTarget:(id)targ withFilter:(GPUImageFilter*)filter{
self.gpuImageView = [[GPUImageView alloc] initWithFrame:self.frame];
UITapGestureRecognizer *gesture = [[UITapGestureRecognizer alloc] initWithTarget:targ action:selector];
[self addGestureRecognizer:gesture];
selectedFilter = filter;
[filter addTarget:self.gpuImageView];
[self setNeedsDisplay];
}
- (void)drawRect:(CGRect)rect
{
[super drawRect:rect];
}
initWithFrame: is not called anyway as I'm creating the view through a XIB, and I don't need any customization in initWithCoder: so I haven't implemented it. I have a GPUImageView (that is a part of a library that I'm using) that I'm creating programatically inside my view, and adding camera input to it (if you ask what happens in the prepare method). Prepare method is called from outside, and I've verified my UIView's frame is correct when it is called. I have a breakpoint on [super drawRect:rect]; and it never gets called, and when I run the program, my custom view is not displayed, nor does receive touch events (but prepare method DOES get called so the instance is created with the correct frame). It's like it has never been put there in interface builder. What could be the cause of this? I've seen many posts about this, and they mostly refer to creating views programatically and indicating problems with 'initWithFrame:'. But I have nothing to do with that method, and I want to use the interface builder/XIB couple, please don't advice me to create the view controller programatically. What could be the cause of it?
Thanks,
Can.
UPDATE: I can verify that -(void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx is also not called in any way.
Ok, I've found the answer. First, I've tried to add another instance programatically, and I've found out that it has autoresizing mask set to none, whereas my original instance has it set to W+H. This clue made me realize that in fact, I was forgetting to add my GPUImageView instance as a subview into my custom view. I've added it, and it worked.
I have a UINavigationController/UITableView and I want to present a UIView over top of it when the table is empty to give the user a prompt on how to add items to the table.
I've never make a UIView (as opposed to a UIViewController before) so I'll step through what I did to make it:
Make a new UIView Class - MakeSentenceHelperView
Make a nib called MakeSentenceHelperView.xib
Set File's owner to MakeSentenceHelperView
Load the nib in the MakeSentenceHelperView:
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
NSLog(#"makesentencehelperview init");
[[NSBundle mainBundle] loadNibNamed:#"MakeSentenceHelperView" owner:self options:nil];
}
return self;
}
and present the MakeSentenceHelperView in the UITableViewController:
//present the placeholder view for sentences
MakeSentenceHelperView *makeSentenceHelperView = [[MakeSentenceHelperView alloc] init];
NSLog(#"present placeholder: self.navigationcontroller.view: %#", self.navigationController.view);
//Something like this:
[self.navigationController.view addSubview:makeSentenceHelperView];
[self.navigationController.view bringSubviewToFront:makeSentenceHelperView];
The class loads and logs ok, but nothing appears in front of the UITableView - where have I gone wrong?
UPDATE: if I add [self.tableView setHidden:YES]; then the tableview disappears and the space is black and empty. I'm assuming this means I'm setting up the View wrong, somewhere.
You can use https://github.com/ecstasy2/toast-notifications-ios/ for showing Toast view liek Android. Check array size and if table view is not showing then called this one and show any custom method.
Thanks to #Aadhira for their link which led me to the problem.
I needed to add awakeFromNib
I was missing [self addSubview:self.view]; at the end of initWithFrame.