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
30 changes: 28 additions & 2 deletions PhotoGether/BaseFeature/BaseFeature.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,14 @@
objects = {

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

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

/* Begin PBXGroup section */
60F33E192CE22D8B00A5C26D /* Extension */ = {
isa = PBXGroup;
children = (
60F33E1A2CE22DAE00A5C26D /* UIControl+Publisher.swift */,
60F33E1C2CE22DB900A5C26D /* UIButton+Publisher.swift */,
);
path = Extension;
sourceTree = "<group>";
};
7B59512E2CDB5E5500B89C85 = {
isa = PBXGroup;
children = (
60F33E192CE22D8B00A5C26D /* Extension */,
7B5951432CDB5E6E00B89C85 /* BaseFeature */,
7B5951392CDB5E5500B89C85 /* Products */,
);
Expand Down Expand Up @@ -131,6 +145,8 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
60F33E1B2CE22DAE00A5C26D /* UIControl+Publisher.swift in Sources */,
60F33E1D2CE22DB900A5C26D /* UIButton+Publisher.swift in Sources */,
7B5951552CDB601900B89C85 /* BaseViewController.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand All @@ -153,6 +169,7 @@
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = "";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
Expand All @@ -164,10 +181,14 @@
PRODUCT_BUNDLE_IDENTIFIER = kr.codesquad.boostcamp9.BaseFeature;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
SKIP_INSTALL = YES;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_INSTALL_OBJC_HEADER = NO;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
TARGETED_DEVICE_FAMILY = 1;
};
name = Debug;
};
Expand All @@ -186,6 +207,7 @@
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = "";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
Expand All @@ -197,10 +219,14 @@
PRODUCT_BUNDLE_IDENTIFIER = kr.codesquad.boostcamp9.BaseFeature;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
SKIP_INSTALL = YES;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_INSTALL_OBJC_HEADER = NO;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
TARGETED_DEVICE_FAMILY = 1;
};
name = Release;
};
Expand Down
10 changes: 10 additions & 0 deletions PhotoGether/BaseFeature/Extension/UIButton+Publisher.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import Combine
import UIKit

public extension UIButton {
var tapPublisher: AnyPublisher<Void, Never> {
controlPublisher(for: .touchUpInside)
.map { _ in }
.eraseToAnyPublisher()
}
}
49 changes: 49 additions & 0 deletions PhotoGether/BaseFeature/Extension/UIControl+Publisher.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import Combine
import UIKit

public extension UIControl {
/// Control Publisher
func controlPublisher(for event: UIControl.Event) -> UIControl.EventPublisher {
return UIControl.EventPublisher(control: self, event: event)
}

/// Event Publisher
struct EventPublisher: Publisher {
public typealias Output = UIControl
public typealias Failure = Never

let control: UIControl
let event: UIControl.Event

public func receive<T>(subscriber: T) where T: Subscriber, Never == T.Failure, UIControl == T.Input {
let subscription = EventSubscription(control: control, subscrier: subscriber, event: event)
subscriber.receive(subscription: subscription)
}
}

/// Event Subscription
private class EventSubscription<EventSubscriber: Subscriber>: Subscription where EventSubscriber.Input == UIControl, EventSubscriber.Failure == Never {
let control: UIControl
let event: UIControl.Event
var subscriber: EventSubscriber?

init(control: UIControl, subscrier: EventSubscriber, event: UIControl.Event) {
self.control = control
self.subscriber = subscrier
self.event = event

control.addTarget(self, action: #selector(eventDidOccur), for: event)
}

func request(_ demand: Subscribers.Demand) {}

func cancel() {
subscriber = nil
control.removeTarget(self, action: #selector(eventDidOccur), for: event)
}

@objc func eventDidOccur() {
_ = subscriber?.receive(control)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
60F33DFC2CE1A91300A5C26D /* PTGGrayButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60F33DFB2CE1A91300A5C26D /* PTGGrayButton.swift */; };
60F33E072CE1E87000A5C26D /* UIImage+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60F33E062CE1E87000A5C26D /* UIImage+.swift */; };
60F33E132CE1ED6400A5C26D /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = 60F33E122CE1ED6400A5C26D /* SnapKit */; };
60F33E152CE21EFA00A5C26D /* PTGMicButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60F33E142CE21EFA00A5C26D /* PTGMicButton.swift */; };
7B59512B2CDB5BB500B89C85 /* DesignSystem.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 7B59512A2CDB5BB500B89C85 /* DesignSystem.xcassets */; };
/* End PBXBuildFile section */

Expand All @@ -20,6 +21,7 @@
05F6467C2CE2062A00694897 /* PTGPrimaryButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PTGPrimaryButton.swift; sourceTree = "<group>"; };
60F33DFB2CE1A91300A5C26D /* PTGGrayButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PTGGrayButton.swift; sourceTree = "<group>"; };
60F33E062CE1E87000A5C26D /* UIImage+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+.swift"; sourceTree = "<group>"; };
60F33E142CE21EFA00A5C26D /* PTGMicButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PTGMicButton.swift; sourceTree = "<group>"; };
7B59511B2CDB5A7000B89C85 /* DesignSystem.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = DesignSystem.framework; sourceTree = BUILT_PRODUCTS_DIR; };
7B59512A2CDB5BB500B89C85 /* DesignSystem.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = DesignSystem.xcassets; sourceTree = "<group>"; };
/* End PBXFileReference section */
Expand Down Expand Up @@ -55,6 +57,7 @@
7B5951262CDB5A7700B89C85 /* DesignSystem */ = {
isa = PBXGroup;
children = (
60F33E142CE21EFA00A5C26D /* PTGMicButton.swift */,
60F33DFB2CE1A91300A5C26D /* PTGGrayButton.swift */,
05A7EFCB2CE1FF0C00DAEAAA /* PTGCircleButton.swift */,
05F6467C2CE2062A00694897 /* PTGPrimaryButton.swift */,
Expand Down Expand Up @@ -163,6 +166,7 @@
05A7EFCC2CE1FF0C00DAEAAA /* PTGCircleButton.swift in Sources */,
60F33E072CE1E87000A5C26D /* UIImage+.swift in Sources */,
60F33DFC2CE1A91300A5C26D /* PTGGrayButton.swift in Sources */,
60F33E152CE21EFA00A5C26D /* PTGMicButton.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
81 changes: 81 additions & 0 deletions PhotoGether/DesignSystem/DesignSystem/PTGMicButton.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import UIKit
import SnapKit

public final class PTGMicButton: UIButton {
private let buttonImage = UIImageView()
private var micState: PTGMicState

public init(micState: PTGMicState) {
self.micState = micState
super.init(frame: .zero)

addViews()
setupConstraints()
configureUI()
}

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

private func addViews() {
addSubview(buttonImage)
}

private func setupConstraints() {
buttonImage.snp.makeConstraints {
$0.width.height.equalTo(24)
$0.center.equalToSuperview()
}
}

private func configureUI() {
backgroundColor = .white.withAlphaComponent(0.2)

buttonImage.contentMode = .scaleAspectFit
buttonImage.image = UIImage(systemName: micState.image)
buttonImage.tintColor = micState.color
}

public override func layoutSubviews() {
super.layoutSubviews()
layer.cornerRadius = bounds.width / 2
}

public func toggleMicState() {
switch micState {
case .on:
micState = .off
case .off:
micState = .on
}

buttonImage.image = UIImage(systemName: micState.image)
buttonImage.tintColor = micState.color
}
}

public extension PTGMicButton {
enum PTGMicState {
case on
case off

var image: String {
switch self {
case .on:
return "microphone"
case .off:
return "microphone.slash"
}
}

var color: UIColor {
switch self {
case .on:
return .white
case .off:
return .red
}
}
}
}