How do I set a property on a custom view instantiated from a XIB - objective-c

I am just trying to get my head around MVC in Objective C and IOS but am having a problem I'm hoping someone can help me with.
I have created a custom view (created as a child UIView in a XIB) that uses a simple delegate protocol for requesting information from its delegate in drawRect. I have a view Controller that implementes the protocol and is connected to the view through interface builder.
The custom view also has a few properties that I want to set on startup.
The problem I have is working out how the controller is supposed to access the view to set these properties as it doesn't appear to have direct access to it. Also the properties don't seem to be visible in interface builder inspector as I would expect unlike the delegate property I added.
Initially I thought I could do something like
[self.view setViewIntProperty:10]
But that would be calling the main XIB view and my custom view is actually a child of this view so I need someway to get that specific child view to I can initialise it from the controller in viewDidLoad.
Hopefully that is clear. I'm sure this should be easy and I've missed something simple but can't see how this should normally work.

You can just create another property on your view controller of type MyCustomView*.
Declare that property as an IBOutlet and you wire that up in IB.
Then in your view controller you can use that property to access that custom view.

Your custom view's properties (as opposed to its outlets) can only be set in code unless you create an IB plugin for it.
Your other subviews can be accessed easily if you create an outlet for each of them in your controller. The view outlet is there as the primary view of that view controller. There is nothing preventing you creating additional outlets to other views/controls. Yu would just need to subclass the view controller and add the outlets as needed. Just remember to set the class name of the controller (in Interface Builder) to that of your custom subclass. That will expose the available outlets for you to connect.
You'd still need to create an Interface Builder plugin if you want to make your control's custom properties available in IB's inspector palette. Unless you plan to reuse it frequently in other applications or make it available to others, it's probably easiest just to set the properties in your source code.

Example for setting corner radius of your custom subview (subclass of UIButton in my case) from xib.
Create a property like this
#property (nonatomic, assign) IBInspectable CGFloat cornerRadius;
Override setter in your custom view's implementation file.
-(void)setCornerRadius:(CGFloat)cornerRadius
{
self.layer.cornerRadius = cornerRadius;
}
Drag your view in xib and change its class to your custom class.
Magic... You will see the custom properties appearing in attribute inspector like this.

Related

Change a button's background image programmatically in cocoa

All of the topics that I have searched are outdated or not complete in Obj-c.
I need to learn how to change a button's background image programmatically, when having the actual button in the InterfaceBuilder. (This sounds odd, but I need it for NSCollectionView as I have many similar button with different background images).
In the interface builder I drag a button onto my view window, what should I do after?
I understand that:
I need to create a NSButton Class
Connect the button from the interface builder to the code
Set the image
I have been struggling with this.
So did you connect the button to an IBOutlet property? If so then all you need to do is use [button setImage:]
If you haven't already done so, make sure the object instance that you want to change the image from is in interface builder, I.e has been dropped in as one of those blue boxes. Then if you set the object's class and have an IBOutlet property in the header file you can just drag the button outlet in the outlets tab (looks like an arrow) to the actual button itself to link the two
Edit: So it appears you're having trouble with the actual connecting part of the button? Chances are your IB file looks a bit like this:
Look for the objects section in the left hand list. These are the actual objects in your code that you can connect your button to. You might see an app delegate object there, which is included in the default IB file generated when you first create a project. If you want to handle the image changing in your app delegate, then simply add this property to your AppDelegate.h file to create an outlet:
#property IBOutlet NSButton *button;
If you go back to interface builder and select the app delegate object, you can see the outlet that you just created under the outlet tab:
Drag the little circle thing to the button to connect it, that should be the easiest bit.
But I'm going to just presume that you want to call it from somewhere else other than your app delegate, and for that we'll need to do some more explaining. If the class you want to call it from is a subclass of NSView and is already in your interface builder, you can just add that line to your view's header file and it will appear under the view's outlet tab.
If you want to call this from another object that isn't a view or such, you'll need to do either two options:
Create the object instance in interface builder. This means that instead of creating in normally with alloc] init]; etc. you'll have to actually drag in an object into interface builder. This can change the structure of your object quite a bit as you'll no longer be able to create it at will, since it will automatically be instantiated whenever you load your .nib file. Also important to note is that your init function will not be called anymore and you'll need to use awakeFromNib instead. If you do choose to go down this route, just drag over an object:
Add your outlet property to the header file:
Set the object's class:
And connect the outlet:
If making objects XIB loaded just isn't your thing, you can always just connect the property outlet to your app delegate/view controller and access it from that instead. Hopefully this clears things up, if this was the problem you were having.
For iOS:
Don't need to create a NSButton subclass.
You only need to add button on Storyboard, set the IBOutlet property for your button (ctr+drag from your button to your view controller), and set the background image with:
[myButton setBackgroundImage:[UIImage imageNamed:#"ImageName"] forState:UIControlStateNormal]
For MacOS:
You can use
setImage:
as describe on Apple Doc, and changes its size/position
(Sorry for my bad English)
ofcourse as Duukee Said,i think no need to create any NSButton Or UIButton Instances Manually When We have an object in Interface Builder,We can just use it's outlet as follows,
UIImage* Desired_Image=[UIImage imageNamed:#"yourimage.png"];
[My_Button setimage:Desired_Image forState:UIcontrolstateNormal];
HTH!Happy Coding :)

Accessing class functions

I have created a UIView class. In this class it has touchesbegan and touches moved functions etc. When I create an instance of this class in my UIView controller they don't work.
Is it not possible for the instance to access these functions? I know the class works because if I drag a UIView onto the View Controller and assign it with the class I can drag it and resize it etc.
I have created a UIView class. In this class it has touchesbegan and
touches moved functions etc. When I create an instance of this class
in my UIView controller they don't work.
Check the userInteractionEnabled property of the view. The default value for any UIView is YES, so your view should track touches, but you may have disabled the view in your storyboard or .xib file.
Is it not possible for the instance to access these functions?
It's possible, and the entire touch handling mechanism relies on it. If your view is enabled, and it's not covered up by some other view, and there aren't any gesture recognizers that are stealing its touch events, it should be receiving touch messages.

How to connect dataSource outlet of a Page View Controller using Storyboard in Interface Builder

According to Apple's documentation here, we should be able to add a Page View Controller into the storyboard and then optionally set the data source by connecting the outlets.
Creating a Page View Controller Interface Using a Storyboard
The Page-Based Application Xcode template creates a new project with a page view controller as the initial scene.
To add a page view controller to an existing storyboard, do the following:
Drag a page view controller out of the library. Add a page view controller scene to your storyboard.
In the Attributes inspector, set up the appropriate options.
Optionally, set a delegate, a data source, or both by connecting the corresponding outlets.
Display it as the first view controller by selecting the option Is Initial View Controller in the Attributes inspector (or present the view controller in your user interface in another way.)
I then defined a UIPageViewController subclass like so
#interface DetailsPageViewController : UIPageViewController <UIPageViewControllerDataSource>
but then when I tried to connect the data source outlet, it does not highlight the controller or allow to connect it. I have also tried implementing UIPageViewControllerDataSource on other controllers but I have the same problem of not being able to connect the outlet.
Can anyone help?
I failed to find a way to do it in IB. Have to use the following instead:
self.delegate=self;
self.dataSource=self;
Note that the Apple documentation states that UIPageViewController is not normally subclassed. Your UIPageViewControllerDataSource does not need to be a subclass of a View Controller. You can make it a subclass of NSObject.
Normally only things that appear on the storyboard, namely UI elements, are listed in the document outline that appears to the left of the storyboard (provided it has not been hidden). If your delegate/datasource is not already there, you can put it there, by dragging an 'Object' (yellow cube) into the document outline, in the appropriate scene.
Then click on the Object that you just added, and use the Identity Inspector pane to alter its concrete class to your data source class. It's then available to be used as the target of a connection in the normal way by dragging a line from the Connections inspector onto it.

iOS: UIView directly implementing a Nib

Sorry if this has an obvious answer, I can't seem to find anything describing this situation. How can I set up a nib file which directly implements a custom UIView? In other words, the top-level view in the nib file becomes an instance of my custom UIView class? The closest I've been able to find so far is to create a custom UIView which loads the nib and sets it's top-level view to a UIView* property (and all it's subviews to the appropriate outlets by loading with owner:self). This works, but as I understand the top-level View does nothing except act as a container for all the other views - which is what my custom UIView is supposed to do. Any suggestions?
Edit: I got this in the end: the issue is my custom view class was set as the File's Owner class, when it should have been set as the top-level View's class. I didn't know it was possible to set outlets on elements in the IB as well as the Owner. Setting the Owner to the class of the ViewController that will hold it and the top-level view to my custom view has it all working.
If you create a project using the single view template, you will get an empty view. If you add a class that is a subclass of UIView, you can change the class of that view in IB to your class (in the identity inspector).
If I have understood your question correctly, then this should help -
In the IB, on the right-pane. select the "custom class" dropdown & fill in your custom UIView class. The image shows UITableViewCell instead of that put your custom UIView class name.
Correct me if I have not understood your question correctly.

The relationship between UIViewController and UIView

I'm trying to understand how these two are connected. Every time you make a UIViewController does it also automatically come with its own UIView?
Also are these from Cocoa or Objective-C?
UIViewController is a Cocoa Touch class built for the purpose of managing UIViews. It expects to have a view hierarchy, but you don't "automatically" get a view (this is slightly inaccurate; see edit below). Usually you will obtain views by calling initWithNibName on your view controller.
There is some built-in magic in Interface Builder which knows that if File's Owner is a UIViewController (or subclass), there is a property called view. That's about it.
Once you have linked a view controller and a view, the view controller does a fair amount of work for you: it registers as a responder for view touch events, registers for device rotation notifications (and handles them automatically, if you wish), helps you take care of some of the details of animation, and handles low-memory conditions semi-automatically.
Edit: correction—if you don't call initWithNibName or set the view property manually, the view property getter will invoke loadView if view is nil. The default implementation of loadView will see if you've set nibBundle and nibName and attempt to load the view from there (which is why you don't have to call initWithNibName, most of the time), but if those properties aren't set, it will instantiate a UIView object with default values. So technically, yes, it does automatically come with its own UIView, but most of the time that's of little value.
UIViewController doesn't automatically come with a view. You have to make a view in the -loadView method. By default, this loads the view from the nib file you've specified. You can also override this method to make a custom view if you prefer not to use a nib.
Also, the view is not created right when the UIViewController is created. UIViewController uses a technique known as lazy-loading to defer the creation of a view until the view is actually accessed for the first time.