iOS App 개발(UI) - Sizuha/devdog GitHub Wiki
- Close Xcode.
- Open your project.pbxproj in a text editor
- Look for developmentRegion and set its value (es for spanish, fr for french,...)
- Look for knownRegions and add the new language to the list
- Open Xcode: Your project info should now show the language name with Development Language next to it.
- 프로젝트 설정의 General → Deployment Info 그룹 → Main interface 항목을 비워놓는다.
- AppDelegate.swift 에서, 직접 루트 뷰 컨트롤러를 지정.
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
self.window = UIWindow(frame: UIScreen.main.bounds)
let mainController = UINavigationController()
mainController.pushViewController(HomeViewController(), animated: false)
self.window?.rootViewController = mainController
self.window?.makeKeyAndVisible()
return true
}
// . . .
}
- SceneDelegate.swift 파일 삭제
- Info.plist에서 .SceneDelegate로 검색해서, 해당 부분(UIApplicationSceneManifest 항목)을 삭제(key와 dict 포함).
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneConfigurationName</key>
<string>Default Configuration</string>
<key>UISceneDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
<key>UISceneStoryboardFile</key>
<string>Main</string>
</dict>
</array>
</dict>
</dict>
-
프로젝트 설정의 General → Deployment Info 그룹 → Main interface 항목을 비워놓는다.
-
프로젝트에서 스토리보드 파일 삭제
-
Info.plist에서 스토리보드 파일연결 제거
-
SceneDelegate.swift 수정
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
window = UIWindow(frame: windowScene.coordinateSpace.bounds)
window?.windowScene = windowScene
window?.rootViewController = ViewController()
window?.makeKeyAndVisible()
}
.xib 파일을 만들 때, 대응되는 ViewController class를 정의한 다음, 해당 ViewController의 인스턴스를 생성하는 방식.
인터페이스 빌더로 화면을 만들고, 별도로 ViewController 클래스를 작성한 다음, 인터페이스 빌더의 컨트롤과 @IBOutlet 으로 연결되기만 하면 되는 듯. 이후는 일반적인 ViewController 사용법과 다르지 않음.
원래 목적대로, .xib 파일을 동적으로 로딩해서, 뷰에 추가하는 방식.
- 인터페이스 빌더로 뷰를 구성
- 뷰에 대응하는 클래스(UIView)를 작성
- 인터페이스 빌더에서, 최상위 뷰를 선택해고, Custom Class → Class 항목을 위에서 작성한 뷰로 연결
- 그리고 필요에 따라 @IBOutlet 등을 지정
그리고 코드에서 사용할 때는,
// xib 로딩: UINib(nibName: "xxx.xib 파일명에서 xxx부분", bundle: nil /* nil이면 메인 번들에서 가져온다 */)
let loadedView = UINib(nibName: "CustomView", bundle: nil).instantiate(withOwner: self, options: nil)[0] as? CustomView
iOS 8 이후로, Landscape일때 기본적으로 statusbar를 표시하지 않게 되어있다. 표시하기 위해서는 다음과 같이 한다.
//UIViewController
override func prefersStatusBarHidden() -> Bool { return false }
https://freakycoder.com/ios-notes-13-how-to-change-status-bar-color-1431c185e845
extension UIViewController {
public func changeStatusBar(color: UIColor) {
if #available(iOS 13, *) { // iOS 13 이상의 경우
// 이 코드는 검증이 필요.
let statusBarHeight: CGFloat = UIApplication.shared.statusBarFrame.size.height
let statusbarView = UIView()
statusbarView.backgroundColor = UIColor.red
view.addSubview(statusbarView)
statusbarView.translatesAutoresizingMaskIntoConstraints = false
statusbarView.heightAnchor.constraint(equalToConstant: statusBarHeight).isActive = true
statusbarView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 1.0).isActive = true
statusbarView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
statusbarView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
}
else {
let statusBar = UIApplication.shared.value(forKeyPath: "statusBarWindow.statusBar") as? UIView
statusBar?.backgroundColor = color
}
}
}
스와이프 제스쳐로 Back되는 기능 막기.
navigationController?.interactivePopGestureRecognizer?.isEnabled = false
광고 등을 배치할 때 유용.
float y = [UIScreen mainScreen].bounds.size.height - myView.frame.size.height;
[myView setFrame:CGRectMake(0, y, myView.frame.size.width, myView.frame.size.height)];
UIViewController* getRootViewCtrl()
{
id<UIApplicationDelegate> appDelegate = [[UIApplication sharedApplication] delegate];
return appDelegate.window.rootViewController;
}
UIView* getRootView()
{
return getRootViewCtrl().view;
}
뷰가 아직 화면에 표시되지 않았는데 dismiss() 메세지를 날린 경우가 이에 해당될 수 있다.
예를들어 override func viewWillAppear(_ animated: Bool) 이벤트 안에서 dismiss()가 호출되는 경우 등. 이 경우는 **override func viewDidAppear(_ animated: Bool)**에서 처리하자.
[myWebView stringByEvaluatingJavaScriptFromString:@"myJavascriptFunction()"];
class MainViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let webConfiguration = WKWebViewConfiguration()
webView = WKWebView(frame: .zero, configuration: webConfiguration)
webView.isOpaque = false
webView.backgroundColor = .white
webView.uiDelegate = self
webView.navigationDelegate = self
view.addSubview(webView)
let request = URLRequest(url: URL_MAIN_PAGE)
webView.load(request)
}
}
extension MainViewController: WKUIDelegate, WKNavigationDelegate {
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
if let url = navigationAction.request.url {
checkCallFromJs(url: url)
}
decisionHandler(.allow)
}
func checkCallFromJs(url: URL) {
print(url.absoluteString)
guard
url.scheme == "YOUR_API",
let funcName = url.host
else { return }
switch funcName {
case "logout": logout()
case "apiWithParam":
let param = url.path
apiWithParam(lang: param)
default: return
}
}
func logout() {
print(#function)
dismiss(animated: true, completion: nil)
}
func apiWithParam(lang: String) {
print(#function)
}
}
function logout() {
location.href = 'YOUR_API://logout';
}
function apiWithParam(p) {
location.href = 'YOUR_API://apiWithParam/' + p;
}
- (void) showImagePicker
{
UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
[imagePicker setDelegate:self];
[getRootViewCtrl() presentViewController:imagePicker animated:YES completion:nil];
}
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
[picker dismissViewControllerAnimated:YES completion:nil];
NSURL* imageUrl;
NSString* path;
if (@available(iOS 11.0, *)) {
imageUrl = [info objectForKey: UIImagePickerControllerImageURL];
NSString* path = imageUrl.path;
NSLog(@"File Path: %@", path);
//UIImage* img = [info objectForKey: UIImagePickerControllerOriginalImage];
}
// . . .
}
let editText = UITextField(frame: ...)
editText.isUserInteractionEnabled = false
https://blog.personal-factory.com/2021/12/29/ios15-transparent-navigationbar-and-tabbar-by-default/
iOS 15アプリをXcode 13以上でビルドすると、NavigationBarとTabBarの背景が透過される。
scrollEdgeAppearance 問題。
NavigationBarをiOS 14以前と同じ見た目にする
// class AppDelegate
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// ...
if #available(iOS 15.0, *) {
// disable UINavigation bar transparent
let navigationBarAppearance = UINavigationBarAppearance()
navigationBarAppearance.configureWithDefaultBackground()
UINavigationBar.appearance().standardAppearance = navigationBarAppearance
UINavigationBar.appearance().compactAppearance = navigationBarAppearance
UINavigationBar.appearance().scrollEdgeAppearance = navigationBarAppearance
}
// ...
}
TabBarをiOS 14以前と同じ見た目にする
// class AppDelegate
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// ...
if #available(iOS 15.0, *) {
// disable UITab bar transparent
let tabBarAppearance: UITabBarAppearance = UITabBarAppearance()
tabBarAppearance.configureWithDefaultBackground()
UITabBar.appearance().scrollEdgeAppearance = tabBarAppearance
UITabBar.appearance().standardAppearance = tabBarAppearance
}
// ...
}