Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ public final class ParseItemFilterResultUseCaseImpl: ParseItemFilterResultUseCas
}
case "레벨":
let levelText = value.replacingOccurrences(of: "레벨", with: "").trimmingCharacters(in: .whitespaces)
let parts = levelText.split(separator: "~").map { $0.trimmingCharacters(in: .whitespaces) }
let parts = levelText.split(separator: "~")
.map { $0.trimmingCharacters(in: .whitespaces) }
.map { $0.filter { $0.isNumber } }
if let low = Int(parts.first ?? ""), let high = Int(parts.last ?? "") {
criteria.startLevel = low
criteria.endLevel = high
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public final class ItemFilterBottomSheetReactor: Reactor {
case closeButtonTapped
case filterSelected(indexPath: IndexPath)
case filterDeselected(indexPath: IndexPath)
case changeLevelRange(low: Int, high: Int)
case changeLevelRange(low: Int?, high: Int?)
case applyButtonTapped([(String, String)])
case resetFilters
}
Expand All @@ -26,7 +26,7 @@ public final class ItemFilterBottomSheetReactor: Reactor {
case setScrolls(selectedIndex: Int?)
case appendSelectedItem(indexPath: IndexPath)
case removeSelectedItem(indexPath: IndexPath)
case setLevelRange(low: Int, high: Int)
case setLevelRange(low: Int?, high: Int?)
case resetFilters
}

Expand All @@ -47,7 +47,7 @@ public final class ItemFilterBottomSheetReactor: Reactor {
var etcItems: [String] = ["마스터리북", "스킬북", "소비", "설치", "이동수단"]
var selectedScrollIndexes: Int?
var selectedItemIndexes: [IndexPath] = []
var levelRange: (low: Int, high: Int) = (1, 200)
var levelRange: (low: Int?, high: Int?) = (nil, nil)
@Pulse var route: Route = .none
}

Expand Down Expand Up @@ -150,7 +150,7 @@ public final class ItemFilterBottomSheetReactor: Reactor {
newState.levelRange = (low, high)
case .resetFilters:
newState.selectedItemIndexes = []
newState.levelRange = (0, 200)
newState.levelRange = (nil, nil)
newState.scrollCategories = ["무기 주문서", "방어구 주문서", "기타 주문서"]
newState.weaponScrolls = []
newState.armorScrolls = []
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public final class ItemFilterBottomSheetViewController: BaseViewController, View

enum FilterItem: Hashable {
case job(String)
case level
case level(low: Int?, upper: Int?)
case weapons(String)
case projectiles(String)
case armors(String)
Expand Down Expand Up @@ -218,13 +218,16 @@ private extension ItemFilterBottomSheetViewController {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CheckBoxButtonListSmallCell.identifier, for: indexPath) as! CheckBoxButtonListSmallCell
cell.inject(title: title)
return cell
case .level:
case .level(let low, let upper):
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: FilterLevelSectionCell.identifier, for: indexPath) as! FilterLevelSectionCell
cell.inject(input: .init(lowValue: low, highValue: upper))
guard let reactor = self.reactor else { return UICollectionViewCell() }
let rowValue = cell.levelSectionView.slider.lowerValueObservable.map { Int($0) }
let highValue = cell.levelSectionView.slider.upperValueObservable.map { Int($0) }
Observable.combineLatest(rowValue, highValue)
.map { low, high in Reactor.Action.changeLevelRange(low: low, high: high) }
let lowValue = cell.levelSectionView.slider.lowerValueObservable
let highValue = cell.levelSectionView.slider.upperValueObservable
Observable.combineLatest(lowValue, highValue)
.map { low, high in
return Reactor.Action.changeLevelRange(low: low.map { Int($0)}, high: high.map { Int($0)})
}
.bind(to: reactor.action)
.disposed(by: cell.disposeBag)

Expand Down Expand Up @@ -262,7 +265,13 @@ private extension ItemFilterBottomSheetViewController {

// 섹션별 아이템 추가
snapshot.appendItems(reactor.currentState.jobs.map { .job($0) }, toSection: .job)
snapshot.appendItems([.level], toSection: .level)
snapshot.appendItems(
[.level(
low: reactor.currentState.levelRange.low,
upper: reactor.currentState.levelRange.high
)],
toSection: .level
)
snapshot.appendItems(reactor.currentState.weapons.map { .weapons($0) }, toSection: .weapons)
snapshot.appendItems(reactor.currentState.projectiles.map { .projectiles($0) }, toSection: .projectiles)
snapshot.appendItems(reactor.currentState.armors.map { .armors($0) }, toSection: .armors)
Expand Down Expand Up @@ -405,6 +414,10 @@ extension ItemFilterBottomSheetViewController {
// Reactor에 액션 전달
owner.reactor?.action.onNext(.resetFilters)

if let cell = owner.mainView.contentCollectionView.cellForItem(at: IndexPath(row: 0, section: 1)) as? FilterLevelSectionCell {
cell.levelSectionView.slider.lowerValue = 0
cell.levelSectionView.slider.upperValue = 200
}
// UI에서 직접 deselect
owner.mainView.contentCollectionView.indexPathsForSelectedItems?.forEach {
owner.mainView.contentCollectionView.deselectItem(at: $0, animated: false)
Expand Down Expand Up @@ -571,7 +584,7 @@ extension ItemFilterBottomSheetViewController: UICollectionViewDataSource {
return reactor.currentState.etcItems[indexPath.row]
case .level:
let range = reactor.currentState.levelRange
return "\(range.low) ~ \(range.high)"
return "\(range.low ?? 0) ~ \(range.high ?? 200)"
default:
return ""
}
Expand All @@ -587,7 +600,6 @@ extension ItemFilterBottomSheetViewController: UICollectionViewDataSource {
if let cell = owner.mainView.contentCollectionView.cellForItem(at: deselectedIndex) as? FilterLevelSectionCell {
cell.levelSectionView.slider.lowerValue = 0
cell.levelSectionView.slider.upperValue = 200
owner.reactor?.action.onNext(.changeLevelRange(low: 0, high: 200))
}
owner.reactor?.action.onNext(.filterDeselected(indexPath: deselectedIndex))
case .weaponScrolls, .armorsScrolls, .etcScrolls:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,15 @@ private extension FilterLevelSectionCell {
}
}
}

extension FilterLevelSectionCell {
struct Input {
let lowValue: Int?
let highValue: Int?
}

func inject(input: Input) {
levelSectionView.slider.lowerValue = input.lowValue.map { CGFloat($0) }
levelSectionView.slider.upperValue = input.highValue.map { CGFloat($0) }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public class FilterLevelSectionView: UIView {
private var isEdit = false

let leftInputBox: InputBox = {
let box = InputBox(label: "범위", placeHodler: "1")
let box = InputBox(label: "범위", placeHodler: "0")
box.textField.keyboardType = .numberPad
return box
}()
Expand Down Expand Up @@ -48,7 +48,7 @@ public class FilterLevelSectionView: UIView {

private let lowerLabel: UILabel = {
let label = UILabel()
label.attributedText = .makeStyledString(font: .b_s_r, text: "1", color: .neutral500)
label.attributedText = .makeStyledString(font: .b_s_r, text: "0", color: .neutral500)
return label
}()

Expand All @@ -66,8 +66,8 @@ public class FilterLevelSectionView: UIView {

public var disposeBag = DisposeBag()

public init(initialLowerValue: CGFloat = 1, initialUpperValue: CGFloat = 200) {
self.slider = FilterSlider(minimumValue: 1, maximumValue: 200, initialLowerValue: initialLowerValue, initialUpperValue: initialUpperValue)
public init(initialLowerValue: CGFloat = 0, initialUpperValue: CGFloat = 200) {
self.slider = FilterSlider(minimumValue: 0, maximumValue: 200, initialLowerValue: initialLowerValue, initialUpperValue: initialUpperValue)
super.init(frame: .zero)
addViews()
setupConstraints()
Expand Down Expand Up @@ -134,17 +134,17 @@ public extension FilterLevelSectionView {
.withUnretained(self)
.subscribe { owner, value in
guard !owner.isEdit else { return }
let lowValue = Int(value)
owner.leftInputBox.textField.text = value == owner.slider.minimumValue ? "1" : "\(lowValue)"
let lowValue = Int(value ?? 0)
owner.leftInputBox.textField.text = value == owner.slider.minimumValue ? nil : "\(lowValue)"
}
.disposed(by: disposeBag)

slider.upperValueObservable
.withUnretained(self)
.subscribe { owner, value in
guard !owner.isEdit else { return }
let upperValue = Int(value)
owner.rightInputBox.textField.text = value == owner.slider.minimumValue ? "200" : "\(upperValue)"
let upperValue = Int(value ?? 200)
owner.rightInputBox.textField.text = value == owner.slider.maximumValue ? nil : "\(upperValue)"
}
.disposed(by: disposeBag)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ public class FilterSlider: UIControl {
// MARK: - Value Relays
private let minimumValueRelay = BehaviorRelay<CGFloat>(value: 0)
private let maximumValueRelay = BehaviorRelay<CGFloat>(value: 200)
private let lowerValueRelay = BehaviorRelay<CGFloat>(value: 0)
private let upperValueRelay = BehaviorRelay<CGFloat>(value: 200)
private let lowerValueRelay = BehaviorRelay<CGFloat?>(value: nil)
private let upperValueRelay = BehaviorRelay<CGFloat?>(value: nil)

// MARK: - Public properties
public var minimumValue: CGFloat {
Expand All @@ -36,23 +36,23 @@ public class FilterSlider: UIControl {
set { maximumValueRelay.accept(newValue) }
}

public var lowerValue: CGFloat {
public var lowerValue: CGFloat? {
get { lowerValueRelay.value }
set { lowerValueRelay.accept(boundValue(newValue, lower: minimumValue, upper: maximumValue)) }
set { lowerValueRelay.accept(boundValue(newValue ?? minimumValue, lower: minimumValue, upper: maximumValue)) }
}

public var upperValue: CGFloat {
public var upperValue: CGFloat? {
get { upperValueRelay.value }
set { upperValueRelay.accept(boundValue(newValue, lower: minimumValue, upper: maximumValue)) }
set { upperValueRelay.accept(boundValue(newValue ?? maximumValue, lower: minimumValue, upper: maximumValue)) }
}
Comment on lines +39 to 47

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

lowerValueupperValue의 setter 로직이 nil 값을 제대로 처리하지 못하고 있습니다. newValue ?? minimumValue와 같이 nil-coalescing 연산자를 사용하면 newValuenil일 때 minimumValue 또는 maximumValue로 대체되어 BehaviorRelaynil이 저장되지 않습니다. 이로 인해 슬라이더의 상태가 nil이 될 수 없게 되어, 레벨 필터를 선택적으로 만들려는 원래 의도와 다르게 동작할 수 있습니다. newValuenil일 때도 nilBehaviorRelay에 전달되도록 setter를 수정해야 합니다.

Suggested change
public var lowerValue: CGFloat? {
get { lowerValueRelay.value }
set { lowerValueRelay.accept(boundValue(newValue, lower: minimumValue, upper: maximumValue)) }
set { lowerValueRelay.accept(boundValue(newValue ?? minimumValue, lower: minimumValue, upper: maximumValue)) }
}
public var upperValue: CGFloat {
public var upperValue: CGFloat? {
get { upperValueRelay.value }
set { upperValueRelay.accept(boundValue(newValue, lower: minimumValue, upper: maximumValue)) }
set { upperValueRelay.accept(boundValue(newValue ?? maximumValue, lower: minimumValue, upper: maximumValue)) }
}
public var lowerValue: CGFloat? {
get { lowerValueRelay.value }
set { lowerValueRelay.accept(newValue.map { boundValue($0, lower: minimumValue, upper: maximumValue) }) }
}
public var upperValue: CGFloat? {
get { upperValueRelay.value }
set { upperValueRelay.accept(newValue.map { boundValue($0, lower: minimumValue, upper: maximumValue) }) }
}


public let isThumbTracking: BehaviorRelay<Bool> = .init(value: false)

// MARK: - Observables
public var minimumValueObservable: Observable<CGFloat> { minimumValueRelay.asObservable() }
public var maximumValueObservable: Observable<CGFloat> { maximumValueRelay.asObservable() }
public var lowerValueObservable: Observable<CGFloat> { lowerValueRelay.asObservable() }
public var upperValueObservable: Observable<CGFloat> { upperValueRelay.asObservable() }
public var lowerValueObservable: Observable<CGFloat?> { lowerValueRelay.asObservable() }
public var upperValueObservable: Observable<CGFloat?> { upperValueRelay.asObservable() }

// MARK: - UI Elements
private let trackView = UIView()
Expand Down Expand Up @@ -151,8 +151,8 @@ public class FilterSlider: UIControl {
Observable.combineLatest(minimumValueRelay, maximumValueRelay)
.subscribe(onNext: { [weak self] minVal, maxVal in
guard let self = self else { return }
let clampedLower = self.boundValue(self.lowerValueRelay.value, lower: minVal, upper: maxVal)
let clampedUpper = self.boundValue(self.upperValueRelay.value, lower: minVal, upper: maxVal)
let clampedLower = self.boundValue(self.lowerValueRelay.value ?? self.minimumValue, lower: minVal, upper: maxVal)
let clampedUpper = self.boundValue(self.upperValueRelay.value ?? self.maximumValue, lower: minVal, upper: maxVal)
if clampedLower != self.lowerValueRelay.value {
self.lowerValueRelay.accept(clampedLower)
}
Expand Down Expand Up @@ -186,8 +186,8 @@ public class FilterSlider: UIControl {
}

private func updateTrackAndThumb(animated: Bool) {
let lowerX = position(for: lowerValueRelay.value)
let upperX = position(for: upperValueRelay.value)
let lowerX = position(for: lowerValueRelay.value ?? minimumValue)
let upperX = position(for: upperValueRelay.value ?? maximumValue)

lowerThumbCenterX?.update(offset: lowerX - bounds.midX)
upperThumbCenterX?.update(offset: upperX - bounds.midX)
Expand Down Expand Up @@ -238,8 +238,8 @@ public class FilterSlider: UIControl {
let deltaValue = (maximumValue - minimumValue) * deltaLocation / (bounds.width - Constant.thumbSize)
previousLocation = location

var newLower = lowerValueRelay.value
var newUpper = upperValueRelay.value
var newLower = lowerValueRelay.value ?? minimumValue
var newUpper = upperValueRelay.value ?? maximumValue

switch activeThumb {
case .lower:
Expand Down