I am creating an application with multiple buttons and corresponding labels.
The layout is in a nib (.xib) file which is connected to the class I have the code below in. I'm trying to avoid using an IBOutlet for every label, since this would create a lot of extra code.
I know there is a way to reference a label using the tag:
UILabel *countLabel = (UILabel *)[self.view viewWithTag:numOfTag];
I called this within my buttonPressed method. However, when I try to change the text
[countLabel setText:#"newLabel"];
the app crashes. Am I accessing it wrong? I'm new to XCode..Help!
Thanks :)
This is not the correct way of doing it. Firstly whenever you creating UILabel in your XIB file, you can name them there only so technically, there is no need for you to set the label values in the connected view controller. Secondly, even if you want to reset the labels at the later stage, connecting them using IBOutlets is the best bet. Scanning through all sub views and then finding a particular subview with its tag is more tedious and should be avoided in this case.
EDIT:
For your case where you need to create more than 50 buttons and labels, I would suggest you to create a custom UIView with UIButton and UILabel in it. And then in the view where you want to display them, create objects in a for loop and display them. And when a UIButton is tapped it will be easy for you to fetch the associated UILabel though the custom UIView object.
Your label must not be found with viewWithTag:, so it will be nil. And when you try to access property of nil control, it might give you a crash.
Check if your label is not nil and check if it is label using
if(countLabel != nil && [countLabel isKindOfClass:[UILabel class]])
[countLabel setText:#"newLabel"];
else
NSLog(#"countLabel is not found");
viewWithTag: will search the view in the receiver’s hierarchy whose tag property matches the value in the tag parameter. This will return first control it will encounter with tag in hierarchy. So make sure your tag needs to so unique in entire view. Just make sure you are giving immediate parent of Label instead of self.view.
Edit:
To can try this way also if you are not able to track issue.
for (UIView *child in self.view.subviews) {
if([child isKindOfClass:[UILabel class]] && [child tag] == numOfTag)
{
[child setText:#"newLabel"];
break;
}
}
Related
I have a view-based NSTableView (using the Image&Text CellView).
Depending on the dataItems in the dataSource, I select an appropriate CellView to display them.
One of the templates has an additional (to the default CellView) label on it. But I don't know what is the right way to access it when I instantiate the CellView (I need to set label's value).
My question basically is - should I search for the subview (like in the following snippet) or should I subclass the CellView, add a property and so on?
NSArray* subviews = [cellView subviews];
for (id view in subviews) {
ViewType* view = (ViewType*)view;
if ( view != nil ) {
if ( [view.identifier isEqualToString:#"idSetInIB"] ) {
[view setStringValue:dataItem.someValue];
}
}
}
Thanks.
The Apple TableView Programming guide gives good basic examples on how to subclass NSTableCellView for a view based table.
It's probably a little easier to do without bindings.
And certainly better for learning it.
However, you should be able to just add another text field in Interface Builder to the table cell view.
Read the guide. It will help tremendously.
Is there a way to set all UILabels as hidden in Objective-C? I'm showing and hiding labels based on if statements and feel like I'm writing really bulky code. Is there a way to select all UILabels to setHidden:YES a la CSS?
Edit: I need one of them visible at a time, not all hidden at once.
Thanks!
If all your labels lay at the same view you can use it's subviews property:
for (UIView *subview in self.view.subviews) {
if ([subview isKindOfClass:[UILabel class]]) {
subview.hidden = YES;
}
}
And if there are numerous of views with labels you can even add a category to the whole UIView.
#interface UIView (HideLabels)
- (void)hideAllLabels:(BOOL)hide withExcludedLabel:(UILabel *)label;
#end
#implementation UIView (HideLabels)
- (void)hideAllLabels:(BOOL)hide withExcludedLabel:(UILabel *)label
{
for (UIView *subview in self.view.subviews) {
if (subview != label && [subview isKindOfClass:[UILabel class]]) {
subview.hidden = YES;
}
}
}
#end
There's no other way to do this.
Edit: the code above updated according to your needs.
If you only need 1 UILabel at all time, you can reuse the same UILabel. The advantage is you use a bit less memory and you don't need to manage all the UILabels. The disadvantage is that you need to recalculate/store the coordinates to put the UILabel and stores the content of the UILabel (the management is shifted to this).
Now that the requirement has changed, the below answer is no longer valid. However, I still keep it there, in case anyone wants to hide/show all labels.
I don't think you can do it like CSS, but we can use a trick to avoid having to loop through all the UILabels to setHidden.
You can put all the UILabels as subview of a transparent UIView. The size and origin of the transparent UIView should be configured so that the coordinate is the same as when you don't use the transparent view (to avoid confusion). When you want to hide all UILabels, you can just hide the whole transparent UIView.
This has a drawback is that all the UILabels must be on top or under the existing view. This means that you cannot freely adjust some label to be on top of certain element, and some label to be below certain element on the existing view. You need to create another view for that purpose, and things will get quite messy there.
Suppose I have a UISlider interface element that I connected to a property in my view controller called alphaSlider. Is it possible to pull out the name of that view controller property at runtime?
The approach I tried was this:
NSString *objectName = [slider description];
Here is my approach in more detail
for (UISlider *slider in sliders)
{
NSString *objectName = [slider description];
// Do some stuff with the NSString
}
But I discovered that the description is not what I thought it was (it's just a listing of the slider properties).
Not sure if I understand what you are asking, but it might be slider.tag
So if I understand you correctly, you have a bunch of UISlider interface elements that are wired to a bunch of UISlider IBOutlets in your code, and you want to figure out which one is the one you have at the moment (in your loop).
Have you tried testing for equality with each of your IBOutlets to find the one that matches? You can do this using == because the pointers will be the same.
Otherwise, I don't believe it is possible since the mapping really only exists in the xib file and is accessed at runtime when the view is being built and the IBOutlets are being populated.
I have an NSView subclass which registers for drag files in init method like this:
[self registerForDraggedTypes:[NSArray arrayWithObject:NSFilenamesPboardType]];
The drag drop works perfectly fine, but if I add a subview to this view with the exact same frame, it doesn't work any more. My guess is that the subview is block the drag event to go to super view. Can can I avoid that? Thanks
Also, I know I am asking two questions, but I don't want to create a new topic just for this: When I am dragging, my cursor doesn't change to the "+" sign like with other drags, how can I do that?
Thanks again.
UPDATE:
Here's the how I have it set up in my IB:
The DrawView is the custom class I was talking about that registered for draggedtypes. And the Image view simply is a subview, I dragged an image from the media section...
If it helps, here's my relevant code for DragView:
- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender {
return NSDragOperationCopy;
}
- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender {
NSPasteboard *pboard;
pboard = [sender draggingPasteboard];
NSArray *list = [pboard propertyListForType:NSFilenamesPboardType];
if ([list count] == 1) {
BOOL isDirectory = NO;
NSString *fileName = [list objectAtIndex:0];
[[NSFileManager defaultManager] fileExistsAtPath:fileName
isDirectory: &isDirectory];
if (isDirectory) {
NSLog(#"AHH YEA");
} else {
NSLog(#"NOO");
}
}
return YES;
}
The answer to the second part of your question is this:
- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender{
return NSDragOperationCopy;
}
if you return NSDragOperationCopy, you get the mouse badge for a copy operation. (You can and should, of course, not just return NSDragOperationCopy unconditionally; check the objects on the pasteboard to see if you can accept them.)
I'm not sure what the answer to the first part of your question is, because I'm unable to recreate the subview blocking effect.
Okay, the answer is unfortunately that you can't. The image you dragged is contained in an NSImageView, and NSImageViews accept drag events themselves, so it's grabbing the drag event and not doing anything with it. If your subview was a custom class, you could either a) not implement drag and drop, in which case the drags would be passed through; b) implement drag and drop to accept drags for the subview. In this case, you're using a class over which you don't have any control. If all you want it to do is display an image, you should be able to make another NSView subclass that does nothing but draw the image in drawRect:
As mentioned in the comments, NSImageViews have their own drag and drop enabled by default (used for accepting images that are dragged onto the NSImageView). If you don't want to use this behavior and instead want to use the super view's drag and drop behavior, you'll want to unregister the dragging behavior in the NSImageView.
Objc:
[imageView unregisterDraggedTypes];
Swift:
imageView.unregisterDraggedTypes()
(in case anyone else stumbled across this question first and not the linked one in the comments)
I want to put a custom control inside the view for my NSCollectionViewItem.
Lets say I have a custom NSView called BoxesView.BoxesView is just a view that draws a predetermined number of boxes in its view. That number of boxes is set in the init method. Lets say I set it to 8.
When I load up the collection view, all the other controls in the view work fine, (buttons, sliders, etc.) but my view won't draw.
If I set a breakpoint in the drawRect method of BoxesView it shows that the number of boxes to draw is 0! If I set a breakpoint in my init method where I set numBoxes to 8, it shows that numBoxes does actually get set to 8. Also, the init method only gets called 1 time even though there are multiple rows in the collection view.
What am I doing wrong?
UPDATE
I was able to get this working by setting the itemPrototype to load from a xib instead of being in the same xib as the NSCollectionViewItem. This is great, except it only works on 10.6 and not 10.5.
UPDATE 2
What I'm trying to do, is stick my custom view inside the view that already existed for the NSCollectionViewItem that already exists. What happens is the member variable mBoxWidth gets blown away and is zero so when it goes to draw it, nothing happens.
#implementation DumbView
- (id)initWithFrame:(NSRect)frame {
self = [super initWithFrame:frame];
if (self) {
mBoxWidth = 3;
}
return self;
}
- (void)drawRect:(NSRect)dirtyRect {
NSRect bounds = self.bounds;
[[NSColor redColor]set];
[NSBezierPath fillRect:NSMakeRect(bounds.origin.x, bounds.origin.y, mBoxWidth, mBoxWidth)];
}
#end
I didn't implement initWithCoder. That fixes everything.
NSCollectionViewItem uses a prototype view, which is duplicated and wired up for each item in the collection's represented objects.
You could go through all the trouble to make an IBPlugin for your custom view (one that exposes the numberOfBoxesToDraw binding), but that's a pain in the ass and there's an easier way: manual bindings.
Using Manual Bindings with NSCollectionView/Item
First, subclcass NSCollectionViewItem, tell IB to use this new subclass, and make sure you have an outlet in it (like boxView) that is connected to your custom view.
Next, subclass NSCollectionView (set IB to use this subclass) and override -newItemForRepresentedObject:. In it, you'll first call super (storing the result to a local variable), then manually bind your "boxView"'s number of boxes to the represented object with the "numberOfBoxes" key you're using in your model.
Have you tried overloading copyWithZone?
I'm guessing your item is getting copied and not directly init'd.