If I display a string in a custom field using drawtext(txt,ax,ay) method, how can I calculate the string width / height.
Width:
int stringWidth = getFont().getAdvance(yourString);`
Height:
int stringHeight = getFont().getHeight();`
Those are if you're making the calls from within one of your Field's methods (meaning you have extended a Field). However, if you're simply trying to draw the string and are wondering what ax and ay represent, those are positions, not dimensions.
Related
I want two independent rows to have the same width and scroll simultaneously. I have tried to achieve this in this way:
#Composable
fun TwoRows() {
val scrollState = rememberScrollState()
Column(Modifier.fillMaxWidth()) {
Row(Modifier.fillMaxWidth().horizontalScroll(scrollState)) {
for (i in 0 until 100) {
Box(Modifier.width(10.dp).height(10.dp).background(Color.Red))
Spacer(Modifier.width(90.dp))
}
}
Row(Modifier.fillMaxWidth().horizontalScroll(scrollState)) {
for (i in 0 until 100) {
Box(Modifier.width(100.dp).height(10.dp).background(Color.Green))
}
}
}
}
The first row consists of 10 dp width red rectangles and 90 dp width spacers. The second row consists of only 100 dp width green rectangles. I'm expecting these rows to have the same width but the layout inspector shows they're not:
Because of this the elements do not match expecting positions:
How can I fix it?
This is happening because each of .size(), width(), .height() modifiers transform incoming value into pixel just in time it applied. I.e. they apply independently - the first modifier converts its value to pixels, and then the second, and so on. There is no guarantee that you get the same dp value after transforming it to px and back because of rounding. Here is how dp->px->dp transformation works for your case:
dp = 10.dp // Incoming dp value
density = 2.625 // Screen density scale value of your emulator
unrounded_px = 10 * density = 26.25
px = 26
restored_dp = 26 / density = 9.904762.dp
Most likely that the LayoutInspector doesn't round computed dp value to the nearest integer, but simply discards the fractional part. I think this is the reason why it shows different value. Such behavior is especially likely to occur when you work with relatively small views on low pixel density screens.
You can achieve desired behavior wrapping your red Box and Spacer into the Box with specified size:
Box(Modifier.width(100.dp).height(10.dp)) {
Box(Modifier.width(10.dp).height(10.dp).background(Color.Red))
Spacer(Modifier.width(90.dp))
}
In widget iOS14, I want to show values in graphical form using bar chart.
I have used this library "https://github.com/dawigr/BarChart" to draw bar chart.
But in Xcode12+, it's not showing negative values and considering negative value as 0 and throwing warning as shown in screen shot.
"[SwiftUI] Invalid frame dimension (negative or non-finite)"
You could try to normalise your input Values to prevent getting errors like this.
e.g.: if your data set contains values from -10 to 100, your min normalised value would be 0 and your max normalised value 1. This only works if your numbers are CGFloat, Double or something like this, numbers in Int format would be rounded up.
This could be done by using an extension like this:
extension Array where Element == Double {
var noramlized: [Double] {
if let min = self.min(), let max = self.max() {
return self.map{ ($0 - min) / (max - min) }
}
return []
}
}
I don't no how you get your values for the frame exactly, but I think you did something like this:
// normalise your data set:
let data : [Double] = [Double]()
youChart(inputData: data.noramlized)
// get values for the frame
let height = data.noramlized.max()
// if your normalised value is too small for your purpose (your max will always be 1.0 but in relation to the rest it might fit), you can scale it up like height * 20.
// the width might be a non changing value that you will enter manually or it will append on the count of bars in your chart.
I was writing a program in swift and just now I noticed that I can directly access a CGRect frame's width and height properties directly without using the CGSize width and height. That is I am now able to write a code like this.
#IBOutlet var myView: UIView!
override func viewDidLoad()
{
super.viewDidLoad()
var height = myView.frame.height
var height1 = myView.frame.size.height
}
In Objective C, when I tried to write the same code, the line height = view.frame.height is throwing an error. Can anyone please tell me the difference(if any) in these two lines of code.
I just looked into the CGRect structure reference. In Swift there is an extension defined which have members height and width. Please have a look at the code below
struct CGRect {
var origin: CGPoint
var size: CGSize
}
extension CGRect {
...
var width: CGFloat { get }
var height: CGFloat { get }
...
}
So that you can directly fetch height and width values from a CGRect. As you can see these are only getters, so you will get an error if you try to set these values using view.frame.height = someValue
frame is of CGRect structure, apart from its width and height have only getters, they can only be positive. From the documentation:
Regardless of whether the height is stored in the CGRect data structure as a positive or negative number, this function returns the height as if the rectangle were standardized. That is, the result is never a negative number.
However, size is of CGSize structure, from the documentation:
A CGSize structure is sometimes used to represent a distance vector, rather than a physical size. As a vector, its values can be negative. To normalize a CGRect structure so that its size is represented by positive values, call the standardized function.
So the difference is obvious.
In Objective C, when I tried to write the same code, the line height = view.frame.height is throwing an error. Can anyone please tell me the difference (if any) in these two lines of code.
CGGeometry.h defines a couple of types, among them the C struct CGRect. This struct has two members: origin and size.
That's all you can access in C (and Objective-C) using dot notation. Neither C nor Objective-C offer extensions for structs.
Swift imports the type as a Swift struct. The difference is that Swift does allow for extensions on structs. So it exposes several free C functions as extensions:
CGRectGetMinX() — CGRect.minX
CGRectGetMidX() — CGRect.midX
CGRectGetMaxX() — CGRect.maxX
CGRectGetWidth() — CGRect.width
[... same for y]
These C functions are there since ages—they just live in a dusty corner of CoreGraphics.
They are quite useful but you have to know their semantics (which differ a bit from the standard accessors): They normalise the dimensions.
This means that they convert a rect with negative width or height to a rect that covers the same area with positive size and offset origin.
let rect = CGRect(x: 0, y: 0, width: 10, height: -10)
assert(rect.width == rect.size.width) // OK
assert(rect.height == rect.size.height) // boom
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....
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.