How to access custom view properties after initialize - ios

I'm targeting IOS 8+.
I have a form that is used in more than one place. So I decided to create a custom view where I define the various "form" text fields.
I have built my XIB, and the UIView subclass contains the outlets for each textField.
The view is composed of a background image and a scroll with the form fields over it.
Now, my first obstacle was: I need to have this custom view in a container that may or may not have a navigation bar. This made me create a constraint outlet so I could update its value to push down the scroller view. This way I'd have the whole image in the frame, the top being behind the navbar and the scroller bellow the nav bar).
Here's a manual drawing to help understanding the problem.
It's very possible that I'm making a lot of mess and confusion on my way to solve this. :)
The problem is:
After awakeFromNib runs I have no access to the constraint property. I then noticed the same thing happens for the TextFields outlets.
So, how can I access the custom view's properties when I instantiate them programatically?
Something like:
Controller:
let customView = SignupView(frame: f)
view.addSubview(customView)
customView.pushScrollerDownBy(50.0)
Custom view:
func pushScrollerDownBy(yOffset: CGFloat) {
//topScrollerConstraint is the outlet for the textField.
topScrollerConstraint.constant = yOffset //right now topScrollerConstraint is nil.
}

You should check if you have connected your topScrollerConstraint to the file's owner since it will not get instantiated and therefore, error. Here is a recent SO question regarding difference between these two:
What is File’s owner in XIB in this case?

Related

Add a UIView over the top of UITableViewController in Swift

I currently use a UITableViewController (PFQueryTableViewController), I would like to display a UIView over the top of the TableView.
Ideally I'd like to do this in the storyboard, so I can easily add additional Labels/buttons to it. However, I can't seem to figure out how or if you can do it in storyboard. Is that right?
I have tried it programmatically. I first created the variable at the top:
var filterLabelView:UIView = UIView()
I then added the following in ViewDidLoad :
filterLabelView.frame = CGRect(x: self.view.frame.width/2, y: self.view.frame.height/2, width: self.view.frame.width, height: 40)
filterLabelView.center = CGPointMake(self.view.frame.size.width / 2, self.view.frame.size.height / 2)
filterLabelView.backgroundColor = UIColor.redColor()
self.view.addSubview(filterLabelView) // See below
I also tried:
self.view.insertSubview(filterLabelView, aboveSubview: tableView)
This creates the red UIView, but it seems to be embedded to the tableview, as when I scroll up and down, the View moves with it.
What I want to create
Ideally, I want the view to sit at the bottom of the screen, and not move when the user scrolls. An example screenshot of how I want it look is below:
I have read that the best way is to use a UIViewController with a UITableView inside it, but I'd rather not take that approach seeing how much my app is already built up.
Can anyone help me create this look? Thanks in advance
You have to derive from UIViewController to get full layout control.
Then simply add a UITableView instance in Storyboard. Constrain it normally, edge-flush, top-flush, and have customView.top = tableView.bottom. Make a normal outlet to your controller.
You just need to remember to make your custom UIViewController adopt the usual dataSource and delegate protocols, as well as assigning itself as those roles to the properties of the UITableView on initialization (usually viewDidLoad()).
There's one more finesse related to clearing the selected cell when on viewDidAppear(), but there's nothing else special about a UITableViewController -- it's just a UIViewController with a built-in tableView property and automatically assigned delegates and a very inflexible layout.
ADDENDUM based on comment about how to get around this: It's sort of like asking if there is any way to make a screwdriver drive a nail into wood. Just use the right tool. The top-level view property of a UITableViewController is the UITableView itself. See below, a stock UITableViewController with nothing else, zero code, in layout debug mode.
This means the entire tree of views is embedded in a scrolled entity, so all subviews will scroll with it.
It really shouldn't be that big a deal to re-do this one VC in Storyboard. After all, your rendering code in cellForRowAtIndexPath and your dataSource and delegate generally, don't change at all.
ADDENDUM 2:
Not knowing Parse intimately, this may not be possible. (!! Thanks, Parse.) If Parse doesn't supply you with a separable PFQueryTableView that you can embed yourself, the following workaround may be possible, but you'll need to understand the Objective-C code:
https://www.parse.com/questions/pfquery-tableview-for-uiviewcontroller

Hide Outlets from Connections Inspector for Custom View

I have created a custom view (Quantity View) with nib file in Swift. I have created some IBOutlets & IBActions (for buttons, labels etc.) in my custom view.
I tried to use this custom view (Quantity View) by assigning class name to a UIView in my storyboard.
It's showing me all the IBOutlets & IBActions in the Connections Inspector, as shown in this screenshot: .
I just want to show only delegate for the Custom view.
Possible Answer:
I thought I can use the -viewWithTag to get the views instead of Outlets.
But, I want to know if it's possible with having Outlets also or if there is much better way to do this?
What are the other possible ways (optimum) to handle this situation?
You can also consider the following solution:
You can take the subviews of your QuantityViews(custom view) and you can identify the specific views by its frame origin.
Note : you should know the customview subviews frame
Its not possible to hide IBOutlets from storyboard if you declare the class members as IBs (IBOutlets or IBActions).
The IBOutlets or the IBActions are just indicators to the interface builder so that it can show the names on it when you try to bind them it actually calls the setValue: forKey: method to set the view's reference to the IBOutlet property.
Now if you try to access an subview from the file's owner class without any IBoutlets you need to have a pointer to point it, so for that either you can get the reference using ObjectID which is assigned to the subview by the interface builder or you can get it using the viewWithTag: method.
The ObjectID you need to find all time when you add or replace a subview from the view, so better and convenient approach is to use tag property of UIView class.
So my conclusion to this problem is to access the views using the viewWithTag method you mentioned earlier.
I think your way is correct. But sometimes Xcode doesn't work correctly.
The following makes the IBOutlets and IBActions reappear and work properly:
Clean project your project in Xcode.
Quit Xcode completely.
Delete all contents of ~/Library/Developer/Xcode/DerivedData/.
Restart MacOS just in case.
I hope you will resolve that :)

How are NSTableCellViews supposed to be laid out?

I have a fairly basic MainWindow.xib with a source list-style sidebar. I created it by dragging the Source List template into the window, which already contains two NSTableCellViews: HeaderCell and DataCell.
The latter consists of an icon (using NSImageView) and a label (NSTextField). Instead, I want the label and another, smaller label underneath. In IB, this looks as follows:
If I focus on just DataCell, it highlights accordingly:
Thing is, actually running the program, it looks nothing like the template:
Notice how the two NSTextFields just get smashed together into one. My understanding was that view-based NSOutlineViews (and view-based NSTableViews, for that matter) are supposed to be designed as a template from within IB. Instead, the dimensions from the template seem to get mostly ignored.
Here's the code that sets the view's values from the data source:
public class TourSourceListDelegate : NSOutlineViewDelegate
{
public override bool IsGroupItem(NSOutlineView outlineView, MonoMac.Foundation.NSObject item)
{
return (item as TourSourceListDataSource.Item).IsHeader;
}
public override NSView GetView(NSOutlineView outlineView, NSTableColumn tableColumn, MonoMac.Foundation.NSObject item)
{
if (IsGroupItem(outlineView, item))
{
return outlineView.MakeView("HeaderCell", this);
}
else
{
var data = item as TourSourceListDataSource.Item;
var dataView = outlineView.MakeView("DataCell", this);
(dataView.Subviews[0] as NSTextField).StringValue = data.Name;
(dataView.Subviews[1] as NSTextField).StringValue = data.Date_start.ToShortDateString();
return dataView;
}
}
}
I've tried overriding GetRowHeight, but that doesn't seem to resolve the problem (it makes more room, but still doesn't let the views distribute themselves properly), nor does it seem necessary.
I've also tried playing with the various Autosizing, Autoresizes Subviews, etc. toggles in IB, but that doesn't seem to produce intuitive results, and again, it doesn't seem necessary — the view as presented in IB is exactly what I want, just with slightly longer labels in practice.
I haven't tried converting this to AutoLayout yet.
What obvious step am I missing?
Some more info that probably doesn't make a difference: this is a Xamarin.Mac/MonoMac project with Xcode 5.0, MacOSX10.8.sdk, Xamarin Studio 4.0.12, Xamarin.Mac 4.0.12, and Mono 3.2.3 (targeting Mono / .NET 4.0). I've also enabled App Sandboxing.
What's important in interface builder is the view hierarchy. What kind of view is that cell? Are those labels really subviews of the cellview or not? The hierarchy should look something like:
One thing that's fishy that I see is accessing dataView.Subviews[0] and [1]. If you're adding subviews to your cells then should be creating your own NSTableViewCell subclasses, with each view connecting to the subclass' IBOutlet properties. The subclass doesn't need any code in its implementation, just the declaration of its properties in #interface, such as titleField and descriptionField, and an empty #implementation that auto-synthesizes them.
Then makeViewWithIdentifier (or apprently the glue MakeView in Xamarin) when passed the right identifier should create your NSTableViewCell subclass, and at runtime you can verify that using po dataView in the debugger. Then you access the subviews using the properties of your NSTableViewCell subclass' interface instead of assuming which view is in which position with the subview array, using dataView.titleField and dataView.descriptionField.
If your cell view has one text field then you can use NSTableViewCell without subclassing, but do connect up the textField outlet (its connected by default as long as you don't delete & recreate the cell view's label view) so you can access it through the property, again instead of having to dive into the subviews array.
All that said, it's not really clear why you're seeing what you are. It looks like those aren't the subviews you expect, and might even look like the wrong fonts as well as in the wrong positions. Using a custom subclass of NSTableViewCell and verifying its class at runtime is a good way of making sure it's creating the view you expect, but you can also dump the subview within the debugger using po [dataView _subtreeDescription].

monotouch - reusable iOS custom view

OK, so this one is probably a bit challenging, as it is monotouch...
I have a reusable custom toolbar that that I plan to reuse on multiple screens.
I want to create the toolbar in Interface Builder
I want to be able to update labels on the toolbar from ANY view controller. So I might have five different view controllers that all have this toolbar, and can update labels on it.
So my question is:
How do I even begin to subclass UIView in MonoTouch? If I create a new "iPhone View", all it gives me is a nib. I have no place to hook up outlets or actions.
Given that you are able to tell me how to set up MonoTouch to have C# code files for subclassing UIView so I can handle events and access properties, how do I hook it up to the nib? I see a lot of people saying to use initWithFrame to call loadNibNamed in Objective-C, but this doesn't correctly translate to MonoTouch. How would I do that in MonoTouch?
Given that #1 and #2 are fulfilled, how do I load this custom toolbar into any viewcontroller, and add it at specific coordinates on the screen? Doing the initWithFrame CGRect stuff doesn't seem to have a simple path in MonoTouch
If anyone is able to figure this out, you are amazing!!!!
Thanks you in advance!
So after a lot of digging, I finally found the answer.
The most informative tutorial was here:
http://sgmunn.com/blog/2012/03/using-loadnib-to-load-a-view/
Essentially, you need to create custom subviews, set them up in the interface builder, then do some simple mapping on the MonoTouch side. But the key point that was throwing me off from the example was this:
You must, when subclassing UIView, implement the base constructor for IntPtr
When you do that, everything falls into place. You have your Outlets and Actions hooked up, and can manipulate them at will.
EDIT: To be more descriptive than just linking...
How do I even begin to subclass UIView in MonoTouch? If I create a new
"iPhone View", all it gives me is a nib. I have no place to hook up
outlets or actions.
Create a new iPhone view, it'll create a nib. Open the nib, set the "custom class" to your custom class. MonoTouch should generate it automatically. If it doesn't, create a new C# class and subclass UIView, but make sure you create the constructor for IntPtr. If you use Storyboards, when you call Storyboard.InstantiateViewController() it'll fill in the IntPtr value for you. Note that if you DO use Storyboard.InstantiateViewController, you need to set the identifier as well to your custom class' name.
Once you create that iPhone view, you should be able to open up "code view" in IB and hook up your outlets and actions.
Given that you are able to tell me how to set up MonoTouch to have C#
code files for subclassing UIView so I can handle events and access
properties, how do I hook it up to the nib? I see a lot of people
saying to use initWithFrame to call loadNibNamed in Objective-C, but
this doesn't correctly translate to MonoTouch. How would I do that in
MonoTouch?
There is no initWithFrame: or CGRect in MonoTouch, you use RectangleF. If you load the nib programmatically, you can use the constructor that takes in a RectangleF and use that to set x, y, width, height.
Given that #1 and #2 are fulfilled, how do I load this custom toolbar
into any viewcontroller, and add it at specific coordinates on the
screen? Doing the initWithFrame CGRect stuff doesn't seem to have a
simple path in MonoTouch
For Storyboards use Storyboard.InstantiateViewController (make sure you set the identifier in IB). For everything else, use:
var nibObjects = NSBundle.MainBundle.LoadNib("YourViewName", theController, null);
var instantiatedView = (YourClassName)Runtime.GetNSObject(nibObjects.ValueAt(0));
as depicted in the above link.
Enjoy! - Allison.
Instead of loading Nib files from iOS 9 onwards you can use container views and storyboard reference to achieve the same.
You can check here

Programmatically accessing subviews of UIView added in Interface Builder

I have a nib file where I have a view that contains a background image, a button and another image that covers the full screen (a shadow) that needs to be moved to the front.
On the view, I'm creating child views, and after creating those and adding them using [self addView] I need to move to the front the shadow image.
I'm currently using the tag attribute to find that view, but I'm thinking there's probably a better way, by means of identifying the subviews I add in Interface Builder by some name.
I tries adding a IBOutlet to connect the subview with its parent, but it didn't work (and made no sense, since the subview is already connected to its parent in some way).
The IBOutlets way should work, and is probably the best way to do it. Make sure you made the proper connection in Interface Builder after you declared them in the .h file.
The iPhone does a lazy loading of view controllers. The nib might not have been loaded in initWithCoder or any init method for that matter as Kendall specified.
viewDidLoad is the preferred place to access anything from the nib if you want to access them before the view is displayed.
Hope that helps.
At what point are you trying to access the subviews? If you try within init of a ViewController, the IBOutlets will be nil. The first method you can get at them is probably viewDidLoad.
The reason it does make sense to do things this way is that IBOutlets are just direct pointers to some component, even if they are already subviews of something else. Just saves a lot of hunting.
Using the Tag is a perfectly valid way to locate specific views, so long as you're using the viewWithTag: method. If you're already using tags, there's no need to change to IBOutlets unless you just don't like calling viewWithTag:.

Resources