[Swift] MVC 모드로 Massive View Controller가 만든 부채 애플리케이션(개인 개발)을 팩스로 전송했습니다.

178736 단어 iOSSwiftmvctech

MassivewController 소개


기계 ViewController가 쉽다는 거 알아요?
ViewController가 지나치게 많은 기능을 로드하는 기능입니다.

수정의 계기


오랜만에 앱을 수정하려고 기세등등하게 발표했는데, 켜면 ViewController 1개가 장문의 글로 쓰여져 있어 유지보수성이 낮습니다.
며칠 전 MVC 모드(Swift) 샘플 기사도 기고한 바 있는데, 유지보수성이 더 높은 코드로 이전한 경험, 실제로 느낀 이점 등을 기재할 수 있으면 더 좋을 것 같아 이 기사를 쓰고 있다.
https://zenn.dev/tanukidevelop/articles/33eedf516abb1e

응용 프로그램 소개


쉽게 말하면 오프라인에서도 역할을 발휘할 수 있는'메모장 활용'이다.
수첩을 만들 때마다 수첩에 '클래스 이름' 을 설정할 수 있습니다.(갑판? 그게 뭐야?)
프로그램이 시작될 때 분류 일람표를 빠뜨리지 말고 반복해서 가져오십시오. 첫 페이지에 50음 순서로 배열하십시오.
이번에는 우선 그 화면을 팩스로 전송했다.

상기 이미지에는 분류 이름'공백'과'great'의 수첩이 존재하기 때문에 이런 화면이 되었다.
또한 각 칸을 누르면 화면 이동을 할 수 있다.

수정 전 화면에 설치된 기능


  • 화면 왼쪽 상단의 UINavigationButton(ハンバーガーメニュー)를 누르면 프로그램 라이브러리 SideMenu를 열고 다른 설정을 열 수 있습니다.

  • 프로그램이 시작될 때 이 화면이 열립니다.응용 프로그램에서 저장된 데이터를 읽고 표시합니다. (이 데이터는 응용 프로그램이 끝난 후에도 저장되며 다음 시작 후에도 저장하고 참조할 수 있습니다.)
  • 수정 전 코드


    이것은 Userdefault의 학급 구성이다.
  • FolderListViewController.Swift
  • FolderListViewController.xib

  • 기계 ViewController이기 때문에 xib 파일에 대상을 직접 설정하여 マシマシViewController와 관련이 있습니다.
    또한 주처리는 모두 .swiftファイル에 기재되었다.
    지루한 코드라 기사를 먼저 읽는 것을 추천해도 괜찮다.

    FolderListViewController.Swift


    
    import UIKit
    import SafariServices
    import SwiftReorder
    import RxSwift
    import RxCocoa
    import SideMenu
    import UIKit
    import MessageUI
    
    
    class FolderListViewController: UIViewController,SettingsDelegate,UIScrollViewDelegate, MFMailComposeViewControllerDelegate {
        var dataManager = DataManager.shared
        let disposeBag = DisposeBag()
        var menuNavigationController: SideMenuNavigationController? = nil
        var folderList = [String]()
        @IBOutlet weak var tableView: UITableView!
        @IBOutlet weak var bannerView: UIView!
        
        override func viewDidLoad() {
            super.viewDidLoad()
            dataManager.loadColor()
            setThemeColor()
            dataManager.loadOriginalDataArray()
            loadSettings()
            loadNavigationController()
            loadTableView()
            UpgradeNotice.shared.fire()
            AdMobHelper.shared.setupBannerAd(adBaseView: self.bannerView, rootVC: self)
            AdMobHelper.shared.setupInterstitialAd(rootVC: self)
    
        }
        
        override func viewWillAppear(_ animated: Bool) {
            super.viewWillAppear(animated)
            self.navigationItem.title = "デッキカテゴリー 一覧"
            self.tableView.reloadData()
            self.getFolderList()
        }
        
        override func viewDidAppear(_ animated: Bool) {
            super.viewDidAppear(animated)
            APIRequest.getShareSheetData()
        }
        
        func getFolderList() {
            var emptyList = [String]()
            for (i,deckModel) in dataManager.deckList.enumerated() {
                emptyList.append(deckModel.folderName)
            }
            let orderedSet:NSOrderedSet = NSOrderedSet(array: emptyList)
            var folderUniqueList = orderedSet.array as! [String]
            folderUniqueList.sort()
            self.folderList = folderUniqueList
            print(folderUniqueList)
            print("並び替え終わり")
    
        }
        
        func loadSettings() {
            let menuViewController = SettingsViewController()
            menuViewController.delegate = self
            //サイドメニューのナビゲーションコントローラを生成
            menuNavigationController = SideMenuNavigationController(rootViewController: menuViewController)
            //設定を追加
            menuNavigationController!.settings = makeSettings()
    //        //左,右のメニューとして追加
            SideMenuManager.default.leftMenuNavigationController = menuNavigationController        
            SideMenuManager.default.addScreenEdgePanGesturesToPresent(toView: self.view, forMenu: .left)
        }
        
        //サイドメニューの設定
        private func makeSettings() -> SideMenuSettings {
            var settings = SideMenuSettings()
            //動作を指定
            settings.presentationStyle = .menuSlideIn
            //メニューの陰影度
            settings.presentationStyle.onTopShadowOpacity = 10.0
            //ステータスバーの透明度
            settings.statusBarEndAlpha = 0
            return settings
        }
            
        func loadNavigationController() {
            self.navigationController?.navigationBar.tintColor = .white
            self.navigationController?.navigationBar.titleTextAttributes = [
            // 文字の色
                .foregroundColor: UIColor.white
            ]
            var createBarButtonItem: UIBarButtonItem = UIBarButtonItem(image: UIImage(named: "menu_icon.png")!, style: .plain, target: self, action: #selector(settingBarButtonTapped(_:)))
    
            self.navigationItem.leftBarButtonItems = [createBarButtonItem]
        }
        
        func loadTableView() {
            tableView.separatorInset = .zero
            tableView.isEditing = false
            tableView.allowsSelectionDuringEditing = true
            
            tableView.delegate = self
            tableView.dataSource = self
            tableView.register(UINib(nibName: "ArchiveTableViewCell", bundle: nil), forCellReuseIdentifier: "ArchiveCell")
            tableView.register(UINib(nibName: "DeckListTableViewCell", bundle: nil), forCellReuseIdentifier: "CustomCell")
            self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
    
            
            tableView.tableFooterView = UIView(frame: CGRect.zero)
        }
        
        func setThemeColor() {
            self.navigationController?.navigationBar.barTintColor = dataManager.themeColor
            self.tableView.reloadData()
            loadSettings()
        }
        
        // 選択されたサイドバーのアイテムを取得
        func tappedSettingsItem(indexpath: IndexPath) {
            switch indexpath.row {
            case SettingsMenu.openCreateDeckRecipeSite.rawValue:
                let webPage = "https://www.pokemon-card.com/deck/deck.html"
                let safariVC = SFSafariViewController(url: NSURL(string: webPage)! as URL)
                safariVC.modalPresentationStyle = .fullScreen
                present(safariVC, animated: true, completion: nil)
            case SettingsMenu.backup.rawValue:
                if MFMailComposeViewController.canSendMail()==false {
                    showSimpleAlertView(title: nil, message: "メールアプリが開けませんでした。", ButtonText: "OK")
                    return
                }
                var mailViewController = MFMailComposeViewController()
                mailViewController.mailComposeDelegate = self
                var backupmsg = ""
                for deckModel in dataManager.deckList {
                    backupmsg += "デッキ名:" + deckModel.deckTitle + "\nデッキコード:" + deckModel.duckRecipeId + "\nメモ:" + deckModel.memo + "\n\n"
                }
                backupmsg += "\n\n【アーカイブされたデッキレシピ】\n"
                
                for deckModel in dataManager.archiveDeckList {
                    backupmsg += "デッキ名:" + deckModel.deckTitle + "\nデッキコード:" + deckModel.duckRecipeId + "\nメモ:" + deckModel.memo + "\n\n"
                }
                mailViewController.setMessageBody(backupmsg, isHTML: false)
                present(mailViewController, animated: true, completion: nil)
                
            case SettingsMenu.changeThemeColor.rawValue:
                let alertView = UIAlertController(
                    title: "テーマカラー:変更",
                    message: "\n\n\n\n\n\n\n\n\n",
                    preferredStyle: .alert)
    
                let pickerView = UIPickerView(frame:
                    CGRect(x: 0, y: 50, width: 270, height: 162))
                pickerView.dataSource = self
                pickerView.delegate = self
    
                // comment this line to use white color
                pickerView.backgroundColor = UIColor.lightGray.withAlphaComponent(0.2)
    
                alertView.view.addSubview(pickerView)
                
                let action = UIAlertAction(title: "OK", style: UIAlertAction.Style.default) { _ in
                    self.dataManager.saveColor()
                    AdMobHelper.shared.setupInterstitialAd(rootVC: self)
                }
    
                alertView.addAction(action)
                
                present(alertView, animated: true, completion: {
                    pickerView.frame.size.width = alertView.view.frame.size.width
                })
                
            case SettingsMenu.Request.rawValue:
                let webPage = "https://forms.gle/NecGPHqX9UZFALPdA"
                let safariVC = SFSafariViewController(url: NSURL(string: webPage)! as URL)
                safariVC.modalPresentationStyle = .fullScreen
                present(safariVC, animated: true, completion: nil)
            case SettingsMenu.Howtouse.rawValue:
                showSimpleAlertView(title: nil, message: "デッキレシピ表示行(以下、行)を長押しして移動させると並び替えができます。\n\n行を左にスワイプすると「アーカイブ」できます。\n\nアーカイブされたデッキ一覧画面で行を左スワイプすると「元に戻す」「(デッキレシピ)削除」できます。\n\nデッキレシピ詳細画面で右上の共有ボタンを押すとツイートすることができます。\n\n詳細画面下部の「デッキレシピをサーバーにシェアする」を押すとサーバーにアップロードされ、トップ画面の「他のプレイヤーによってシェアされたデッキレシピ」から確認可能です。\n\nデッキ編集画面に「カテゴリー」を追加しました。こちらを変更して確定(反映)することでアプリのトップ画面でのデッキカテゴリーで分類されるようになります。", ButtonText: "OK")
            case SettingsMenu.CheckUpdate.rawValue:
                let webPage = "https://apps.apple.com/jp/app/%E3%83%9D%E3%82%B1%E3%82%AB%E3%83%87%E3%83%83%E3%82%AD%E7%AE%A1%E7%90%86/id1570965372"
                let safariVC = SFSafariViewController(url: NSURL(string: webPage)! as URL)
                safariVC.modalPresentationStyle = .fullScreen
                present(safariVC, animated: true, completion: nil)
                
            default:
                break
            }
        }
        
    
        @objc func settingBarButtonTapped(_ sender: UIBarButtonItem) {
            present(menuNavigationController!, animated: true, completion: nil)
        }
        
        func showSimpleAlertView(title: String?,message: String, ButtonText: String) {
            let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
            let defaultAction:UIAlertAction = UIAlertAction(title: ButtonText, style: .default, handler:{ (action:UIAlertAction!) -> Void in
                alertController.dismiss(animated: true, completion: nil) // 閉じる
            })
            alertController.addAction(defaultAction)
            self.present(alertController, animated: true, completion: nil)
        }
        
    
    }
    
    
    extension FolderListViewController: UITableViewDelegate {
        
        func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
            return true
        }
        
        func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
            if (indexPath.row ==  self.folderList.count + 1) {
                // シェアされたデッキレシピが押下された
                let vc = ShareListViewController()
                self.navigationController?.pushViewController(vc, animated: true)
            } else if (indexPath.row ==  self.folderList.count) {
                // アーカイブされたデッキレシピが押下された時
                let vc = ArchiveListViewController()
                self.navigationController?.pushViewController(vc, animated: true)
            } else {
                let listVC = ListViewController()
                listVC.selectFolder = self.folderList[indexPath.row]
                self.navigationController?.pushViewController(listVC, animated: true)
            }
        }
    
    }
    
    extension FolderListViewController: UITableViewDataSource {
        
        func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
            return 60
    
        }
        func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            return self.folderList.count + 1 + 1
        }
        
        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            
            if (indexPath.row < self.folderList.count) {
                guard let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") else {
                    return UITableViewCell()
                }
                cell.textLabel?.text = folderList[indexPath.row]
                return cell
            }
            
            if (indexPath.row == self.folderList.count) {
                guard let cell = tableView.dequeueReusableCell(withIdentifier: "ArchiveCell") as? ArchiveTableViewCell  else {
                    return UITableViewCell()
                }
                cell.backgroundColor = dataManager.themeColor
                cell.title?.text = "アーカイブされたデッキレシピ"
                return cell
            }
            
            if (indexPath.row == self.folderList.count + 1) {
                guard let cell = tableView.dequeueReusableCell(withIdentifier: "ArchiveCell") as? ArchiveTableViewCell  else {
                    return UITableViewCell()
                }
                cell.backgroundColor = dataManager.themeColor
                cell.title?.text = "他のプレイヤーによってシェアされたデッキレシピ"
                return cell
            }
            return UITableViewCell()
        }
        
        func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
            return true
        }
        
        func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCell.EditingStyle {
            return .none //表示させない。
        }
        
        func tableView(_ tableView: UITableView, shouldIndentWhileEditingRowAt indexPath: IndexPath) -> Bool {
            return false //ずれない。
        }
    }
    
    extension FolderListViewController: UIPickerViewDataSource {
        func numberOfComponents(in pickerView: UIPickerView) -> Int {
            return 1
        }
        
        func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
            return dataManager.colorList.count
        }
        
    
    }
    
    extension FolderListViewController: UIPickerViewDelegate {
        // UIPickerViewに表示する配列
        func pickerView(_ pickerView: UIPickerView,
                        titleForRow row: Int,
                        forComponent component: Int) -> String? {
            
            return String(dataManager.colorList[row])
        }
        
        // UIPickerViewのRowが選択された時の挙動
        func pickerView(_ pickerView: UIPickerView,
                        didSelectRow row: Int,
                        inComponent component: Int) {
            switch row {
            case ThemeColor.themeGrass.rawValue:
                dataManager.themeColor = UIColor.rgba(red: 60, green: 179, blue: 113, alpha: 1.0)
                setThemeColor()
                break
            case ThemeColor.themeFire.rawValue:
                dataManager.themeColor = UIColor.rgba(red: 203, green: 86, blue: 70, alpha: 1.0)
                setThemeColor()
                break
            case ThemeColor.themeWater.rawValue:
                dataManager.themeColor = UIColor.rgba(red: 0, green: 191, blue: 255, alpha: 1.0)
                setThemeColor()
                break
            case ThemeColor.themeLightning.rawValue:
                dataManager.themeColor = UIColor.rgba(red: 255, green: 215, blue: 0, alpha: 1.0)
                setThemeColor()
                break
            case ThemeColor.themePsychic.rawValue:
                dataManager.themeColor = UIColor.rgba(red: 255, green: 105, blue: 180, alpha: 1.0)
                setThemeColor()
                break
            case ThemeColor.themeFighting.rawValue:
                dataManager.themeColor = UIColor.rgba(red: 210, green: 105, blue: 30, alpha: 1.0)
                setThemeColor()
                break
            case ThemeColor.themeDarkness.rawValue:
                dataManager.themeColor = UIColor.rgba(red: 25, green: 25, blue: 112, alpha: 0.8)
                setThemeColor()
                break
            case ThemeColor.themeMetal.rawValue:
                dataManager.themeColor = UIColor.rgba(red: 192, green: 192, blue: 192, alpha: 1.0)
                setThemeColor()
                break
            default:
                break
            }
        }
    }
    
    

    개수에 대하여 개수 전의 문제점을 조사하다

  • 모든 ViewController는 다른 화면에서도 마찬가지로 처리됩니다.FolderListViewController.Swift에도 공동 처리 가능한 내용이 기재되어 있다.)
  • View의 처리, 상업 논리의 처리가 혼합되어 어디에 뭐라고 쓰여 있는지 모르겠다.
  • FolderListViewController.SwiftPickerViewの処理가 모두 기재되어 있다.
  • 이른 말을 따로 쓰지 않아 페이트의 ViewController가 됐다.

    MVC 모드를 위한 수정 사항


    우선 MVC 모드를 개작해 뷰의 묘사UITableViewDelegate,UITableViewDatasource, 비즈니스 로직의 처리View部分를 분리해 날씬화를 목표로 한다.
    또한 다른 화면의 중복 처리는 원류를 계승하는Model部分을 만들고 모든 일람에 나타난 화면에서 상기 종류를 계승한다.
    우리 반에 기재되지 않은 것은 화면에 존재하는 처리뿐이다.예를 들어 전체 화면을 사용하지 않지만 두 화면의 기능만 사용하는 경우 댓글에 알기 쉽게 기재된다.

    또한 본 글의 원본 코드에는 나타나지 않았지만 다음과 같은 종류의 처리가 존재합니다.
  • DataManager(싱글 클래스)
  • 단식류에서 수첩의 데이터를 관리하기 때문에 응용 프로그램의 어느 곳에서든 데이터를 참고할 수 있다.
  • Admob(Google 광고)을 사용하기 때문에 광고 처리를 요청합니다.
  • FolderListModel.swift(Model)


    import Foundation
    
    
    class FolderListModel: NSObject,UITableViewDataSource {
        // シングルトンクラス
        var dataManager = DataManager.shared
        
        // Modelを監視するクラス
        let notificationCenter = NotificationCenter()
        
        // カテゴリー名(文字列)を管理する配列。この配列を元に表示を行う。
        private(set) var folderNameList: [String] = [
        ] {
            didSet{
                // Modelで管理している配列に変化があった場合に呼び出されて、通知する。
                notificationCenter.post(name: .init(rawValue: "changeFolderNameList"), object: nil, userInfo: ["list" : folderNameList])
            }
        }
        
        // MARK: logic
        
        // 最新の値を取得してTableViewを更新する
        func reacquisitionListAndReloadTableView(tableView: UITableView) {
            self.getCategoryFilterdDeckList()
            tableView.reloadData()
        }
        // カテゴリーを漏れなく抜けなく取り出して、表示する
        func getCategoryFilterdDeckList() {
            var emptyList = [String]()
            for (i,deckModel) in dataManager.deckList.enumerated() {
                emptyList.append(deckModel.folderName)
            }
            let orderedSet:NSOrderedSet = NSOrderedSet(array: emptyList)
            var folderUniqueList = orderedSet.array as! [String]
            folderUniqueList.sort()
            self.folderNameList = folderUniqueList
        }
        
        
        // MARK: UITableViewDatasoruce    
        func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            // 「アーカイブされたデッキレシピ」「他のプレイヤーによってシェアされたデッキレシピ」
            return self.folderNameList.count + 1 + 1
        }
        
        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            
            if (indexPath.row < self.folderNameList.count) {
                guard let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") else {
                    return UITableViewCell()
                }
                let deckModel: String = self.folderNameList[indexPath.row]
                cell.textLabel?.text = deckModel
                return cell
            }
            
            if (indexPath.row == self.folderNameList.count) {
                guard let cell = tableView.dequeueReusableCell(withIdentifier: "ArchiveCell") as? ArchiveTableViewCell  else {
                    return UITableViewCell()
                }
                cell.backgroundColor = dataManager.themeColor
                cell.title?.text = "アーカイブされたデッキレシピ"
                return cell
            }
            
            if (indexPath.row == self.folderNameList.count + 1) {
                guard let cell = tableView.dequeueReusableCell(withIdentifier: "ArchiveCell") as? ArchiveTableViewCell  else {
                    return UITableViewCell()
                }
                cell.backgroundColor = dataManager.themeColor
                cell.title?.text = "他のプレイヤーによってシェアされたデッキレシピ"
                return cell
            }
            return UITableViewCell()
        }
        
        func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
            return true
        }
        
        func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCell.EditingStyle {
            return .none //表示させない。
        }
        
        func tableView(_ tableView: UITableView, shouldIndentWhileEditingRowAt indexPath: IndexPath) -> Bool {
            return false //ずれない。
        }
    }
    
    
    모델 부분은 책임BaseListViewController.swift의 처리로 변경되었다.
    셀의 표시 수와 컨텐트를 표시할 때는 셀의 표시 컨텐트(배열)를 유지하는 Model 섹션을 사용하는 것이 좋다고 생각합니다.
    적어도 Controller는 모델과View 사이의 다리를 관철해야 하기 때문에 나는 이 설계에 문제가 없다고 생각한다.
    Model 처리UITableViewDataSource 방법 등을 Controller 섹션에 기재합니다.

    FolderListView.swift(View 부분)


    import Foundation
    import UIKit
    
    class FolderListView: UIView {
        @IBOutlet weak var tableView: UITableView!
        @IBOutlet weak var bannerView: UIView!
        
        override init(frame: CGRect){
            super.init(frame: frame)
            loadNib()
        }
        
        required init(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)!
            loadNib()
        }
        
        func loadNib(){
            let view = Bundle.main.loadNibNamed("FolderListView", owner: self, options: nil)?.first as! UIView
            view.frame = self.bounds
            self.addSubview(view)
        }
        
    }
    
    UITableViewDataSource 새로 만들 때만 만드십시오FolderListView.xib, 제작 후 View와 연결하십시오(제작 시 미리 연결할 수 없습니다)
    그림을 참고하여 진행하세요.

    FolderListViewController.swift(Controller 섹션)


    import UIKit
    import SafariServices
    import SwiftReorder
    import RxSwift
    import RxCocoa
    import SideMenu
    import UIKit
    import MessageUI
    
    
    class FolderListViewController: BaseListViewController,UIScrollViewDelegate {
        var myModel: FolderListModel? {
            // セットされるたびにdidSetが動作する
            didSet {
                // ViewとModelとを結合し、Modelの監視を開始する
                registerModel()
            }
        }
        
        // MARK: Lifecycle
        
        override func loadView() {
            super.loadView()
            self.view = FolderListView()
        }
        
        override func viewDidLoad() {
            super.viewDidLoad()
            
            prepareViewController()
    
            self.myModel = FolderListModel()
            
            // インスタンス生成直後なのでクラッシュの可能性が低く、gurad letを利用しない。
            self.myModel!.getCategoryFilterdDeckList()
    
            // サイドメニュー
            settingSideMenu()
            settingNavigationController()
            
            // アップデートを確認する。
            UpgradeNotice.shared.fire()
    
            let folderListView = self.view as! FolderListView
            guard let myModel = self.myModel else { return }
            settingTableView(tableView: folderListView.tableView, delegate: self, datasource: myModel)
        }
        
        override func viewWillAppear(_ animated: Bool) {
            super.viewWillAppear(animated)
            self.navigationItem.title = "デッキカテゴリー 一覧"
            
            let folderListView = self.view as! FolderListView
            guard let model = myModel else { return }
    
            model.reacquisitionListAndReloadTableView(tableView: folderListView.tableView)
        }
        
        override func viewDidAppear(_ animated: Bool) {
            super.viewDidAppear(animated)
            APIRequest.getShareSheetData()
        }
        
        // サブメニューを表示する処理
        @objc override func settingBarButtonTapped(_ sender: UIBarButtonItem) {
            present(menuNavigationController!, animated: true, completion: nil)
        }
        
        // 継承元クラス setThemeColor:で色変更の処理を行い、追加で継承先の本クラスでテーブルビューを更新する処理を走らせる。
        override func setThemeColor() {
            super.setThemeColor()
            let folderListView = self.view as! FolderListView
            self.reloadTableView(tableView: folderListView.tableView)
        }
        
        
        private func registerModel() {
              guard let model = myModel else { return }
              
              // 配列が変化したらnotificationCenterで通知を受け取る。
              model.notificationCenter.addObserver(forName: .init(rawValue: "changeFolderNameList"),
                                                   object: nil,
                                                   queue: nil,
                                                   using: {
                  [unowned self] notification in
                  let folderListView = self.view as! FolderListView
                  folderListView.tableView.reloadData()
              })
          }
    }
    
    
    extension FolderListViewController: UITableViewDelegate {
        
        func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
            return true
        }
        
        func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
            guard let myModel = self.myModel else { return }
            if (indexPath.row ==  myModel.folderNameList.count + 1) {
                // シェアされたデッキレシピが押下された
                let vc = ShareListViewController()
                self.navigationController?.pushViewController(vc, animated: true)
            } else if (indexPath.row ==  myModel.folderNameList.count) {
                // アーカイブされたデッキレシピが押下された時
                let vc = ArchiveListViewController()
                self.navigationController?.pushViewController(vc, animated: true)
            } else {
                let listVC = ListViewController()
                listVC.selectFolder = myModel.folderNameList[indexPath.row]
                self.navigationController?.pushViewController(listVC, animated: true)
            }
        }
    
    }
    

    BaseListViewController.swift (상속자의 반)


    어떻게 처리해도 많아서 코드량이 많아졌어요.
    다른 부분의 중복된 코드는 한 종류에만 기재된다
    방법이 있겠지.
    import UIKit
    import SafariServices
    import SwiftReorder
    import RxSwift
    import RxCocoa
    import SideMenu
    import UIKit
    import MessageUI
    
    
    
    class BaseListViewController: UIViewController, SettingsDelegate, MFMailComposeViewControllerDelegate {
    
        // シングルトンクラス
        var dataManager = DataManager.shared
        
        // サイドメニュー
        var menuNavigationController: SideMenuNavigationController? = nil
    
    
        // MARK: Lifecycle
    
        override func viewDidLoad() {
            super.viewDidLoad()
        }
        
    
        // MARK: AdMob
        
        // AdMobにバナー広告をリクエストする処理
        func requestAdMobOfBannerAd (bannerView: UIView)  {
            AdMobHelper.shared.setupBannerAd(adBaseView: bannerView, rootVC: self)
            AdMobHelper.shared.setupInterstitialAd(rootVC: self)
        }
        
        // AdMobに全画面広告をリクエストする処理
        func requestAdMobOfInterstitialAd ()  {
            AdMobHelper.shared.setupInterstitialAd(rootVC: self)
        }
        
        // MARK: データマネージャ(シングルトン)にアクセスする処理
        func prepareDataManagerForShowData() {
            dataManager.loadColor()
            dataManager.loadOriginalDataArray()
        }
        
        // MARK: 汎用的な一覧画面で行われる処理
        
        // TableViewを更新する(テーマカラー更新後などもこちらのメソッドを呼び出す)
        func reloadTableView (tableView: UITableView)  {
            tableView.reloadData()
        }
        
        // 全画面で共通しているデザインなどの画面の準備
        func prepareViewController() {
            dataManager.loadColor()
            dataManager.loadOriginalDataArray()
            setThemeColor()
            settingNavigationController()
        }
        
        // TableViewで必要な設定を行う。Delegate,DataSourceは各ViewControllerでself,Modelを設定する
        func settingTableView(tableView: UITableView, delegate:NSObject,datasource: NSObject)  {
            tableView.separatorInset = .zero
            tableView.isEditing = false
            tableView.allowsSelectionDuringEditing = true
            
            tableView.delegate = delegate as! UITableViewDelegate
            tableView.dataSource = datasource as! UITableViewDataSource
            
            tableView.register(UINib(nibName: "ArchiveTableViewCell", bundle: nil), forCellReuseIdentifier: "ArchiveCell")
            tableView.register(UINib(nibName: "DeckListTableViewCell", bundle: nil), forCellReuseIdentifier: "CustomCell")
            tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
            tableView.tableFooterView = UIView(frame: CGRect.zero)
        }
        
        
        // シンプルなAlertViewを表示する共通処理
        func showSimpleAlertView(title: String?,message: String, ButtonText: String) {
            let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
            let defaultAction:UIAlertAction = UIAlertAction(title: ButtonText, style: .default, handler:{ (action:UIAlertAction!) -> Void in
                alertController.dismiss(animated: true, completion: nil) // 閉じる
            })
            alertController.addAction(defaultAction)
            self.present(alertController, animated: true, completion: nil)
        }
        
        // カラーを設定する
        func setThemeColor() {
            if #available(iOS 15.0, *) {
                let appearance = UINavigationBarAppearance()
                appearance.configureWithOpaqueBackground()
                appearance.backgroundColor = dataManager.themeColor
                navigationController?.navigationBar.standardAppearance = appearance
                appearance.titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.white]
                navigationController?.navigationBar.scrollEdgeAppearance = appearance
            } else {
                navigationController?.navigationBar.barTintColor = dataManager.themeColor
            }
        }
        
        // MARK: SideMenuが表示されているフォルダ一覧、デッキ一覧で表示する処理
        
        // FolderListViewControllerでサブメニューを表示する処理
        @objc func settingBarButtonTapped(_ sender: UIBarButtonItem) {
        }
        
        func settingSideMenu() {
            let menuViewController = SettingsViewController()
            menuViewController.delegate = self
            //サイドメニューのナビゲーションコントローラを生成
            menuNavigationController = SideMenuNavigationController(rootViewController: menuViewController)
            
            
            //設定を追加
            menuNavigationController!.settings = makeSettings()
    //        //左,右のメニューとして追加
            SideMenuManager.default.leftMenuNavigationController = menuNavigationController
            
            if #available(iOS 15.0, *) {
                let appearance = UINavigationBarAppearance()
                appearance.configureWithOpaqueBackground()
                appearance.backgroundColor = dataManager.themeColor
                appearance.titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.white]
                SideMenuManager.default.leftMenuNavigationController?.navigationBar.scrollEdgeAppearance = appearance
                SideMenuManager.default.leftMenuNavigationController?.navigationBar.standardAppearance = appearance
            } else {
                navigationController?.navigationBar.barTintColor = dataManager.themeColor
            }
            SideMenuManager.default.addScreenEdgePanGesturesToPresent(toView: self.view, forMenu: .left)
        }
        
        //サイドメニューの設定
        private func makeSettings() -> SideMenuSettings {
            var settings = SideMenuSettings()
            //動作を指定
            settings.presentationStyle = .menuSlideIn
            //メニューの陰影度
            settings.presentationStyle.onTopShadowOpacity = 10.0
            //ステータスバーの透明度
            settings.statusBarEndAlpha = 0
            return settings
        }
        
        // NavigationBarItemを設定する
        func settingNavigationController() {
            self.navigationController?.navigationBar.tintColor = .white
            self.navigationController?.navigationBar.titleTextAttributes = [
            // 文字の色
                .foregroundColor: UIColor.white
            ]
    
            var createBarButtonItem: UIBarButtonItem =
            UIBarButtonItem(image: UIImage(systemName: "list.bullet")!, style: .plain, target: self, action: #selector(settingBarButtonTapped(_:)))
            self.navigationItem.leftBarButtonItems = [createBarButtonItem]
        }
        
        // 選択されたサイドバーのアイテムを取得
        func tappedSettingsItem(indexpath: IndexPath) {
            switch indexpath.row {
            case SettingsMenu.openCreateDeckRecipeSite.rawValue:
                let webPage = "https://www.pokemon-card.com/deck/deck.html"
                let safariVC = SFSafariViewController(url: NSURL(string: webPage)! as URL)
                safariVC.modalPresentationStyle = .fullScreen
                present(safariVC, animated: true, completion: nil)
            case SettingsMenu.backup.rawValue:
                if MFMailComposeViewController.canSendMail()==false {
                    showSimpleAlertView(title: nil, message: "メールアプリが開けませんでした。", ButtonText: "OK")
                    return
                }
                var mailViewController = MFMailComposeViewController()
                mailViewController.mailComposeDelegate = self
                var backupmsg = ""
                for deckModel in dataManager.deckList {
                    backupmsg += "デッキ名:" + deckModel.deckTitle + "\nデッキコード:" + deckModel.duckRecipeId + "\nメモ:" + deckModel.memo + "\n\n"
                }
                backupmsg += "\n\n【アーカイブされたデッキレシピ】\n"
                
                for deckModel in dataManager.archiveDeckList {
                    backupmsg += "デッキ名:" + deckModel.deckTitle + "\nデッキコード:" + deckModel.duckRecipeId + "\nメモ:" + deckModel.memo + "\n\n"
                }
                mailViewController.setMessageBody(backupmsg, isHTML: false)
                present(mailViewController, animated: true, completion: nil)
                
            case SettingsMenu.changeThemeColor.rawValue:
                let alertView = UIAlertController(
                    title: "テーマカラー:変更",
                    message: "\n\n\n\n\n\n\n\n\n",
                    preferredStyle: .alert)
    
                let pickerView = UIPickerView(frame:
                    CGRect(x: 0, y: 50, width: 270, height: 162))
                pickerView.dataSource = self
                pickerView.delegate = self
    
                // comment this line to use white color
                pickerView.backgroundColor = UIColor.lightGray.withAlphaComponent(0.2)
    
                alertView.view.addSubview(pickerView)
                
                let action = UIAlertAction(title: "OK", style: UIAlertAction.Style.default) { _ in
                    self.dataManager.saveColor()
                    AdMobHelper.shared.setupInterstitialAd(rootVC: self)
                }
    
                alertView.addAction(action)
                
                present(alertView, animated: true, completion: {
                    pickerView.frame.size.width = alertView.view.frame.size.width
                })
                
            case SettingsMenu.Request.rawValue:
                let webPage = "https://forms.gle/NecGPHqX9UZFALPdA"
                let safariVC = SFSafariViewController(url: NSURL(string: webPage)! as URL)
                safariVC.modalPresentationStyle = .fullScreen
                present(safariVC, animated: true, completion: nil)
            case SettingsMenu.Howtouse.rawValue:
                showSimpleAlertView(title: nil, message: "デッキレシピ表示行(以下、行)を長押しして移動させると並び替えができます。\n\n行を左にスワイプすると「アーカイブ」できます。\n\nアーカイブされたデッキ一覧画面で行を左スワイプすると「元に戻す」「(デッキレシピ)削除」できます。\n\nデッキレシピ詳細画面で右上の共有ボタンを押すとツイートすることができます。\n\n詳細画面下部の「デッキレシピをサーバーにシェアする」を押すとサーバーにアップロードされ、トップ画面の「他のプレイヤーによってシェアされたデッキレシピ」から確認可能です。\n\nデッキ編集画面に「カテゴリー」を追加しました。こちらを変更して確定(反映)することでアプリのトップ画面でのデッキカテゴリーで分類されるようになります。", ButtonText: "OK")
            case SettingsMenu.CheckUpdate.rawValue:
                let webPage = "https://apps.apple.com/jp/app/%E3%83%9D%E3%82%B1%E3%82%AB%E3%83%87%E3%83%83%E3%82%AD%E7%AE%A1%E7%90%86/id1570965372"
                let safariVC = SFSafariViewController(url: NSURL(string: webPage)! as URL)
                safariVC.modalPresentationStyle = .fullScreen
                present(safariVC, animated: true, completion: nil)
                
            default:
                break
            }
        }
    
        
        
    
        /*
        // MARK: - Navigation
    
        // In a storyboard-based application, you will often want to do a little preparation before navigation
        override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
            // Get the new view controller using segue.destination.
            // Pass the selected object to the new view controller.
        }
        */
    
    }
    
    // MARK: PickerView
    
    extension BaseListViewController: UIPickerViewDataSource {
        func numberOfComponents(in pickerView: UIPickerView) -> Int {
            return 1
        }
        
        func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
            return dataManager.colorList.count
        }
        
    
    }
    
    extension BaseListViewController: UIPickerViewDelegate {
        // UIPickerViewに表示する配列
        func pickerView(_ pickerView: UIPickerView,
                        titleForRow row: Int,
                        forComponent component: Int) -> String? {
            
            return String(dataManager.colorList[row])
        }
        
        // UIPickerViewのRowが選択された時の挙動
        func pickerView(_ pickerView: UIPickerView,
                        didSelectRow row: Int,
                        inComponent component: Int) {
            switch row {
            case ThemeColor.themeGrass.rawValue:
                dataManager.themeColor = UIColor.rgba(red: 60, green: 179, blue: 113, alpha: 1.0)
                setThemeColor()
                break
            case ThemeColor.themeFire.rawValue:
                dataManager.themeColor = UIColor.rgba(red: 203, green: 86, blue: 70, alpha: 1.0)
                setThemeColor()
                break
            case ThemeColor.themeWater.rawValue:
                dataManager.themeColor = UIColor.rgba(red: 0, green: 191, blue: 255, alpha: 1.0)
                setThemeColor()
                break
            case ThemeColor.themeLightning.rawValue:
                dataManager.themeColor = UIColor.rgba(red: 255, green: 215, blue: 0, alpha: 1.0)
                setThemeColor()
                break
            case ThemeColor.themePsychic.rawValue:
                dataManager.themeColor = UIColor.rgba(red: 255, green: 105, blue: 180, alpha: 1.0)
                setThemeColor()
                break
            case ThemeColor.themeFighting.rawValue:
                dataManager.themeColor = UIColor.rgba(red: 210, green: 105, blue: 30, alpha: 1.0)
                setThemeColor()
                break
            case ThemeColor.themeDarkness.rawValue:
                dataManager.themeColor = UIColor.rgba(red: 25, green: 25, blue: 112, alpha: 0.8)
                setThemeColor()
                break
            case ThemeColor.themeMetal.rawValue:
                dataManager.themeColor = UIColor.rgba(red: 192, green: 192, blue: 192, alpha: 1.0)
                setThemeColor()
                break
            default:
                break
            }
        }
    }
    

    잡담


    NavigationBar의 배경색이 투명해지는 것에 대해 Xcode13(iOS 15)이 있기 때문에 그곳의 일은 수정 중이다.

    팩스의 장점


    상당히 크다.
    원래는 Massiveview Controller로 썼어요.
    MVC 모드로 쓰는 게 익숙하지 않다고 할 수 있다.
    당초 MVC 모드를 만들 때마다 그 자체로 에너지를 소모할 예정이었지만 그런 일은 없었다.
    그러나 Model-View-Ctroller 클래스를 유사하게 복사합니다.
    각자의 책임과 의무로 나뉘어 각자 기재 처리를 하면 길고 가는 코드가 되어 효율적으로 진행할 수 있고 팩스 조작에 시간이 걸리지 않는다.

    좋은 웹페이지 즐겨찾기