Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
5ef719b
[Del, Add] 에셋 삭제 및 추가 (#164)
yurim830 Jun 5, 2025
ff86be2
[Fix] glassBorder가 userInteracton을 가로채는 문제 해결 (#164)
yurim830 Jun 5, 2025
85fb3ec
[Feat] ACTextField에 글래스모피즘 이니셜라이저 구현 (#164)
yurim830 Jun 5, 2025
738b0b5
[Chore] 닉네임, 생년월일 텍스트필드 스타일 수정 (#164)
yurim830 Jun 5, 2025
79391ff
[Chore] SpotSearch textField 생성자 수정 (#164)
yurim830 Jun 5, 2025
4a96e9f
[Chore]유효성 메시지 타입 2.0 수정 (#164)
yurim830 Jun 5, 2025
eceb085
[Chore] 유효성메시지 컴포넌트 2.0 수정 (#164)
yurim830 Jun 5, 2025
ddc1b51
[Refactor] 닉네임 검증 로직 수정 및 개선 (#164)
yurim830 Jun 5, 2025
27a1aa9
[Refactor] 메소드 분리 (#164)
yurim830 Jun 5, 2025
b315da8
[Fix] 닉네임 카운터 2.0 UI 적용, ScrollView height 오류 수정 (#164)
yurim830 Jun 5, 2025
be20eda
[Chore] 텍스트필드 보더 색 변경 코드 삭제 (#164)
yurim830 Jun 5, 2025
e00be52
[Chore] 닉네임, 생년월일 textField place holder 수정 (#164)
yurim830 Jun 5, 2025
f95d65b
[Fix] ACTextField 폰트 설정 오류 수정 (#164)
yurim830 Jun 5, 2025
a964bee
[Fix] 첫 진입 시 유효성 메시지 보이지 않게 처리(#164)
yurim830 Jun 5, 2025
e607b9e
[Fix] 유효성 메시지 표시되는 진짜 원인 찾아서 수정 (#164)
yurim830 Jun 5, 2025
46b610f
[Chore] 안 쓰는 메소드 삭제, 유효성메시지 인셋 수정 (#164)
yurim830 Jun 5, 2025
8e528e8
[Chore] 코드 정리 (#164)
yurim830 Jun 5, 2025
a06ba32
[Chore] 코드 정리 (#164)
yurim830 Jun 5, 2025
82124f0
[Fix] 프로필 편집 뷰 레이아웃 경고 해결 (#164)
yurim830 Jun 5, 2025
8162301
[Chore] 프로필수정API 변경사항 반영 (프로필이미지 required -> optional) (#164)
yurim830 Jun 6, 2025
58aba16
[Fix] GlassBorder static 선언으로 인한 문제 해결 (#164)
yurim830 Jun 6, 2025
3df2481
[Chore] 변수명 관련 코드리뷰 반영 (#164)
yurim830 Jun 6, 2025
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
58 changes: 42 additions & 16 deletions ACON-iOS/ACON-iOS/Global/Extensions/UIView+.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,38 +43,64 @@ extension UIView {
$0.contentMode = .scaleAspectFit
}
}


//MARK: - add GlassBorder (주의 - bound가 0이면 적용 안 됨)

static var glassBorderView = GlassmorphismView(.buttonGlassDefault)


}


//MARK: - GlassBorder 설정

extension UIView {

private struct AssociatedKeys {
static var glassBorderView: UInt8 = 0
}

private var glassBorderView: GlassmorphismView? {
get {
return objc_getAssociatedObject(self, &AssociatedKeys.glassBorderView) as? GlassmorphismView
}
set {
objc_setAssociatedObject(self, &AssociatedKeys.glassBorderView, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}

/// 주의: bounds가 0이면 적용 안 됨
func addGlassBorder(_ attributes: GlassBorderAttributes) {
self.layer.borderWidth = 0

glassBorderView?.removeFromSuperview()

let outerPath = UIBezierPath(roundedRect: bounds, cornerRadius: attributes.cornerRadius)
let innerRect = bounds.insetBy(dx: attributes.width, dy: attributes.width)
let innerPath = UIBezierPath(roundedRect: innerRect, cornerRadius: max(0, attributes.cornerRadius - attributes.width/2))
outerPath.append(innerPath.reversing())

UIView.glassBorderView = GlassmorphismView(attributes.glassmorphismType)

self.addSubview(UIView.glassBorderView)
UIView.glassBorderView.snp.makeConstraints {

let newGlassBorderView = GlassmorphismView(attributes.glassmorphismType)
self.glassBorderView = newGlassBorderView

self.addSubview(newGlassBorderView)

newGlassBorderView.snp.makeConstraints {
$0.edges.equalToSuperview()
}

let maskLayer = CAShapeLayer()
maskLayer.path = outerPath.cgPath
maskLayer.fillRule = .evenOdd

let maskView = UIView(frame: bounds)
maskView.layer.addSublayer(maskLayer)
UIView.glassBorderView.mask = maskView
newGlassBorderView.mask = maskView

newGlassBorderView.isUserInteractionEnabled = false
}


func refreshGlassBorder() {
glassBorderView?.refreshBlurEffect()
}

func removeGlassBorder() {
UIView.glassBorderView.removeFromSuperview()
glassBorderView?.removeFromSuperview()
}
Comment on lines +50 to 104
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@cirtuare UIView+ GlassBorder 관련 추가 수정 사항 생겼습니다!

기존 코드에서는 glassBorder가 static으로 선언되어서 모든 뷰가 하나의 glassBorderView 인스턴스를 공유했습니다.
따라서 한 뷰에서 2개 이상의 glassBorder가 있을 때 UI 업데이트가 정상적으로 안 되는 문제가 있었습니다.
이에, Associated Object를 활용하여 각 뷰마다 고유한 glassBorderView 인스턴스를 가질 수 있도록 했습니다.

Copy link
Contributor

Choose a reason for hiding this comment

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

최고 !!!


}
4 changes: 2 additions & 2 deletions ACON-iOS/ACON-iOS/Global/Literals/StringLiterals.swift
Original file line number Diff line number Diff line change
Expand Up @@ -351,9 +351,9 @@ enum StringLiterals {

static let verifiedAreaDescription = "등록한 지 1주일 이내 지역은 수정이 가능하며 이후 3개월 동안\n변경이 불가해요."

static let nicknamePlaceholder = "16자 이내 영문, 한글, 숫자, . , _ 만 사용가능"
static let nicknamePlaceholder = "닉네임을 입력해주세요"

static let birthDatePlaceholder = "ex) 2025.01.01"
static let birthDatePlaceholder = "2025.01.01"

static let addVerifiedArea = "지역 추가하기"

Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"images" : [
{
"filename" : "ic_local check mark_20.svg",
"filename" : "ic_exclamation_mark.svg",
"idiom" : "universal"
}
],
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"images" : [
{
"filename" : "ic_error_20.svg",
"filename" : "ic_success.svg",
"idiom" : "universal"
}
],
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
92 changes: 76 additions & 16 deletions ACON-iOS/ACON-iOS/Global/UIComponents/ACTextField.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import Lottie

final class ACTextField: UIView {

// MARK: - Internal Property
// MARK: - Public Interface

var observableText: ObservablePattern<String> = ObservablePattern(nil)

Expand All @@ -26,6 +26,11 @@ final class ACTextField: UIView {
}


// MARK: - Internal State

private var isInitialLoad: Bool = true


// MARK: - UI Property

private let horizontalInset: CGFloat = 12
Expand All @@ -36,11 +41,11 @@ final class ACTextField: UIView {

private var icon: UIImage?

private var bgColor: UIColor
private var bgColor: UIColor?

private var borderColor: UIColor
private var borderColor: UIColor?

private var borderWidth: CGFloat
private var borderWidth: CGFloat?

private var cornerRadius: CGFloat

Expand All @@ -55,10 +60,13 @@ final class ACTextField: UIView {
private let animationView = LottieAnimationView(name: "loadingWhite")

private var glassmorphismView: GlassmorphismView?


private var glassBorderAttribute: GlassBorderAttributes?


// MARK: - Initializer

/// 일반 ACTextField
init(
icon: UIImage? = nil,
backgroundColor: UIColor = .gray800,
Expand All @@ -84,10 +92,62 @@ final class ACTextField: UIView {
if doneButton { addDoneButtonToKeyboard() }
}

/// Glassmorphism이 적용된 ACTextField
init(
icon: UIImage? = nil,
borderWidth: CGFloat = 1,
cornerRadius: CGFloat = 4,
fontStyle: ACFontType = .t4R,
doneButton: Bool = true,
backgroundGlassType: GlassmorphismType? = nil,
borderGlassType: GlassmorphismType? = nil
) {
self.icon = icon
self.cornerRadius = cornerRadius
self.fontStyle = fontStyle

super.init(frame: .zero)

setHierarchy()
setLayout()
setStyle()
addTarget()
if doneButton { addDoneButtonToKeyboard() }

if let backgroundGlassType = backgroundGlassType {
setGlassmorphism(backgroundGlassType)
}

if let borderGlassType = borderGlassType {
self.glassBorderAttribute = GlassBorderAttributes(
width: borderWidth,
cornerRadius: cornerRadius,
glassmorphismType: borderGlassType
)
}
}

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

override func layoutSubviews() {
super.layoutSubviews()

if let glassBorderAttribute = glassBorderAttribute {
if isInitialLoad {
self.addGlassBorder(glassBorderAttribute)
isInitialLoad = false
} else {
self.refreshGlassBorder()
}
}

if glassmorphismView != nil {
glassmorphismView?.refreshBlurEffect()
}
}


// MARK: - Private Methods

Expand All @@ -106,7 +166,7 @@ final class ACTextField: UIView {
$0.size.equalTo(iconSize)
}
}

textField.snp.makeConstraints {
let leadingAnchor = icon == nil ? self.snp.leading : iconImageView.snp.trailing
let leadingOffset = icon == nil ? horizontalInset : horizontalSpacing
Expand All @@ -115,13 +175,13 @@ final class ACTextField: UIView {
$0.trailing.equalTo(clearButton.snp.leading).offset(-horizontalSpacing)
$0.centerY.equalToSuperview()
}

clearButton.snp.makeConstraints {
$0.trailing.equalToSuperview().offset(-horizontalInset)
$0.centerY.equalToSuperview()
$0.size.equalTo(iconSize)
}

animationView.snp.makeConstraints {
$0.trailing.equalToSuperview().offset(-horizontalInset)
$0.centerY.equalToSuperview()
Expand All @@ -133,28 +193,28 @@ final class ACTextField: UIView {
self.do {
$0.backgroundColor = bgColor
$0.clipsToBounds = true
$0.layer.borderColor = borderColor.cgColor
$0.layer.borderWidth = borderWidth
$0.layer.borderColor = borderColor?.cgColor
$0.layer.borderWidth = borderWidth ?? 0.0
$0.layer.cornerRadius = cornerRadius
}

textField.do {
$0.autocorrectionType = .no
$0.defaultTextAttributes = [
.font: ACFontType.t4SB.fontStyle.font,
.font: fontStyle.fontStyle.font,
.kern: fontStyle.kerning(isKorean: false),
.foregroundColor: UIColor.acWhite
]
}

animationView.do {
$0.isHidden = true
}

clearButton.do {
$0.setImage(.icClear, for: .normal)
}

if icon != nil {
iconImageView.do {
$0.image = icon
Expand All @@ -169,7 +229,7 @@ final class ACTextField: UIView {
action: #selector(tappedClearButton),
for: .touchUpInside
)

textField.addTarget(
self,
action: #selector(updateObservableText),
Expand Down Expand Up @@ -271,7 +331,7 @@ extension ACTextField {
}
})
}

func setGlassmorphism(_ glassmorphismType: GlassmorphismType) {
self.backgroundColor = .clear

Expand Down
21 changes: 16 additions & 5 deletions ACON-iOS/ACON-iOS/Network/Profile/DTO/PatchProfileRequest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,22 @@
import Foundation

struct PatchProfileRequest: Encodable {
let profileImage: String

let profileImage: String?

let nickname: String

let birthDate: String?


enum CodingKeys: CodingKey {
case profileImage, nickname, birthDate
}

func encode(to encoder: any Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encodeIfPresent(self.profileImage, forKey: .profileImage)
try container.encode(self.nickname, forKey: .nickname)
try container.encodeIfPresent(self.birthDate, forKey: .birthDate)
}

}
2 changes: 1 addition & 1 deletion ACON-iOS/ACON-iOS/Network/Profile/ProfileTargetType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ extension ProfileTargetType: TargetType {
case .getProfile:
return utilPath + "members/me"
case .getNicknameValidity:
return utilPath + "members/nickname/validate"
return utilPath + "nickname/validate"
case .patchProfile(_):
return utilPath + "members/me"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ struct UserInfoModel: Equatable {

struct UserInfoEditModel {

var profileImage: String
var profileImage: String?

var nickname: String

Expand Down
Loading