Access IB instantiated NSBox in MyDocument from another class? - macos

I started with the View Swapping code from Hillegass's book Cocoa Programming for MAC OS X. This code uses a popup menu in MyDocument.nib to swap ViewControllers using displayViewController in MyDocument.m partially shown below. I'm trying instead to use rows of a table in a ViewController to swap ViewControllers by calling displayViewController in MyDocument.m from the ViewController that generated the table:
- (void)displayViewController:(ManagingViewController *)vc
curBox: (NSBox *)windowBox
{
// End editing
NSWindow *w = [windowBox window];
BOOL ended = [w makeFirstResponder:w];
if (!ended) {
NSBeep();
return;
}
...
The problem I am having is when I call displayViewController from another ViewController I need to send along the Interface Builder instantiated NSBox in MyDocument.nib so that the view can be swapped inside the NSBox in MyDocument.m. So do to this I need to be able to access the Interface Builder instantiated NSBox in MyDocument.nib from another ViewController.
Does anyone know how to access the Interface Builder instantiated NSBox in MyDocument.nib from another ViewController?
EDIT:
I've made some progress. The view hierarchy is:
Window
ContentView
NSBox
I've been able to get the top level Window and the current ContentView with the following:
NSWindow *w = self.view.window;
NSView *v = [w contentView];
but I still haven't figured out how to get the NSBox that is within the ContentView. I've tried the following but they haven't worked:
NSBox *b = [v container];
NSBox *b = [v content ];
Anyone have an idea?

I found the solution. The NSBox is a subview of the ContentView. So the following works:
NSWindow *w = self.view.window;
NSArray *va = [[w contentView] subviews];
NSBox *b = [va objectAtIndex:0];

Related

drawRect not being called for NSTableView cell view

I have a view-based NSTableView with my custom view and the normal NSTextField in it. The custom view's background colour should change when a user clicks and it doesn't work because drawRect is not being called. I don't understand why really. Here's the delegate method and custom view code.
MyController
- (void)didAddLabel:(id)sender {
CustomLabel *label = (CustomLabel *)sender;
NSTableRowView *aaa = [_tableView rowViewAtRow:_tableView.selectedRow makeIfNecessary:NO];
MyCustomView *hello = [((NSTableCellView*)[aaa viewAtColumn:0]).subviews objectAtIndex:0];
[hello setBackgroundColor:[label nscolor]];
NSLog(#"Background colour is %#", [hello backgroundColor]);
[hello setNeedsDisplay:YES];
}
My view
class MyCustomView: NSView {
var backgroundColor :NSColor = NSColor.purpleColor()
override func drawRect(dirtyRect: NSRect) {
NSLog("In here")
backgroundColor.setFill()
NSRectFill(dirtyRect)
}
}
Observations: 1) the drawRect is only called upon init of the app. 2) The NSLog row contains the new/selected colour however the background does not change from purple.
Question: why isn't drawRect: called after setNeedsDisplay:?
drawRect: is only called if you don't have layer backing turned on. Are any of the parent views / this view have wantsLayer = YES or have a CALayer assigned to it? In that case, you'll have to use updateLayer: since all children of a layer-backed view are automatically layer-backed.

UISearchBar cannot become first responder after UITableView did re-appear

I have an iPhone app for iOS8. UIPageViewController has a child view of UITableViewController, and that's where I put UISearchController.
When the UITableViewController is presented for the first time, I have no trouble activating the search by doing
[self.searchController.searchBar becomeFirstResponder];
However, once the view controller gets invisible either by pushing to another table view controller, or modally presenting a UIViewController (visually masking it), the searchBar can no longer become first responder after it reappears.
The search itself can be done by
[self.searchController setActive:YES];
but, there is no caret blinking this way (user has to tap inside searchBar to start typing).
I have almost identical UITableViewController with UISearchController without the incident. So, I must be missing something, but I would like your opinion on which direction I should pay attention on solving this.
Below is how I set the search controller.
#interface MonthTableViewController () <UISearchResultsUpdating, UISearchControllerDelegate>
#property (nonatomic, strong) UISearchController *searchController;
UITableViewController *searchController = [[UITableViewController alloc]initWithStyle:UITableViewStylePlain];
searchController.tableView.dataSource = self;
searchController.tableView.delegate = self;
self.searchController = [[UISearchController alloc] initWithSearchResultsController:searchController];
self.searchController.searchResultsUpdater = self;
self.searchController.delegate = self;
self.searchController.searchBar.frame = CGRectMake(self.searchController.searchBar.frame.origin.x, self.searchController.searchBar.frame.origin.y, self.searchController.searchBar.frame.size.width, 44.0);
self.tableView.tableHeaderView = self.searchController.searchBar;
self.definesPresentationContext = YES;
// Hide search bar
CGRect myBounds = self.tableView.bounds;
myBounds.origin.y += self.searchController.searchBar.frame.size.height;
_lastlyAddedTableBoundY = self.searchController.searchBar.frame.size.height;
self.tableView.bounds = myBounds;
I solved it by going Xcode > Product > Analyze.
I simply forgot to add
[super viewWillAppear:(BOOL)animated];
inside -(void)ViewWillAppear:(BOOL)animated on the corresponding UITableViewController.
Hopefully, my mistake can save someone's time in the future.

How to use Storyboard to make popover that can be used in code?

I'm building a collection of forms each of which contains several fields. Some of the fields are UITextFields that will display a date. I've created a new class called DatePickerTextField, a descendant of UITextField. When a DatePickerTextField is tapped I'd like for a UIDatePicker control to appear in a popover.
My question is how do I use the storyboard to implement the popover? I can do a segue when there is a specific, visible control in the scene. But how do I represent a generic popover in the scene that I can attach to any instantiated DatePickerTextField that becomes active?
You can create segue that is not connected to any control but I don't think that there would be way to specify anchor point for popover from code. Another option is to create ViewController that is not connected with any segue. When editing storyboard, create ViewController which will be placed in popover, select it and navigate to Utilities pane->Attributes Inspector. Set Size to Freeform, Status Bar to None, specify unique Identifier that will be used to instantiate ViewController from code. Now you can change the size of ViewController by selecting its View and navigating to Utilities pane->Size Inspector.
After that you can create popover from code:
- (IBAction)buttonPressed:(id)sender {
UIView *anchor = sender;
UIViewController *viewControllerForPopover =
[self.storyboard instantiateViewControllerWithIdentifier:#"yourIdentifier"];
popover = [[UIPopoverController alloc]
initWithContentViewController:viewControllerForPopover];
[popover presentPopoverFromRect:anchor.frame
inView:anchor.superview
permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
}
One caveat is that you need to hold reference to popover as ivar of your class, otherwise it'll crash because UIPopoverController would be released and deallocated after buttonPressed returns:
#interface MyViewController : UIViewController {
// ...
UIPopoverController *popover;
// ...
}
So, I had a similar issue, and in case others might benefit, I figured I'd share it, since I benefit so much from stackoverflow.
This solution allows you to set the anchor of a customizable popover segue. It also allows you to configure the segue to be modal or not (I could not find a way to prevent the segue by dimming the exterior context, so if someone knows how to do that, I would be interested in hearing it); this is accomplished by setting the passthrough views for the popover controller. I also added the capacity to specify a custom view, rather than the view of the source viewcontroller (since I needed this capacity); this portion is not critical to the solution.
DynamicPopoverSegue.h
#interface DynamicPopoverSegue : UIStoryboardPopoverSegue
#property BOOL isModal;
#property UIView* sourceView;
#property CGRect anchor;
#end
DynamicPopoverSegue.m
#implementation DynamicPopoverSegue
- (void)perform
{
if (!self.popoverController.popoverVisible)
{
UIViewController* dst = (UIViewController*)self.destinationViewController;
UIViewController* src = (UIViewController*)self.sourceViewController;
UIView* inView = _sourceView ? _sourceView : src.view;
self.popoverController.contentViewController = dst;
if (!_isModal)
{
[self.popoverController setPassthroughViews:[[NSArray alloc] initWithObjects:inView, nil]];
}
[self.popoverController presentPopoverFromRect:_anchor
inView:inView
permittedArrowDirections:UIPopoverArrowDirectionAny
animated:YES];
}
}
#end
Then you just set your segue to "Custom" in the storyboard, and set the segue class to "DynamicPopoverSegue". In my case, since I wanted to associate it with dynamic layers in a view, I could not set the anchor, so I created the segue by control clicking from the view controller icon in the bar beneath my view controller to the view controller I was using to present the popupover.
To call the popover segue:
[self performSegueWithIdentifier:#"MyPopoverSegue" sender:self];
And to configure the popover segue:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"MyPopoverSegue"])
{
DynamicPopoverSegue* popoverSegue = (DynamicPopoverSegue*)segue;
// set the anchor to wherever you want it to be
popoverSegue.anchor = _destinationFrame;
}
}
- (IBAction)pressItemChooseOprateRoom:(id)sender {
if (isPad){
// UIView *anchor = sender;
UIViewController *viewControllerForPopover =
[self.storyboard instantiateViewControllerWithIdentifier:#"OperateRoomList"];
_myPopover = [[UIPopoverController alloc]
initWithContentViewController:viewControllerForPopover];
CGRect rc=[self getBarItemRc:sender];
[_myPopover presentPopoverFromRect:rc
inView:self.view
permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
[MLControl shared].popover =self;
// [self perfformSegueWithIdentifier:SEGUE_POP_OPERATEROOM sender:self];
}else{
[self iphoneOpenOperateRoomList];
/* [self performSegueWithIdentifier:#"iPhonePushOperateRoom" sender:self];
*/
}
}
-(void)iphoneOpenOperateRoomList{
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
UIViewController *vc = [storyboard instantiateViewControllerWithIdentifier:#"OperateRoomList"];
// if (!index.showTabBar) {
// vc.hidesBottomBarWhenPushed = YES;
// }
[self.navigationController pushViewController:vc animated:YES];
}
Just used the answer from Jonnywho for my SWIFT project. In case you need it:
Here's the SWIFT version:
let anchor: UIView = sender
var viewControllerForPopover = self.storyboard?.instantiateViewControllerWithIdentifier("GameAboutViewController") as! UIViewController?
let popover = UIPopoverController(contentViewController: viewControllerForPopover!)
popover.presentPopoverFromRect(anchor.frame, inView: anchor, permittedArrowDirections: UIPopoverArrowDirection.Any, animated: true)
Add a UIView in the scene dock.
You can add it as a subview to any existing view on the view controller.
You can then toggle it's isHidden property as you require.
You can add multiple such subviews and create many such popups.
This technique will save you from setting up a new View Controller and using segues.

How to design the content of a UIScrollView in a nib easily

I have a scrollview which has to display a view larger than the available display area.
I want to easily design the user interface without moving the embedded view up and down every time I have to do some changes.
The problem is everything outside the visible area is invisible in IB.
Is there any switch or trick to make everything visible in IB?
UPDATE
I have posted another solution here which I think is simpler and better, and works in storyboards.
ORIGINAL
Create your scroll view in the nib with the appropriate superview, position, and size.
Next, create a completely separate, top-level UIView instance by dragging a UIView out of the palette and dropping it into the work area outside of any existing views. In the Attributes inspector, set the Size popup to “None” and make sure the Status Bar, Top Bar, and Bottom Bar are all set to None. Here's an example:
This new top-level view will be your content view. Give your view controller two outlets: scrollView and contentView:
#interface MyViewController
#property (nonatomic, weak) IBOutlet UIScrollView *scrollView;
#property (nonatomic, strong) IBOutlet UIView *contentView;
#end
In the nib, wire up the scrollView outlet to the scroll view and wire up the contentView outlet to the content view.
Build your content view hierarchy inside the content view. Set its size as large as you need - it can be larger than 320x480 (as long as you have set all of its bars to None).
In your view controller's viewDidLoad, add contentView as a subview of scrollView and set scrollView.contentSize to the size of contentView:
#implementation MyViewController
#synthesize scrollView = _scrollView;
#synthesize contentView = _contentView;
- (void)viewDidLoad {
[super viewDidLoad];
[self configureScrollView];
}
- (void)configureScrollView {
CGSize size = self.contentView.bounds.size;
self.contentView.frame = CGRectMake(0, 0, size.width, size.height);
[self.scrollView addSubview:self.contentView];
self.scrollView.contentSize = size;
// If you don't use self.contentView anywhere else, clear it here.
self.contentView = nil;
// If you use it elsewhere, clear it in `dealloc` and `viewDidUnload`.
}

How to create NSTextField programmatically?

I want to create NSTextField programmatically. I am new to Mac App Development.
Can anyone help me in this ?
Update :
I tried this code
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
NSView *myView = [[NSView alloc] init];
NSRect frameRect = NSMakeRect(20,20,100,140);
NSTextField *myTextField = [[NSTextField alloc] initWithFrame:frameRect];
[myView addSubview:myTextField];
}
This does nothing. Please correct me if i'm wrong anywhere.
Thank you.
Creating UI elements programmatically is very simple in Cocoa.
It involves two steps:
Create the view and set a frame to it.
Add it as a subview to any super view.
Following snippet will you help you understand better.
NSRect frameRect = NSMakeRect(20,20,40,40); // This will change based on the size you need
NSTextField *myTextField = [[NSTextField alloc] initWithFrame:frameRect];
[myView addSubview:myTextField];
It looks like you're creating an NSTextField OK, but you're not adding it to anywhere visible in the view hierarchy. Your app probably contains one or more NSWindow instances; if you want a view to appear in a particular window, you should add it as a subview of that window's content view.

Resources