Having a row of equally sized custom NSViews - objective-c

I'm developing a custom NSView and I want a simple application to test how it behaves when initialized and when dealloced. For that I wanted to create a window with an Add and Remove button that would add my custom view, one next to each other, all equally sized. This is proving harder than I though. I had previous experiences with Qt and Gtk+ where this was trivial (as that's how you construct UIs there).
I tried creating an NSBox and adding them to it, but that means that I have to use auto layout and programatically create strings like "[view1]-[view2]-[view3]" which sounds like a pain and even then I'm not sure that's sufficient.
I also tried to use NSCollectienView but I couldn't figure out how to add arbitrary NSViews to it.

Have a look at NSStackView. It's a relatively new class found in the Interface Builder object library (second from right).
You can stack views vertically or horizontally and Cocoa takes care of all the auto-layout for you. In the snippet below I stack 30 text fields on top of one-another:
import Cocoa
#NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
#IBOutlet weak var window: NSWindow!
#IBOutlet weak var stackView: NSStackView!
func applicationDidFinishLaunching(aNotification: NSNotification) {
for each in 0..<30 {
var f = NSTextField()
f.translatesAutoresizingMaskIntoConstraints = false
stackView.addView(f, inGravity:.Top)
}
}
}
If you want something a bit more sophisticated than the above, Apple provide a demo project called InfoBarStackView to get you up and running. Whether it suits your particular requirements or not, this approach is definitely better than NSBox and NSCollectionView for the sort of thing you describe.

Related

Set AutoLayout Size Class Programmatically?

With iOS 8 and Xcode 6, in storyboards we now have the screen size grid letting us select a size class. Where you can select layout formatting for the different screen sizes.
I have found this brilliantly helpful, as it allows me to set the base constraints and then unique ones for each screen size.
My question is, can you do this programmatically? I create my NSLayoutConstraint as normal but I need to be able to specify different constraints for different screen sizes.
iOS 8 introduces the active property on NSLayoutConstraint. It allows you to activate or deactivate a constraint. There are also methods to activate/deactivate multiple constraints.
+ (void)activateConstraints:(NSArray *)constraints
+ (void)deactivateConstraints:(NSArray *)constraints
Keep your constraints in arrays when creating them programmatically.
Create an array for each of the layouts you need.
Activate/Deactivate whatever set of constraints you need from within willTransitionToTraitCollection
To answer your question, you can set the size class programmatically, however, it's a bit of a pain. You must call "setOverrideTraitCollection" but from the parent view controller, not the one you actually wished to make a trait change to.
In my situation, I wanted to change the Master VC of a split view controller on iPad to look differently than the one on the iPhone, however, they are both set to Compact width / Regular height by default. So I subclassed the Master's nav controller and added code to set the Master's traits to Regular width when it's not an iPhone.
Swift code:
class MasterNavigationController: UINavigationController {
override func viewDidLoad() {
super.viewDidLoad()
if (self.traitCollection.userInterfaceIdiom != .Phone) {
let newTraitCollection = UITraitCollection(horizontalSizeClass: .Regular)
self.setOverrideTraitCollection(newTraitCollection, forChildViewController: self.topViewController)
}
}
}
I hope this helps someone looking for a similar solution.
It's a bit confusing & hard to find in the documentation because a "size class" isn't actually a "Class" like NSObject. They're really defined in an enum/typedef called: UIUserInterfaceSizeClass
The way to get the horizontal & vertical size class for a view is with a UITraitCollection
Class/Type methods for UITraitCollection allow you to create one based on a particular display scale (e.g. retina or not), from an array of other trait collections, with a UI idiom (iPad/iPhone), or specific horizontal & vertical options (compact, regular), but to be honest I'm not sure yet how you'd use this...
This question discusses updating constraints when the traitCollection changes, using willTransitionToTraitCollection(newCollection: UITraitCollection!,
withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator!)
You're right that both the UITraitCollection and its properties are readonly, but clearly you can create a new collection for some reason, and handle layout changes when the traitCollection changes.
This previous question is pretty similar & links to an Apple article about using Adaptive Layout. Also check the WWDC video "Building Adaptive Apps with UIKit."

How to create a reusable subview with controls, without using xib file

I'm coding an app, and have several views that are reused (f. ex. containing a textfield, a label and a button)
Now i would like to just create it in a "globalviews.m" file once and add it as a subview to the mainview. (I really don't like the interface builder)
I could easily create a function that returns a UIView, with the components in them, but i would like to access the controls of course.
I was hoping something like (making "searchview" global in the viewcontroller in use)
// making searchview a global thingy
UIView *seachview ;
// rest of code here and then in viewdidload:
UIView *seachview = [[UIView alloc] init] ;
searchview = [[globaviews alloc] thesearchviews_name] ;
[self addsubview:searchview] ;
But how could I make controls inside easily accessible. Like:
NSString *something = searchview.textviewname.text ;
Or would this be a terrible idea to begin with?
Its just the way I would prefer to code...
You can create a custom class that is a subclass of UIView. You could then add properties for each control (the same way you would add NSString, NSNumber etc). Or you could create public methods to modify / get data.
e.g.
- (void)setTextFiledColour:(UIColor *)color;
- (NSString *)getTextFieldText;
My personal opinion (from a lot of experience) is to learn interface builder and deal with it. It is perfectly possible to do what you want and many people agree with you and choose to do it that way. Personally I've never seen it done "right". Meaning that its all done custom to create their own patterns and methodologies, avoiding years of experience and testing that has gone into the patterns provided by interface builder.
I find that storyboards in particular force a very specific pattern and style of coding. I think moving away form that is a huge mistake, as if used correctly it has great potential to abstract away UI / Code, prevents taking shortcuts that come back later on and most importantly when someone else needs to change it, there is no ambiguity, you can't make a mistake with class names or variable names etc.
I've used storyboards a lot and have yet to have an issue with them, on the flip side i've worked with developers who insist on doing it all by hand and have never had so many issues, and shocked at how long it takes to develop applications in this manner.
In the end its up to you.
Note
You mentioned wanting to create a view and reuse it. Its also possible to do this in a .xib file and reuse it that way. Losing some of the storyboard abilities but might give you a little of both worlds.

Is there design pattern for showing same view controller in several places with some customization?

Very frequently we reuse same view controllers when developing universal apps both for iPhone and iPad. But frequently some customization is needed, like:
IF iPad THEN
...
ELSE
...
So, in order to achieve such customization the controller might have some property that is set during initialization of the controller, or there might be custom constructors. Just curious is there design pattern that suites for such situations.
Don't.... :) Use a common class called for instance MyClass and then sub-class it MyClass-iPad & MyClass-iPhone and use two different XIB for each. Avoid using this kind of stuff (there is no need for it).
Explanation:
The iPad version should only be aware of classes of the type Something-iPad this makes the code clean and creates a well defined architecture. If I jump into your code and someone tell's me: "Ok Jacky Boy, you have to make changes on the iPad version". I won't care to look ath the Something-iPhone classes. Most of the logic (business) should be on super class Something where the small tweaks should be on the sub-classes.
On side note, on most of my projects, normally I don't have anything on the Something-iPhone classes, because the design is done on the XIB. On the Something-iPad I would normally keep a reference to a UIPopOverController (just an example) and some rotations tweaks.
Edit 1:
I would receive an NSDictionary on the init of the UIViewController, like this:
initWithNibName:bundle:configurationDictionary:
After receiving this configurationDictionary, I would then use it on the viewDidLoad (for example). You could then do some cool stuff like this:
- (void)viewDidLoad
{
[[self view] setBackgroundColor:[[self configurationDictionary] objectForKey:BACKGROUND_COLOR_KEY]];
}
If you have different initializers or larger chunks of different functionality then it makes sense to define a base class with the core functionality and then an iPad-specific subclass and an iPhone-specific subclass.
But in cases where you only have a trivial difference (for example, displaying an action sheet), then I would simply use something like:
- (void)someMethod {
// a bunch of stuff that is the same
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
// one or two lines for iPad
} else {
// one or two lines for iPhone
}
}
I have plenty of situations where I do both - subclass for the bigger differences and use UI_USER_INTERFACE_IDIOM for trivial differences in the base class.
If you need the same VC in N places but each time initialized a little differently I would move the logic in a specific factory class / category on the VC
like its done with for example SLRequest objects / in ShareKit

Create single .xib for Universal app in Interface Builder? (iOS)

Apologies if this is a silly question, but I've done some googling and searched SO and haven't found anyone asking this exact question.
I have been doing iOS development for some time now, but I am completely new to the Interface Builder. What I want to know is this: is there any way to just create ONE .xib file and then use it for both iPhone and iPad in a Universal application?
It seems silly to me to have to create them separately; why do twice the work laying something out more than once in Interface Builder when I could do it once (with minor adjustments for screen size) in code?
Please let me know if I'm missing/misunderstanding something here. Like I said, I'm a complete Interface Builder newbie :)
EDIT: I have submitted non-interface-builder games to the App Store in the past where the iPhone and iPad versions were identical, so I'm not concerned with making the game look/feel different on each device. I intend for them to look exactly the same, aside from some slight positioning changes due to the difference in aspect ratio.
If you know what the resulting view would look like, based on autoresizing, you can indeed use only one .xib. May come in handy if the view is just some sort of a shared component that autoresizes as you want it to. However, if you need the view to look way different on iPad than on iPhone, just use two .xibs. It’s possible then to load the appropriate one as needed, for example in instance initializer, like this controller’s -init:
- (id)init
{
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad)
{
self = [super initWithNibName:#"YourNibForPad" bundle:nil];
}
else
{
self = [super initWithNibName:#"YourNibForPhone" bundle:nil];
}
if (self) { /* initialize other ivars */ }
return self;
}
The main reason that XIBs are separate files is because Apple feel that UIs designed for iPhones/iPod touches and iPads should be tailored to each respectively. This is echoed in their their iOS App Programming Guide, which says the following:
For views, the main modification is to redesign your view layouts to support the larger screen. Simply scaling existing views may work but often does not yield the best results. Your new interface should make use of the available space and take advantage of new interface elements where appropriate. Doing so is more likely to result in an interface that feels more natural to the user—and not just an iPhone app on a larger screen.
Whilst it can take time to maintain two XIBs for what is effectively one UI, I feel it is more straightforward than using one XIB and then having to connect up most of your UI elements in order to move them around programmatically when that XIB loads. After all, with two XIBs at least you can see what each UI looks like, and then make visual changes easily.
As an aside, don't forget iOS 5's Storyboards (read about them here), which make managing a view/view controller hierarchy much simpler.
Try to name them
MyCell.xib and MyCell ~ ipad.xib
then:
[self.tableView registerNib: #"MyCell" forCellReuseIdentifier: #"MyUniqueIdentifier"];
If your using IB, you need to create 2 separate xib files for iPhone and iPad. You need a separate iPad xib to make your app comply with the Apple iPad UI guidelines.

How do you display custom UIViews in InterfaceBuilder?

I seem to enjoy designing new UIViews and UIControls that implement their own -drawRect: method. This work well for me, especially when composed using UIViews in Interface Builder.
But composing them in Interface Builder is annoying because they just show up as boring plain rectangles.
It would be great if the views would actually render themselves as the built-in controls do.
Is there any way I can structure my project so that Interface Builder will render my custom views?
In order to do this, you actually have to create a plug-in for Interface Builder that uses your custom class. Once you create and install your plug-in, you will be able to drag and drop instances of your class (your view) onto another window/view/whatever just like any other control. To get started with creating IB Plug-Ins, please see the Interface Builder Plug-In Programming Guide. Also, I recommend taking a look at Aaron Hillegass's book, Cocoa Programming for Mac OS X. As well as being very well written and easy to understand, it has a chapter on creating your own IB Palette controls.
This is achievable by marking your UIView subclass with #IBDesignable. Once you do this, your custom views will render themselves in Interface Builder. You can even add parameters that can be configured by marking them as #IBInspectable. Here's a Swift example:
#IBDesignable class customView: UIView {
#IBInspectable var count: Int = 0
}
There's an article on NSHipster that provides more detail.