From 27e8807a80cb3d1b0a50e4f670b3eb546c6f0330 Mon Sep 17 00:00:00 2001 From: Surviveyeomi Date: Tue, 17 Oct 2023 21:06:01 +0900 Subject: [PATCH 1/2] =?UTF-8?q?[#14]=20feat=20:=20=EB=84=A4=EB=B9=84?= =?UTF-8?q?=EA=B2=8C=EC=9D=B4=EC=85=98=20=EB=B2=84=ED=8A=BC=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Helfy.xcodeproj/project.pbxproj | 28 +++- Helfy/AppDelegate.swift | 2 + Helfy/Controller/NavigationController.swift | 85 +++++++++++++ Helfy/Controller/ViewController.swift | 16 --- Helfy/Controller/WriteViewController.swift | 134 ++++++++++++++++++++ Helfy/Model/WriteModel.swift | 10 ++ Helfy/SceneDelegate.swift | 11 +- Helfy/View/NaviButtonView.swift | 9 ++ Helfy/View/WriteView.swift | 20 +++ 9 files changed, 289 insertions(+), 26 deletions(-) create mode 100644 Helfy/Controller/NavigationController.swift delete mode 100644 Helfy/Controller/ViewController.swift create mode 100644 Helfy/Controller/WriteViewController.swift create mode 100644 Helfy/Model/WriteModel.swift create mode 100644 Helfy/View/NaviButtonView.swift create mode 100644 Helfy/View/WriteView.swift diff --git a/Helfy.xcodeproj/project.pbxproj b/Helfy.xcodeproj/project.pbxproj index bdc7f06..4cd7405 100644 --- a/Helfy.xcodeproj/project.pbxproj +++ b/Helfy.xcodeproj/project.pbxproj @@ -7,7 +7,11 @@ objects = { /* Begin PBXBuildFile section */ - 26EA7E4A2AD153C800FFE7ED /* ReportView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EA7E492AD153C800FFE7ED /* ReportView.swift */; }; + 26094BFB2AD69724000AAD3B /* NaviButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26094BFA2AD69724000AAD3B /* NaviButtonView.swift */; }; + 26094C022AD87577000AAD3B /* NavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26094C012AD87577000AAD3B /* NavigationController.swift */; }; + 26094C042AD87943000AAD3B /* WriteView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26094C032AD87943000AAD3B /* WriteView.swift */; }; + 26094C072AD87971000AAD3B /* WriteViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26094C062AD87971000AAD3B /* WriteViewController.swift */; }; + 26094C0A2ADC3E9E000AAD3B /* WriteModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26094C092ADC3E9E000AAD3B /* WriteModel.swift */; }; 26EA7E4C2AD1540300FFE7ED /* ReportViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EA7E4B2AD1540300FFE7ED /* ReportViewController.swift */; }; 2A2E02E32AD54D970026C495 /* LoginViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A2E02E22AD54D970026C495 /* LoginViewController.swift */; }; 2A2E02E62AD5510F0026C495 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 2A2E02E52AD5510F0026C495 /* GoogleService-Info.plist */; }; @@ -52,7 +56,11 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ - 26EA7E492AD153C800FFE7ED /* ReportView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportView.swift; sourceTree = ""; }; + 26094BFA2AD69724000AAD3B /* NaviButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NaviButtonView.swift; sourceTree = ""; }; + 26094C012AD87577000AAD3B /* NavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationController.swift; sourceTree = ""; }; + 26094C032AD87943000AAD3B /* WriteView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WriteView.swift; sourceTree = ""; }; + 26094C062AD87971000AAD3B /* WriteViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WriteViewController.swift; sourceTree = ""; }; + 26094C092ADC3E9E000AAD3B /* WriteModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WriteModel.swift; sourceTree = ""; }; 26EA7E4B2AD1540300FFE7ED /* ReportViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportViewController.swift; sourceTree = ""; }; 2A2E02E22AD54D970026C495 /* LoginViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginViewController.swift; sourceTree = ""; }; 2A2E02E52AD5510F0026C495 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; @@ -111,7 +119,6 @@ 2A99A2632AD681820090155C /* Recovered References */ = { isa = PBXGroup; children = ( - 26EA7E492AD153C800FFE7ED /* ReportView.swift */, ); name = "Recovered References"; sourceTree = ""; @@ -122,6 +129,8 @@ C1735B3B2AD03F61002FFB77 /* QuizViewController.swift */, 26EA7E4B2AD1540300FFE7ED /* ReportViewController.swift */, 2A2E02E22AD54D970026C495 /* LoginViewController.swift */, + 26094C012AD87577000AAD3B /* NavigationController.swift */, + 26094C062AD87971000AAD3B /* WriteViewController.swift */, ); path = Controller; sourceTree = ""; @@ -185,6 +194,7 @@ children = ( C1735B412AD04532002FFB77 /* RankingModel.swift */, 2A99A2662AD682050090155C /* QuizeModel.swift */, + 26094C092ADC3E9E000AAD3B /* WriteModel.swift */, ); path = Model; sourceTree = ""; @@ -196,6 +206,8 @@ C1735B3D2AD042E5002FFB77 /* RankingView.swift */, 2A99A2682AD6882F0090155C /* ReportView.swift */, 2A99A26A2AD688660090155C /* TextView.swift */, + 26094BFA2AD69724000AAD3B /* NaviButtonView.swift */, + 26094C032AD87943000AAD3B /* WriteView.swift */, ); path = View; sourceTree = ""; @@ -344,14 +356,18 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 26094BFB2AD69724000AAD3B /* NaviButtonView.swift in Sources */, C1735B422AD04532002FFB77 /* RankingModel.swift in Sources */, 2A99A2652AD681DB0090155C /* QuizView.swift in Sources */, + 26094C072AD87971000AAD3B /* WriteViewController.swift in Sources */, 2A99A2672AD682050090155C /* QuizeModel.swift in Sources */, C1735B3E2AD042E5002FFB77 /* RankingView.swift in Sources */, 2A2E02E32AD54D970026C495 /* LoginViewController.swift in Sources */, + 26094C0A2ADC3E9E000AAD3B /* WriteModel.swift in Sources */, + 26094C042AD87943000AAD3B /* WriteView.swift in Sources */, 2A99A2692AD6882F0090155C /* ReportView.swift in Sources */, 2AFC98AE2ACD4A2100AB349D /* AppDelegate.swift in Sources */, - 26EA7E4A2AD153C800FFE7ED /* ReportView.swift in Sources */, + 26094C022AD87577000AAD3B /* NavigationController.swift in Sources */, 26EA7E4C2AD1540300FFE7ED /* ReportViewController.swift in Sources */, 2A99A26B2AD688660090155C /* TextView.swift in Sources */, C1735B3C2AD03F61002FFB77 /* QuizViewController.swift in Sources */, @@ -531,7 +547,7 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = M8XKVU2476; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = Helfy/Info.plist; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; @@ -558,7 +574,7 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = M8XKVU2476; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = Helfy/Info.plist; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; diff --git a/Helfy/AppDelegate.swift b/Helfy/AppDelegate.swift index 3fda5de..730e952 100644 --- a/Helfy/AppDelegate.swift +++ b/Helfy/AppDelegate.swift @@ -51,7 +51,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate { // Called when the user discards a scene session. // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. // Use this method to release any resources that were specific to the discarded scenes, as they will not return. + } + } diff --git a/Helfy/Controller/NavigationController.swift b/Helfy/Controller/NavigationController.swift new file mode 100644 index 0000000..fd73981 --- /dev/null +++ b/Helfy/Controller/NavigationController.swift @@ -0,0 +1,85 @@ +// +// NavigationController.swift +// Helfy +// +// Created by YEOMI on 10/13/23. +// + +import UIKit + +class NavigationController: UIViewController { + + override func viewDidLoad() { + super.viewDidLoad() + + // View의 배경색 설정 + view.backgroundColor = .white + + // Navigation Bar 설정 + setupNavigationBar() + } + + private func setupNavigationBar() { + + // 첫 번째 버튼 생성 (WriteView로 이동) + let firstButton = createCustomBarButton(imageName: "highlighter", text: " 글쓰기", action: #selector(firstButtonTapped)) + + + // 두 번째 버튼 생성 (ReportView로 이동) + let secondButton = createCustomBarButton(imageName: "lightbulb.min.badge.exclamationmark", text: "제보하기", action: #selector(secondButtonTapped)) + + // 오른쪽 아이템으로 추가 + navigationItem.rightBarButtonItems = [secondButton ,firstButton] + + //네비게이션바 community + let titleLabel = UILabel() + titleLabel.text = "Community" + + titleLabel.font = UIFont.systemFont(ofSize: 30, weight:.semibold) // 폰트 크기와 굵기 조정 + + titleLabel.textAlignment = .left // 왼쪽 정렬 + + navigationItem.leftBarButtonItem=UIBarButtonItem(customView:titleLabel) + + } + + private func createCustomBarButton(imageName: String, text:String, action: Selector) -> UIBarButtonItem { + let button = UIButton(type:.system) + button.setImage(UIImage(systemName:imageName), for:.normal) + button.addTarget(self, action:action, for:.touchUpInside) + button.tintColor = .black + button.imageView?.contentMode = .scaleAspectFit + + button.widthAnchor.constraint(equalToConstant: 30).isActive = true + button.heightAnchor.constraint(equalToConstant: 30).isActive = true + + + + let label = UILabel() + label.text=text + label.font=UIFont(name:"Helvetica-Bold", size :8.5) + + + let stackView=UIStackView(arrangedSubviews:[button,label]) + stackView.spacing = 5 + stackView.axis = .vertical + + return UIBarButtonItem(customView:stackView) + } + + + @objc private func firstButtonTapped() { + let writeViewController = WriteViewController() + navigationController?.pushViewController(writeViewController, animated: true) + } + + @objc private func secondButtonTapped() { + let reportViewController = ReportViewController() + navigationController?.pushViewController(reportViewController, animated: true) + } + + } + + + + diff --git a/Helfy/Controller/ViewController.swift b/Helfy/Controller/ViewController.swift deleted file mode 100644 index 484b004..0000000 --- a/Helfy/Controller/ViewController.swift +++ /dev/null @@ -1,16 +0,0 @@ -// -// ViewController.swift -// Helfy -// -// Created by 윤성은 on 2023/10/06. -// - -import UIKit - -class ViewController: UIViewController { - - override func viewDidLoad() { - super.viewDidLoad() - } - -} diff --git a/Helfy/Controller/WriteViewController.swift b/Helfy/Controller/WriteViewController.swift new file mode 100644 index 0000000..3cc4976 --- /dev/null +++ b/Helfy/Controller/WriteViewController.swift @@ -0,0 +1,134 @@ +// +// WriteViewController.swift +// Helfy +// +// Created by YEOMI on 10/13/23. +// +import UIKit +// 게시물에 대한 정보를 저장하는 클래스 +class Post { + var title: String = "" // 게시물의 제목 + var image: UIImage? = nil // 게시물의 이미지를 저장. 이미지가 없을 수도 있으므로 옵셔널로 선언 + var hashtags: [String] = [] // 게시물의 해시태그들을 저장 +} + +// 게시물을 작성하는 뷰 컨트롤러 클래스 +class WriteViewController: UITableViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate, UITextFieldDelegate { + + let post = Post() // 게시물 객체 생성 + + // 제목을 입력받는 텍스트 필드 생성 + private let titleField: UITextField = { + let field = UITextField() + field.placeholder = "제목" + field.borderStyle = .roundedRect + return field + }() + + // 이미지를 보여주는 이미지 뷰 생성 + private let imageView: UIImageView = { + let imageView = UIImageView() + imageView.backgroundColor = .lightGray + return imageView + }() + + // 이미지를 선택하는 버튼 생성 + private let imagePickerButton: UIButton = { + let button = UIButton(type: .system) + button.setTitle("사진 선택", for: .normal) + button.translatesAutoresizingMaskIntoConstraints = false + return button + }() + + // 해시태그를 입력받는 텍스트 필드 생성 + private let hashtagField: UITextField = { + let field = UITextField() + field.placeholder = "#해시태그" + field.borderStyle = .roundedRect + return field + }() + + override func viewDidLoad() { + super.viewDidLoad() + + view.backgroundColor = .white + // 위에서 생성한 각 요소들을 테이블 뷰에 추가 + tableView.addSubview(titleField) + tableView.addSubview(imageView) + tableView.addSubview(imagePickerButton) + tableView.addSubview(hashtagField) + + setupUIConstraints() // UI 요소들의 위치와 크기를 설정하는 메서드를 호출 + + titleField.delegate = self + hashtagField.delegate = self + + // 이미지 선택 버튼이 클릭되었을 때의 동작을 설정 + imagePickerButton.addTarget(self, action: #selector(selectImageButtonTapped), for: .touchUpInside) + } + + // UI 요소들의 위치와 크기를 설정하는 메서드 + private func setupUIConstraints() { + titleField.translatesAutoresizingMaskIntoConstraints = false + imageView.translatesAutoresizingMaskIntoConstraints = false + hashtagField.translatesAutoresizingMaskIntoConstraints = false + + // 각 UI 요소들의 위치와 크기를 제약 조건을 통해 정의 + NSLayoutConstraint.activate([ + titleField.topAnchor.constraint(equalTo: tableView.safeAreaLayoutGuide.topAnchor, constant: 20), + titleField.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor), + titleField.trailingAnchor.constraint(equalTo: view.layoutMarginsGuide.trailingAnchor), + titleField.heightAnchor.constraint(equalToConstant: 50), + + imageView.topAnchor.constraint(equalTo: titleField.bottomAnchor, constant: 20), + imageView.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor), + imageView.trailingAnchor.constraint(equalTo: view.layoutMarginsGuide.trailingAnchor), + imageView.heightAnchor.constraint(equalTo: imageView.widthAnchor), // 이미지 높이와 너비 동일 + + imagePickerButton.topAnchor.constraint(equalTo: imageView.bottomAnchor, constant: 20), + imagePickerButton.centerXAnchor.constraint(equalTo: view.centerXAnchor), + + hashtagField.topAnchor.constraint(equalTo: imagePickerButton.bottomAnchor, constant: 10), + hashtagField.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor), + hashtagField.trailingAnchor.constraint(equalTo: view.layoutMarginsGuide.trailingAnchor), + hashtagField.heightAnchor.constraint(equalToConstant: 50) + ]) + } + + // 이미지 선택 버튼이 클릭되었을 때의 동작을 정의하는 메서드 + @objc func selectImageButtonTapped() { + let picker = UIImagePickerController() // 이미지를 선택할 수 있는 이미지 피커 + picker.sourceType = .photoLibrary // 이미지 피커의 소스 타입을 포토 라이브러리로 설정 + picker.delegate = self // 이미지 피커의 델리게이트를 self로 설정 + present(picker, animated: true) // 이미지 피커를 화면에 표시 + } + + // 텍스트 필드에 문자를 입력할 때마다 호출되는 메서드 + func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { + if textField == hashtagField, string == " " { + textField.text = (textField.text ?? "") + " #" // 해시태그 필드에 공백 문자가 입력되면 자동으로 '#' 문자를 추가 + return false + } + return true + } + + // 텍스트 필드의 편집이 끝났을 때 호출되는 메서드 + func textFieldDidEndEditing(_ textField: UITextField) { + if textField == hashtagField { + post.hashtags.removeAll() // 해시태그 배열 초기화 + if let text = textField.text { + var uniqueHashtags = Set() // 중복된 해시태그를 제거하기 위해 Set을 사용 + let hashtags = text.split(separator: " ") // 텍스트를 공백 문자를 기준으로 분리 + for hashtag in hashtags where !hashtag.isEmpty { + // 해시태그가 '#' 문자로 시작하면 Set에 추가합니다. + if hashtag.hasPrefix("#") { + uniqueHashtags.insert(String(hashtag)) + } + } + post.hashtags.append(contentsOf: uniqueHashtags) // 해시태그 배열에 추가 + print("Current Hashtags (final): \(post.hashtags)") // 해시태그 배열을 출력 + textField.text = uniqueHashtags.joined(separator: " ") // 중복이 제거된 해시태그들을 다시 텍스트 필드에 표시 + } + } + } +} diff --git a/Helfy/Model/WriteModel.swift b/Helfy/Model/WriteModel.swift new file mode 100644 index 0000000..b332a1b --- /dev/null +++ b/Helfy/Model/WriteModel.swift @@ -0,0 +1,10 @@ +// +// WriteModel.swift +// Helfy +// +// Created by YEOMI on 10/16/23. +// + +struct MyModel { + let text: String +} diff --git a/Helfy/SceneDelegate.swift b/Helfy/SceneDelegate.swift index f29448b..57887c5 100644 --- a/Helfy/SceneDelegate.swift +++ b/Helfy/SceneDelegate.swift @@ -13,10 +13,13 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { guard let windowScene = (scene as? UIWindowScene) else { return } - - window = UIWindow(windowScene: windowScene) - window?.rootViewController = LoginViewController() - window?.makeKeyAndVisible() + let window = UIWindow(windowScene: windowScene) + //window.rootViewController = LoginViewController() // 첫 화면으로 표시할 ViewController 지정 + window.rootViewController = LoginViewController() + self.window = window + window.makeKeyAndVisible() + + } func sceneDidDisconnect(_ scene: UIScene) { diff --git a/Helfy/View/NaviButtonView.swift b/Helfy/View/NaviButtonView.swift new file mode 100644 index 0000000..f370992 --- /dev/null +++ b/Helfy/View/NaviButtonView.swift @@ -0,0 +1,9 @@ +// +// NaviButtonView.swift +// Helfy +// +// Created by YEOMI on 10/11/23. +// + +import UIKit + diff --git a/Helfy/View/WriteView.swift b/Helfy/View/WriteView.swift new file mode 100644 index 0000000..9f2bb5a --- /dev/null +++ b/Helfy/View/WriteView.swift @@ -0,0 +1,20 @@ +// +// WriteView.swift +// Helfy +// +// Created by YEOMI on 10/13/23. +// + +import UIKit + +class WriteView: UIView { + override init(frame: CGRect) { + super.init(frame: frame) + + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} + From e87a3651198e840e564106388c9da2265ed6e9da Mon Sep 17 00:00:00 2001 From: Surviveyeomi Date: Sat, 11 Nov 2023 15:16:51 +0900 Subject: [PATCH 2/2] =?UTF-8?q?[#26]=20feat=20:=20=EA=B8=80=EC=93=B0?= =?UTF-8?q?=EA=B8=B0=20=EB=B7=B0=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Helfy/Controller/WriteViewController.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Helfy/Controller/WriteViewController.swift b/Helfy/Controller/WriteViewController.swift index 3cc4976..3bdbdfe 100644 --- a/Helfy/Controller/WriteViewController.swift +++ b/Helfy/Controller/WriteViewController.swift @@ -100,7 +100,7 @@ class WriteViewController: UITableViewController, UIImagePickerControllerDelegat let picker = UIImagePickerController() // 이미지를 선택할 수 있는 이미지 피커 picker.sourceType = .photoLibrary // 이미지 피커의 소스 타입을 포토 라이브러리로 설정 picker.delegate = self // 이미지 피커의 델리게이트를 self로 설정 - present(picker, animated: true) // 이미지 피커를 화면에 표시 + present(picker, animated: true) // 이미지 피커를 화면에 표시 } // 텍스트 필드에 문자를 입력할 때마다 호출되는 메서드