How can I detect keyboard opening and closing in jetpack compose? - android-jetpack-compose

The only way I've found in compose is to use accompanist-insets and that removes window insets. And such causes other problems with my app's layout.
The Android way seems to be this and I could pass that into my compose app and act accordingly.
Is there another way in jetpack compose?

Update
With the new WindowInsets API, it gets easier
First, to return the correct values, you need to set:
WindowCompat.setDecorFitsSystemWindows(window, false)
Then to use Keyboard as a state:
#Composable
fun keyboardAsState(): State<Boolean> {
val isImeVisible = WindowInsets.ime.getBottom(LocalDensity.current) > 0
return rememberUpdatedState(isImeVisible)
}
use example:
val isKeyboardOpen by keyboardAsState() // true or false
ps: I've tried to use WindowInsets.isImeVisible, but it returns true in the first call.
Without an experimental API
if you want with the statement, I found this solution:
enum class Keyboard {
Opened, Closed
}
#Composable
fun keyboardAsState(): State<Keyboard> {
val keyboardState = remember { mutableStateOf(Keyboard.Closed) }
val view = LocalView.current
DisposableEffect(view) {
val onGlobalListener = ViewTreeObserver.OnGlobalLayoutListener {
val rect = Rect()
view.getWindowVisibleDisplayFrame(rect)
val screenHeight = view.rootView.height
val keypadHeight = screenHeight - rect.bottom
keyboardState.value = if (keypadHeight > screenHeight * 0.15) {
Keyboard.Opened
} else {
Keyboard.Closed
}
}
view.viewTreeObserver.addOnGlobalLayoutListener(onGlobalListener)
onDispose {
view.viewTreeObserver.removeOnGlobalLayoutListener(onGlobalListener)
}
}
return keyboardState
}
and to detect/check the value you'll only need this:
val isKeyboardOpen by keyboardAsState() // Keyboard.Opened or Keyboard.Closed

Here is a solution that uses OnGlobalLayoutListener to listen to changes to the layout and uses the new window insets APIs to perform calculations, as recommended by the documentation. You can place this code anywhere inside a #Composable function and handle the isKeyboardOpen as you wish. I tested and it works on API 21 and above.
val view = LocalView.current
DisposableEffect(view) {
val listener = ViewTreeObserver.OnGlobalLayoutListener {
val isKeyboardOpen = ViewCompat.getRootWindowInsets(view)
?.isVisible(WindowInsetsCompat.Type.ime()) ?: true
// ... do anything you want here with `isKeyboardOpen`
}
view.viewTreeObserver.addOnGlobalLayoutListener(listener)
onDispose {
view.viewTreeObserver.removeOnGlobalLayoutListener(listener)
}
}
For me the other solutions wouldn't work well: the keyboard would result as always closed.
In OnGlobalLayoutListener-based answers, the used formula does not seem to behave as it should, and old APIs are used.
In the WindowInsetListener-based answer, since view is not the root view, no window insets would be applied on it. I tried replacing view with view.rootView, and although the keyboard-detection code would then work, passing the root view to setOnApplyWindowInsetsListener replaces any listener set by components, which is obviously unwanted.

I found a way with Android's viewTreeObserver. It essentially is the Android version but it calls a callback that can be used in compose.
class MainActivity : ComponentActivity() {
var kbGone = false
var kbOpened: () -> Unit = {}
var kbClosed: () -> Unit = {}
override fun onCreate(state: Bundle?) {
super.onCreate(state)
setContent {
kbClosed = {
// dismiss the keyboard with LocalFocusManager for example
}
kbOpened = {
// something
}
MyComponent()
}
setupKeyboardDetection(findViewById<View>(android.R.id.content))
}
fun setupKeyboardDetection(contentView: View) {
contentView.viewTreeObserver.addOnGlobalLayoutListener {
val r = Rect()
contentView.getWindowVisibleDisplayFrame(r)
val screenHeight = contentView.rootView.height
val keypadHeight = screenHeight - r.bottom
if (keypadHeight > screenHeight * 0.15) { // 0.15 ratio is perhaps enough to determine keypad height.
kbGone = false
kbOpened()
} else(!kbGone) {
kbGone = true
kbClosed()
}
}
}
}

Detecting whether keyboard is opening or closing can be inspected with WindowInsest.ime
Set WindowCompat.setDecorFitsSystemWindows(window, false)
To check whether it's open or close use
WindowInsets.isImeVisible
Check if it's going up or opening with using bottom offset however it's not always reliable you need to do extra steps to check if it's opening or closing
val offsetY = WindowInsets.ime.getBottom(density)
you can compare a previous value and detect if it's opening closing, open or close
https://stackoverflow.com/a/73358604/5457853
When it opens it returns values such as
17:40:21.429 I OffsetY: 1017
17:40:21.446 I OffsetY: 38
17:40:21.463 I OffsetY: 222
17:40:21.479 I OffsetY: 438
17:40:21.496 I OffsetY: 586
17:40:21.513 I OffsetY: 685
17:40:21.530 I OffsetY: 764
17:40:21.546 I OffsetY: 825
17:40:21.562 I OffsetY: 869
17:40:21.579 I OffsetY: 907
17:40:21.596 I OffsetY: 937
17:40:21.613 I OffsetY: 960
17:40:21.631 I OffsetY: 979
17:40:21.646 I OffsetY: 994
17:40:21.663 I OffsetY: 1004
17:40:21.679 I OffsetY: 1010
17:40:21.696 I OffsetY: 1014
17:40:21.713 I OffsetY: 1016
17:40:21.730 I OffsetY: 1017
17:40:21.746 I OffsetY: 1017
While closing
17:40:54.276 I OffsetY: 0
17:40:54.288 I OffsetY: 972
17:40:54.303 I OffsetY: 794
17:40:54.320 I OffsetY: 578
17:40:54.337 I OffsetY: 430
17:40:54.354 I OffsetY: 331
17:40:54.371 I OffsetY: 252
17:40:54.387 I OffsetY: 191
17:40:54.404 I OffsetY: 144
17:40:54.421 I OffsetY: 109
17:40:54.437 I OffsetY: 79
17:40:54.454 I OffsetY: 55
17:40:54.471 I OffsetY: 37
17:40:54.487 I OffsetY: 22
17:40:54.504 I OffsetY: 12
17:40:54.521 I OffsetY: 6
17:40:54.538 I OffsetY: 2
17:40:54.555 I OffsetY: 0
17:40:54.571 I OffsetY: 0

Now, with the new WindowInsets api, WindowInsets.isImeVisible can be used. For reference, see this.

In Jetpack compose:
#Composable
fun isKeyboardVisible(): Boolean = WindowInsets.ime.getBottom(LocalDensity.current) > 0
It will return true or false,
True -> Keyboard Visible
False -> Keyboard Not Visible

Also we can use WindowInsetListener, something like this
#Composable
fun keyboardAsState(): State<Boolean> {
val keyboardState = remember { mutableStateOf(false) }
val view = LocalView.current
LaunchedEffect(view) {
ViewCompat.setOnApplyWindowInsetsListener(view) { _, insets ->
keyboardState.value = insets.isVisible(WindowInsetsCompat.Type.ime())
insets
}
}
return keyboardState
}

Related

How is Self in protocol interpreted in subclass?

After reading an discussion about using Self in protocol, I did an experiment (see code below). I thought the code would fail to compile because, from my understanding, for class B to conform to Copyable protocol, it should have init(_ instance: B), which I didn't define. But the code actually works.
I wonder why? Thanks for any explanation.
1 import Foundation
2 protocol Copyable: class {
3 init(_ instance: Self)
4 }
5 class A: Copyable {
6 var x: Int
7
8 init(x: Int) {
9 self.x = x
10 }
11
12 required init(_ instance: A) {
13 self.x = instance.x
14 }
15 }
16 class B: A {
17 var y: Int
18
19 init(x: Int, y: Int) {
20 self.y = y
21 super.init(x: x)
22 }
23
24 required init(_ instance: A) {
25 self.y = 1
26 super.init(instance)
27 }
28 }
29 var a = A(x:1)
30 var b = B(a)
According to the documentation Self in this case will be A since A is the one conforming to the protocol, B is only doing it indirectly as a subclass of A.
So when A conforms to Copyable you are saying that A and all its subclasses must have an init(_ instance: A)
In a protocol declaration or a protocol member declaration, the Self type refers to the eventual type that conforms to the protocol.
You can actually test this by removing the required init(_ instance: A) init and you will get an error even if you have an init(_ instance: B), so since A is the class conforming to the protocol you must have an init where the instance argument is A

Awesome WM: Placing tiled clients in specific order on startup

I've installed Awesome WM about a week ago. Since then I've been trying to place terminal clients (bare terminal and vim, vifm, htop) in a specific order on startup. Here is a visual representation of what I'm trying to achieve:
########################
# # htop #
# ###########
# vim # bare #
# ###########
# # vifm #
########################
I've managed to place vim in the right position, but other windows are placed in what seems to be an arbitrary order, which changes with every reboot. Here is the content of my autostart.lua config:
1 local awful = require("awful")
1
2 awful.spawn.single_instance(terminal.."-e xmodmap ~/.Xmodmap; exit")
3 awful.spawn.single_instance("brave-browser", {
4 fullscreen = true,
5 focus = true
6 })
7
8 awful.spawn(terminal.." -e vim", {
9 tag = "edit",
10 placement = awful.placement.left,
11 callback = function(c) awful.client.setmaster(c) end})
12 awful.spawn(terminal.." -e htop", {
13 tag = "edit",
14 height = 80,
15 placement = awful.placement.top_right})
16 awful.spawn(terminal, {
17 tag = "edit",
18 placement = awful.placement.right})
19 awful.spawn(terminal.." -e vifm", {
20 tag = "edit",
21 placement = awful.placement.bottom_right})
22
23 awful.spawn(terminal.." -e neomutt", {
24 tag = "communication",
25 fullscreen = true })
26
27 awful.spawn("Spotify", { tag = "read" })
The layout of the tag with which I have problem is "tile". I'm on Awesome v4.3. What client property should I add to get the desired behavior?
To get clients been spawned in the desired positions on startup callback option should be used. Here is a chunk of my autostart.lua file related to the issue:
1 local awful = require("awful")
1
2 local function spawn_vifm ()
3 awful.spawn(terminal.." -e vifm", {
4 tag = "edit",
5 placement = awful.placement.bottom_right
6 })
7 end
8
9 local function spawn_term ()
10 awful.spawn(terminal, {
11 tag = "edit",
12 placement = awful.placement.right,
13 callback = function(c) spawn_vifm() end
14 })
15 end
16
17 local function spawn_htop ()
18 awful.spawn(terminal.." -e htop", {
19 tag = "edit",
20 placement = awful.placement.top_right,
21 callback = function(c) spawn_term() end
22 })
23 end
.......
38 awful.spawn(terminal.." -e vim", {
39 tag = "edit",
40 placement = awful.placement.left,
41 callback = function(c)
42 awful.client.setmaster(c)
43 store_active_client(awful.tag.find_by_name(awful.screen.focused(), "edit"), c)
44 spawn_htop()
45 end
46 })
Spawning the next client in the callback function of the previous one ensures, that the placement property will be preserved for both of them.
I don't know what you mean by this: "The layout of the tag with which I have problem is tiled left." I assume you mean that your terminals aren't tiling properly? I've used AwesomeWM for about a week a few years ago, but realized quickly it needs a lot of tinkering to get exactly how you want it. What's happening to you is exactly what I was running into.
Found it easier just to use LXDE and Devilspie2. You can Lua script windows to undecorate & maximise, jump to other desktops or whatever you want, fairly easily. This might help get you where you're going, but it's hard to say, without clarification on your question.
local screenwidth = awful.screen.geometry.width
local screenheight = awful.screen.geometry.height
local halfwidth = math.floor( screenwidth /2 )
local thirdheight = math.floor( screenheight /3 )
awful .spawn( terminal .." -e vim", {
tag = "edit",
width = halfwidth,
height = screenheight,
placement = awful .placement .left,
callback = function(c) awful .client .setmaster(c) end } )
awful .spawn( terminal.." -e htop", {
tag = "edit",
width = halfwidth,
height = thirdheight,
placement = awful .placement .top_right } )
awful .spawn( terminal, { -- bare
tag = "edit",
width = halfwidth,
height = thirdheight,
placement = awful .placement .right } )
awful .spawn( terminal .." -e vifm", {
tag = "edit",
width = halfwidth,
height = thirdheight,
placement = awful .placement .bottom_right } )
Also, I'd point out that Conky might be a viable solution, if you're just looking to view terminal output on your desktop, while scripting in Lua.

In Swift, outwards pingpong sequence?

Say you have
for i in 0 ... 10 {
print(i)
}
of course it will print 0,1,2,3,4,5,6,7,8,9,10
for i in 0 ..< 5 {
that's 0,1,2,3,4.
I want to start at a certain integer and pingpong outwards over the count of numbers
So,
for i in function or something (10, 3)
that's 3 4 2 5 1 6 0 7 8 9
for i in function or something (10, 8) {
would be 8 9 7 6 5 4 3 2 1 0
for i in function or something (10, 2) {
would be 2 3 1 4 0 5 6 7 8 9
So it's just an outwards pingpong.
What should I type where I have written function or something (10, 2)?
There might be some really cool syntax possible, along the lines 0 # 7 # 10.
What about something like (0..<10).outPong(3)?
How to formulate such a sequence?
Here's a naive example of how you'd do an outwards pingpong, at the call level.
Call exampleLoad for each of the items in RA, outwards pingpoing:
func loadItemsPongwise(startWith: Int) {
// RA = ... this is your array of some type
exampleLoad(startWith)
let k = RA.count
var howManyDone: Int = 0
var distance: Int = 1
while howManyDone < ( k - 1 ) {
let tryRight = alreadyLoaded + distance
if tryRight < k {
howManyDone = howManyDone + 1
exampleLoad(RA[tryRight])
}
let tryLeft = alreadyLoaded - distance
if tryLeft >= 0 {
howManyDone = howManyDone + 1
exampleLoad(RA[tryLeft])
}
distance = distance + 1
}
}
Of course, something like this wouild be much nicer:
func loadItemsPongwise(startWith: Int) {
for i in ???? {
exampleLoad(i)
}
}
public extension ClosedRange where Bound: AdditiveArithmetic {
func πŸ“(
by contiguousAdvancement: Bound,
startingAt start: Bound
) -> AnySequence<Bound> {
guard contains(start)
else { return .init( EmptyCollection() ) }
var advancement = contiguousAdvancement
typealias Operate = (Bound, Bound) -> Bound
var pingPong: Operate = (+)
var contiguouslyAdvance: Operate = (-)
return .init(
sequence(first: start) { previous in
pingPongIterate: do {
defer { advancement += contiguousAdvancement }
let pingPonged = pingPong(previous, advancement)
guard self.contains(pingPonged)
else { break pingPongIterate }
(pingPong, contiguouslyAdvance) = (contiguouslyAdvance, pingPong)
return pingPonged
}
let contiguouslyAdvanced = contiguouslyAdvance(previous, contiguousAdvancement)
return self.contains(contiguouslyAdvanced)
? contiguouslyAdvanced
: nil
}
)
}
}
public extension ClosedRange where Bound: AdditiveArithmetic & ExpressibleByIntegerLiteral {
func πŸ“(startingAt start: Bound) -> AnySequence<Bound> {
πŸ“(by: 1, startingAt: start)
}
}
public extension ClosedRange where Bound: BinaryInteger {
func πŸ“(by contiguousAdvancement: Bound = 1) -> AnySequence<Bound> {
πŸ“(by: contiguousAdvancement, startingAt: (upperBound + lowerBound) / 2)
}
}
public extension ClosedRange where Bound: FloatingPoint {
func πŸ“(by contiguousAdvancement: Bound = 1) -> AnySequence<Bound> {
πŸ“(by: contiguousAdvancement, startingAt: (upperBound + lowerBound) / 2)
}
}
XCTAssertEqual(
Array( (2...10).πŸ“() ),
[6, 7, 5, 8, 4, 9, 3, 10, 2]
)
XCTAssertEqual(
Array( (2...10).πŸ“(startingAt: 7) ),
[7, 8, 6, 9, 5, 10, 4, 3, 2]
)
XCTAssertEqual(
Array( (-1.5...7.5).πŸ“(by: 1.5) ),
[3, 4.5, 1.5, 6, 0, 7.5, -1.5]
)
XCTAssertEqual(
Array( (0...6).πŸ“(by: -1) ),
[3, 2, 4, 1, 5, 0, 6]
)
XCTAssertEqual(
Array( (0...3).πŸ“(startingAt: 4) ),
[]
)
So I went the route of taking the analogy of ping-pong seriously. I left some comments for clarity.
It simulates an actual ping pong ball bouncing (starting from the net, oddly), back and forth on a ping pong table that has a net that might not be centered. If it's about to go off the edge on one side, then it just goes to the other side and I like to imagine it does smaller and smaller bounces until it rolls off the table.
Here's the code with comments and a test:
// It's supposed to be a ping pong table πŸ€·β€β™‚οΈ
struct 🟦: IteratorProtocol, Sequence {
typealias Element = Int
// The table *is* the iterator
typealias Iterator = 🟦
let leftEdgePosition: Int
/// The starting point for the ball
let netPosition: Int
let rightEdgePosition: Int
/// For convenience in checking whether different ball positions are on the table.
private let tableBounds: ClosedRange<Int>
init(leftEdgePosition: Int, netPosition: Int, rightEdgePosition: Int) {
self.leftEdgePosition = leftEdgePosition
self.netPosition = netPosition
self.rightEdgePosition = rightEdgePosition
self.tableBounds = leftEdgePosition...rightEdgePosition
}
private var distanceFromNet = 0
/// The side of the table the ping pong ball is headed toward
private var ballDirection: PingPongBallDirection = .towardLeftEdge
func makeIterator() -> 🟦 {
return self
}
/// This gets called for each iteration in the for loop. Once the ball goes beyond the table, we should return nil to stop the for loop.
mutating public func next() -> Int? {
// the ball position we will return if this position is on the table
let ballPosition = ballDirection.locationCalculator(netPosition, distanceFromNet)
// the ball position we will return if the first ball position is not on the table
let redirectedPosition = (!ballDirection).locationCalculator(netPosition, distanceFromNet)
// determine which ball position to return and set up our state for the next call to next()
var ballPositionToReturn: Int?
if tableBounds.contains(ballPosition) {
ballPositionToReturn = ballPosition
let ballMirrorPosition = (!ballDirection).locationCalculator(netPosition, distanceFromNet)
let ballIsTrailingOff = !tableBounds.contains(ballMirrorPosition)
if !ballIsTrailingOff {
// switch the direction because the ball hit the table
ballDirection = !ballDirection
}
// If we're heading to the right, i.e 3 -> 4 in the case of 0 << 3 >> 10, then increase
// the distance from the net.
// If we're trailing off and not ping-ponging any more, then we need to add distance.
if ballDirection == .towardRightEdge || ballIsTrailingOff {
distanceFromNet += 1
}
} else if tableBounds.contains(redirectedPosition) {
ballPositionToReturn = redirectedPosition
// reflect the redirection
ballDirection = !ballDirection
// add distance when we redirect
distanceFromNet += 1
}
return ballPositionToReturn
}
}
enum PingPongBallDirection {
case towardLeftEdge
case towardRightEdge
/// Returns the oppposite direction
static prefix func !(direction: PingPongBallDirection) -> PingPongBallDirection {
switch direction {
case towardLeftEdge: return towardRightEdge
case towardRightEdge: return towardLeftEdge
}
}
// In our world, right is greater and left is lesser.
var locationCalculator: (Int, Int) -> Int {
switch self {
case .towardLeftEdge: return (-)
case .towardRightEdge: return (+)
}
}
}
// Make the syntax work
precedencegroup PingPongPrecedenceGroup {
associativity: left
// this makes sure the ping pong operator gets evaluated before the assignment operator
higherThan: AssignmentPrecedence
}
infix operator ...: PingPongPrecedenceGroup
func ... (lhs: ClosedRange<Int>, rhs: Int) -> 🟦 {
return 🟦(leftEdgePosition: lhs.lowerBound, netPosition: lhs.upperBound, rightEdgePosition: rhs)
}
for i in 0...10 {
for j in 0...i...10 {
print(j, terminator: " ")
}
print()
}
// OUTPUT:
// 0 1 2 3 4 5 6 7 8 9 10
// 1 2 0 3 4 5 6 7 8 9 10
// 2 3 1 4 0 5 6 7 8 9 10
// 3 4 2 5 1 6 0 7 8 9 10
// 4 5 3 6 2 7 1 8 0 9 10
// 5 6 4 7 3 8 2 9 1 10 0
// 6 7 5 8 4 9 3 10 2 1 0
// 7 8 6 9 5 10 4 3 2 1 0
// 8 9 7 10 6 5 4 3 2 1 0
// 9 10 8 7 6 5 4 3 2 1 0
// 10 9 8 7 6 5 4 3 2 1 0
stateless
Just for anyone working on the syntax.
I wasted an hour of my life figuring out a stateless conversion.
(I couldn't make it simple or elegant - maybe someone else can!)
var plaground = "directly convert a single ping pong index to a plain index"
let L: Int = 10
let S: Int = 7
func ppiToIndex(_ ppi: Int) -> Int {
let inner = S+1 < (L-S) ? (S+1) : (L-S)
let pp = (ppi+1) / ( (ppi % 2 == 1) ? 2 : -2 )
let way = (S < L/2) ? -(inner-ppi-1) : (inner-ppi-1)
return (ppi < inner*2-1) ? S+pp : S+way
}
for i in 0..<L {
print(" \(i) \(ppiToIndex(i)) ")
}
inner is how many from the start inclusive to the nearer end inclusive.
pp is a full-on endless ping pong.
way is the correct direction +/- to add once you pass the inner area.
Think of how you'd do this with a deck of cards, or a set of matchsticks. Just split the series into two at the point where you want to start, reverse the order of one of the resulting series, and alternate pulling values off the two series.
Here's a utility that alternates between two series until both are exhausted:
func alternateUntilBothAreExhausted<T> (arr1:Array<T>, arr2:Array<T>)
-> Array<T> {
var result = Array<T>()
var arr1 = arr1; var arr2 = arr2
while true {
if let last1 = arr1.popLast() {
result.append(last1)
}
if let last2 = arr2.popLast() {
result.append(last2)
}
if arr1.isEmpty && arr2.isEmpty {
return result
}
}
}
So we start with one series, split it, reverse one, and alternate:
func pingPong<T>(array:Array<T>, startingAtIndex ix:Int) -> Array<T> {
let arr1 = array[..<ix]
let arr2 = array[ix...]
return alternateUntilBothAreExhausted(
arr1: Array(arr1), arr2: Array(arr2.reversed()))
}
Example:
let ping = pingPong(array: Array(0..<10), startingAtIndex:4)
// [3, 4, 2, 5, 1, 6, 0, 7, 8, 9]
Wrapping that up in your desired syntax is trivial and is left as an exercise for the reader.

swift: what does arc4andom_uniform() means

This might be a lame question and even have already answered in SO.i have even searched about this but could not understand in a proper way. what is happening here..??please help me out to understand this.
let size = Double(arc4random_uniform(5)) + 1
for index in 0..<ITEM_COUNT
{
let y = Double(arc4random_uniform(100)) + 50.0
let size = Double(arc4random_uniform(5)) + 1
entries.append(ChartEntry(x: Double(index) + 0.5, y: y, size: CGFloat(size)))
}
arc4random_uniform(x) returns a random value between 0 and x-1
Examples:
arc4random_uniform(2) -> returns 0 or 1 randomly
arc4random_uniform(2) == 0 returns true or false randomly
arc4random_uniform(6) + 1 returns a number between 1 and 6 (like a dice roll)
There are a multitude of reasons that arc4random_uniform(5) returns a number between 0 and 5, but the main one is that this is a basic functionality in programming, where numbers start at zero. An example of why this would be useful would be returning a random value from an array. Example:
func randomArrayValue(array: [Int]) -> Int {
let index = arc4random_uniform(array.count)
return array[index]
}
let arrayOfInt = [10,20,30]
print("Random Int: \(randomArrayValue(array: arrayOfInt))")
//"Random Int: 10"
//"Random Int: 20"
//"Random Int: 30"
For these three lines of code in your questions:
let y = Double(arc4random_uniform(100)) + 50.0
let size = Double(arc4random_uniform(5)) + 1
entries.append(ChartEntry(x: Double(index) + 0.5, y: y, size: CGFloat(size)))
y is a random variable between 50 and 149
size is a random variable between 1 and 5
you then add an item onto an array that goes onto a chart. The value being added specifies the x location (the index) and the y location (the random y value). Size is some code specific requirement, which we wouldn't be able to help with without seeing the functionality.

Multiplication table in Swift ios

I am learning how to make a multiplication table in swift and used
override func viewDidLoad() {
let n = Int(str)!
while (i<=10) {
let st = "\(n) * \(i) = \(n * i)"
lbl.text = st
i += 1
}
this code. i have a label in which i want to show the table, but the problem is that only the last result which is say 2*10 = 20 is showing and not all the other value. i am confused what to do, please help what to do so that all the values are displayed.
Glad you've decided to learn Swift. You're on the right track, but as others have said, your final iteration of the loop is replacing the contents of lbl.text.
There are many ways to achieve what you want, but for problems like this I'd suggest starting off working in a playground rather than worrying about labels, viewDidLoad and suchlike.
Here's a nice Swift-y way to do what you want
let n = 12
let table = Array(0...10).map({"\(n) * \($0) = \(n * $0)"}).joinWithSeparator("\n")
print("\(table)")
Gives…
12 * 0 = 0
12 * 1 = 12
12 * 2 = 24
12 * 3 = 36
12 * 4 = 48
12 * 5 = 60
12 * 6 = 72
12 * 7 = 84
12 * 8 = 96
12 * 9 = 108
12 * 10 = 120
To break that down…
// Take numbers 0 to 10 and make an array
Array(0...10).
// use the map function to convert each member of the array to a string
// $0 represents each value in turn.
// The result is an array of strings
map({"\(n) * \($0) = \(n * $0)"}).
// Join all the members of your `String` array with a newline character
joinWithSeparator("\n")
Try it for yourself. In Xcode, File -> New -> Playground, and just paste in that code. Good luck!
That's because every time the loop iterates, it overwrites the previous value in label.text. You need to append the new value to existing string value in label, as suggested by RichardG.
let n = Int(str)!
while (i<=10) {
let st = "\(n) * \(i) = \(n * i)"
lbl.text = lbl.text + " " +st //Append new value to already existing value in label.text
i += 1
}
There is also a possibility of UI issue. You have to provide number of lines to the label or it will mess up the display. It also needs to be of size enough to hold your data. A better option would be UITextView which is scrollable, if you are unwilling to handle cases for label height and width. But if you want to stick with UILabel, the following code will resize the label depending on text for you:
lbl.numberOfLines = 0; //You only need to call this once. Maybe in `viewDidLoad` or Storyboard itself.
lbl.text = #"Some long long long text"; //here you set the text to label
[lbl sizeToFit]; //You must call this method after setting text to label
You can also handle that by Autolayout constraints.
Easy way to do it with SWIFT 2.0
var tableOf = 2 //Change the table you want
for index in 1...10 {
print("\(tableOf) X \(index) = \(index * tableOf)")
}
OUTPUT
repeat-while loop, performs a single pass through the loop block first before considering the loop's condition (exactly what do-while loop does).
1) repeat...while Loop
var i = Int()
repeat {
print("\(i) * \(i) = \(i * 11)")
i += 1
} while i <= 11
2) While Loop
var i = Int()
while i <= 11
{
print("\(i) * \(i) = \(i * 11)")
i += 1
}
3) For Loop
for n in 1..<11
{
print("\(n) * \(n) = \(n * 10)")
}

Resources