Skip to content

Commit

Permalink
Fix bug replacing of root widget on UIKit (#1706)
Browse files Browse the repository at this point in the history
* UIStackViewTest

* fix bug replacing of root widget on UIKit

---------

Co-authored-by: Jake Wharton <[email protected]>
  • Loading branch information
Alex009 and JakeWharton authored Nov 30, 2023
1 parent 5cbb1bc commit 0bd1bb3
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,16 @@ public abstract class AbstractWidgetChildrenTest<W : Any> {
assertThat(names()).containsExactly("one", "two", "three")
}

@Test public fun replace() {
// Models what happens when a conditional flips from the second branch to the first.
// The new item will be added and then the old item will be removed.
// From https://github.com/cashapp/redwood/pull/1706.
children.insert(0, widget("one"))
children.insert(0, widget("new one"))
children.remove(1, 1)
assertThat(names()).containsExactly("new one")
}

private fun <W : Any> Widget.Children<W>.insert(index: Int, widget: W) {
insert(
index = index,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,13 @@ public class UIViewChildren(
else -> { view, index -> parent.insertSubview(view, index.convert<NSInteger>()) }
},
private val remove: (index: Int, count: Int) -> Array<UIView> = { index, count ->
Array(count) {
parent.typedSubviews[index].also(UIView::removeFromSuperview)
// Per the docs, these properties are read-only copies we can use stable indexing into.
val subviews = when (parent) {
is UIStackView -> parent.typedArrangedSubviews
else -> parent.typedSubviews
}
Array(count) { offset ->
subviews[index + offset].also(UIView::removeFromSuperview)
}
},
) : Widget.Children<UIView> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,14 @@
*/
package app.cash.redwood.widget

import platform.UIKit.UIStackView
import platform.UIKit.UIView
import platform.UIKit.subviews

@Suppress("UNCHECKED_CAST")
internal val UIView.typedSubviews: List<UIView>
get() = subviews as List<UIView>

@Suppress("UNCHECKED_CAST")
internal val UIStackView.typedArrangedSubviews: List<UIView>
get() = arrangedSubviews as List<UIView>
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright (C) 2022 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package app.cash.redwood.widget

import app.cash.redwood.widget.testing.AbstractWidgetChildrenTest
import platform.UIKit.UILabel
import platform.UIKit.UIStackView
import platform.UIKit.UIView

/**
* [UIViewChildren] has special support for [UIStackView] containers which (somewhat annoyingly)
* use a different collection for managing the children.
*/
class UIStackViewChildrenTest : AbstractWidgetChildrenTest<UIView>() {
private val parent = UIStackView()
override val children = UIViewChildren(parent)

override fun widget(name: String): UIView {
return UILabel().apply { text = name }
}

override fun names(): List<String> {
return parent.arrangedSubviews.map { (it as UILabel).text!! }
}
}

0 comments on commit 0bd1bb3

Please sign in to comment.