Is there any way to get the width of the segments in a UISegmentedControl that is proportional to content? - ios

In the case where the segments in a UISegmentedControl are proportional to content widthForSegmentAtIndex seems to return 0 when called from viewDidLayoutSubviews. Is there anyway to get the actual widths? (I am using autolayout.)
If not is there any way to get the subview that corresponds to a segment? segmentedControl.subviews[index] does not work reliably.
If there is no way to do that is there an alternative to UISegmentedControl that can be used?
Thank you!

From my testing it appears that the width of the segments will always be equal, regardless of the size of the text of each individual segment.
CGFloat segmentWidth = segmentedControl.frame.size.width / segmentedControl.numberOfSegments;
EDIT
Unfortunately, the above answer does not work if the segmentedControl is using apportionsSegmentWidthsByContent. I can't find an easy way to get the width of the segment in that case.
However, you could manually get the text of the segment you're interested in, then calculate how long it 'should' be (depending on how much padding you want on either side), then use that number and use the setWidth:forSegmentAtIndex: method to set the segment to the calculated width, then you would know for sure the width for the segment.

The following category gives NSSegmentedControl the ability to tell you its computed segment widths. It's not perfect- see the disclaimer in comments.
#implementation NSSegmentedControl (SegmentedControlWidthComputation)
/* Note: This implementation doesn't properly handle cells with images. It also makes some guesses about AppKit's behavior, and should be treated as an estimation rather than an exact value. */
- (CGFloat)computedWidthForSegment:(NSInteger)SEGIDX
{
CGFloat pad = 0;
switch (self.controlSize) {
case (NSControlSizeMini): pad = 16; break;
case (NSControlSizeSmall): pad = 22; break;
case (NSControlSizeRegular): pad = 28; break;
}
return [[NSAttributedString alloc] initWithString:[self labelForSegment:SEGIDX] attributes:#{NSFontAttributeName:self.font}].size.width + pad;
}
#end

Related

CGRect intersects 4 rects. How to figure out which rect it intersects mostly?

If you need more details: I have UICollectionViewCell which is draggable. When the cell overlaps other cells during dragging I want to know which one it overlaps mostly in order to replace overlapped cell with this cell. Is there any efficient way to do this? I found nothing helpful in CGGeometry. Thank you.
You can use CGRectUnion() in a loop, the one with a smallest area will be the one with the greatest overlap. You could write a function that deals with (and returns a) CGRects, but youd probably need to loop though your views (cells) again to find the correct one, so I'd keep it UIView level...
Eg
//helpers
CGFloat CGRectGetArea(CGRect theRect){
return theRect.size.width * theRect.size.height;
}
-(UIView *)viewClosestToView:(UIView*)keyView fromViews:(NSArray*)comparisonViews{
UIView *result=nil;
CGFloat smallestArea=0.0;
for (UIView *view in comparisonViews){
CGFloat area = CGRectGetArea( CGRectUnion(view.frame, keyView.frame) );
if ((area < smallestArea)|| ((NSInteger)smallestArea==0) ){
smallestArea=area;
result=view;
}
}
return result;
}
Use CGRectIntersection(r1,r2) to find the intersection rect (if any). Multiply width and height of the returned rectangle to get the area. Compare the 4 areas to find the largest.
This should be way more than fast enough for UI manipulation.

How to determine Line Spacing in pixels from a given font (for TVirtualTreeView)

I would like to determine real font line height based on font taken from system. The font I use is system icon font.
Here is my code so far.
LOGFONTW lf;
ZeroMemory(&lf, sizeof(lf));
// Get icon font size from the system
if (SystemParametersInfoW(SPI_GETICONTITLELOGFONT, sizeof(lf), &lf, 0))
{
int H = 7;
{
// Create TBitmap and TFont
boost::scoped_ptr<Graphics::TBitmap> bmp(new Graphics::TBitmap);
boost::scoped_ptr<TFont> fnt(new TFont);
// Assign font from the system
fnt->Name = lf.lfFaceName;
fnt->Height = lf.lfHeight;
bmp->Canvas->Font->Assign(fnt.get());
// Calc height (returns 13 for default font size, 96 DPI but should be more like 18)
H = bmp->Canvas->TextHeight("Wq");
}
VST->DefaultNodeHeight = H;
VST->Font->Name = lf.lfFaceName;
VST->Font->Height = lf.lfHeight;
}
Now here is the problem. The above calculates text height which is 13 pixels for default font size at 96 DPI. But it should be 18 actually for nice pitch and line spacing. The difference increases as DPI is enlarged or font size is enlarged. If VirtualTreeView DefaultNoteHeight is set to 13 pixels it all looks very tight and lines are too close to each other.
What I need to know is actually line spacing as explained here:
http://msdn.microsoft.com/en-us/library/xwf9s90b%28v=VS.71%29.aspx
Please explain how do I extract line spacing from the given font from the system.
I believe GetTextMetrics holds the key but I just miss small piece of the puzzle to put it all together how to use it together with the above.
Examples in Delphi also welcome doesn't have to be in C++ Builder.
Update:
I've revised the formula a bit by adding:
H = bmp->Canvas->TextHeight("Wq");
// Take care of smaller heights to make them minimum 18 pixels
H = (H < 18)? 18 : H;
Seems to work OK for all font sizes I've tested with (for larger fonts it is a bit more tight but that's OK).
I have used this code to calculate the rect needed to display a block of text, it might be of some use to you:
It's an adaptation from my original code, but it should give you the idea: DT_CALCRECT is the flag to set so it returns the rect needed.
RECT rc_ancho_en_gui;
int height = 0;
rc_ancho_en_gui.left = 0;
rc_ancho_en_gui.right = 100;
rc_ancho_en_gui.top = 0;
rc_ancho_en_gui.bottom = 100;
height = DrawText(BitmapHandle, "Wq", 2, &rc_ancho_en_gui, DT_CALCRECT | DT_WORDBREAK);
I used before TextHeight but it didn't work fine, either giving me more or less than what was really needed. This way I haven't had any issues with the resulting height.

UISlider limitations in IPhone

In UISlider Control if I set "MinimumValue = 0" & "MaximimValue=2000" then I am not able to set the exact value. On release of slider thumb image it changes the value to + or - 5 to10. My slider width = 225px.
For example:- if I try to set the slider value to 100 then on release of slider thumb it shows me result like 105 or 95.
Code
IBOutlet UISlider * slider;
//"Slider" Object attached using XIB(.NIB)
slider.minimumValue = 0;
slider.maximumValue = 100;
//Attached Below Method Using XIB(.NIB) with "value Changed" event.
-(IBAction)slider_Change:(id)sender;
{
//Assigning value to UITextField
textFiled.text = [NSString stringWithFormat:#"%d",(((int) (slider.value)) * 20)];
}
Because you have set your slider range to be so large each pixel on the screen is equivalent to 8.888.
2000 / 255 = 8.8888888889
To get graduations of 1 you'll need to move the slider 1/8 of a pixel (this is impossible).
Alternatives are:
Make the slider longer
Reduce the slider range
Use a different control UIStepper
In addition to the answer by #rjstelling, it seems as if you are trying to convert the value of the slider to become an integer value. I believe that the default of the slider is a double value, this would cause the value to become rounded as it is increasing, since there will not be decimal values. Unless it is a requirement, see if you can use double.
textFiled.text = [NSString stringWithFormat:#"%f",(slider.value * 20.0)];
or if for some reason you'd want to restrict the level of decimal places you could go, you could do:
textField.text = [NSString stringWithFormat:#"%.1f", slider.value * 20.0];
This will give you the result of a decimal like 1.5 as your answer.
You could do anything with the decimal or even do something like %1.2f, %4.3f, or even %10.4f,
those will produce results like: 0.34, 0012.446, and something extreme like 000000345.1111
Hope this helps

how to calculate Labelfield and HorizontalFieldmanager height in blackberry

I have HorizontalFieldManager,VerticalFieldManager and LabelField. LabelField placed in HorizontalFieldManager and multiple HorizontalFieldManager are placed in VerticalFieldManager.
When i try to get LabelField height using labelfield.getHeight(); it returns 0 . if there are multiple line in Labelfield, it also give me height returns 0. same issue i m facing for HorizontalFieldManager .
After getting there height i want to calculate VerticalFieldManager height and set height dynamically for the screen.
How can i calculate the height of Label or Horizontalfieldmanager?
Use labelField.getPreferredHeight() and manager.getPreferredHeight() not labelField.getHeight()
This should work for you
The method Farid suggests it's a bit difficult to use, because you will need labelWidth.
For multiline label this labelWidth may not be the same as parent managers available width, or some exact width you had set, because each line can have different width depending on if words did fit maxWidth or didn't.
NOTE: as Nate pointed out in comments, it's a good idea to also add advance for spaces.
I modified the code to include that.
Here is the method I use to overcome these problems:
public int getPreferredHeight() {
String text = getText();
int maxWidth = getManager().getPreferredWidth();
int spaceAdvance = getFont().getAdvance(' ');
String[] words = StringUtilities.stringToWords(text);
int lastWordAdvance = 0;
int lines = 1;
for (int i = 0; i < words.length; i++) {
int wordAdvance = getFont().getAdvance(words[i]) + spaceAdvance;
if (lastWordAdvance + wordAdvance < maxWidth ) {
lastWordAdvance += wordAdvance;
} else {
lines++;
lastWordAdvance = wordAdvance;
}
}
return (int)lines * getFont().getHeight();
}
If you can leave the decision about the size of the LabelField until after it has been laid out, then you will get it accurately from getHeight(). This means you can actually get the correct result, including factoring in any margin or padding for the LabelField, that I think
int maxWidth = getManager().getPreferredWidth();
will miss.
Initially this seems quite difficult, because typically it is good to know the height when you add the Field to the screen. But the general principle is that you should do this in the Manager's sublayout, so you are just moving the code that is dependent on the height, a bit later in the process. And this has the benefit that the layout is dynamic, so if the LabelField's text is changed, then layout will be invoked and your code that is dependent on the height gets re-invoked too.
It is also possible to use logic like this in sublayout():
super.sublayout(...);
if (myField.getHeight() < 100 ) {
myField.setMargin((100 - myField.getHeight())/2, 0, (100 - myField.getHeight())/2, 0);
super.sublayout(...);
}
This is not a production suitable example, hard coding a pixel height is not recommended. It is just an easy example to understand....

iOS: Algorithm to center a word in a sentence inside of a UILabel

I need to center a particular word in a sentence by truncating the beginning and endings of a long sentence inside of a UILabel, For example
NSString mySentence = #"This is my very long sentence to give you an example of what I am trying to do.. And its still going..";
NSString myWord = #"example";
<Algorithm goes here>
Should display:
"...sentence to give you an example of what I am trying to..."
If the word is closer to one end or the other, just do your best to center and display an appropriate amount of the text, example:
NSString myWord = #"my";
"This is my very long sentence to give you an example of what..."
Any ideas?
Thanks.
I believe you can do a scan for your search to be placed. Let's say you have the index in a variable named centerWordIndex. You could then split the string based on whitechars, and add words to the beginning and the end of your word until you are out of words at each side, or until the size of you string matches the size of the label.
what do you think :) ?
The data you need to start is:
the width of your label (call it labelWidth)
the width of the word you want to center (call it wordWidth)
Then the size on each side you have to work with is (labelWidth - wordWidth) / 2. Don't worry about using this value, it's just the target.
You can use the ability to calculate the size of a NSString using
CGSize newSize = [myString sizeWithFont: myFont];
CGFloat newWidth = newSize.width;
The goal of the algorithm should be to continue to add words on each side of the centered word and recalculating the total width each step. If you've gone past labelWidth, you can't add that word or anymore from that direction. So, in pseudocode, one approach is:
calculate labelWidth and wordWidth
set currentWidth to wordWidth
set currentLeftPosition to position of first letter of word
set currentRightPosition to position of last letter of word
set currentString to word
set currentImbalance to 0
algorithm start:
scan for position of 2 spaces to left of currentLeftPosition or start of string
set leftTrialPosition to position found
set leftTrialString as between leftTrialPosition and currentRightPosition inclusive
calculate trialLeftWidth of leftTrialString
scan for position of 2 spaces to right of currentRightPosition or end of string
set rightTrialPosition to position found
set rightTrialString as between currentLeftPosition and rightTrialPositon inclusive
calculate trialRightWidth of rightTrialString
if (trialLeftWidth - currentImbalance <= trialRightWidth
&& trialLeftWidth <= labelWidth
&& trialLeftWidth != currentWidth)
set currentImbalance -= calculate width of string from leftTrialPosition to currentLeftPosition
set currentLeftPosition = leftTrialPosition
set currentWidth = trialLeftWidth
set currentString = leftTrialString
else if (same checks for right)
same steps using right data
else if (both left and right are larger than label or both sides no longer grow bigger)
algorithm is done - return here
recurse to algorithm start
Using this basic strategy, you track the left imbalance (negative) or right imbalance (positive) throughout the algorithm and preferentially add the left or right word accordingly until you have the biggest string of complete words that can fit on the label or you have used the full string. The key iOS specific part here is the NSString method that calculates the width.

Resources