How can I add a view after the nib is loaded? - objective-c

Previously, I generate the programme using code, and I have a super class that have a background image all the time. But now I change my coding to using XIB, how can I insert a view after the Nib is loaded? But I don't want all my XIB have a view. Thanks.

-(void)awakeFromNib {
[view addSubview:newView];
}

awakeFromNib is the right method to use. You don't want to cover up views by just adding the subview. Insert the subview instead.
- (void)awakeFromNib
{
UIView *targetView;
UIView *viewToInsert = [self pointerToViewToInsert];
for (UIView *aSubview in self.view.subviews){
if ([aSubview isEqual:self.someView]){
targetView = aSubview;
break;
}
}
if (targetView){
int targetIndex = [self.view.subviews indexOfObject:targetView];
[self.view insertSubview:viewToInsert atIndex:targetIndex];
}

Related

Auto layout on subview inside subview (NSView)

I'm having hard time trying to properly set the auto layout for a subview inside another subview.
I'm using an example where two toolbar items show two different subviews (which works as expected), and those two share a third subview that's the one that does not fit well.
The code to add the subview is very simple:
[subView removeFromSuperview];
[itemXSubView addSubview:subView];
[self.window setContentView:itemXView];
First I remove the third and shared subview (subView) in case it was already added, then add it to the item[1-2]SubView and set the content of the window with the subview item[1-2]View, [1-2] depending on the toolbar button selected. Everything else is done with auto layout conditions.
The result is that the third and shared subview is always misplaced and/or cut, as in the example below. Resizing the window and changing from the first or the second view usually aggravates the issue.
Example of third subview items cut
Test updates
Tried to delegate the main window and override two resize functions (as per #the4kman suggestions), but they did never get called. The init is the only being called:
#interface viewController: NSView <NSWindowDelegate>
#end
#implementation viewController
-(id)init
{
if((self=[super init])) { }
return self;
}
- (void)resizeSubviewsWithOldSize:(NSSize)oldSize;
{
[super resizeSubviewsWithOldSize:oldSize];
}
- (void)resizeWithOldSuperviewSize:(NSSize)oldSize;
{
[super resizeWithOldSuperviewSize:oldSize];
}
- (void)layout
{
[super layout];
}
Another suggestion that got called, but sadly with no actual improvement. Delegated the window to viewController and set the main view (self.view) to the nested subView. Tried also combining with [itemXSubView setNeedsLayout:true];:
#interface viewController: NSViewController <NSWindowDelegate>
#end
#implementation viewController
-(void)viewWillLayout
{
[super viewDidLayout];
[self.view setNeedsLayout:true];
}
#end
Thanks in advance!
itemXSubView resizes its subviews but it doesn't fit them. You have to fit subView inside item1SubView before addSubview.
- (IBAction)item1Action:(id)sender {
NSLog(#"Item 1 action triggered");
[subView removeFromSuperview];
subView.frame = item1SubView.bounds;
[item1SubView addSubview:subView];
[self.window setContentView:item1View];
}

List the tag of Subviews which are visible in a UIview

I have UIView, Initially i will have 4 subview, in runtime. my code will hide to 2 view, so i need the tag values of remaining two view which are visible, is it possible.
Please help me.
Thanks,
Nikhil.Ch
First, iterate the subviews of the view. Check if the subview is a UIView class. From here, you can now check whether the subview is hidden or not.
for (UIView *subview in self.view.subviews) {
if ([subview isKindOfClass:[UIView class]]) {
if (!subview.isHidden) {
NSLog(#"%i", subview.tag);
}
}
}

Present SKScene from UIVIewController after selection on UICollectionView

I'm trying to create a side scrolling menu for a project that includes multiple games. I want to let the user scroll a UICollectionView horizontally (inside my initial ViewController) and tap on it to load one of the games.
I've been trying to use:
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
if(indexPath.row == 1) {
[self showSpinWheel];
}
- (void)showSpinWheel {
SKSceneLoader *spinLoaderView = [[SKSceneLoader alloc] init];
[self presentViewController:spinLoaderView animated:YES completion:nil];
}
when a cell is selected, but it keeps crashing when I tap on it.
SKSceneLoader has this code but it doesn't matter, since it crashes even before loading it.
- (void)viewWillLayoutSubviews {
[super viewWillLayoutSubviews];
// Configure the view.
SKView * skView = (SKView *)self.view;
if(!skView.scene) {
skView.showsFPS = NO;
skView.showsNodeCount = NO;
// Create and configure the scene.
SKScene * scene = [SpinWheel sceneWithSize:skView.bounds.size];
scene.scaleMode = SKSceneScaleModeAspectFill;
// Present the scene.
[skView presentScene:scene];
// background sound
}
}
This is the error:
-[UIView scene]: unrecognized selected sent to instance
NSInvalidArgumentException, etc, etc.
How can I tell the correct view to load the scene? From what I understand I'm trying to load the SKCcene into UIView which is not correct. Do I need to load another UIViewController first and load the scene there? There must be a way to load a SKScene from a non-skcene scene.
As the methods name suggests, -presentViewController:animated:completion: only accepts a UIViewController. The easiest thing to do would be to create a new UIViewController subclass that handles the creation of the SKScene and present that.
Based on your comments/edits the issue is with this line.
if(!skView.scene) { ...
The view property on a UIViewController is just a UIView by default. Since UIView doesn't have a scene property, thats the cause of the error. What you'll need to do is get it to be a SKView instead. Since you dont have a nib, the easiest way would be to override -loadView in SKSceneLoader to set view to a SKView.
- (void)loadView
{
self.view = [[SKView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
}

Removing View from its superview getting a bad access error XCODE

since the release of the storyboard it has been a long time since I've used addSubview and removeFromSuperview. Obviously I have forgotten how to use them.
So on my UIViewControllerA I have this code for a button:
- (IBAction)buttonClickHandler:(id)sender {
dyf_FacebookViewController *controller = [self.storyboard instantiateViewControllerWithIdentifier:#"Facebook"];
[self.view addSubview:controller.view];
}
Which loads a UIView that is handled by UIViewControllerB. now this view can be called by many different ViewControllers to be a subview, which is why I have it remove itself from the superview.
So on UIViewControllerB I have this code:
- (IBAction)close:(id)sender {
[self.view removeFromSuperview];
}
Unfortunately that returns with a bad access error. Please help!
Thanks
Michael
EDIT:
The subview added (subview's viewcontroller) will be in control for removing itself as it is in control of the button on the subview. So I do not have access to the original controller.view variable as that is in ViewControllerA
You'll have to keep a reference to your dyf_FacebookViewController object, so you can do the following:
- (IBAction)close:(id)sender {
[controller.view removeFromSuperview];
}
What you're doing now is to remove your main view from its superview, which can have serious consequences, giving you a bad access.
if ([controller.view superView]) {
[controller.view removeFromSuperview];
}
Try this
- (IBAction)close:(id)sender {
for (UIView *subView in self.view.subviews) {
[subView removeFromSuperview];
}
}
If you want to remove the view you added, then keep a copy of the view controller...
// in this VC.m
#interface
#property (nonatomic, strong) dyf_FacebookViewController *dyfController;
#end
- (IBAction)buttonClickHandler:(id)sender {
self.dyfController = [self.storyboard instantiateViewControllerWithIdentifier:#"Facebook"];
[self.view addSubview:self.dyfController.view];
}
Later, to close the view...
[self.dyfController.view removeFromSuperview];

How to set size for popover? [duplicate]

I have a UIPopoverController hosting a UINavigationController, which contains a small hierarchy of view controllers.
I followed the docs and for each view controller, I set the view's popover-context size like so:
[self setContentSizeForViewInPopover:CGSizeMake(320, 500)];
(size different for each controller)
This works as expected as I navigate forward in the hierarchy-- the popover automatically animates size changes to correspond to the pushed controller.
However, when I navigate "Back" through the view stack via the navigation bar's Back button, the popover doesn't change size-- it remains as large as the deepest view reached. This seems broken to me; I'd expect the popover to respect the sizes that are set up as it pops through the view stack.
Am I missing something?
Thanks.
I was struggling with the same issue. None of the above solutions worked for me pretty nicely, that is why I decided to do a little investigation and find out how this works.
This is what I discovered:
When you set the contentSizeForViewInPopover in your view controller it won't be changed by the popover itself - even though popover size may change while navigating to different controller.
When the size of the popover will change while navigating to different controller, while going back, the size of the popover does not restore
Changing size of the popover in viewWillAppear gives very strange animation (when let's say you popController inside the popover) - I'd not recommend it
For me setting the hardcoded size inside the controller would not work at all - my controllers have to be sometimes big sometimes small - controller that will present them have the idea about the size though
A solution for all that pain is as follows:
You have to reset the size of currentSetSizeForPopover in viewDidAppear. But you have to be careful, when you will set the same size as was already set in field currentSetSizeForPopover then the popover will not change the size. For this to happen, you can firstly set the fake size (which will be different than one which was set before) followed by setting the proper size. This solution will work even if your controller is nested inside the navigation controller and popover will change its size accordingly when you will navigate back between the controllers.
You could easily create category on UIViewController with the following helper method that would do the trick with setting the size:
- (void) forcePopoverSize {
CGSize currentSetSizeForPopover = self.contentSizeForViewInPopover;
CGSize fakeMomentarySize = CGSizeMake(currentSetSizeForPopover.width - 1.0f, currentSetSizeForPopover.height - 1.0f);
self.contentSizeForViewInPopover = fakeMomentarySize;
self.contentSizeForViewInPopover = currentSetSizeForPopover;
}
Then just invoke it in -viewDidAppear of desired controller.
Here's how I solved it for iOS 7 and 8:
In iOS 8, iOS is silently wrapping the view you want in the popover into the presentedViewController of the presentingViewController view controller. There's a 2014 WWDC video explaining what's new with the popovercontroller where they touch on this.
Anyways, for view controllers presented on the navigation controller stack that all want their own sizing, these view controllers need (under iOS 8) to call this code to dynamically set the preferredContentSize:
self.presentingViewController.presentedViewController.preferredContentSize = CGSizeMake(320, heightOfTable);
Replace heightOfTable with your computed table or view height.
In order to avoid a lot of duplicate code and to create a common iOS 7 and iOS 8 solution, I created a category on UITableViewController to perform this work when viewDidAppear is called in my tableviews:
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self setPopOverViewContentSize];
}
Category.h:
#import <UIKit/UIKit.h>
#interface UITableViewController (PreferredContentSize)
- (void) setPopOverViewContentSize;
#end
Category.m:
#import "Category.h"
#implementation UITableViewController (PreferredContentSize)
- (void) setPopOverViewContentSize
{
[self.tableView layoutIfNeeded];
int heightOfTable = [self.tableView contentSize].height;
if (heightOfTable > 600)
heightOfTable = 600;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
if ([[[UIDevice currentDevice] systemVersion] floatValue] < 8.0)
self.preferredContentSize=CGSizeMake(320, heightOfTable);
else
self.presentingViewController.presentedViewController.preferredContentSize = CGSizeMake(320, heightOfTable);
}
}
#end
This is an improvement on krasnyk's answer.
Your solution is great, but it isn't smoothly animated.
A little improvement gives nice animation:
Remove last line in the - (void) forcePopoverSize method:
- (void) forcePopoverSize {
CGSize currentSetSizeForPopover = self.contentSizeForViewInPopover;
CGSize fakeMomentarySize = CGSizeMake(currentSetSizeForPopover.width - 1.0f, currentSetSizeForPopover.height - 1.0f);
self.contentSizeForViewInPopover = fakeMomentarySize;
}
Put [self forcePopoverSize] in - (void)viewWillAppear:(BOOL)animated method:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self forcePopoverSize];
}
And finally - set desired size in - (void)viewDidAppear:(BOOL)animated method:
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
CGSize currentSetSizeForPopover = self.contentSizeForViewInPopover;
self.contentSizeForViewInPopover = currentSetSizeForPopover;
}
You need to set the content size again in viewWillAppear. By calling the delagate method in which you set the size of popovercontroller. I had also the same issue. But when I added this the problem solved.
One more thing: if you are using beta versions lesser than 5. Then the popovers are more difficult to manage. They seem to be more friendly from beta version 5. It's good that final version is out. ;)
Hope this helps.
In the -(void)viewDidLoad of all the view controllers you are using in navigation controller, add:
[self setContentSizeForViewInPopover:CGSizeMake(320, 500)];
I reset the size in the viewWillDisappear:(BOOL)animated method of the view controller that is being navigated back from:
-(void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
CGSize contentSize = [self contentSizeForViewInPopover];
contentSize.height = 0.0;
self.contentSizeForViewInPopover = contentSize;
}
Then when the view being navigated back to appears, I reset the size appropriately:
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
CGSize contentSize;
contentSize.width = self.contentSizeForViewInPopover.width;
contentSize.height = [[self.fetchedResultsController fetchedObjects] count] * self.tableView.rowHeight;
self.contentSizeForViewInPopover = contentSize;
}
For iOS 8 the following works:
- (void) forcePopoverSize {
CGSize currentSetSizeForPopover = self.preferredContentSize;
CGSize fakeMomentarySize = CGSizeMake(currentSetSizeForPopover.width - 1.0f, currentSetSizeForPopover.height - 1.0f);
self.preferredContentSize = fakeMomentarySize;
self.navigationController.preferredContentSize = fakeMomentarySize;
self.preferredContentSize = currentSetSizeForPopover;
self.navigationController.preferredContentSize = currentSetSizeForPopover;
}
BTW I think, this should be compatible with previous iOS versions...
Well i worked out. Have a look.
Made a ViewController in StoryBoard. Associated with PopOverViewController class.
import UIKit
class PopOverViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.preferredContentSize = CGSizeMake(200, 200)
self.navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .Done, target: self, action: "dismiss:")
}
func dismiss(sender: AnyObject) {
self.dismissViewControllerAnimated(true, completion: nil)
}
}
See ViewController:
//
// ViewController.swift
// iOS8-PopOver
//
// Created by Alvin George on 13.08.15.
// Copyright (c) 2015 Fingent Technologies. All rights reserved.
//
import UIKit
class ViewController: UIViewController, UIPopoverPresentationControllerDelegate
{
func showPopover(base: UIView)
{
if let viewController = self.storyboard?.instantiateViewControllerWithIdentifier("popover") as? PopOverViewController {
let navController = UINavigationController(rootViewController: viewController)
navController.modalPresentationStyle = .Popover
if let pctrl = navController.popoverPresentationController {
pctrl.delegate = self
pctrl.sourceView = base
pctrl.sourceRect = base.bounds
self.presentViewController(navController, animated: true, completion: nil)
}
}
}
override func viewDidLoad(){
super.viewDidLoad()
}
#IBAction func onShow(sender: UIButton)
{
self.showPopover(sender)
}
func adaptivePresentationStyleForPresentationController(controller: UIPresentationController) -> UIModalPresentationStyle {
return .None
}
}
Note: The func showPopover(base: UIView) method should be placed before ViewDidLoad. Hope it helps !
For me this solutions works.
This is a method from my view controller which extends UITableViewController and is the root controller for UINavigationController.
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
self.contentSizeForViewInPopover = self.tableView.bounds.size;
}
And don't forget to set content size for view controller you gonna push into navigation stack
- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath{
dc = [[DetailsController alloc] initWithBookmark:[[bookmarksArray objectAtIndex:indexPath.row] retain] bookmarkIsNew:NO];
dc.detailsDelegate = self;
dc.contentSizeForViewInPopover = self.contentSizeForViewInPopover;
[self.navigationController pushViewController:dc animated:YES];
}
if you can imagine the assambler, I think this is slightly better:
- (void) forcePopoverSize {
CGSize currentSetSizeForPopover = self.contentSizeForViewInPopover;
self.contentSizeForViewInPopover = CGSizeMake(0, 0);
self.contentSizeForViewInPopover = currentSetSizeForPopover;
}
The accepted answer is not working fine with iOS 8. What I did was creating my own subclass of UINavigationController for use in that popover and override the method preferredContentSize in this way:
- (CGSize)preferredContentSize {
return [[self.viewControllers lastObject] preferredContentSize];
}
Moreover, instead of calling forcePopoverSize (method implemented by #krasnyk) in viewDidAppear I decided to set a viewController (which shows popover) as a delegate for previously mentioned navigation (in popover) and do (what force method does) in:
-(void)navigationController:(UINavigationController *)navigationController
didShowViewController:(UIViewController *)viewController
animated:(BOOL)animated
delegate method for a passed viewController. One important thing, doing forcePopoverSize in a UINavigationControllerDelegate method is fine if you do not need that animation to be smooth if so then do leave it in viewDidAppear.
I was facing same problem, but you don't want to set contentsize in viewWillAppear or viewWillDisappear method.
AirPrintController *airPrintController = [[AirPrintController alloc] initWithNibName:#"AirPrintController" bundle:nil];
airPrintController.view.frame = [self.view frame];
airPrintController.contentSizeForViewInPopover = self.contentSizeForViewInPopover;
[self.navigationController pushViewController:airPrintController animated:YES];
[airPrintController release];
set contentSizeForViewInPopover property for that controller before pushing that controller to navigationController
I've had luck by putting the following in the viewdidappear:
[self.popoverController setPopoverContentSize:self.contentSizeForViewInPopover animated:NO];
Although this may not animate nicely in the case when you're pushing/popping different-sized popovers. But in my case, works perfectly!
All that you have to do is:
-In the viewWillAppear method of the popOvers contentView, add the snippet given below. You will have to specify the popOver's size first time when it is loaded.
CGSize size = CGSizeMake(width,height);
self.contentSizeForViewInPopover = size;
I had this issue with a popover controller whose popoverContentSize = CGSizeMake(320, 600) at the start, but would get larger when navigating through its ContentViewController (a UINavigationController).
The nav controller was only pushing and popping custom UITableViewControllers, so in my custom table view controller class's viewDidLoad i set self.contentSizeForViewInPopover = CGSizeMake(320, 556)
The 44 less pixels are to account for the Nav controller's nav bar, and now I don't have any issues anymore.
Put this in all view controllers you are pushing inside the popover
CGSize currentSetSizeForPopover = CGSizeMake(260, 390);
CGSize fakeMomentarySize = CGSizeMake(currentSetSizeForPopover.width - 1.0f,
currentSetSizeForPopover.height - 1.0f);
self.contentSizeForViewInPopover = fakeMomentarySize;
self.contentSizeForViewInPopover = currentSetSizeForPopover;
Faced the same issue and fixed it by setting content view size to navigation controller and view controller before the init of UIPopoverController was placed.
CGSize size = CGSizeMake(320.0, _options.count * 44.0);
[self setContentSizeForViewInPopover:size];
[self.view setFrame:CGRectMake(0.0, 0.0, size.width, size.height)];
[navi setContentSizeForViewInPopover:size];
_popoverController = [[UIPopoverController alloc] initWithContentViewController:navi];
I'd just like to offer up another solution, as none of these worked for me...
I'm actually using it with this https://github.com/nicolaschengdev/WYPopoverController
When you first call your popup use this.
if ([sortTVC respondsToSelector:#selector(setPreferredContentSize:)]) {
sortTVC.preferredContentSize = CGSizeMake(popoverContentSortWidth,
popoverContentSortHeight);
}
else
{
sortTVC.contentSizeForViewInPopover = CGSizeMake(popoverContentSortWidth,
popoverContentSortHeight);
}
Then in that popup use this.
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:YES];
if ([self respondsToSelector:#selector(setPreferredContentSize:)]) {
self.preferredContentSize = CGSizeMake(popoverContentMainWidth,
popoverContentMainheight);
}
else
{
self.contentSizeForViewInPopover = CGSizeMake(popoverContentMainWidth,
popoverContentMainheight);
}
}
-(void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:YES];
self.contentSizeForViewInPopover = CGSizeZero;
}
Then repeat for child views...
This is the correct way in iOS7 to do this,
Set the preferred content size in viewDidLoad in each view controller in the navigation stack (only done once). Then in viewWillAppear get a reference to the popover controller and update the contentSize there.
-(void)viewDidLoad:(BOOL)animated
{
...
self.popoverSize = CGSizeMake(420, height);
[self setPreferredContentSize:self.popoverSize];
}
-(void)viewWillAppear:(BOOL)animated
{
...
UIPopoverController *popoverControllerReference = ***GET REFERENCE TO IT FROM SOMEWHERE***;
[popoverControllerReference setPopoverContentSize:self.popoverSize];
}
#krasnyk solution worked well in previous iOS versions but not working in iOS8. The following solution worked for me.
- (void) forcePopoverSize {
CGSize currentSetSizeForPopover = self.preferredContentSize;
//Yes, there are coupling. We need to access the popovercontroller. In my case, the popover controller is a weak property in the app's rootVC.
id mainVC = [MyAppDelegate appDelegate].myRootVC;
if ([mainVC valueForKey:#"_myPopoverController"]) {
UIPopoverController *popover = [mainVC valueForKey:#"_myPopoverController"];
[popover setPopoverContentSize:currentSetSizeForPopover animated:YES];
}
}
It is not the best solution, but it works.
The new UIPopoverPresentationController also has the resizing issue :( .
You need to set the preferredContentSizeproperty of the NavigationController in viewWillAppear:
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
self.navigationController.preferredContentSize = CGSizeMake(320, 500);}