How to scroll to a certain item in a Column(Modifier.verticalScroll())? - android-jetpack-compose

I have a Column with TextViews with indices from -10 to 10.
Column(
modifier = modifier
.verticalScroll(
state = scrollState,
enabled = isScrollEnabled,
reverseScrolling = reverseScrollDirection
)) {
for(i in -10..10) {
TextView("Index $i")
}
}
Naturally, this Column start with -10 and is incrementing by 1 until 10; so I have to scroll forward all the 21 indices (picture left hand side).
How can I align 0 to the beginning, so that I start the view scrollable either 10 back until -10 or 10 forward until 10 (see attached picture right hand side)?

Column doesn't have a concept of item, it has just a bunch of composable functions called inside of it. Because of that, ScrollState only has a method to scroll to position in pixels.
If you know the height of your items, you can calculate the pixel position and pass it to rememberScrollState(). Otherwise, you can use LazyColumn which can scroll to specific item.

Related

Multicolumn list with flexible columns width - iOS

I want to create list with two columns. Text in first column should be aligned to left and text in second column should be aligned to right. Space between columns should be constant. Different cases depends on texts length should be covered:
1.
Text 123
TextText 12
Tex 123
Text 1
Te 123456
T 12
Te 1234
Te 1
Text 123
TextText 12
TextTextTextTextText 123
Text 1
Both columns should have flexible width depends on the longest text. They also should have some minimum width so it will not be completely invisible if text in other column will be too long. Whole list also should have flexible width, of course there is some max width and then text in first column should be divided into lines. I don't want to divide into lines text in second column as long as I don't have to.
How to create such list?
I try to use for it two stack views one next to the other. These two stack views I have in my custom control. My custom control is added to container view using xib and have constraints equals to 0 for top, leading and bottom and greater or equals 0 for trailing. To these stack views I add labels programatically and I also set content hugging priority for them (first label has lower priority than second one). One think stil doesn't work, first stack view is as width as first label in this stack view. It doesn't change his width when other labels with longer texts are added. Is it possible to have working solution without calculating stack views width manually?
private void Initialize()
{
_firstStackView = new UIStackView
{
TranslatesAutoresizingMaskIntoConstraints = false,
Axis = UILayoutConstraintAxis.Vertical,
Spacing = 4
};
_secondStackView = new UIStackView
{
TranslatesAutoresizingMaskIntoConstraints = false,
Axis = UILayoutConstraintAxis.Vertical,
Spacing = 4,
Alignment = UIStackViewAlignment.Trailing
};
Add(_firstStackView);
Add(_secondStackView);
AddConstraints();
}
private void AddConstraints()
{
NSLayoutConstraint.ActivateConstraints(
new[]
{
_firstStackView.LeadingAnchor.ConstraintEqualTo(LeadingAnchor),
_secondStackView.LeadingAnchor.ConstraintEqualTo(_firstStackView.TrailingAnchor, 20),
TrailingAnchor.ConstraintEqualTo(_secondStackView.TrailingAnchor),
_firstStackView.TopAnchor.ConstraintEqualTo(TopAnchor),
_firstStackView.BottomAnchor.ConstraintEqualTo(BottomAnchor),
_secondStackView.CenterYAnchor.ConstraintEqualTo(_firstStackView.CenterYAnchor),
_secondStackView.WidthAnchor.ConstraintGreaterThanOrEqualTo(WidthAnchor, 0.25f),
_firstStackView.WidthAnchor.ConstraintGreaterThanOrEqualTo(WidthAnchor, 0.25f),
});
}
You can use UICollectionView to create list with two columns. Space between columns and grid size can be adjusted by changing UICollectionViewLayout.
Implement text adaptive width and height:
Instantiate UILabel
Set the UILabel property to get the text content and font
Calculate the size according to text and font
Use CGSize to set the maximum width you want
Set the frame according to the size
You can see some useful info here
For more details, you can refer to the following doc:
Collection Views in Xamarin.iOS | Microsoft Docs

Jetpack Compose - Row Clipping Children When Width Increases

Here on the right , I have a list of items in a composable , Every item is inside a row , All the items are inside a column
All the children of the are getting clipped to fit the screen which I don't want , I want these items to render completely even if outside of screen since I have a zoomable container above them
As you can see how text in the text field is all in one line vertically rather than expanding the width , This is the problem
Code :
Row(
modifier = modifier.zIndex(3f),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center,
) {
SimpleNodesList(
modifier = Modifier.padding(8.dp),
parentNode = state.center,
nodes = state.center.left,
renderRight = false,
)
SimpleNode(node = state.center, parentNode = null)
SimpleNodesList(
modifier = Modifier.padding(8.dp),
parentNode = state.center,
nodes = state.center.right,
renderLeft = false
)
}
Simple Nodes List is a column of rows , I have one column on left and one on the right , If the width of the left column increases , right row get's clipped by the screen
Using this modifier does the job for the row , In my case I also needed this layout modifier , wrapContentSize(unbounded = true) was working but children weren't clickable for some reason outside the bounds of the zoom container !
I also had to create a modifier zoomable rather than use a zoomable box , so the zoomable touch events would be dispatched on the this composable rather than the parent !
modifier = Modifier
.layout { measurable, constraints ->
val r =
measurable.measure(constraints = constraints.copy(maxWidth = Constraints.Infinity))
layout(r.measuredWidth, r.measuredHeight, placementBlock = {
r.placeWithLayer(0, 0, 0f) {
}
})
}
.wrapContentSize(unbounded = true)
If you are using hard-coded width for the text, applying Modifier.wrapContentSize() on every container might do the job
Use SimpleFlowRow in place of Row. It will fix the clipping issue.

How can I change sizes and x/y coords of several UIViews with constraints in one method?

I have two UITableViews.
Once the left one(for first level category) is clicked the right one(for second level) appears.
A UIView with one UILabel and one UIButton appears when the secondary category is clicked. This UIView is shown in the first screenshot. Two-level category is displayed in the UILabel(which says '미디어 > 작가 / 시나리오') and 'X' is a button to delete the category from the array containing it and hide the UIView.
I set the maximum number of categories by 3. The next one shows the second set of category; which is 7 pixels apart from the first UIView. Also, the height of the rounded rectangle view is 19.
If the second one is too long to put in the same line as the first one is in, the background UIView(light blue) should expand its height and make space for the second UIView. To handle this, I got the following codes executed when the second category is added. The margin between the upper and the lower should be 7 pixels too.I didn't add the code going back to single line because even the following one doesn't work as I want.
I want the result to be somewhat like this:
//tableViewBorder: a UIImageView to separate two UITableViews
//index: a UIView to show what level of category each UITableView is for; which says '1차 분야', '2차 분야' in the screenshot
//dffcltyBtnContainer: a UIView on the bottom of the view controller. it is set to be on the bottom by constraints and its height is fixed.
lightBlueView.frame.size.height = 45
index.frame.origin.y = lightBlueView.frame.maxY + 7
catTableView1.frame.origin.y = index.frame.maxY
catTableView2.frame.origin.y = index.frame.maxY
tableViewBorder.frame.origin.y = index.frame.maxY
catTableView1.frame.size.height = dffcltyBtnContainer.frame.minY -
catTableView1.frame.minY - 44
catTableView2.frame.size.height = dffcltyBtnContainer.frame.minY -
catTableView1.frame.minY - 44
tableViewBorder.frame.size.height = dffcltyBtnContainer.frame.minY -
catTableView1.frame.minY - 44
The code work well when it is called in a UIButton action method. The next one shows what the result of the code above works in the button-triggered method. The height of the two UITableViews correctly decreased by the same amount of change of lightBlueView.
If the sum of two rounded rectangles plus 7 is bigger than the width of lightBlueView, the second one is supposed to go down..
However, this one does not work at all in the desired code block, as if it does not exist. 'lightBlueView' has its 19 pixels of height fixed by a constraint and so are its margins with other components with the same level in the UIView(the highest level in the view controller).**
I guess it is because the height of lightBlueView is set but not sure. Setting x and y coordinate of the second category view will be another big challenge thought but that isn't the issue now :(( What does not make the lightBlueView not change its height in the code block below? Or are their some better ways to implement what I want to do? Anything would be a big help to me. Thanks in advance.
//method 'displayResults' shows or hides UIViews for each selected category. I only change '.hidden' values in it.
//selectedResult1~3Text are UILabels.
//selectedCat is an array of struct with two Strings: prim and secn. This one has what categories are chosen.
switch selectedCats.count{
case 1:
///----change width of label----//
selectedResult1Text.text =
selectedCats[0].prim + " > " + selectedCats[0].secn
selectedResult1Text.sizeToFit()
///----change width of label----//
displayResults(true, d: false, t: false)
break
case 2:
///----change width of label----//
selectedResult2Text.text =
selectedCats[1].prim + " > " + selectedCats[1].secn
selectedResult2Text.sizeToFit()
///----change width of label----//
catTableView1.frame.origin.y = index.frame.maxY
catTableView2.frame.origin.y = index.frame.maxY
tableViewBorder.frame.origin.y = index.frame.maxY
catTableView1.frame.size.height = dffcltyBtnContainer.frame.minY -
catTableView1.frame.minY - 44
catTableView2.frame.size.height = dffcltyBtnContainer.frame.minY -
catTableView1.frame.minY - 44
tableViewBorder.frame.size.height = dffcltyBtnContainer.frame.minY -
catTableView1.frame.minY - 44
displayResults(true, d: true, t: false)
break
case 3:
///----change width of label----//
selectedResult3Text.text =
selectedCats[2].prim + " > " + selectedCats[2].secn
selectedResult3Text.sizeToFit()
///----change width of label----//
displayResults(true, d: true, t: true)
break
default:
displayResults(false, d: false, t: false)
break}

Changing view's alpha based on UIScrollView's contentOffset.x when scrolling multiple screens

I have a horizontal UIScrollView that has a width of 960.
The UIScrollView contains 3 UIView's, each one set to a background color.
The first one is pink, the second blue, and the third is green.
What I need to do is change the alpha of the colors(views) based on the user scrolling.
So if the user is on the first screen (pink) and starts to scroll to the second page (blue), then the pink should start to fade and the blue will become more visible, and once the user fully swipes to that 2nd page it would be all blue.
Here is how I do this when using only one UIView:
func scrollViewDidScroll(scrollView: UIScrollView) {
// Offset Percentage
var percentageHorizontalOffset = scrollView.contentOffset.x / 320;
// Reduce alpha
pinkView.alpha = percentageHorizontalOffset
}
This is easy because the percentage goes from 0.0 to 1.0
However, I need to modify the code to support 3 screens. I tried this earlier by replacing 320 with 960 in the method above but this causes a couple problems.
The first is that you are no longer getting a percentage in the range of 0.0 to 1.0, once you scroll on the second page it will be a range of 1.0 to 2.0 which won't help me properly modify the alpha.
The second is that the alpha change doesn't feel very smooth, especially when the UIScrollView is swiped quickly. I setup a mess of if statements earlier trying to get this to work but nothing worked.
How can I properly fade the alpha of all 3 UIView's based on the contentOffset.x when my UIScrollView's content size is bigger than just one screen?
Simplest way would be to treat the 3 colored views as an array.
// Put all your views in an array
#property NSArray *coloredViews;
// Later where you set things up, allocate the array.
coloredViews=#[pinkView,blueView,greenView];
Then use the following algorithm to set the alpha.
This assumes only 2 of N views on screen at a time. One on left and one on the right.
The alpha of the left is based on the amount of it off screen.
The alpha of the right is based on the amount of it on screen.
So as left goes off it goes to 0 causing right to go to 1 and vice versa.
This makes your scroll method:
func scrollViewDidScroll(scrollView: UIScrollView) {
// Work out which view is showing on the left. 0 to N-1
// Based on each being 320 wide.
CGFloat offset=scrollView.contentOffset.x;
NSUInteger leftHandSideViewIndex=(NSUInteger)offset.x/320;
// Left hand side alpha is 1 minus percentage (as a fraction) off screen
self.coloredViews[leftHandSide].alpha =
1.0 - (offset - (320.0 * leftHandSideViewIndex))/320.0;
// Right hand side alpha is 1 - percentage (as a fraction) on screen
// Only do this if there is a right hand side.
if (leftHandSideViewIndex < self.coloredViews.count-1){
self.coloredViews[leftHandSide+1].alpha =
1.0 - ((320.0 * leftHandSizeViewIndex+1)-offset)/320.0;
}
}
Give it a try. Might not be 100% right, but hopefully gives you the idea.You probably want to set a width variable rather than using 320.0.

How can I horizontally center two side-by-side UILabels?

I have a series of labels that I want to center horizontally (that is, along the X axis), like this:
thing1: value1
thing2: value2
etc.
Autolayout doesn't seem to allow this. Even if I stick the two together and use the guidelines to center the pair, the center constraint only applies to the first label in the pair.
The various "things" will have different lengths, and I want to colons to line up (right-justified) and the values on the left to line up (left-justified).
Is there any way to do this?
This is easy to do in code. The x-offset of the first label should be half the difference between the width of the parent view and the sum of the widths of the two labels. For example:
CGFloat xOffset = ((parentView.frame.size.width -
(view1.frame.size.width + view2.frame.size.width)) / 2);
// now position the first label using this offset
view1.frame = CGRectMake(xOffset,
view1.frame.origin.y,
view1.frame.size.width,
view1.frame.size.height);
// and position the second view relative to the first
view2.frame = CGRectMake(CGRectGetMaxX(view1.frame),
view2.frame.origin.y,
view2.frame.size.width,
view2.frame.size.height);

Resources