Customize Consent Preferences

We use cookies to help you navigate efficiently and perform certain functions. You will find detailed information about all cookies under each consent category below.

The cookies that are categorized as "Necessary" are stored on your browser as they are essential for enabling the basic functionalities of the site. ... 

Always Active

Necessary cookies are required to enable the basic features of this site, such as providing secure log-in or adjusting your consent preferences. These cookies do not store any personally identifiable data.

Functional cookies help perform certain functionalities like sharing the content of the website on social media platforms, collecting feedback, and other third-party features.

Analytical cookies are used to understand how visitors interact with the website. These cookies help provide information on metrics such as the number of visitors, bounce rate, traffic source, etc.

Performance cookies are used to understand and analyze the key performance indexes of the website which helps in delivering a better user experience for the visitors.

Advertisement cookies are used to provide visitors with customized advertisements based on the pages you visited previously and to analyze the effectiveness of the ad campaigns.

SwiftUI series 10: Building a Better Fruit Search UI in SwiftUI: Custom Components and Search Implementation

In this tutorial, we'll explore how to create a polished search interface with custom components and smooth interactions. We'll break down the implementation into three main parts: FruitRow, CategoryButton, and the main BetterSearchView.

1. FruitRow Component

First, let's look at our custom row component for displaying fruit information:

struct FruitRow: View {
    let fruit: Fruit

    var body: some View {
        HStack(spacing: 16) {
            Text(fruit.emoji)
                .font(.title)
                .frame(width: 50, height: 50)
                .background(Color.gray.opacity(0.1))
                .clipShape(Circle())
            VStack(alignment: .leading, spacing: 4) {
                Text(fruit.name)
                    .font(.headline)
                HStack {
                    Text(fruit.category)
                        .font(.caption)
                        .foregroundColor(.secondary)
                    Text("•")
                        .foregroundStyle(.secondary)
                    Text("Vitamins: \(fruit.vitamins)")
                        .font(.caption)
                        .foregroundStyle(.secondary)
                }
            }
        }
        .padding(.vertical, 4)
    }
}

Key Features:

  • Circular emoji container with gray background
  • Clean layout with fruit name as headline
  • Secondary information displayed with proper spacing
  • Consistent padding for optimal appearance

2. CategoryButton Component

Next, let's examine our custom category filter button:

struct CategoryButton: View {
    let title: String
    let isSelected: Bool
    let action: () -> Void

    var body: some View {
        Button(action: action) {
            Text(title)
                .font(.subheadline)
                .fontWeight(.medium)
                .padding(.horizontal, 16)
                .padding(.vertical, 8)
                .background(isSelected ? Color.accentColor : Color.gray.opacity(0.1))
                .foregroundStyle(isSelected ? .white : .primary)
                .clipShape(Capsule())
        }
    }
}

Key Features:

  • Capsule-shaped button design
  • Visual feedback for selected state
  • Consistent padding and spacing
  • Reusable component design

3. BetterSearchView Implementation

Finally, let's look at how we bring it all together:

struct BetterSearchView: View {
    @StateObject private var viewModel = FruitViewModel()

    var body: some View {
        NavigationStack {
            VStack(spacing: 1) {
                // Category Filter ScrollView
                ScrollView(.horizontal, showsIndicators: false) {
                    HStack(spacing: 12) {
                        CategoryButton(title: "All", isSelected: viewModel.selectedCategory == nil) {
                            viewModel.selectedCategory = nil
                        }

                        ForEach(viewModel.categories, id: \.self) { category in
                            CategoryButton(title: category, 
                                         isSelected: viewModel.selectedCategory == category) {
                                viewModel.selectedCategory = category
                            }
                        }
                    }
                    .padding(.horizontal)
                }
                .padding(.vertical, 8)

                // Fruit List
                List {
                    ForEach(viewModel.filteredFruits) { fruit in
                        FruitRow(fruit: fruit)
                    }
                }
                .listStyle(.inset)
            }
            .searchable(text: $viewModel.searchText, 
                       placement: .navigationBarDrawer(displayMode: .always),
                       prompt: "Search The Name Or Select Category...")
            .navigationTitle("A Better Fruit Search")
            .navigationBarTitleDisplayMode(.large)
        }
    }
}

Key Features:

  1. Navigation and Search

    • Large navigation title
    • Persistent search bar
    • Custom search prompt
  2. Category Filtering

    • Horizontal scrolling category buttons
    • "All" option to clear filters
    • Visual feedback for selected category
  3. List Implementation

    • Clean, inset list style
    • Custom row components
    • Smooth scrolling experience

Putting It All Together

The view hierarchy creates a seamless user experience:

  1. Search bar at the top
  2. Horizontal scrolling categories
  3. Filtered list of fruits
  4. Custom-styled rows for each fruit

Best Practices Implemented

  1. Component Separation

    • Reusable components (FruitRow, CategoryButton)
    • Clean, maintainable code structure
  2. User Experience

    • Smooth animations
    • Clear visual hierarchy
    • Intuitive filtering system
  3. Performance

    • Efficient list rendering
    • Minimal view updates
    • Smooth scrolling