Jetpack Compose Tutorial : Build Modern Android UI Step by Step

Friday, July 03, 2026 Add Comment


Halo guys, kali ini kita akan membahas tentang Development Android UI dengan Jetpack Compose. Kalau pada artikel sebelum-sebelumnya, ketika membuat desain UI di android kita menggunakan XML. Pada masa sekarang ini, tidak perlu lagi bergantung pada XML karena Jetpack Compose telah menjadi standar utama dalam modern android development.

Jetpack Compose adalah Framework UI dari Google untuk menyederhanakan proses pengembangan UI di Android. Project baru saat ini banyak mengadopsi Compose sebagai fondasi utama karena menawarkan kode yang lebih ringkas, mudah dibaca, dan lebih mudah dirawat dibandingkan pendekatan berbasis XML.

XML vs Jetpack Compose 

Berikut adalah perbedaan antara XML dan Jetpack Compose :

  • UI & Logic
    • XML :  Didefinisikan langsung di dalam file XML. UI dan logic berada di dua tempat yang berbeda.
    • Jetpack Compose : Didefinisikan langsung menggunakan Kotlin. UI dan logic dalam satu barisan code sehingga lebih ringkas dan mudah dipahami. 
  • Development Approach
    • XML : XML menggunakan pendekatan Imperative, artinya developer memberi instruksi secara eksplisit mengenai apa yang harus diubah pada tampilan. Setiap perubahan harus memperbaharui komponen UI secara manual.
    • Jetpack Compose : Jetpack Compose menggunakan pendekatan Declarative, artinya developer hanya mendefinisikan seperti apa UI seharusnya. Selebihkan framework akan menangani otomatis update UI dengan mengikuti perubahan state. 
  • Boilerplate Code
    • XML : Lebih banyak karena harus menulis code tambahan seperti XML, View Binding, Fragment Transaction dan sinkronisasi antara XML dan Kotlin
    • Jetpack Compose : Lebih sedikit karena menghilangkan sebagian besar boilerplate dari XML.  
  • Preview 
    • XML :  Layout Preview terbatas dan harus build ulang aplikasi.
    • Jetpack Compose : Live Preview dan hot reload tanpa rebuild aplikasi.
  • Reusability
    • XML : Include Layout / Custom View
    • Jetpack Compose : Composable Function

Kesimpulannya, Jetpack Compose menawarkan pendekatan yang lebih modern dengan kode yang lebih ringkas, mudah dipahami, dan lebih sesuai dengan pengembangan Android saat ini.

Oke sekarang kita akan langsung praktekkan membuat aplikasi Android dengan Jetpack Compose.

Pertama-tama download dan install Android Studio dulu jika belum pernal terinstall. Bisa di download dan install  di sini https://developer.android.com/studio

Saya pakai Android Studio versi Quail 1. Untuk Kotlin menggunakan versi 2.4.0 dan AGP (Android Gradle Plugin) versi 9.2.1. Bisa dilihat di source code project yang akan saya share di akhir artikel.

1. Buat project baru di Android Studio, kemudian pilih Empty Activity


 2. Isikan nama project, package name, dll

 

3. Setelah selesai maka struktur projectnya akan seperti berikut.

  

 4. Kita coba jalankan projectnya di emulator dan hasilnya sebagai berikut.

5. Kita akan coba menambahkan komponen lain seperti Column, Row, Text dan Button dengan Composable Function 

Composable Function adalah fungsi Kotlin yang digunakan untuk mendeskripsikan tampilan (UI). Fungsi ini ditandai dengan anotasi @Composable.


@Composable
fun Greeting(modifier: Modifier = Modifier) {
    Column(
        modifier = modifier
    ) {
        Row(
            modifier = Modifier
                .fillMaxWidth()
                .padding(8.dp)
        ) {
            Text(
                text = "Name : ",
            )
            Text(
                text = "Wim",
            )
        }

        Row(
            modifier = Modifier
                .fillMaxWidth()
                .padding(8.dp)
        ) {
            Text(
                text = "Email : ",
            )
            Text(
                text = "wim.sonevel@yahoo.co.id",
            )
        }

        Button(
            onClick = {
                println("Button pressed")
            }
        ) {
            Text("Click Me")
        }
    }
}


Di function yang kita buat terdapat komponen Modifier. Modifier digunakan untuk mengatur ukuran, warna, padding, margin, posisi, background, click listener dan berbagai properti lainnya.
6. Sekarang kita akan update perubahan UI dengan Live Edit tanpa harus rebuild ulang di emulator. Edit function berikut dengan ditandai dengan anotasi @Preview.

@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
    AndroidComposeTheme {
        Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
            Greeting(modifier = Modifier.padding(innerPadding))
        }
    }
}


Untuk menjalankan Live Edit, klik icon refresh di pojok atas kanan emulator.

7. Berikut source code lengkap di MainActivity.


package com.example.android_compose

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.example.android_compose.ui.theme.AndroidComposeTheme

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContent {
            AndroidComposeTheme {
                Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
                    Greeting(modifier = Modifier.padding(innerPadding))
                }
            }
        }
    }
}

@Composable
fun Greeting(modifier: Modifier = Modifier) {
    Column(
        modifier = modifier
    ) {
        Row(
            modifier = Modifier
                .fillMaxWidth()
                .padding(8.dp)
        ) {
            Text(
                text = "Name : ",
            )
            Text(
                text = "Wim",
            )
        }

        Row(
            modifier = Modifier
                .fillMaxWidth()
                .padding(8.dp)
        ) {
            Text(
                text = "Email : ",
            )
            Text(
                text = "wim.sonevel@yahoo.co.id",
            )
        }

        Button(
            onClick = {
                println("Button pressed")
            }
        ) {
            Text("Click Me")
        }
    }
}

@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
    AndroidComposeTheme {
        Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
            Greeting(modifier = Modifier.padding(innerPadding))
        }
    }
}

8. Jalankan aplikasi dan hasilnya sebagai berikut.



Source code lengkapnya bisa dilihat di https://github.com/wimsonevel/Android-Compose

Oke sekian dulu artikel kali ini, next kita akan melanjutkan dengan membuat komponen lainnya dengan Jetpack Compose.

Semoga bermanfaat dan terima kasih sudah berkunjung. ^^

Kotlin Basics for Modern Android Development

Tuesday, June 30, 2026 Add Comment

        
Halo Guys, saya kembali lagi setelah hampir 8 tahun vacuum menulis di blog. Saya bersyukur sampai hari ini diberikan kesehatan dan kesempatan lagi untuk mengamalkan ilmu yang sudah saya pelajari selama ini. Mudah-mudahan saya bisa konsisten lagi untuk memberikan edukasi ke teman-teman pembaca setia blog ini di tengah gempuran AI saat ini. Saya rasa teman-teman harus punya pengetahuan dasar-dasarnya agar tetap memiliki fundamental yang kuat ketimbang hanya mengandalkan AI saja tanpa tahu apa yang dibuat. Dari 8 tahun tentunya banyak sekali ilmu dan pengalaman yang sudah saya raih. Pada kesempatan kali ini, saya akan menulis lagi tentang Android Development setelah tertinggal jauh dari artikel sebelumnya dan sekarang banyak teknologi baru yang digunakan dalam mengembangkan aplikasi android. Oke langsung saja kali ini saya akan membahas tentang bahasa pemrograman kotlin yang saat ini menjadi teknologi utama di dunia Android Development dari yang sebelumnya menggunakan Java.

Kenapa Kotlin?
Kotlin adalah bahasa pemrograman modern untuk mengembangkan aplikasi jadi lebih mudah dan lebih produktif. Bahasa kotlin dirancang agar lebih ringkas, efisien, mudah dibaca, dan membantu mengurangi kesalahan yang sering terjadi saat menulis kode.
Pada tahun 2017, Google secara resmi mengumumkan kotlin sebagai bahasa resmi dalam pengembangan Android. Sejak saat itu, kotlin mendapat dukungan dari Google dan semakin banyak digunakan oleh developer di seluruh dunia.

Keunggulan Kotlin
Berikut ini adalah beberapa keunggulan kotlin :

1. Sintaks Lebih Ringkas
        Penulisan kode lebih singkat dibanding Java dan minim kode berulang (boilerplate).
2. Null Safety
        Fitur null safety membantu mencegah kesalahan akibat null, penyebab paling umum terjadi crash  pada aplikasi android.
3. Interoperabilitas dengan Java
        Kotlin dapat digunakan bersama Java dalam satu project termasuk memanfaatkan library Java yang sudah ada.
4. Resmi Didukung untuk Android
        Kotlin merupakan bahasa yang direkomendasikan oleh Google untuk pengembangan aplikasi Android
5. Coroutines
        Kotlin menyediakan fitur Coroutines untuk menjalankan proses asinkron dengan lebih mudah dan efisien.
6. Multiplatform
        Selain Android, Kotlin juga dapat digunakan untuk membangun aplikasi backend, web, ios dan desktop dalam Kotlin Multiplatform.
7. Dukungan Komunitas dan Ekosistem yang Berkembangan
        Dukungan dan popularitas kotlin terus meningkat sehingga dokumentasi, library, serta komunitas developer-nya semakin lengkap.

Kotlin Data Types, Variables and Operators

Data Type di kotlin terdiri dari :


Int, Double, Long, Float, Boolean, Char, String

Contohnya :

val age: Int = 30
val price: Double = 99.99
val isActive: Boolean = true
val grade: Char = 'A'
val name: String = "Wim"

Conditions and Booleans 

Kotlin memiliki Boolean dan Operator Boolean seperti :


<, ==, >, !=, <=, >=

Contohnya :

val isLoggedIn = true

val age = 25

println(age >= 18)
println(age < 18)

Untuk Condition sendiri terdiri dari :
- If Expression

val age = 25

if (age >= 18) {
    println("You are an adult")
}

- If Else Expression

val age = 15

if (age >= 18) {
    println("You are an adult")
} else {
    println("You are underage")
}

- Else If Expression

val score = 85

if (score >= 90) {
    println("Grade A")
} else if (score >= 80) {
    println("Grade B")
} else if (score >= 70) {
    println("Grade C")
} else {
    println("Grade D")
}

- If as an Expression
Salah satu keunikan di kotlin adalah if di kotlin dapat digunakan sebagai sebuah expression yang langsung mengembalikan sebuah nilai.

val age = 20

val status = if (age >= 18) {
    "Adult"
} else {
    "Underage"
}

println(status)

Fitur lainnya dari kotlin adalah membuat pengecekan rentang nilai menjadi lebih sederhana dalam Range operator (...). Kita akan coba menggunakan range dalam statement If.

val score = 85

if (score in 75..100) {
    println("Passed")
}

- When Statement
Secara definisi hampir sama dengan switch, tetapi jauh lebih fleksibel. Berikut contohnya :


val day = 1

when (day) {
    1 -> println("Monday")
    2 -> println("Tuesday")
    3 -> println("Wednesday")
    4 -> println("Thursday")
    5 -> println("Friday")
    else -> println("Weekend")
}


Arrays and Lists 

- Array adalah kumpulan data yang bersifat fixed size. Artinya tidak dapat ditambah atau dikurangi elemennya, kecuali di copy ke array baru.


val fruits = arrayOf("Apple", "Strawberry", "Watermelon")

Yang unik di kotlin adalah array tidak terikat pada tipe data element. Artinya kita bisa menggabungkan beberapa tipe data yang berbeda.

val mix = arrayOf("Apple", 20)

- List adalah kumpulan data yang lebih fleksibel dibandingkan array. List merupakan bagian dari Kotlin Collections yang mana kita bisa memanfaatkan fitur-fitur di dalamnya untuk mengolah data seperti filter(), map(), sorted(), dan find().
Ada dua jenis List di kotlin :
  • List (Immutable)
  • MutableList (Mutable)

Immutable List
Karena bersifat immutable, data hanya dapat dibaca dan tidak dapat diubah setelah dibuat.

val fruits = listOf("Apple", "Strawberry", "Watermelon")

println(fruits)

MutableList List
Kebalikannya dari Immutable List, data dapat ditambah, dihapus dan diubah.

val fruits = mutableListOf("Apple", "Strawberry", "Watermelon")
fruits.add("Orange")
fruits.remove("Watermelon")
fruits[0] = "Mango"

println(fruits)

Loops 

Looping atau perulangan dapat dilakukan di Array maupun di List


val fruits = listOf("Apple", "Strawberry", "Watermelon")

for (fruit in fruits) {
    println(fruit)
}

Output:

Apple
Strawberry
Watermelon

Jika ingin mengakses index dan value

val fruits = listOf("Apple", "Strawberry", "Watermelon")

for ((index, fruit) in fruits.withIndex()) {
	println("Item at $index is $fruit\n")
}

Output:

Item at 0 is Apple
Item at 1 is Strawberry
Item at 2 is Watermelon

Selain perulangan maju, di kotlin juga bisa looping mundur, step dan alphabet.

for (i in 1..5) print(i)

Output : 12345

for (i in 5 downTo 1) print(i)

Output : 54321

for (i in 3..6 step 2) print(i)

Output : 35

for (i in 'a'..'f') print (i)

Output : abcdef


Nullable and non-nullable variables 

Di kotlin ada fitur Null Safety yang dirancang untuk mencegah kesalahan oleh nilai null, yang sering menyebabkan aplikasi crash terutama NullPointerException. Secara default, semua variabel di Kotlin bersifat non-nullable, artinya variabel tersebut tidak boleh menyimpan nilai null kecuali harus dideklarasikan secara eksplisit sebagai nullable.

Non-Nullable Variables
Variabel non-nullable hanya dapat menyimpan nilai sesuai dengan tipe datanya.


val name: String = "Wim"

Nullable Variables
Untuk mendeklarasikan sebuah variabel menyimpan nilai null, tambahkan tanda (?) setelah tipe data.

var name: String? = null

Safe Call Operator
Di nullable variable, kita tidak dapat langsung mengakses properti atau fungsi miliknya karena nilai bisa saja null. Maka dari itu gunakan safe call operator (?.) untuk mengaksesnya.

var name: String? = null
println(name?.length)

Elvis Operator
Operator ini berfungsi memberikan nilai default apabila sebuah variabel bernilai null dengan oerator (?:).

var name: String? = null
val displayName = name ?: "No Name"

println(displayName)

Not-Null Assertion
Operator Not-Null Assertion (!!) berfungsi untuk mengubah nullable menjadi non-nullable.

val name: String? = "Wim"

println(name!!.length)

Apabila ternyata nilainya null, maka akan mengalami error NullPointerException. Oleh karena itu, penggunaan (!!) sebaiknya dihindari kecuali jika kita benar-benar yakin bahwa nilainya tidak mungkin null.

Type Checks and Casts 

Kotlin menyediakan fitur Type Checks dan Type Casts yang berfungsi untuk mengetahui tipe data suatu objek saat aplikasi sedang berjalan atau mengubah suatu objek menjadi tipe data tertentu.
Fitur ini banyak digunakan ketika menemukan objek yang memiliki tipe umum seperti Any atau struktur data yang dapat menyimpan berbagai jenis tipe data.

- Type Checks dengan is atau !is
Operator is digunakan untuk memeriksa apakah sebuah objek merupakan tipe data tertentu, sedangkan !is sebaliknya.


val name: Any = "Wim"

if (name is String) {
    println("Length: ${name.length}")
}

- Unsafe Cast dan Safe Cast
Unsafe Cast (as) digunakan untuk mengubah suatu objek menjadi tipe tertentu. Gunakan (as) hanya jika Anda yakin tipe datanya benar, karena kalau gagal akan menyebabkan error ClassCastException.

val name: Any = "Wim"

if (name is String) {
    println("Length: ${name.length}")
}

Sedangkan Safe Cast (as?) digunakan untuk menghandle terjadinya error ClassCastException dengan mengembalikan nilai null.

val data: Any = 100

val text = data as? String

println(text)

Output:

null

Jadi kesimpulannya Type Checks dan Type Casts menangani objek dengan tipe data yang berbeda secara aman.

Oke mungkin sekian dulu pembahasan kali ini, next kita akan membuat aplikasi android dengan kotlin. Semoga bermanfaat dan stay tuned untuk materi berikutnya. 

Terima kasih. ^^

How to Schedule Automatic Start and Stop of AWS EC2 Instances Using EventBridge Scheduler

Friday, June 26, 2026 Add Comment
Halo Guys, pada artikel kali ini saya mau membagikan pengalaman saya tentang cara menghemat biaya tagihan AWS dengan menjadwalkan EC2 agar otomatis menyala dan mati pada rentang jam tertentu menggunakan AWS EventBridge Scheduler. Metode ini sangat cocok diterapkan di server development, staging maupun testing yang tidak perlu harus 24 jam aktif. Misalkan digunakan hanya pada jam kerja 09:00 - 17:00. Oke tidak perlu berlama-lama berikut saya jelaskan step by step :

1. Buka AWS EventBridge Scheduler
  • Masuk ke AWS Console
  • Navigasi ke Amazon EventBridge
  • Pilih Menu Schedules
  • Klik Create Schedule

2. Membuat Schedule untuk Menyalakan EC2

  • Klik Create Schedule
  • Beri nama Schedule Name start-staging-ec2
  • Pilih Schedule Pattern Recurring Schedule
  • Pilih Timezone : Asia/Jakarta 

3. Konfigurasi Menyalakan Instance

Kita akan set jam untuk instance menyala otomatis setiap harinya pada pukul 08:00 WIB dengan cron expression berikut :  


0 8 * * ? *

Artinya : Every day at 08:00 AM


4. Konfigurasi Target

  • Target API : All APIs
  • Service : Pilih Amazon EC2 dan API StartInstances
  • Input Payload : 

{
  "InstanceIds": [
    "i-xxxxxxxxxxxxxxxxx"
  ]
}


5. Konfigurasi Permissions 

Saat membuat schedule, AWS akan meminta execution role. Kamu bisa membuat role baru atau existing IAM role. 

Untuk membuat role baru silahkan klik Go to IAM Console.
Buat Role dengan nama EventBridgeSchedulerEC2Role. Role harus punya permission sebagai berikut :


{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ec2:StartInstances",
                "ec2:StopInstances",
                "ec2:DescribeInstances"
            ],
            "Resource": "*"
        }
    ]
}


Setelah selesai kemudian di bagian permission pilih role yang sudah dibuat. Lalu klik Next kemudian Save Schedule.

6. Membuat Schedule untuk Mematikan EC2
  • Klik Create Schedule
  • Beri nama Schedule Name stop-staging-ec2
  • Pilih Schedule Pattern Recurring Schedule
  • Pilih Timezone : Asia/Jakarta 
7. Konfigurasi Mematikan Instance

Kita akan set jam untuk instance mati otomatis setiap harinya pada pukul 23:00 WIB dengan cron expression berikut :  


0 23 * * ? *

Artinya : Every day at 11:00 PM

8. Konfigurasi Target Stop Instance 

Sama seperti No. 4 tapi pilih API StopInstances

9. Verifikasi Schedule

Pastikan kedua schedule berstatus Enabled dan gunakan IAM Role yang memiliki izin StartInstances dan StopInstances.


*Note : Yang perlu diperhatikan adalah ketika instance di stop maka Public IP akan berubah. Oleh karena itu saya menyarankan agar mendedikasikan Elastic IP untuk instance tersebut agar tetap memakai IP yang sama. 

Sekian dan semoga bermanfaat. ^^
 

(Tutorial iOS) Add Load More in UITableView

Monday, August 27, 2018 Add Comment
Hello guys, pada kesempatan sebelumnya saya sudah menjelaskan tentang bagaimana membuat custom tableview di ios. Bisa dilihat di sini https://wimsonevel.blogspot.com/2017/07/tutorial-ios-custom-uitableviewcell-in.html.

Tutorial kali ini saya akan akan menjelaskan bagaimana menambahkan load more di tableview atau bisa disebut paging. Mekanismenya ketika kita men-drag scroll sampai ke bawah, pada saat itu lakukan request untuk memuat data.

Oke langsung saja.

Pertama kita perlu menambahkan sebuah View yang bernama UIActivityIndicatorView dibagian footer dari UITabelView. Akan muncul loading indicator ketika tableview di drag atau scroll ke bawah. Buat extension dari UITableView dengan beberapa fungsi yaitu showLoadingFooter, hideLoadingFooter dan isLoadingFooterShowing.

import Foundation
import UIKit
// MARK: Loading Footer extension UITableView { func showLoadingFooter(){ let loadingFooter = UIActivityIndicatorView(activityIndicatorStyle: .gray) loadingFooter.frame.size.height = 60 loadingFooter.hidesWhenStopped = true loadingFooter.startAnimating() tableFooterView = loadingFooter } func hideLoadingFooter(){ let tableContentSufficentlyTall = (contentSize.height > frame.size.height) let atBottomOfTable = (contentOffset.y >= contentSize.height - frame.size.height) if atBottomOfTable && tableContentSufficentlyTall { UIView.animate(withDuration: 0.2, animations: { self.contentOffset.y = self.contentOffset.y - 60 }, completion: { finished in self.tableFooterView = UIView() }) } else { self.tableFooterView = UIView() } } func isLoadingFooterShowing() -> Bool { return tableFooterView is UIActivityIndicatorView } }
Kemudian kita perlu mengimplementasikan function scrollViewDidEndDragging di ViewController.

override func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
        if scrollView == tableView {
            if ((scrollView.contentOffset.y + scrollView.frame.size.height) >= scrollView.contentSize.height) {
                if !tableView.isLoadingFooterShowing() {
                    loadData()
                }
            }
        }
    }


Buat method loadData(), kemudian tampilkan loading indicator di tableview footer diikuti dengan reload data atau dari request data.

func loadData() {
        // do network request here
        
        self.tableView.showLoadingFooter()
        
        DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
            
            self.foods.append(contentsOf: self.moreFoods)
            self.tableView.reloadData()
            
            self.tableView.hideLoadingFooter()
        }
    }

Kode lengkapnya :

import UIKit

class ViewController: UITableViewController {

    var foods = [Food(thumb: "rendang", name: "Rendang", country: "Indonesia"),
                 Food(thumb: "nasi_goreng", name: "Nasi Goreng", country: "Indonesia"),
                 Food(thumb: "sushi", name: "Sushi", country: "Japan"),
                 Food(thumb: "tom_yum_goong", name: "Tom Yum Goong", country: "Thailand"),
                 Food(thumb: "pad_thai", name: "Pad Thai", country: "Thailand"),
                 Food(thumb: "som_tam", name: "Som Tam", country: "Thailand"),
                 Food(thumb: "dim_sum", name: "Dim Sum", country: "Hongkong"),
                 Food(thumb: "ramen", name: "Ramen", country: "Japan"),
                 Food(thumb: "peking_duck", name: "Peking Duck", country: "China"),
                 Food(thumb: "massaman_curry", name: "Massaman Curry", country: "Thailand")]
    
    var moreFoods: [Food] = []
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        
        self.moreFoods.append(contentsOf: self.foods)
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    func loadData() {
        // do network request here
        
        self.tableView.showLoadingFooter()
        
        DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
            
            self.foods.append(contentsOf: self.moreFoods)
            self.tableView.reloadData()
            
            self.tableView.hideLoadingFooter()
        }
    }

}

struct Food {
    
    var thumb = String()
    var name = String()
    var country = String()
    
}

class CustomCell: UITableViewCell {
    
    @IBOutlet weak var thumbImageView: UIImageView!
    @IBOutlet weak var nameLabel: UILabel!
    @IBOutlet weak var countryLabel: UILabel!
    
}

extension ViewController {
    
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return foods.count
    }
    
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
        let food = foods[indexPath.row]
        
        let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! CustomCell
        
        cell.thumbImageView.image = UIImage(named: food.thumb)
        
        cell.nameLabel?.text = food.name
        cell.countryLabel.text = food.country
        
        return cell
    }
    
    override func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
        if scrollView == tableView {
            if ((scrollView.contentOffset.y + scrollView.frame.size.height) >= scrollView.contentSize.height) {
                if !tableView.isLoadingFooterShowing() {
                    loadData()
                }
            }
        }
    }
}


Build dan jalankan maka hasilnya seperti dibawah ini.


Source lengkap dapat dilihat di https://github.com/wimsonevel/Paging-UITableView

Sekian tutorial singkat kali ini.
Jangan lupa share ke social media kalian ^^.

(Tutorial iOS) Pull to Refresh with UIRefreshControl

Monday, August 20, 2018 1 Comment

Hello guys, pada kesempatan sebelumnya saya sudah menjelaskan tentang bagaimana membuat custom tableview di ios. Bisa dilihat di sini https://wimsonevel.blogspot.com/2017/07/tutorial-ios-custom-uitableviewcell-in.html.

Tutorial kali ini pada dasarnya meneruskan tutorial yang sebelumnya yakni dengan menambahkan Pull to Refresh. Untuk membuat pull to refresh di iOS dapat menggunakan widget yang dinamakan UIRefreshControl. Implementasinya cukup sederhana yaitu dengan menempakannya ke dalam table view yang kita buat.

Oke, langsung aja ke TKP.

Pertama, deklarasikan variabel UIRefreshControl berikut.

let pullRefresh = UIRefreshControl()

Buat function untuk menghandle refresh control. Di sini lah tempat menghandle network request. Untuk sementara saya menggunakan thread biasa.

func pullRefresh(_ refreshControl: UIRefreshControl) {
    // do network request here
        
    DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
        self.tableView.reloadData()
        refreshControl.endRefreshing()
    }
}

Lalu atur properties-nya (warna, text, dll) sesuai keinginan.

// Refresh Control Properties
pullRefresh.addTarget(self, action: #selector(ViewController.pullRefresh(_:)), for: UIControlEvents.valueChanged)
        
pullRefresh.tintColor = UIColor.red
pullRefresh.attributedTitle = NSAttributedString(string: "Please wait ...")

Kemudian tambakan ke dalam tableview sebagai subview.

// Add to Table View
if #available(iOS 10.0, *) {
    tableView.refreshControl = pullRefresh
} else {
    tableView.addSubview(pullRefresh)
}

Berikut source code lengkapnya :

import UIKit

class ViewController: UITableViewController {

    
    var foods = [Food(thumb: "rendang", name: "Rendang", country: "Indonesia"),
                 Food(thumb: "nasi_goreng", name: "Nasi Goreng", country: "Indonesia"),
                 Food(thumb: "sushi", name: "Sushi", country: "Japan"),
                 Food(thumb: "tom_yum_goong", name: "Tom Yum Goong", country: "Thailand"),
                 Food(thumb: "pad_thai", name: "Pad Thai", country: "Thailand"),
                 Food(thumb: "som_tam", name: "Som Tam", country: "Thailand"),
                 Food(thumb: "dim_sum", name: "Dim Sum", country: "Hongkong"),
                 Food(thumb: "ramen", name: "Ramen", country: "Japan"),
                 Food(thumb: "peking_duck", name: "Peking Duck", country: "China"),
                 Food(thumb: "massaman_curry", name: "Massaman Curry", country: "Thailand")]
    
    let pullRefresh = UIRefreshControl()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        
        // Refresh Control Properties
        pullRefresh.addTarget(self, action: #selector(ViewController.pullRefresh(_:)), for: UIControlEvents.valueChanged)
        
        pullRefresh.tintColor = UIColor.red
        pullRefresh.attributedTitle = NSAttributedString(string: "Please wait ...")
        
        // Add to Table View
        if #available(iOS 10.0, *) {
            tableView.refreshControl = pullRefresh
        } else {
            tableView.addSubview(pullRefresh)
        }

    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    func pullRefresh(_ refreshControl: UIRefreshControl) {
        // do network request here
        
        DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
            self.tableView.reloadData()
            refreshControl.endRefreshing()
        }
    }

}

struct Food {
    var thumb = String()
    var name = String()
    var country = String()
}

class CustomCell: UITableViewCell {
    
    @IBOutlet weak var thumbImageView: UIImageView!
    @IBOutlet weak var nameLabel: UILabel!
    @IBOutlet weak var countryLabel: UILabel!
    
}

extension ViewController {
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return foods.count
    }
    
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
        let food = foods[indexPath.row]
        
        let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! CustomCell
        
        cell.thumbImageView.image = UIImage(named: food.thumb)
        
        cell.nameLabel?.text = food.name
        cell.countryLabel.text = food.country
        
        return cell
        
    }

}


Build dan run, maka hasilnya sebagai berikut :


Source code lengkap dapat dilihat di https://github.com/wimsonevel/PullToRefresh

Sekian tutorial singkat kali ini, semoga bermanfaat.
Jangan lupa share ke social media kalian ya ^^