Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create UIKit screen and integrate with Decide #2

Draft
wants to merge 6 commits into
base: feature/integrate-decide-to-swiftui
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Features/DonutShop/Sources/DonutShop/Models/Donut.swift
Original file line number Diff line number Diff line change
Expand Up @@ -203,8 +203,8 @@ public extension Donut {
id: Donut.all.count,
name: String(localized: "New Donut", comment: "New donut-placeholder name."),
dough: .plain,
glaze: .chocolate,
topping: .sprinkles
glaze: .none,
topping: .none
)
}

Expand Down
12 changes: 3 additions & 9 deletions Features/DonutShop/Sources/DonutShop/Models/FoodTruckModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,7 @@ import Decide

final class FoodTruckState: AtomicState {

@Property public var donuts = Donut.all
@Mutable @Property public var editorDonut: Donut = Donut(
id: Donut.all.count,
name: String(localized: "New Donut", comment: "New donut-placeholder name."),
dough: .plain,
glaze: .chocolate,
topping: .sprinkles
)

@Mutable @Property public var donuts = Donut.all
@Mutable @Property public var selectedDonut: Donut = Donut.cosmos

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//
// DonutDetailsCell.swift
//
//
// Created by Anton Kolchunov on 14.08.23.
//

import SwiftUI
import UIKit

class DonutDetailsCell: UITableViewCell {

static var identifier = "DonutDetailsCell"

var hostingController: UIHostingController<AnyView>?

func configure<T: View>(_ swiftUIView: T) {
let view = AnyView(swiftUIView)
hostingController = UIHostingController(rootView: view)
guard let hostingController = hostingController else { return }
contentView.addSubview(hostingController.view)
hostingController.view.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
hostingController.view.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
hostingController.view.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
hostingController.view.topAnchor.constraint(equalTo: contentView.topAnchor),
hostingController.view.bottomAnchor.constraint(equalTo: contentView.bottomAnchor)
])
}

override func prepareForReuse() {
super.prepareForReuse()
hostingController?.view.removeFromSuperview()
hostingController = nil
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
//
// DonutIngredientCell.swift
//
//
// Created by Anton Kolchunov on 14.08.23.
//

import Foundation
import UIKit

class DonutIngredientCell: UITableViewCell {

static var identifier = "DonutIngredientCell"

let nameLabel = UILabel()
let valueLabel = UILabel()

override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)

nameLabel.translatesAutoresizingMaskIntoConstraints = false
valueLabel.translatesAutoresizingMaskIntoConstraints = false

contentView.addSubview(nameLabel)
contentView.addSubview(valueLabel)
valueLabel.textColor = .gray

NSLayoutConstraint.activate([
nameLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16),
nameLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 12),
nameLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -12),
valueLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -12),
valueLabel.centerYAnchor.constraint(equalTo: nameLabel.centerYAnchor)
])
}

required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
//
// DonutDetails.swift
//
//
// Created by Anton Kolchunov on 11.08.23.
//

import SwiftUI
import Decide

struct DonutDetailsView: View {
@State private var isPresentingEditor = false
@Bind(\FoodTruckState.$selectedDonut) var detailsDonut

var body: some View {
DonutDetails()
.edgesIgnoringSafeArea(.all)
.navigationTitle(detailsDonut.name)
.navigationBarTitleDisplayMode(.inline)
.toolbarRole(.editor)
.toolbar {
ToolbarItemGroup {
Button {
isPresentingEditor = true
} label: {
Label("Edit", systemImage: "pencil")
}
}
}
.fullScreenCover(isPresented: $isPresentingEditor) {
NavigationView {
DetailsDonutEditor()
}
}
}
}

struct DonutDetails: UIViewControllerRepresentable {
typealias UIViewControllerType = DonutDetailsTableViewController

func makeUIViewController(context: Context) -> DonutDetailsTableViewController {
DonutDetailsTableViewController()
}

func updateUIViewController(_ uiViewController: DonutDetailsTableViewController, context: Context) {
// Don't need to do anything here
}
}

struct DonutDetailsPreview: PreviewProvider {
struct Preview: View {
var body: some View {
NavigationView {
DonutDetailsView()
}
}
}

static var previews: some View {
Preview()
}
}

class DonutDetailsTableViewController: UITableViewController, EnvironmentObservingObject {

@DefaultEnvironment var environment
@DefaultBind(\FoodTruckState.$selectedDonut) var donut

func environmentDidUpdate() {
_ = donut // we need to read property in order to subscribe.
MaximBazarov marked this conversation as resolved.
Show resolved Hide resolved
tableView.reloadData()
}

let sections = ["", "Flavor profile", "Ingredients"]
let ingredientsTitles = ["Dough", "Glaze", "Topping"]

init() {
super.init(style: .insetGrouped)
}

required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

override func viewDidLoad() {
super.viewDidLoad()

environmentDidUpdate()

tableView.register(DonutDetailsCell.self, forCellReuseIdentifier: DonutDetailsCell.identifier)
tableView.register(DonutIngredientCell.self, forCellReuseIdentifier: DonutIngredientCell.identifier)
tableView.contentInsetAdjustmentBehavior = .always

self.tableView.isScrollEnabled = false
}

override func numberOfSections(in tableView: UITableView) -> Int {
return sections.count
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 2 {
return 3
}
return 1
}

override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return sections[section]
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.section == 0 || indexPath.section == 1 {
let cell = tableView.dequeueReusableCell(
withIdentifier: DonutDetailsCell.identifier,
for: indexPath
) as! DonutDetailsCell
let view = indexPath.section == 0 ? buildDonutView() : buildFlavorDetailsView()
cell.configure(view)
return cell
}

let cell = buildIngredientsCell(for: indexPath)
return cell
}

private func buildDonutView() -> any View {
return DonutView(donut: donut)
.frame(minWidth: 100, maxWidth: .infinity, minHeight: 100, maxHeight: .infinity)
.listRowInsets(.init())
.padding(.horizontal, 20)
.padding(.vertical)
.background()
}

private func buildFlavorDetailsView() -> any View {
return DonatFlavorDetailsView(
mostPotentFlavor: donut.flavors.mostPotent,
flavors: donut.flavors
).padding(20)
}

private func buildIngredientsCell(for indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(
withIdentifier: DonutIngredientCell.identifier,
for: indexPath
) as! DonutIngredientCell
let ingredient = getIngredient(for: indexPath)
cell.nameLabel.text = ingredientsTitles[indexPath.row]
cell.valueLabel.text = ingredient?.name ?? "None"
cell.selectionStyle = .none
return cell
}

private func getIngredient(for indexPath: IndexPath) -> (any Ingredient)? {
switch indexPath.row {
case 0:
return donut.dough
case 1:
return donut.glaze
case 2:
return donut.topping
default:
fatalError()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//
// DetailsDonutEditor.swift
//
//
// Created by Anton Kolchunov on 15.08.23.
//

import SwiftUI

struct DetailsDonutEditor: View {
@Environment(\.dismiss) var dismiss

var body: some View {
NavigationView {
DonutEditor()
}
.navigationViewStyle(StackNavigationViewStyle())
.navigationBarTitle("Edit Donut")
.navigationBarTitleDisplayMode(.inline)
.navigationBarItems(trailing: Button("Close") {
dismiss()
})
}
}

struct DetailsDonutEditor_Previews: PreviewProvider {
static var previews: some View {
NavigationStack {
DetailsDonutEditor()
}
}
}


Loading