Multiple Instances of the Same UIButton? - objective-c

The Problem
I'm creating a custom UIView, where I need multiple instances of a UIButton. I'm hoping to save memory (and code!) through creating one instance of each button, and then using tags to identify which button is which. I could be creating new 'up' and 'down' buttons, but I'd like to see if I can do this smarter.
The way I have it setup is 3 (should work for any number, however) UILabels, with a 'up' and 'down' button below or above each button respectively. I'm also using a count for the CGPoint location of each button, to match up with the corresponding UILabel.
What I'm doing
So what I am doing is setting the xCount to the default value, creating the 1st label, and then creating the 1st 'up' and 'down' buttons before adding the label and buttons as subviews.
Next I increase the xCount, and change the tags and set the frame for each the label, and both buttons. However, this moves the once 1st button(s) over to the new frame (as expected).
What I've tried:
I've tried setting each button to be a copy of itself after each use (after the 1st label and buttons are added to the subview), but this gives errors upon run.
button = [button copy];
This is more of a code formatting issue, rather than a problem, but I'm looking for some smarter insight onto the problem :).
This project is using Automatic Reference Counting, if that changes anything.
Thanks in advance!

First, your concerns about memory are unfounded. Just create the number of buttons you need.
Second, if you change the frame of a button (or any view), then it moves. You can't have one button in two places.
Third, copying an object uses the same amount of memory as creating two from scratch; new memory has to be allocated for the copy.
Fourth, UIButtons don't conform to NSCopying, so you can't copy them.
Fifth, your concerns about memory are unfounded. Just create the number of buttons you need.

Related

What Cocoa Views and Controls Will Create Something like Part of the Network Prefs Display (Mac OS)? [duplicate]

This question already has an answer here:
NSTableView with +/- buttons like in System Preferences using only Interface Builder
(1 answer)
Closed 7 years ago.
I'm building an OSX app and want to create a set of controls similar to what's found at bottom of the standard Network Preferences configuration panel. I'm running into some layout problems that I wouldn't have expected.
These are my specific questions:
What contains the 3 buttons so there's similar shading all they way across the row where the buttons are positioned? In particular, what's causing the area without buttons to have shading?
How do you do this without getting a double border where the row of buttons meets up with the table?
I want to do this with an xib file. This may be incredibly simple, but I'm missing something I guess.
I find that if you make a button with style "Gradient" and type "Momentary Change", then it looks like the other buttons but does not respond to clicks, so you can use that as the area after the last button. (The NSMomentaryChangeButton is documented as changing the image and title when clicked, so if you don't use an image or title, nothing should change.)
If you check Refuses First Responder in the attributes inspector, then it will not be possible to highlight this blank button using Full Keyboard Access.
Ken Thomases also brings up the issue of the blank button being shown as a button to Accessibility. One can fix that by using a subclass of NSButtonCell that has just one method:
- (BOOL)accessibilityIsIgnored
{
return YES;
}
I think that's easier than writing a custom view.
As d00dle says, avoid double borders by slightly overlapping things.
Since you want the slack space to have the same background as the buttons, and since the buttons can change appearance from release to release of the OS, the best thing to do is to get the frameworks to draw it like it would the buttons.
Rather than using an actual button as JWWalker suggests, I have used a custom view that leverages NSButtonCell to draw the background. The advantage is that you can be sure there's no chance of getting undesirable behavior. For example, a button could get focus (for users who have All Controls selected in System Preferences > Keyboard > Shortcuts > Full Keyboard Access) so that the user could Tab to it. Accessibility will report the presence of the button through VoiceOver. Etc.
Configure the button cell just like the buttons (set buttonType and bezelStyle). In the view's -drawRect: call [buttonCell drawWithFrame:rect inView:self];, where rect is similar to the frames of the buttons. Since one way to avoid double borders is to make the buttons larger than the view's bounds, you may need to do the same for rect. For example, you might want to use NSInsetRect(self.bounds, -1, -1).
The buttons are buttons... This can be accomplished with a custom view drawing border and the background "shading".
To avoid the double border where the table and the custom view meet you simply align it so they overlap by 1 point (pixel) or avoid drawing the top border in your custom view.
I don't know of any standard object capable of doing this.

First segment of my segment controller doesn't respond

I have a settings screen with a couple of segmented controls on it. All was well until I added a third. Now the new one works, and the top one works, but the one in the middle doesn't...
It used to work fine, but now the first segment doesn't respond. If I click on segments 2 through 4 my controller's method is called as expected. If I click on the first segment... nothing.
I suspect there is another flag somewhere that I hit by mistake, but I can't find it. Yes, all of the segments are Enabled. Any ideas?
I would suggest looking through the hierarchy in detail: the tap is possibly trapped by a partially overlapping view.
If a tap does not respond because the size of an element changes, it may because it is hidden by one of these:
an unconventionally long navigation item
a view with no visible content
a title view
This amazing tool has saved me time and again: Spark Inspector. It shows you the intricate overlapping layers of all your UIViews.

Change size of window in Cocoa?

I have a window whose size I need to change when the user clicks on it. I am using [self setFrame:windowFrame display:YES animate:YES] to accomplish this.
Even though the window successfully changes size (I increase its height), it moves the contents of the window up with it. How do I prevent this from happening? I want the contents to remain in place.
I am on OSX Mountain Lion developing an app for OSX using Objective-C and Cocoa.
EDIT: Constraints and/or Springs and Struts will not work as I need to move the contents around after the window is resized.
Constraints and/or Springs and Struts will not work as I need to move the contents around after the window is resized.
In that case, you should use NSViewAnimation.
A single view animation can actually perform multiple animations to multiple views, and you can even do one to a window, despite the class's name and the fact that windows aren't views in Cocoa.
You create a view animation with initWithViewAnimations:, which takes an array of dictionaries. Each dictionary identifies the target (NSViewAnimationTargetKey) and what to do to it: Either change the target's frame (NSViewAnimationStartFrameKey and NSViewAnimationEndFrameKey) or fade the target in or out (NSViewAnimationEffectKey). For your case, you'll be changing the targets' frames.
When the user does the thing that causes the resize of the window, you'll need to compute the desired overall size of the window (taking care to adjust its frame's position so it doesn't grow off the screen), as well as the new frames—both positions and sizes—of your views. Everything that will move and/or change size, create a dictionary for it and throw it into the array. Then create the view animation.
An NSViewAnimation is a kind of NSAnimation, which provides all the methods for starting and stopping the animation, monitoring its progress, hooking into it, and chaining multiple NSAnimations together. If nothing else, you'll need to start the animation.
If you are using the Interface Builder to build these views, then I believe one approach is to set the "struts and springs." These are available under the "size inspector" and are the red arrows and bars above the "autosizing" label. Play around with these to get the effect that you want, but the general idea is that the arrows control how the size of the view adjusts to changes in the size of the parent view, and the bars control the relationship of the edges of the view to the edges of the parent view as the size changes.
In constraint-based layout, set the views around the edge of your window to be a fixed distance from their superview's edge.
Xcode will infer a lot of resizability from that; if anything still isn't resizing properly, adjust its constraints so that its width and/or height is no longer constant.
The easiest way is to move your views until blue lines show up in the editor. Each blue line corresponds to a rule in the HIG about how things should be lain out, and if you drop the view there, Xcode will create constraints matching those guidelines. For example, if you set a view 20 points from the right edge of its superview, you'll get a blue line for that, and if you drop the view there, you'll create a constraint that the view must remain that distance from that edge.
The superview isn't the only view with which you can create HIG-based constraints. You can also create guideline constraints between sibling views. For example, if you put a button next to another button at the appropriate distance, you'll get a blue line across that distance, and if you drop it, you'll create a constraint that those two buttons must remain that distance from each other.
If you want to do something really custom, the three buttons in the lower-right corner of the nib editor will let you create any constraint you want. What you have selected determines what constraints you can create; the nib editor's outline view will help you make sure you have the selection you want.
You are going to have to iterate through all of your subviews and change their frame positions based on the delta of your window frame.
so if you expand your window frame by 20 in all directions, all your subviews are going to have to increase their frame positions by (20,20) to offset the windows movement.

How to organize XIB files with many overlapping elements?

I have some XIB files which are very difficult to edit because many of the subviews overlap each other completely. For example, if I position a popup volume slider where it will pop up, it covers some UILabels which become impossible to click. My only chance to be able to edit them is to double-click on them in the Document window tree, move them aside, edit, then move them back. Sometimes there are 3 or more widgets that occupy the same location in the XIB, even though only a few are visible at a time while the application is running.
How are conditionally-visible screen elements actually supposed to be organized?
I would like to be able to hide groups of views to reveal what's beneath them, but I don't see a way to do that in IB.
If I create UIViewControllers for every group, I can edit them in separate windows, but I can't see them in context, and I need a lot of view controllers...
Tip: Hold down shift while right clicking the location of the object you wish to select.
I don't think IB is able to hide groups of views during design-time, but there's no reason you couldn't add that behavior yourself using an IB plugin.

Cocoa one row table view or a horizontal list view

Is it posible to use table view to show just one row of a big amount of elements? What I'm looking for is for some kind of horizontal list, like we have in XCode preferences or Aperture image list.
It would behave just like a one columnt table view, but instead of showing the elements vertically, it should be horizontally.
Can you point me to where should I start from?
If you're okay with Leopard-only, The new NSCollectionView supports horizontal display. Just set the collection view's number of rows to 1 in Interface Builder; it'll even handle the horizontal scroll bar for you. The IconCollection sample code provides a simple demonstration of how it works. It's bindings work similarly to a table view's, except instead of rows and columns, each object represented gets an 'item' (an object of type NSCollectionViewItem) that displays it, and those items will be laid out in a grid. The sample code above demonstrates how to set up these 'items' in Interface Builder, which is definitely the easiest way.
With a table view? No. If I'm understanding correctly what you want, in the past I've created my own NSView subclass for this type of control. Define a data source protocol similar to NSTableView, and in your NSView drawRect method, draw the elements in order one by one from left to right. You can either keep track of paging in your control, or put it in a scroll view and resize yourself whenever the number of items changes.
Usually this type of thing starts off pretty simple, and gets a bit complex once you start handling caching, paging, selection, mouse and keyboard input and so on. My advice, start as simple as possible and add new features one by one, only after you've finished the previous task.