Skip to content

Commit 67aee30

Browse files
authored
Merge pull request #13 from boostcampwm-2024/feat/#12-create-mic-button
2 parents 33bf1f0 + eda8de4 commit 67aee30

File tree

5 files changed

+172
-2
lines changed

5 files changed

+172
-2
lines changed

PhotoGether/BaseFeature/BaseFeature.xcodeproj/project.pbxproj

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,14 @@
77
objects = {
88

99
/* Begin PBXBuildFile section */
10+
60F33E1B2CE22DAE00A5C26D /* UIControl+Publisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60F33E1A2CE22DAE00A5C26D /* UIControl+Publisher.swift */; };
11+
60F33E1D2CE22DB900A5C26D /* UIButton+Publisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60F33E1C2CE22DB900A5C26D /* UIButton+Publisher.swift */; };
1012
7B5951552CDB601900B89C85 /* BaseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B5951442CDB5E7100B89C85 /* BaseViewController.swift */; };
1113
/* End PBXBuildFile section */
1214

1315
/* Begin PBXFileReference section */
16+
60F33E1A2CE22DAE00A5C26D /* UIControl+Publisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIControl+Publisher.swift"; sourceTree = "<group>"; };
17+
60F33E1C2CE22DB900A5C26D /* UIButton+Publisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIButton+Publisher.swift"; sourceTree = "<group>"; };
1418
7B5951382CDB5E5500B89C85 /* BaseFeature.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = BaseFeature.framework; sourceTree = BUILT_PRODUCTS_DIR; };
1519
7B5951442CDB5E7100B89C85 /* BaseViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseViewController.swift; sourceTree = "<group>"; };
1620
/* End PBXFileReference section */
@@ -26,9 +30,19 @@
2630
/* End PBXFrameworksBuildPhase section */
2731

2832
/* Begin PBXGroup section */
33+
60F33E192CE22D8B00A5C26D /* Extension */ = {
34+
isa = PBXGroup;
35+
children = (
36+
60F33E1A2CE22DAE00A5C26D /* UIControl+Publisher.swift */,
37+
60F33E1C2CE22DB900A5C26D /* UIButton+Publisher.swift */,
38+
);
39+
path = Extension;
40+
sourceTree = "<group>";
41+
};
2942
7B59512E2CDB5E5500B89C85 = {
3043
isa = PBXGroup;
3144
children = (
45+
60F33E192CE22D8B00A5C26D /* Extension */,
3246
7B5951432CDB5E6E00B89C85 /* BaseFeature */,
3347
7B5951392CDB5E5500B89C85 /* Products */,
3448
);
@@ -131,6 +145,8 @@
131145
isa = PBXSourcesBuildPhase;
132146
buildActionMask = 2147483647;
133147
files = (
148+
60F33E1B2CE22DAE00A5C26D /* UIControl+Publisher.swift in Sources */,
149+
60F33E1D2CE22DB900A5C26D /* UIButton+Publisher.swift in Sources */,
134150
7B5951552CDB601900B89C85 /* BaseViewController.swift in Sources */,
135151
);
136152
runOnlyForDeploymentPostprocessing = 0;
@@ -153,6 +169,7 @@
153169
GENERATE_INFOPLIST_FILE = YES;
154170
INFOPLIST_KEY_NSHumanReadableCopyright = "";
155171
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
172+
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
156173
LD_RUNPATH_SEARCH_PATHS = (
157174
"$(inherited)",
158175
"@executable_path/Frameworks",
@@ -164,10 +181,14 @@
164181
PRODUCT_BUNDLE_IDENTIFIER = kr.codesquad.boostcamp9.BaseFeature;
165182
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
166183
SKIP_INSTALL = YES;
184+
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
185+
SUPPORTS_MACCATALYST = NO;
186+
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
187+
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
167188
SWIFT_EMIT_LOC_STRINGS = YES;
168189
SWIFT_INSTALL_OBJC_HEADER = NO;
169190
SWIFT_VERSION = 5.0;
170-
TARGETED_DEVICE_FAMILY = "1,2";
191+
TARGETED_DEVICE_FAMILY = 1;
171192
};
172193
name = Debug;
173194
};
@@ -186,6 +207,7 @@
186207
GENERATE_INFOPLIST_FILE = YES;
187208
INFOPLIST_KEY_NSHumanReadableCopyright = "";
188209
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
210+
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
189211
LD_RUNPATH_SEARCH_PATHS = (
190212
"$(inherited)",
191213
"@executable_path/Frameworks",
@@ -197,10 +219,14 @@
197219
PRODUCT_BUNDLE_IDENTIFIER = kr.codesquad.boostcamp9.BaseFeature;
198220
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
199221
SKIP_INSTALL = YES;
222+
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
223+
SUPPORTS_MACCATALYST = NO;
224+
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
225+
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
200226
SWIFT_EMIT_LOC_STRINGS = YES;
201227
SWIFT_INSTALL_OBJC_HEADER = NO;
202228
SWIFT_VERSION = 5.0;
203-
TARGETED_DEVICE_FAMILY = "1,2";
229+
TARGETED_DEVICE_FAMILY = 1;
204230
};
205231
name = Release;
206232
};
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import Combine
2+
import UIKit
3+
4+
public extension UIButton {
5+
var tapPublisher: AnyPublisher<Void, Never> {
6+
controlPublisher(for: .touchUpInside)
7+
.map { _ in }
8+
.eraseToAnyPublisher()
9+
}
10+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import Combine
2+
import UIKit
3+
4+
public extension UIControl {
5+
/// Control Publisher
6+
func controlPublisher(for event: UIControl.Event) -> UIControl.EventPublisher {
7+
return UIControl.EventPublisher(control: self, event: event)
8+
}
9+
10+
/// Event Publisher
11+
struct EventPublisher: Publisher {
12+
public typealias Output = UIControl
13+
public typealias Failure = Never
14+
15+
let control: UIControl
16+
let event: UIControl.Event
17+
18+
public func receive<T>(subscriber: T) where T: Subscriber, Never == T.Failure, UIControl == T.Input {
19+
let subscription = EventSubscription(control: control, subscrier: subscriber, event: event)
20+
subscriber.receive(subscription: subscription)
21+
}
22+
}
23+
24+
/// Event Subscription
25+
private class EventSubscription<EventSubscriber: Subscriber>: Subscription where EventSubscriber.Input == UIControl, EventSubscriber.Failure == Never {
26+
let control: UIControl
27+
let event: UIControl.Event
28+
var subscriber: EventSubscriber?
29+
30+
init(control: UIControl, subscrier: EventSubscriber, event: UIControl.Event) {
31+
self.control = control
32+
self.subscriber = subscrier
33+
self.event = event
34+
35+
control.addTarget(self, action: #selector(eventDidOccur), for: event)
36+
}
37+
38+
func request(_ demand: Subscribers.Demand) {}
39+
40+
func cancel() {
41+
subscriber = nil
42+
control.removeTarget(self, action: #selector(eventDidOccur), for: event)
43+
}
44+
45+
@objc func eventDidOccur() {
46+
_ = subscriber?.receive(control)
47+
}
48+
}
49+
}

PhotoGether/DesignSystem/DesignSystem.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
60F33DFC2CE1A91300A5C26D /* PTGGrayButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60F33DFB2CE1A91300A5C26D /* PTGGrayButton.swift */; };
1313
60F33E072CE1E87000A5C26D /* UIImage+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60F33E062CE1E87000A5C26D /* UIImage+.swift */; };
1414
60F33E132CE1ED6400A5C26D /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = 60F33E122CE1ED6400A5C26D /* SnapKit */; };
15+
60F33E152CE21EFA00A5C26D /* PTGMicButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60F33E142CE21EFA00A5C26D /* PTGMicButton.swift */; };
1516
7B59512B2CDB5BB500B89C85 /* DesignSystem.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 7B59512A2CDB5BB500B89C85 /* DesignSystem.xcassets */; };
1617
/* End PBXBuildFile section */
1718

@@ -20,6 +21,7 @@
2021
05F6467C2CE2062A00694897 /* PTGPrimaryButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PTGPrimaryButton.swift; sourceTree = "<group>"; };
2122
60F33DFB2CE1A91300A5C26D /* PTGGrayButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PTGGrayButton.swift; sourceTree = "<group>"; };
2223
60F33E062CE1E87000A5C26D /* UIImage+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+.swift"; sourceTree = "<group>"; };
24+
60F33E142CE21EFA00A5C26D /* PTGMicButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PTGMicButton.swift; sourceTree = "<group>"; };
2325
7B59511B2CDB5A7000B89C85 /* DesignSystem.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = DesignSystem.framework; sourceTree = BUILT_PRODUCTS_DIR; };
2426
7B59512A2CDB5BB500B89C85 /* DesignSystem.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = DesignSystem.xcassets; sourceTree = "<group>"; };
2527
/* End PBXFileReference section */
@@ -55,6 +57,7 @@
5557
7B5951262CDB5A7700B89C85 /* DesignSystem */ = {
5658
isa = PBXGroup;
5759
children = (
60+
60F33E142CE21EFA00A5C26D /* PTGMicButton.swift */,
5861
60F33DFB2CE1A91300A5C26D /* PTGGrayButton.swift */,
5962
05A7EFCB2CE1FF0C00DAEAAA /* PTGCircleButton.swift */,
6063
05F6467C2CE2062A00694897 /* PTGPrimaryButton.swift */,
@@ -163,6 +166,7 @@
163166
05A7EFCC2CE1FF0C00DAEAAA /* PTGCircleButton.swift in Sources */,
164167
60F33E072CE1E87000A5C26D /* UIImage+.swift in Sources */,
165168
60F33DFC2CE1A91300A5C26D /* PTGGrayButton.swift in Sources */,
169+
60F33E152CE21EFA00A5C26D /* PTGMicButton.swift in Sources */,
166170
);
167171
runOnlyForDeploymentPostprocessing = 0;
168172
};
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import UIKit
2+
import SnapKit
3+
4+
public final class PTGMicButton: UIButton {
5+
private let buttonImage = UIImageView()
6+
private var micState: PTGMicState
7+
8+
public init(micState: PTGMicState) {
9+
self.micState = micState
10+
super.init(frame: .zero)
11+
12+
addViews()
13+
setupConstraints()
14+
configureUI()
15+
}
16+
17+
required init?(coder: NSCoder) {
18+
fatalError("init(coder:) has not been implemented")
19+
}
20+
21+
private func addViews() {
22+
addSubview(buttonImage)
23+
}
24+
25+
private func setupConstraints() {
26+
buttonImage.snp.makeConstraints {
27+
$0.width.height.equalTo(24)
28+
$0.center.equalToSuperview()
29+
}
30+
}
31+
32+
private func configureUI() {
33+
backgroundColor = .white.withAlphaComponent(0.2)
34+
35+
buttonImage.contentMode = .scaleAspectFit
36+
buttonImage.image = UIImage(systemName: micState.image)
37+
buttonImage.tintColor = micState.color
38+
}
39+
40+
public override func layoutSubviews() {
41+
super.layoutSubviews()
42+
layer.cornerRadius = bounds.width / 2
43+
}
44+
45+
public func toggleMicState() {
46+
switch micState {
47+
case .on:
48+
micState = .off
49+
case .off:
50+
micState = .on
51+
}
52+
53+
buttonImage.image = UIImage(systemName: micState.image)
54+
buttonImage.tintColor = micState.color
55+
}
56+
}
57+
58+
public extension PTGMicButton {
59+
enum PTGMicState {
60+
case on
61+
case off
62+
63+
var image: String {
64+
switch self {
65+
case .on:
66+
return "microphone"
67+
case .off:
68+
return "microphone.slash"
69+
}
70+
}
71+
72+
var color: UIColor {
73+
switch self {
74+
case .on:
75+
return .white
76+
case .off:
77+
return .red
78+
}
79+
}
80+
}
81+
}

0 commit comments

Comments
 (0)