Skip to content
Snippets Groups Projects
Commit 4ca61015 authored by Bruno Muniz's avatar Bruno Muniz :apple:
Browse files

Fixed scrolling only on qr and non-qr

parent 5a2dbbe2
No related branches found
No related tags found
2 merge requests!54Releasing 1.1.4,!50Search UI 2.0
Showing
with 68 additions and 558 deletions
...@@ -33,24 +33,20 @@ public final class SearchContainerController: UIViewController { ...@@ -33,24 +33,20 @@ public final class SearchContainerController: UIViewController {
lazy private var screenView = SearchContainerView() lazy private var screenView = SearchContainerView()
private let qrController = SearchQRController()
private var cancellables = Set<AnyCancellable>() private var cancellables = Set<AnyCancellable>()
private let viewModel = SearchContainerViewModel() private let viewModel = SearchContainerViewModel()
private let emailController = SearchEmailController() private let leftController = SearchLeftController()
private let phoneController = SearchPhoneController() private let rightController = SearchRightController()
private var drawerCancellables = Set<AnyCancellable>() private var drawerCancellables = Set<AnyCancellable>()
private let usernameController = SearchUsernameController()
public override func loadView() { public override func loadView() {
view = screenView view = screenView
screenView.scrollView.delegate = self
embedControllers() embedControllers()
} }
public override func viewWillAppear(_ animated: Bool) { public override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated) super.viewWillAppear(animated)
statusBarController.style.send(.darkContent) statusBarController.style.send(.darkContent)
navigationController?.navigationBar.customize( navigationController?.navigationBar.customize(
backgroundColor: Asset.neutralWhite.color backgroundColor: Asset.neutralWhite.color
) )
...@@ -88,16 +84,18 @@ public final class SearchContainerController: UIViewController { ...@@ -88,16 +84,18 @@ public final class SearchContainerController: UIViewController {
.actionPublisher .actionPublisher
.receive(on: DispatchQueue.main) .receive(on: DispatchQueue.main)
.sink { [unowned self] in .sink { [unowned self] in
let page = CGFloat($0.rawValue) if $0 == .qr {
let point: CGPoint = CGPoint(x: screenView.frame.width * page, y: 0.0) let point = CGPoint(x: screenView.frame.width, y: 0.0)
screenView.scrollView.setContentOffset(point, animated: true) screenView.scrollView.setContentOffset(point, animated: true)
} else {
screenView.scrollView.setContentOffset(.zero, animated: true)
}
}.store(in: &cancellables) }.store(in: &cancellables)
viewModel.coverTrafficPublisher viewModel.coverTrafficPublisher
.receive(on: DispatchQueue.main) .receive(on: DispatchQueue.main)
.sink { [unowned self] in presentCoverTrafficDrawer() } .sink { [unowned self] in presentCoverTrafficDrawer() }
.store(in: &cancellables) .store(in: &cancellables)
} }
@objc private func didTapBack() { @objc private func didTapBack() {
...@@ -105,92 +103,28 @@ public final class SearchContainerController: UIViewController { ...@@ -105,92 +103,28 @@ public final class SearchContainerController: UIViewController {
} }
private func embedControllers() { private func embedControllers() {
addChild(qrController) addChild(leftController)
addChild(emailController) addChild(rightController)
addChild(phoneController)
addChild(usernameController)
screenView.scrollView.addSubview(qrController.view) screenView.scrollView.addSubview(leftController.view)
screenView.scrollView.addSubview(emailController.view) screenView.scrollView.addSubview(rightController.view)
screenView.scrollView.addSubview(phoneController.view)
screenView.scrollView.addSubview(usernameController.view)
usernameController.view.snp.makeConstraints { leftController.view.snp.makeConstraints {
$0.top.equalTo(screenView.segmentedControl.snp.bottom) $0.top.equalTo(screenView.segmentedControl.snp.bottom)
$0.width.equalTo(screenView) $0.width.equalTo(screenView)
$0.bottom.equalTo(screenView) $0.bottom.equalTo(screenView)
$0.left.equalToSuperview() $0.left.equalToSuperview()
$0.right.equalTo(emailController.view.snp.left) $0.right.equalTo(rightController.view.snp.left)
}
emailController.view.snp.makeConstraints {
$0.top.equalTo(screenView.segmentedControl.snp.bottom)
$0.width.equalTo(screenView)
$0.bottom.equalTo(screenView)
$0.right.equalTo(phoneController.view.snp.left)
} }
phoneController.view.snp.makeConstraints { rightController.view.snp.makeConstraints {
$0.top.equalTo(screenView.segmentedControl.snp.bottom) $0.top.equalTo(screenView.segmentedControl.snp.bottom)
$0.width.equalTo(screenView) $0.width.equalTo(screenView)
$0.bottom.equalTo(screenView) $0.bottom.equalTo(screenView)
$0.right.equalTo(qrController.view.snp.left)
} }
qrController.view.snp.makeConstraints { leftController.didMove(toParent: self)
$0.top.equalTo(screenView.segmentedControl.snp.bottom) rightController.didMove(toParent: self)
$0.width.equalTo(screenView)
$0.bottom.equalTo(screenView)
}
qrController.didMove(toParent: self)
emailController.didMove(toParent: self)
phoneController.didMove(toParent: self)
usernameController.didMove(toParent: self)
}
}
extension SearchContainerController: UIScrollViewDelegate {
public func scrollViewDidScroll(_ scrollView: UIScrollView) {
let pageOffset = scrollView.contentOffset.x / view.frame.width
scrollSegmentedControlTrack(using: pageOffset)
updateSegmentedControlButtonsColor(using: pageOffset)
}
private func scrollSegmentedControlTrack(using pageOffset: CGFloat) {
let amountOfTabs = 4.0
let tabWidth = screenView.bounds.width / amountOfTabs
if let leftConstraint = screenView.segmentedControl.leftConstraint {
leftConstraint.update(offset: pageOffset * tabWidth)
}
}
private func updateSegmentedControlButtonsColor(using pageOffset: CGFloat) {
let qrRate = highlightRateFor(page: 3, offset: pageOffset)
let emailRate = highlightRateFor(page: 1, offset: pageOffset)
let phoneRate = highlightRateFor(page: 2, offset: pageOffset)
let usernameRate = highlightRateFor(page: 0, offset: pageOffset)
screenView.segmentedControl.qrCodeButton.updateHighlighting(rate: qrRate)
screenView.segmentedControl.emailButton.updateHighlighting(rate: emailRate)
screenView.segmentedControl.phoneButton.updateHighlighting(rate: phoneRate)
screenView.segmentedControl.usernameButton.updateHighlighting(rate: usernameRate)
}
private func highlightRateFor(page: CGFloat, offset: CGFloat) -> CGFloat {
let lowerBound = page - 1
let upperBound = page + 1
if offset > lowerBound && offset < upperBound {
if (offset - lowerBound) > 1 {
return 1 - (offset - page)
} else {
return offset - lowerBound
}
} else {
return 0
}
} }
} }
......
import UIKit
final class SearchEmailController: UIViewController {
lazy private var screenView = SearchEmailView()
override func loadView() {
view = screenView
}
}
...@@ -8,7 +8,7 @@ import Countries ...@@ -8,7 +8,7 @@ import Countries
import DrawerFeature import DrawerFeature
import DependencyInjection import DependencyInjection
final class SearchUsernameController: UIViewController { final class SearchLeftController: UIViewController {
@Dependency private var hud: HUDType @Dependency private var hud: HUDType
@Dependency private var coordinator: SearchCoordinating @Dependency private var coordinator: SearchCoordinating
...@@ -17,10 +17,10 @@ final class SearchUsernameController: UIViewController { ...@@ -17,10 +17,10 @@ final class SearchUsernameController: UIViewController {
@KeyObject(.sharingEmail, defaultValue: false) var isSharingEmail: Bool @KeyObject(.sharingEmail, defaultValue: false) var isSharingEmail: Bool
@KeyObject(.sharingPhone, defaultValue: false) var isSharingPhone: Bool @KeyObject(.sharingPhone, defaultValue: false) var isSharingPhone: Bool
lazy private var screenView = SearchUsernameView() lazy private var screenView = SearchLeftView()
private let viewModel = SearchLeftViewModel()
private var cancellables = Set<AnyCancellable>() private var cancellables = Set<AnyCancellable>()
private let viewModel = SearchUsernameViewModel()
private var drawerCancellables = Set<AnyCancellable>() private var drawerCancellables = Set<AnyCancellable>()
private let adrpURLString = "https://links.xx.network/adrp" private let adrpURLString = "https://links.xx.network/adrp"
private var dataSource: SearchTableViewDiffableDataSource! private var dataSource: SearchTableViewDiffableDataSource!
...@@ -339,7 +339,7 @@ final class SearchUsernameController: UIViewController { ...@@ -339,7 +339,7 @@ final class SearchUsernameController: UIViewController {
} }
extension SearchUsernameController: UITableViewDelegate { extension SearchLeftController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let item = dataSource.itemIdentifier(for: indexPath) { if let item = dataSource.itemIdentifier(for: indexPath) {
switch item { switch item {
......
import UIKit
final class SearchPhoneController: UIViewController {
lazy private var screenView = SearchPhoneView()
override func loadView() {
view = screenView
}
}
...@@ -16,10 +16,10 @@ enum SearchQRError: Equatable { ...@@ -16,10 +16,10 @@ enum SearchQRError: Equatable {
case alreadyFriends(String) case alreadyFriends(String)
} }
final class SearchQRController: UIViewController { final class SearchRightController: UIViewController {
@Dependency private var permissions: PermissionHandling @Dependency private var permissions: PermissionHandling
lazy private var screenView = SearchQRView() lazy private var screenView = SearchRightView()
private let camera = Camera() private let camera = Camera()
private var status: SearchQRStatus? private var status: SearchQRStatus?
......
import UIKit
import Models
import Combine
import XXModels
final class SearchTableController: UITableViewController {
private let viewModel: SearchViewModel
private var cancellables = Set<AnyCancellable>()
private(set) var dataSource = [Contact]()
init(_ viewModel: SearchViewModel) {
self.viewModel = viewModel
super.init(style: .grouped)
}
required init?(coder: NSCoder) { nil }
override func viewDidLoad() {
super.viewDidLoad()
tableView.backgroundColor = .clear
tableView.separatorStyle = .none
tableView.register(SearchCell.self)
viewModel.itemsRelay
.receive(on: DispatchQueue.main)
.sink { [unowned self] in
dataSource = $0
tableView.reloadData()
}.store(in: &cancellables)
}
override func tableView(
_ tableView: UITableView,
cellForRowAt indexPath: IndexPath
) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(forIndexPath: indexPath, ofType: SearchCell.self)
let username = dataSource[indexPath.row].username!
cell.setup(
title: username,
subtitle: username,
avatarTitle: username,
avatarImage: nil,
avatarSize: .large
)
return cell
}
override func tableView(
_: UITableView,
numberOfRowsInSection: Int
) -> Int { dataSource.count }
}
...@@ -7,12 +7,12 @@ import DependencyInjection ...@@ -7,12 +7,12 @@ import DependencyInjection
typealias SearchSnapshot = NSDiffableDataSourceSnapshot<SearchSection, SearchItem> typealias SearchSnapshot = NSDiffableDataSourceSnapshot<SearchSection, SearchItem>
struct SearchUsernameViewState { struct SearchLeftViewState {
var input = "" var input = ""
var snapshot: SearchSnapshot? var snapshot: SearchSnapshot?
} }
final class SearchUsernameViewModel { final class SearchLeftViewModel {
@Dependency var session: SessionType @Dependency var session: SessionType
var hudPublisher: AnyPublisher<HUDStatus, Never> { var hudPublisher: AnyPublisher<HUDStatus, Never> {
...@@ -23,13 +23,13 @@ final class SearchUsernameViewModel { ...@@ -23,13 +23,13 @@ final class SearchUsernameViewModel {
successSubject.eraseToAnyPublisher() successSubject.eraseToAnyPublisher()
} }
var statePublisher: AnyPublisher<SearchUsernameViewState, Never> { var statePublisher: AnyPublisher<SearchLeftViewState, Never> {
stateSubject.eraseToAnyPublisher() stateSubject.eraseToAnyPublisher()
} }
private let successSubject = PassthroughSubject<Contact, Never>() private let successSubject = PassthroughSubject<Contact, Never>()
private let hudSubject = CurrentValueSubject<HUDStatus, Never>(.none) private let hudSubject = CurrentValueSubject<HUDStatus, Never>(.none)
private let stateSubject = CurrentValueSubject<SearchUsernameViewState, Never>(.init()) private let stateSubject = CurrentValueSubject<SearchLeftViewState, Never>(.init())
func didEnterInput(_ string: String) { func didEnterInput(_ string: String) {
stateSubject.value.input = string stateSubject.value.input = string
......
import HUD
import UIKit
import Models
import Combine
import Defaults
import XXModels
import Countries
import Foundation
import Integration
import PushFeature
import CombineSchedulers
import DependencyInjection
enum SelectedFilter {
case username
case email
case phone
var prefix: String {
switch self {
case .username:
return "U"
case .phone:
return "P"
case .email:
return "E"
}
}
}
struct SearchViewState: Equatable {
var input: String = ""
var phoneInput: String = ""
var selectedFilter: SelectedFilter = .username
var country: Country = .fromMyPhone()
}
final class SearchViewModel {
@Dependency private var session: SessionType
var hudPublisher: AnyPublisher<HUDStatus, Never> {
hudSubject.eraseToAnyPublisher()
}
var placeholderPublisher: AnyPublisher<Bool, Never> {
placeholderSubject.eraseToAnyPublisher()
}
var statePublisher: AnyPublisher<SearchViewState, Never> {
stateSubject.eraseToAnyPublisher()
}
var successPublisher: AnyPublisher<Contact, Never> {
successSubject.eraseToAnyPublisher()
}
var backgroundScheduler: AnySchedulerOf<DispatchQueue>
= DispatchQueue.global().eraseToAnyScheduler()
let itemsRelay = CurrentValueSubject<[Contact], Never>([])
private let successSubject = PassthroughSubject<Contact, Never>()
private let hudSubject = CurrentValueSubject<HUDStatus, Never>(.none)
private let placeholderSubject = CurrentValueSubject<Bool, Never>(true)
private let stateSubject = CurrentValueSubject<SearchViewState, Never>(.init())
func didSelect(filter: SelectedFilter) {
stateSubject.value.selectedFilter = filter
}
func didInput(_ string: String) {
stateSubject.value.input = string.trimmingCharacters(in: .whitespacesAndNewlines)
}
func didInputPhone(_ string: String) {
stateSubject.value.phoneInput = string.trimmingCharacters(in: .whitespacesAndNewlines)
}
func didChooseCountry(_ country: Country) {
stateSubject.value.country = country
}
func didTapSearch() {
hudSubject.send(.on(nil))
backgroundScheduler.schedule { [weak self] in
guard let self = self else { return }
do {
var content = self.stateSubject.value.selectedFilter.prefix
if self.stateSubject.value.selectedFilter == .phone {
content += self.stateSubject.value.phoneInput + self.stateSubject.value.country.code
} else {
content += self.stateSubject.value.input
}
try self.session.search(fact: content) { result in
self.placeholderSubject.send(false)
switch result {
case .success(let searched):
self.hudSubject.send(.none)
self.itemsRelay.send([searched])
case .failure(let error):
self.hudSubject.send(.error(.init(with: error)))
self.itemsRelay.send([])
}
}
} catch {
self.hudSubject.send(.error(.init(with: error)))
}
}
}
func didSet(nickname: String, for contact: Contact) {
if var contact = try? session.dbManager.fetchContacts(.init(id: [contact.id])).first {
contact.nickname = nickname
_ = try? session.dbManager.saveContact(contact)
}
}
func didTapRequest(contact: Contact) {
hudSubject.send(.on(nil))
var contact = contact
contact.nickname = contact.username
backgroundScheduler.schedule { [weak self] in
guard let self = self else { return }
do {
try self.session.add(contact)
self.hudSubject.send(.none)
self.successSubject.send(contact)
} catch {
self.hudSubject.send(.error(.init(with: error)))
}
}
}
}
import UIKit
import Shared
final class SearchCell: UITableViewCell {
private let titleLabel = UILabel()
private let subtitleLabel = UILabel()
private let separatorView = UIView()
private let avatarView = AvatarView()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
selectionStyle = .none
backgroundColor = Asset.neutralWhite.color
titleLabel.textColor = Asset.neutralActive.color
subtitleLabel.textColor = Asset.neutralDisabled.color
separatorView.backgroundColor = Asset.neutralLine.color
titleLabel.font = Fonts.Mulish.semiBold.font(size: 14.0)
subtitleLabel.font = Fonts.Mulish.regular.font(size: 12.0)
contentView.addSubview(titleLabel)
contentView.addSubview(avatarView)
contentView.addSubview(subtitleLabel)
contentView.addSubview(separatorView)
setupConstraints()
}
required init?(coder: NSCoder) { nil }
override func prepareForReuse() {
super.prepareForReuse()
titleLabel.text = nil
subtitleLabel.text = nil
avatarView.prepareForReuse()
}
func setup(
title: String,
subtitle: String,
avatarTitle: String,
avatarImage: Data?,
avatarSize: AvatarView.Size
) {
titleLabel.text = title
subtitleLabel.text = subtitle
avatarView.setupProfile(
title: avatarTitle,
image: avatarImage,
size: avatarSize
)
}
private func setupConstraints() {
titleLabel.snp.makeConstraints {
$0.top.equalToSuperview().offset(10)
$0.left.equalTo(avatarView.snp.right).offset(16)
$0.right.lessThanOrEqualToSuperview().offset(-20)
}
subtitleLabel.snp.makeConstraints {
$0.top.equalTo(titleLabel.snp.bottom).offset(3)
$0.left.equalTo(titleLabel)
$0.bottom.equalToSuperview().offset(-22)
}
avatarView.snp.makeConstraints {
$0.left.equalToSuperview().offset(28)
$0.width.height.equalTo(48)
$0.bottom.equalToSuperview().offset(-16)
}
separatorView.snp.makeConstraints {
$0.height.equalTo(1)
$0.left.equalToSuperview().offset(24)
$0.right.equalToSuperview().offset(-24)
$0.bottom.equalToSuperview()
}
}
}
import UIKit
import Shared
final class SearchEmailView: UIView {
let inputField = SearchComponent()
init() {
super.init(frame: .zero)
inputField.set(
placeholder: Localized.Ud.Search.Email.input,
imageAtRight: nil
)
addSubview(inputField)
inputField.snp.makeConstraints {
$0.top.equalToSuperview().offset(15)
$0.left.equalToSuperview().offset(15)
$0.right.equalToSuperview().offset(-15)
$0.bottom.lessThanOrEqualToSuperview()
}
}
required init?(coder: NSCoder) { nil }
}
...@@ -2,7 +2,7 @@ import UIKit ...@@ -2,7 +2,7 @@ import UIKit
import Shared import Shared
import Combine import Combine
final class SearchUsernamePlaceholderView: UIView { final class SearchLeftPlaceholderView: UIView {
let titleLabel = UILabel() let titleLabel = UILabel()
let subtitleWithInfo = TextWithInfoView() let subtitleWithInfo = TextWithInfoView()
......
import UIKit import UIKit
import Shared import Shared
final class SearchUsernameView: UIView { final class SearchLeftView: UIView {
let tableView = UITableView() let tableView = UITableView()
let inputField = SearchComponent() let inputField = SearchComponent()
let emptyView: UIView = { let emptyView: UIView = {
...@@ -24,7 +24,7 @@ final class SearchUsernameView: UIView { ...@@ -24,7 +24,7 @@ final class SearchUsernameView: UIView {
return view return view
}() }()
let placeholderView = SearchUsernamePlaceholderView() let placeholderView = SearchLeftPlaceholderView()
init() { init() {
super.init(frame: .zero) super.init(frame: .zero)
......
import UIKit
import Shared
final class SearchPhoneView: UIView {
let inputField = SearchComponent()
init() {
super.init(frame: .zero)
inputField.set(
placeholder: Localized.Ud.Search.Phone.input,
imageAtRight: nil
)
addSubview(inputField)
inputField.snp.makeConstraints {
$0.top.equalToSuperview().offset(15)
$0.left.equalToSuperview().offset(15)
$0.right.equalToSuperview().offset(-15)
$0.bottom.lessThanOrEqualToSuperview()
}
}
required init?(coder: NSCoder) { nil }
}
import UIKit
import Shared
final class SearchPlaceholderView: UIView {
let titleView = TextWithInfoView()
let didTapInfo: () -> Void
init(didTapInfo: @escaping () -> Void) {
self.didTapInfo = didTapInfo
super.init(frame: .zero)
let paragraph = NSMutableParagraphStyle()
paragraph.lineSpacing = 5
paragraph.lineHeightMultiple = 1.0
paragraph.alignment = .center
titleView.setup(
text: "Your searches are anonymous.\nSearch information is never linked to your account or personally identifiable.",
attributes: [
.foregroundColor: Asset.neutralBody.color,
.font: Fonts.Mulish.regular.font(size: 16.0) as Any,
.paragraphStyle: paragraph
],
didTapInfo: { didTapInfo() }
)
addSubview(titleView)
titleView.snp.makeConstraints { make in
make.top.equalToSuperview().offset(60)
make.left.equalToSuperview().offset(60)
make.right.equalToSuperview().offset(-60)
}
}
required init?(coder: NSCoder) { nil }
}
final class SearchEmptyView: UIView {
private let title = UILabel()
init() {
super.init(frame: .zero)
backgroundColor = Asset.neutralWhite.color
title.textColor = Asset.neutralBody.color
title.font = Fonts.Mulish.regular.font(size: 12.0)
addSubview(title)
title.snp.makeConstraints { make in
make.top.equalToSuperview().offset(20)
make.left.equalToSuperview().offset(30)
make.right.equalToSuperview().offset(-30)
}
}
required init?(coder: NSCoder) { nil }
func set(filter: String) {
title.text = Localized.Ud.noneFound(filter)
}
}
import UIKit import UIKit
import Shared import Shared
final class SearchQRView: UIView { final class SearchRightView: UIView {
let statusLabel = UILabel() let statusLabel = UILabel()
let imageView = UIImageView() let imageView = UIImageView()
let stackView = UIStackView() let stackView = UIStackView()
let animationView = DotAnimation()
let overlayView = OverlayView() let overlayView = OverlayView()
let animationView = DotAnimation()
let actionButton = CapsuleButton() let actionButton = CapsuleButton()
init() { init() {
......
...@@ -12,7 +12,6 @@ final class SearchSegmentedButton: UIControl { ...@@ -12,7 +12,6 @@ final class SearchSegmentedButton: UIControl {
imageView.contentMode = .center imageView.contentMode = .center
titleLabel.textAlignment = .center titleLabel.textAlignment = .center
titleLabel.textColor = Asset.neutralWhite.color
titleLabel.font = Fonts.Mulish.semiBold.font(size: 13.0) titleLabel.font = Fonts.Mulish.semiBold.font(size: 13.0)
addSubview(titleLabel) addSubview(titleLabel)
...@@ -23,27 +22,16 @@ final class SearchSegmentedButton: UIControl { ...@@ -23,27 +22,16 @@ final class SearchSegmentedButton: UIControl {
required init?(coder: NSCoder) { nil } required init?(coder: NSCoder) { nil }
func setup( func setup(title: String, icon: UIImage) {
title: String, imageView.image = icon
icon: UIImage, titleLabel.text = title
iconColor: UIColor = Asset.neutralDisabled.color, imageView.tintColor = discreteColor
titleColor: UIColor = Asset.neutralDisabled.color titleLabel.textColor = discreteColor
) {
self.imageView.image = icon
self.titleLabel.text = title
self.imageView.tintColor = iconColor
self.titleLabel.textColor = titleColor
} }
func updateHighlighting(rate: CGFloat) { func setSelected(_ bool: Bool) {
let color = UIColor.fade( imageView.tintColor = bool ? highlightColor : discreteColor
from: discreteColor, titleLabel.textColor = bool ? highlightColor : discreteColor
to: highlightColor,
pcent: rate
)
imageView.tintColor = color
titleLabel.textColor = color
} }
private func setupConstraints() { private func setupConstraints() {
......
...@@ -13,35 +13,29 @@ final class SearchSegmentedControl: UIView { ...@@ -13,35 +13,29 @@ final class SearchSegmentedControl: UIView {
private let trackView = UIView() private let trackView = UIView()
private let stackView = UIStackView() private let stackView = UIStackView()
private var leftConstraint: Constraint?
private let trackIndicatorView = UIView() private let trackIndicatorView = UIView()
private(set) var leftConstraint: Constraint? private let emailButton = SearchSegmentedButton()
private(set) var usernameButton = SearchSegmentedButton() private let phoneButton = SearchSegmentedButton()
private(set) var emailButton = SearchSegmentedButton() private let qrCodeButton = SearchSegmentedButton()
private(set) var phoneButton = SearchSegmentedButton() private let usernameButton = SearchSegmentedButton()
private(set) var qrCodeButton = SearchSegmentedButton()
var actionPublisher: AnyPublisher<Item, Never> { var actionPublisher: AnyPublisher<Item, Never> {
actionSubject.eraseToAnyPublisher() actionSubject.eraseToAnyPublisher()
} }
private var cancellables = Set<AnyCancellable>() private var cancellables = Set<AnyCancellable>()
private let actionSubject = PassthroughSubject<Item, Never>() private let actionSubject = CurrentValueSubject<Item, Never>(.username)
init() { init() {
super.init(frame: .zero) super.init(frame: .zero)
trackView.backgroundColor = Asset.neutralLine.color trackView.backgroundColor = Asset.neutralLine.color
trackIndicatorView.backgroundColor = Asset.brandPrimary.color trackIndicatorView.backgroundColor = Asset.brandPrimary.color
usernameButton.setup(
title: Localized.Ud.Tab.username,
icon: Asset.searchTabUsername.image,
iconColor: Asset.brandPrimary.color,
titleColor: Asset.brandPrimary.color
)
qrCodeButton.setup(title: Localized.Ud.Tab.qr, icon: Asset.searchTabQr.image) qrCodeButton.setup(title: Localized.Ud.Tab.qr, icon: Asset.searchTabQr.image)
emailButton.setup(title: Localized.Ud.Tab.email, icon: Asset.searchTabEmail.image) emailButton.setup(title: Localized.Ud.Tab.email, icon: Asset.searchTabEmail.image)
phoneButton.setup(title: Localized.Ud.Tab.phone, icon: Asset.searchTabPhone.image) phoneButton.setup(title: Localized.Ud.Tab.phone, icon: Asset.searchTabPhone.image)
usernameButton.setup(title: Localized.Ud.Tab.username, icon: Asset.searchTabUsername.image)
stackView.distribution = .fillEqually stackView.distribution = .fillEqually
stackView.addArrangedSubview(usernameButton) stackView.addArrangedSubview(usernameButton)
...@@ -61,25 +55,38 @@ final class SearchSegmentedControl: UIView { ...@@ -61,25 +55,38 @@ final class SearchSegmentedControl: UIView {
required init?(coder: NSCoder) { nil } required init?(coder: NSCoder) { nil }
private func setupBindings() { private func setupBindings() {
usernameButton usernameButton.publisher(for: .touchUpInside)
.publisher(for: .touchUpInside)
.sink { [unowned self] in actionSubject.send(.username) } .sink { [unowned self] in actionSubject.send(.username) }
.store(in: &cancellables) .store(in: &cancellables)
emailButton emailButton.publisher(for: .touchUpInside)
.publisher(for: .touchUpInside)
.sink { [unowned self] in actionSubject.send(.email) } .sink { [unowned self] in actionSubject.send(.email) }
.store(in: &cancellables) .store(in: &cancellables)
phoneButton phoneButton.publisher(for: .touchUpInside)
.publisher(for: .touchUpInside)
.sink { [unowned self] in actionSubject.send(.phone) } .sink { [unowned self] in actionSubject.send(.phone) }
.store(in: &cancellables) .store(in: &cancellables)
qrCodeButton qrCodeButton.publisher(for: .touchUpInside)
.publisher(for: .touchUpInside)
.sink { [unowned self] in actionSubject.send(.qr) } .sink { [unowned self] in actionSubject.send(.qr) }
.store(in: &cancellables) .store(in: &cancellables)
actionSubject
.removeDuplicates()
.receive(on: DispatchQueue.main)
.sink { [unowned self] in
let tabWidth = bounds.width / 4
if let leftConstraint = leftConstraint {
leftConstraint.update(offset: tabWidth * CGFloat($0.rawValue))
setNeedsLayout()
UIView.animate(withDuration: 0.25) { self.layoutIfNeeded() }
}
qrCodeButton.setSelected($0 == .qr)
emailButton.setSelected($0 == .email)
phoneButton.setSelected($0 == .phone)
usernameButton.setSelected($0 == .username)
}.store(in: &cancellables)
} }
private func setupConstraints() { private func setupConstraints() {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment