Unity Resizing Dropdown Dynamically - unity3d

I have a dropdown list on a filter panel which needs to stretch dynamically to fit the content as shown:
I have tried several content size fitters but cannot find anything, If possible I would like to set a max width it can expand to then truncate everything longer than that, I would also like it to expand only to the right with a right pivot point. I have found a similar example here: https://forum.unity3d.com/threads/resize-standard-dropdown-to-fit-content-width.400502/
Thanks!

Well. Lets start with the code from that Unity forum thread.
var widest = 0f;
foreach (var item in _inputMines.GetComponentsInChildren<Text>()) {
widest = Mathf.Max(item.preferredWidth, widest);
}
_inputMines.GetComponent<LayoutElement>().preferredWidth = widest + 40;
We want to have a max-width allowed, so any content longer than this will be truncated. So let's add that variable:
var maxWidth = 250f;
//or whatever value; you may wish this to be a property so you can edit it in the inspector
var widest = 0f;
foreach (var item in _inputMines.GetComponentsInChildren<Text>()) {
Now we use the smaller of the two width values. Then apply it to the content layout:
}
widest = Mathf.Min(maxWidth, widest);
_inputMines.GetComponent<LayoutElement>().preferredWidth = widest + 40;
The +40 should be retained because that deals with the scrollbar. Your maxWidth value should be chosen to account for this.
Finally we want the longer items to get cut off nicely.
Give each dropdown item a Rect Mask 2D component (Component -> UI -> Rect Mask 2D). The template object exists in the scene hierarchy before the game is run, just disabled. You can just add the component to the parent transform, the one with the image (so the text is clipped).
You'll need to make sure that the mask covers the same width as the image graphic and expands along with it, possibly slightly shorter on the X direction so the text gets cut off before the image border is drawn. This should happen automatically, but you will need to check and possibly make some alterations to the template object. Alternatively you can use an Image Mask, but you'll have to play with that one yourself, but it will allow for non-rectangular clipping.
That's it!

Related

unity instantiating multiple objects on the same position. I would like to spread them out

I am trying to spread out the instantiated objects to fill up the grey area in a grid layout. So far I am stuck at this point where I have all the objects stacked on eachother. I did try a few things but they would make the object not visible. Here is the code I have and two pictures. One of the scene and one of the hierarchy. scene picture hierarchy picture
public class stockSpawner : MonoBehaviour
{
[SerializeField] GameObject stockPrefab;
int x;
void Start()
{
x = Mathf.RoundToInt(scene2Calc.nS);
}
void Update()
{
if (x >= 1)
{
Instantiate(stockPrefab, transform);
x--;
}
else { Debug.Log(x + "this is x value");}
}
}
It seems all of your assets are UI. If you want to create a grid, normally I would say to instantiate the objects and change the transform at which they spawn in a double loop. In one loop you would iterate the horizontal axis of spawning while the other moves the vertical. However, as your setup is all UI, you can use some nifty components to do all the heavy lifting for you.
Attach a grid layout group to the parent object of where you want to spawn all of these UI objects. I would recommend attaching this component to the Panel in your screenshot. I would also recommend changing your installation code slightly as you are working with UI objects. To assure anchoring, rescaling, etc. work, you will want to use Instantiate(stockPrefab, transform, false);. That last parameter is instantiateInWorldSpace, which from the docs,
When you assign a parent Object, pass true to position the new object
directly in world space. Pass false to set the Object’s position
relative to its new parent..
Now getting back to the main portion of your question. If you added the grid layout component to your panel, the objects will now be aligned into a grid. There are various different fields on this component you can change. They are on the docs, but I will also list them here for clarity.
Padding The padding inside the edges of the layout group.
Cell Size The size to use for each layout element in the group.
Spacing The spacing between the layout elements.
Start Corner The corner where the first element is located.
Start Axis Which primary axis to place elements along. Horizontal will fill an entire row before a new row is started. Vertical will fill an entire column before a new column is started.
Child Alignment The alignment to use for the layout elements if they don't fill out all the available space.
Constraint Constraint the grid to a fixed number of rows or columns to aid the auto layout system.
If you properly fill out all of these fields, your UI objects when spawned and childed to the Panel object that has this grid layout component will now not be on top of each other, but will form a grid.

Unity3d. UI elements snapping/anchoring

I have canvas with vertical layout and 2 elements within (in fact it's element with only recttransform on it, let's call it container). So these 2 containers take a half of the screen by height and stretched by width, ok. How can I place an text element in above container and snap it to the bottom of this container? I tried press bottom button in recttransform widget (also with shift and alt) and it seems it doesn't affect my transform at all
P.s. May be I can use some free plugin instead of default unity components of UI layout?
There are different ways of placing your UI elements
Simply drag and drop it to the bottom where you want it
Use the anchor widget to set the anchoring to bottom with horizontal stretch and hold shift to also set pivot. Then set Pos Y to 0. Set Left and Right to 0.
Assuming you also want other elements in your containers, place a Vertical Layout Group on each container and make sure that your text element is the last child of the container in the hierarchy.
I would also advise you to seek out tutorials on Unity UI anchoring, positioning, scaling, and layout. You need a deeper understanding of how these things interact than you are likely to get from Stack Overflow. Otherwise you will suddenly find that your UI behaves in unexpected ways when rearranged or displayed on a different aspect ratio.
It's fairly easy with Unity UI system. You just need to get used to it. Here are simple steps to accomplish what you want:
Create Text element as a child of that container.
Select your newly created element and edit its RectTransform component values:
2.1. Set both Y axis anchors (min and max) to 0.
2.2. Set pivot value to 0 as well.
2.3. Set Pos Y value to 0 as well.
Now your Text element is anchored at the bottom of the container and its position (and height) is measured from the bottom of the Text element itself.

Map Center & Bounds do not update when Container Div is Resized

(pretty new to Mapbox and JS, so I'm in a bit over my head)
I'm working on a page where the user needs to adjust the size of map container. It needs to be able to get accurate bounds and center point of the resized map (via map.getBounds and map.getCenter).
When I use JS to adjust the size of the container div, the center and bounds are not adjusted.
Fiddle: http://jsfiddle.net/xcow63sm/3/
Panning/zooming results in updated center and bounds. Browser window resize (if you have width or height set to 100%) works too. I would expect changing the container dimensions would adjust center and bounds.
However, if you use the form fields to adjust the height/width of the container div, the center and bounds do not. I've tried (with increasing desperation):
function resize (){
var inputwidth = document.getElementsByName("mapwidth")[0].value;
var inputheight = document.getElementsByName("mapheight")[0].value;
var mapDiv = document.getElementById('map');
var mapcenter = map.getCenter();
mapDiv.style.width = inputwidth;
mapDiv.style.height = inputheight;
map.updateSize();
map.update();
map.resize();
map.invalidateSize();
document.getElementById("mapcenter").value = mapcenter;
Edit: this seems to apply but I can't make sense of it: Resizing a leaflet map on container resize
If I understand you, you have two issues: you don't like how the map is positioned after its size changes, and you're getting white space.
Map positioning after resize
There are at least three valid options for how a map should update if, say, its width and height are suddenly doubled:
keep the same northern and western bound, extend the eastern and southern bounds. (Expanding the viewing area right and down)
move all four bounds outwards (keeping the centre of the map in the same place)
leave all four bounds where they are are, but change the zoom, so the same geographic area is displayed, but in more detail.
I think your issue is that Mapbox is choosing option 1, but you want one of the other two options. The solution, as I think you've discovered, is simply to do your own maths and set the bounds how you want them.
Map failing to repaint correctly
Your second issue is that when you resize, calling map.resize() isn't updating the internal size of the map properly, and you're left with white space. The reason for this is you're using animated CSS properties. So when you do this:
mapDiv.style.width = inputwidth;
mapDiv.style.height = inputheight;
map.resize();
This doesn't work because the map's size hasn't actually transitioned to the new size yet. You can work around it like this:
mapDiv.style.width = inputwidth;
mapDiv.style.height = inputheight;
window.setTimeout(()=>map.resize(), 500);
There's probably a better event you can listen to, to keep updating as the map area expands.
Updated fiddle here: http://jsfiddle.net/xcow63sm/11/
Btw you should consult the documentation here. I'm not sure where you got map.updateSize() from but that's not Mapbox-GL-JS :)

Auto layout constraints not taking effect on child controls until parent container is resized (swift)

I have a program with an NSSplitView (containing two panes) filling the window. The left pane is an NSStackView and it contains three NSBoxes. The top two each contain an NSTextField for displaying a string of weather information that can change length every time it is updated by code in the background. I have added the relevant constraints to the top two NSBoxes (and their child NSTextFields) to make them expand or shrink to fit the size of the text fields inside them as the text changes length. The third NSBox fills up the remaining space in the stack view.
The problem is that when the window loads, the NSBoxes (as expected) display the correct size as designed in interface builder to fit the default string in the text fields. However, when the update code kicks in and changes the text of the text fields to the downloaded data, which is longer than the default string, the NSBoxes do not adjust their height to fit the larger text. They only change their height when the splitter of the split view is moved. This is annoying because I have to move the splitter every time the number of lines in the text fields changes. Why is this happening and how can I make the boxes update their height to fit the text when it is longer or shorter than before?
It is much like this question (the only source of information I found on my problem): NSScrollView with auto layout not resizing until first manual window resize, however the solution did not work for me.
Below is an image of the interface (again, the top two boxes should resize to fit their text but this only happens when the splitter is moved). It shows longer text than the boxes can display and they haven't resized:
One option is to create a subclass of NSBox and override the intrinsicContentSize property. In your implementation you'd measure the height of the text contained in the box's text field, take account of any vertical padding that exists between the vertical edges of the text field and the corresponding edges of the box, and return the result of your calculation as the height part of the NSSize return value. Then, any time you want the boxes to resize, you can call invalidateIntrinsicContentSize.
I created a sample app to see if I could get this to work which I've posted here. There are a couple of tricky parts which you should be aware of:
Calculating Text Height
The most involved bit of coding in this approach is calculating the height of the text. Fortunately, Apple has documentation that tells you exactly how to do it; here's how that looks in Swift:
// Properties of your NSBox subclass
var layoutManager: NSLayoutManager {
return textStorage.layoutManagers.first! as! NSLayoutManager
}
var textContainer: NSTextContainer {
return layoutManager.textContainers.first! as! NSTextContainer
}
var typesetter: NSTypesetter {
return layoutManager.typesetter
}
// The textStorage object lies at the heart of the text stack. Create this
// object first, the rest follows.
lazy var textStorage: NSTextStorage! = {
var initialString: String = ""
var ts = NSTextStorage(attributedString: self.textField.attributedStringValue)
var lm = NSLayoutManager()
lm.typesetterBehavior = NSTypesetterBehavior.Behavior_10_2_WithCompatibility
var tc = NSTextContainer()
lm.addTextContainer(tc)
ts.addLayoutManager(lm)
return ts
}()
Setting the hugging-resisting priorities for your NSBox subclass
To get the demo working correctly I found that I needed to set the vertical hugging priority and vertical compression resistance values of the NSBox objects to 900.

How to layout items "relatively" instead of absolute position?

What is the best way to layout iPhone UI items? Right now I am using CGRectMake and specifying the exact x and y coordinates of every UI item (label, view, button, etc) and adding every UI item to the current view.
One problem I see with this is that if one of the items change height or width, I will have to adjust the x and y coordinates of other UI items, so maintenance will be a challenge.
Is there an alternative to doing this? in HTML/CSS, I am used to just placing items relative to each other using margins, not absolute positioning. What do you recommend I do to keep maintenance easy and stress-free?
What I have been doing is using global constants to set the height, width, xoffset and yoffsets. Eg. I know I want 2 buttons next to each other, A and B where A is 20, 20 from origin and B is 20 right of A:
Constants.m:
CGFloat buttonHeight = 50, buttonWidth = 100;
CGFloat buttonAXoffset = 20, buttonAYoffset = 0;
CGFloat buttonBXoffset = 20, buttonBYoffset = 0;
CGFloat initialXoffset = 0, initialYoffset = 20;
(And use Constants.h to declare them with extern CGFloat blah)
To draw the buttons, set CGFloat creatorCursorX = initialXoffset, creatorCursorY = initialYoffset; which will be your relative cursor.
Then, just above creating the button A, do creatorCursorX += buttonAXoffset; creatorCursorY += buttonAYOffset;, and use creatorCursorX and creatorCursorY to set its position and buttonHeight and buttonWidth
After creating button A, do creatorCursorX += buttonWidth;
So when you get to making button B, you already have a cursor which you can use to place button B relative to button A just like above. Add offset before, add size after. And this makes it easy to switch buttons (and other layout items) around.
You can edit this method to place items down the screen as well.
Btw, to start the cursor on a new line, just set creatorCursorX = initialXoffset again and add the Y distance to creatorCursorY
I'm not an expert in this area, but I've found this method to be the most efficient and make it easy to adjust later. No need to sift through code, just shuffle chunks around for order, and go to the Constants.m to change the sizes.
I don't think there is an automatic way to achieve this.
I think your only option for this is to write a method, but even this is going to be tricky to work with, you'll need to determine the affected element and know which elements to reposition (see below for an extremely trivial example).
-(void)adjustUIElements:(CGSize)newSize{
//for example
button1.frame = CGRectMake(
(button1.frame.origin.x + newSize.width),
(button1.frame.origin.y + newSize.height),
button1.frame.size.width,
button1.frame.size.height);
//etc.
}
The great thing about this example is that you can run it any time there's a size change. It'll take a little while to get right but then you can reuse it in any function you see fit.
No. As for as I know, Relative Layout is not possible in iOS. You can use Interface builder to design the UI, which will be easier to position the UI elements.
You can also consider using the autoResizingMask property of UIView. Which is,
An integer bit mask that determines how the receiver resizes itself when its superview’s bounds change.
I couldn't find anything on the public-facing Apple site about it, but I read on one of the rumor sites that Apple will be introducing a new layout system in Lion called Cocoa Autolayout. Unfortunately, it won't be available for the next iOS release.
I don't want to link to any specific documents, but a search for "Cocoa Autolayout" on your favorite search engine will give you some general information how autolayout works.
If you're a registered iOS developer you should have access to one of the WWDC sessions on the topic.