From b7b3d862cce03f9158bf46c27f25bb5f6a0bbd18 Mon Sep 17 00:00:00 2001 From: wonkwh Date: Sat, 24 Feb 2024 15:11:34 +0900 Subject: [PATCH] publish --- apple-log/index.html | 100 ++--- buildingbetterviews/index.html | 203 +++++---- collectionviewlikeswiftui/index.html | 137 +++---- deeplink/index.html | 12 +- enumdriventableviewdevelopment/index.html | 182 ++++---- fluid-interface/index.html | 201 +++++---- index.html | 69 ++-- inputaccessaryview-in-safearea-bug/index.html | 45 +- integratingswiftuiwithuikit/index.html | 101 +++-- intrinsiccontentsize/index.html | 69 ++-- page/2/index.html | 33 ++ refactoringappdelegate/index.html | 227 +++++----- reuable-protocol/index.html | 62 +-- robots.txt | 1 + search_index.en.js | 2 +- signinwithapple/index.html | 109 +++-- site.css | 2 +- sitemap.xml | 10 + stackviewcontrollable/index.html | 66 +-- stackviewcontroller/index.html | 64 +-- .../index.html | 167 ++++---- swiftuicustomviewmodifiers/index.html | 131 +++--- swininjecttutorial/index.html | 387 ++++++++++++++++++ tags/dependancy-injection/index.html | 96 +++++ tags/design-pattern/index.html | 36 ++ tags/index.html | 10 +- tags/protocol/index.html | 36 ++ tags/swift/index.html | 36 ++ tags/uikit/index.html | 36 ++ .../index.html | 209 +++++----- 30 files changed, 1747 insertions(+), 1092 deletions(-) create mode 100644 swininjecttutorial/index.html create mode 100644 tags/dependancy-injection/index.html diff --git a/apple-log/index.html b/apple-log/index.html index 5451791..ef8fbb5 100644 --- a/apple-log/index.html +++ b/apple-log/index.html @@ -50,81 +50,81 @@

usage-official-swift-log

  • https://github.com/chrisaljoudi/swift-log-oslog
  • swift-log의 oslog backend 패키지를 Package.swift에 추가

    -
    -dependencies: [
    -        .package(url: "https://github.com/chrisaljoudi/swift-log-oslog.git", from: "0.2.1")
    +
    dependencies: [
    +        .package(url: "https://github.com/chrisaljoudi/swift-log-oslog.git", from: "0.2.1")
         ],
     targets: [
         .target(
    -        name: "VExtensionUtility",
    +        name: "VExtensionUtility",
             dependencies: [
    -            "LoggingOSLog"
    -        ]
    +            "LoggingOSLog"
    +        ]
         ),
    -

    generate Log class

    +
    +

    generate Log class

    -
    -//
    -//  Log.swift
    -//
    -//  Created by kwanghyun.won` on 2020/01/06.
    -//
    -
    -import Logging
    -import LoggingOSLog
    -
    -/// Example:
    -///     Log.setup(subsystem: "com.Vingle.DesignKitDemo", level: .debug)
    -///     Log.info("setup Log Complete!")
    -public final class Log {
    -
    -    public static var `default`: Logger = Logger(label: "com.Vingle.app", factory: LoggingOSLog.init)
    -
    -    public class func setup(subsystem: String, level: Logger.Level = .debug) {
    -        self.default = Logger(label: subsystem, factory: LoggingOSLog.init)
    -        self.default.logLevel = level
    +
    //
    +//  Log.swift
    +//
    +//  Created by kwanghyun.won` on 2020/01/06.
    +//
    +
    +import Logging
    +import LoggingOSLog
    +
    +/// Example:
    +///     Log.setup(subsystem: "com.Vingle.DesignKitDemo", level: .debug)
    +///     Log.info("setup Log Complete!")
    +public final class Log {
    +
    +    public static var `default`: Logger = Logger(label: "com.Vingle.app", factory: LoggingOSLog.init)
    +
    +    public class func setup(subsystem: String, level: Logger.Level = .debug) {
    +        self.default = Logger(label: subsystem, factory: LoggingOSLog.init)
    +        self.default.logLevel = level
         }
     }
     
    -public extension Log {
    -    static func trace(_ message: Logger.Message, functionName: String = #function, fileName: String = #file) {
    -        let className = fileName.extractClassName()
    -        self.default.trace("\(className):\(functionName) \(message)")
    +public extension Log {
    +    static func trace(_ message: Logger.Message, functionName: String = #function, fileName: String = #file) {
    +        let className = fileName.extractClassName()
    +        self.default.trace("\(className):\(functionName) \(message)")
         }
     
    -    static func debug(_ message: Logger.Message, functionName: String = #function, fileName: String = #file) {
    -        let className = fileName.extractClassName()
    -        self.default.debug("\(className):\(functionName) \(message)")
    +    static func debug(_ message: Logger.Message, functionName: String = #function, fileName: String = #file) {
    +        let className = fileName.extractClassName()
    +        self.default.debug("\(className):\(functionName) \(message)")
         }
     
    -    static func info(_ message: Logger.Message, functionName: String = #function, fileName: String = #file) {
    -        let className = fileName.extractClassName()
    -        self.default.info("\(className):\(functionName) \(message)")
    +    static func info(_ message: Logger.Message, functionName: String = #function, fileName: String = #file) {
    +        let className = fileName.extractClassName()
    +        self.default.info("\(className):\(functionName) \(message)")
         }
     
    -    static func notice(_ message: Logger.Message, functionName: String = #function, fileName: String = #file) {
    -        let className = fileName.extractClassName()
    -        self.default.notice("\(className):\(functionName) \(message)")
    +    static func notice(_ message: Logger.Message, functionName: String = #function, fileName: String = #file) {
    +        let className = fileName.extractClassName()
    +        self.default.notice("\(className):\(functionName) \(message)")
         }
     
    -    static func warning(_ message: Logger.Message, functionName: String = #function, fileName: String = #file) {
    -        let className = fileName.extractClassName()
    -        self.default.warning("\(className):\(functionName) \(message)")
    +    static func warning(_ message: Logger.Message, functionName: String = #function, fileName: String = #file) {
    +        let className = fileName.extractClassName()
    +        self.default.warning("\(className):\(functionName) \(message)")
         }
     
    -    static func error(_ message: Logger.Message, functionName: String = #function, fileName: String = #file) {
    -        let className = fileName.extractClassName()
    -        self.default.error("\(className):\(functionName) \(message)")
    +    static func error(_ message: Logger.Message, functionName: String = #function, fileName: String = #file) {
    +        let className = fileName.extractClassName()
    +        self.default.error("\(className):\(functionName) \(message)")
         }
     
    -    static func critical(_ message: Logger.Message, functionName: String = #function, fileName: String = #file) {
    -        let className = fileName.extractClassName()
    -        self.default.critical("\(className):\(functionName) \(message)")
    +    static func critical(_ message: Logger.Message, functionName: String = #function, fileName: String = #file) {
    +        let className = fileName.extractClassName()
    +        self.default.critical("\(className):\(functionName) \(message)")
         }
     }
    -

    usage

    +
    +

    usage

    diff --git a/buildingbetterviews/index.html b/buildingbetterviews/index.html index 10bc3a3..475a881 100644 --- a/buildingbetterviews/index.html +++ b/buildingbetterviews/index.html @@ -51,14 +51,14 @@

    Building Better View

    -

    개요

    +

    개요

    1. A UIView instance. This is your standard view that you’ll be displaying in an app. It can be a regular class, or a custom subclass as you need.
    2. A ViewData protocol. This is what’s going to keep track of the data that needs to be displayed in your view. Most commonly this will be a slice of a model, used specifically for rendering the view.
    3. A configure(viewData: ViewData) function. This is what’s going to map your View to your ViewData.

    Diagram

    -

    일반적인 구현

    +

    일반적인 구현

    API

    -
    -{
    -  "resultCount": 20,
    -  "results": [
    +
    {
    +  "resultCount": 20,
    +  "results": [
         {
    -      "wrapperType": "track",
    -      "kind": "song",
    -      "artistId": 409076743,
    -      "collectionId": 1438614238,
    -      "trackId": 1438614348,
    -      "artistName": "IU",
    -      "collectionName": "Bbibbi - Single",
    -      "trackName": "Bbibbi",
    -      "collectionCensoredName": "Bbibbi - Single",
    -      "trackCensoredName": "Bbibbi",
    -      "artistViewUrl": "https://music.apple.com/us/artist/iu/409076743?uo=4",
    -      "collectionViewUrl": "https://music.apple.com/us/album/bbibbi/1438614238?i=1438614348&uo=4",
    -      "trackViewUrl": "https://music.apple.com/us/album/bbibbi/1438614238?i=1438614348&uo=4",
    -      "previewUrl": "https://audio-ssl.itunes.apple.com/itunes-assets/AudioPreview128/v4/12/d9/6b/12d96b32-66b2-359c-b4d9-9d595b585a0d/mzaf_6786449235518924873.plus.aac.p.m4a",
    -      "artworkUrl30": "https://is5-ssl.mzstatic.com/image/thumb/Music118/v4/43/cf/eb/43cfeb10-6da1-d6dd-1cc5-e1f461947052/source/30x30bb.jpg",
    -      "artworkUrl60": "https://is5-ssl.mzstatic.com/image/thumb/Music118/v4/43/cf/eb/43cfeb10-6da1-d6dd-1cc5-e1f461947052/source/60x60bb.jpg",
    -      "artworkUrl100": "https://is5-ssl.mzstatic.com/image/thumb/Music118/v4/43/cf/eb/43cfeb10-6da1-d6dd-1cc5-e1f461947052/source/100x100bb.jpg",
    -      "collectionPrice": 1.29,
    -      "trackPrice": 1.29,
    -      "releaseDate": "2018-10-10T12:00:00Z",
    -      "collectionExplicitness": "notExplicit",
    -      "trackExplicitness": "notExplicit",
    -      "discCount": 1,
    -      "discNumber": 1,
    -      "trackCount": 1,
    -      "trackNumber": 1,
    -      "trackTimeMillis": 194425,
    -      "country": "USA",
    -      "currency": "USD",
    -      "primaryGenreName": "K-Pop",
    -      "isStreamable": true
    -    },
    -    ...
    -

    Model

    + "wrapperType": "track", + "kind": "song", + "artistId": 409076743, + "collectionId": 1438614238, + "trackId": 1438614348, + "artistName": "IU", + "collectionName": "Bbibbi - Single", + "trackName": "Bbibbi", + "collectionCensoredName": "Bbibbi - Single", + "trackCensoredName": "Bbibbi", + "artistViewUrl": "https://music.apple.com/us/artist/iu/409076743?uo=4", + "collectionViewUrl": "https://music.apple.com/us/album/bbibbi/1438614238?i=1438614348&uo=4", + "trackViewUrl": "https://music.apple.com/us/album/bbibbi/1438614238?i=1438614348&uo=4", + "previewUrl": "https://audio-ssl.itunes.apple.com/itunes-assets/AudioPreview128/v4/12/d9/6b/12d96b32-66b2-359c-b4d9-9d595b585a0d/mzaf_6786449235518924873.plus.aac.p.m4a", + "artworkUrl30": "https://is5-ssl.mzstatic.com/image/thumb/Music118/v4/43/cf/eb/43cfeb10-6da1-d6dd-1cc5-e1f461947052/source/30x30bb.jpg", + "artworkUrl60": "https://is5-ssl.mzstatic.com/image/thumb/Music118/v4/43/cf/eb/43cfeb10-6da1-d6dd-1cc5-e1f461947052/source/60x60bb.jpg", + "artworkUrl100": "https://is5-ssl.mzstatic.com/image/thumb/Music118/v4/43/cf/eb/43cfeb10-6da1-d6dd-1cc5-e1f461947052/source/100x100bb.jpg", + "collectionPrice": 1.29, + "trackPrice": 1.29, + "releaseDate": "2018-10-10T12:00:00Z", + "collectionExplicitness": "notExplicit", + "trackExplicitness": "notExplicit", + "discCount": 1, + "discNumber": 1, + "trackCount": 1, + "trackNumber": 1, + "trackTimeMillis": 194425, + "country": "USA", + "currency": "USD", + "primaryGenreName": "K-Pop", + "isStreamable": true + }, + ... +
    +

    Model

    -
    -struct SearchResult: Decodable {
    -    let resultCount: Int
    -    let results: [Result]
    +
    struct SearchResult: Decodable {
    +    let resultCount: Int
    +    let results: [Result]
     }
     
    -struct Result: Decodable {
    -    let trackId: Int
    -    let trackName: String
    -    let primaryGenreName: String
    -    var averageUserRating: Float?
    -    var screenshotUrls: [String]?
    -    let artworkUrl100: String // app icon
    -    var formattedPrice: String?
    -    var description: String?
    -    var releaseNotes: String?
    -    var artistName: String?
    -    var collectionName: String?
    +struct Result: Decodable {
    +    let trackId: Int
    +    let trackName: String
    +    let primaryGenreName: String
    +    var averageUserRating: Float?
    +    var screenshotUrls: [String]?
    +    let artworkUrl100: String // app icon
    +    var formattedPrice: String?
    +    var description: String?
    +    var releaseNotes: String?
    +    var artistName: String?
    +    var collectionName: String?
     }
     
    -

    view 구현

    +
    +

    view 구현

    -
    -class ListCell: UICollectionViewCell {
    -    let imageView = AspectFitImageView(image: nil, cornerRadius: 16)
    -    let nameLabel = UILabel(text: "TrackName", font: .boldSystemFont(ofSize: 18))
    -    let subtitleLabel = UILabel(text: "Subtitle Label", font: .systemFont(ofSize: 16), numberOfLines: 2)
    +
    class ListCell: UICollectionViewCell {
    +    let imageView = AspectFitImageView(image: nil, cornerRadius: 16)
    +    let nameLabel = UILabel(text: "TrackName", font: .boldSystemFont(ofSize: 18))
    +    let subtitleLabel = UILabel(text: "Subtitle Label", font: .systemFont(ofSize: 16), numberOfLines: 2)
     
    -    override init(frame: CGRect) {
    -        super.init(frame: frame)
    +    override init(frame: CGRect) {
    +        super.init(frame: frame)
     
             hstack(
    -            imageView.withWidth(80),
    -            stack(nameLabel, subtitleLabel, spacing: 4),
    -            spacing: 16
    -        ).withMargins(.allSides(16))
    +            imageView.withWidth(80),
    +            stack(nameLabel, subtitleLabel, spacing: 4),
    +            spacing: 16
    +        ).withMargins(.allSides(16))
         }
     
    -    required init?(coder aDecoder: NSCoder) {
    +    required init?(coder aDecoder: NSCoder) {
             fatalError()
         }
     }
    -
    +
    -
    -    override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    -        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! ListCell
    +
        override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    +        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! ListCell
     
    -        let track = results[indexPath.item]
    +        let track = results[indexPath.item]
     
             cell.nameLabel.text = track.trackName
    -        if let url = URL(string: track.artworkUrl100) {
    +        if let url = URL(string: track.artworkUrl100) {
                 Nuke.loadImage(with: url, into: cell.imageView)
             }
     
    -        cell.subtitleLabel.text = "\(track.artistName ?? "") \(track.collectionName ?? "")"
    -
    + cell.subtitleLabel.text = "\(track.artistName ?? "") • \(track.collectionName ?? "")" +
    -
    -class ListController: UICollectionViewController, UICollectionViewDelegateFlowLayout {
    +
    class ListController: UICollectionViewController, UICollectionViewDelegateFlowLayout {
     
    -    fileprivate let cellId = "cellId"
    -    fileprivate let footerId = "footerId"
    +    fileprivate let cellId = "cellId"
    +    fileprivate let footerId = "footerId"
     
    -    override func viewDidLoad() {
    -        super.viewDidLoad()
    +    override func viewDidLoad() {
    +        super.viewDidLoad()
             collectionView.backgroundColor = .white
     
    -        collectionView.register(ListCell.self, forCellWithReuseIdentifier: cellId)
    -        collectionView.register(LoadingFooter.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter, withReuseIdentifier: footerId)
    +        collectionView.register(ListCell.self, forCellWithReuseIdentifier: cellId)
    +        collectionView.register(LoadingFooter.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter, withReuseIdentifier: footerId)
     
             fetchData()
         }
     
    -    var results = [Result]()
    +    var results = [Result]()
     
    -    fileprivate let searchTerm = "BTS"
    +    fileprivate let searchTerm = "BTS"
     
    -    fileprivate func fetchData() {
    -        let urlString = "https://itunes.apple.com/search?term=\(searchTerm)&offset=0&limit=20"
    -        Service.shared.fetchGenericJSONData(urlString: urlString) { (searchResult: SearchResult?, err) in
    +    fileprivate func fetchData() {
    +        let urlString = "https://itunes.apple.com/search?term=\(searchTerm)&offset=0&limit=20"
    +        Service.shared.fetchGenericJSONData(urlString: urlString) { (searchResult: SearchResult?, err) in
     
    -            if let err = err {
    -                print("Failed to paginate data:", err)
    -                return
    -            }
    +            if let err = err {
    +                print("Failed to paginate data:", err)
    +                return
    +            }
     
    -            self.results = searchResult?.results ?? []
    +            self.results = searchResult?.results ?? []
                 DispatchQueue.main.async {
    -                self.collectionView.reloadData()
    +                self.collectionView.reloadData()
                 }
             }
         }
     
    -    override func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
    -        let footer = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: footerId, for: indexPath)
    -        return footer
    +    override func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
    +        let footer = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: footerId, for: indexPath)
    +        return footer
         }
     
    -    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize {
    -        let height: CGFloat = isDonePaginating ? 0 : 100
    -        return .init(width: view.frame.width, height: height)
    +    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize {
    +        let height: CGFloat = isDonePaginating ? 0 : 100
    +        return .init(width: view.frame.width, height: height)
         }
     
    -    override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    -        return results.count
    +    override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    +        return results.count
         }
     
    -    var isPaginating = false
    -    var isDonePaginating = false
    +    var isPaginating = false
    +    var isDonePaginating = false
     
    -    override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    -        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! ListCell
    +    override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    +        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! ListCell
     
    -        let track = results[indexPath.item]
    +        let track = results[indexPath.item]
             cell.configure(viewData: track)
    -        // initiate pagination
    -        if indexPath.item == results.count - 1 && !isPaginating {
    -            print("fetch more data")
    +        // initiate pagination
    +        if indexPath.item == results.count - 1 && !isPaginating {
    +            print("fetch more data")
     
    -            isPaginating = true
    +            isPaginating = true
     
    -            let urlString = "https://itunes.apple.com/search?term=\(searchTerm)&offset=\(results.count)&limit=20"
    -            Service.shared.fetchGenericJSONData(urlString: urlString) { (searchResult: SearchResult?, err) in
    +            let urlString = "https://itunes.apple.com/search?term=\(searchTerm)&offset=\(results.count)&limit=20"
    +            Service.shared.fetchGenericJSONData(urlString: urlString) { (searchResult: SearchResult?, err) in
     
    -                if let err = err {
    -                    print("Failed to paginate data:", err)
    -                    return
    -                }
    +                if let err = err {
    +                    print("Failed to paginate data:", err)
    +                    return
    +                }
     
    -                if searchResult?.results.count == 0 {
    -                    self.isDonePaginating = true
    -                }
    +                if searchResult?.results.count == 0 {
    +                    self.isDonePaginating = true
    +                }
     
    -                sleep(2)
    +                sleep(2)
     
    -                self.results += searchResult?.results ?? []
    +                self.results += searchResult?.results ?? []
                     DispatchQueue.main.async {
    -                    self.collectionView.reloadData()
    +                    self.collectionView.reloadData()
                     }
    -                self.isPaginating = false
    -            }
    +                self.isPaginating = false
    +            }
             }
     
    -        return cell
    +        return cell
         }
     
    -    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    -        return .init(width: view.frame.width, height: 100)
    +    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    +        return .init(width: view.frame.width, height: 100)
         }
     
     }
     
    -

    Different States

    +
    +

    Different States

    -

    state enum을 이용하여 refactoring

    +

    state enum을 이용하여 refactoring

    -
    -    case loading
    -    case paging([Result])
    -    case populated([Result])
    -    case empty
    -    case error(Error)
    -
    -    var currentResults: [Result] {
    -        switch self {
    -        case .paging(let results):
    -            return results
    -        case .populated(let results):
    -            return results
    -        default:
    -            return []
    +
        case loading
    +    case paging([Result])
    +    case populated([Result])
    +    case empty
    +    case error(Error)
    +
    +    var currentResults: [Result] {
    +        switch self {
    +        case .paging(let results):
    +            return results
    +        case .populated(let results):
    +            return results
    +        default:
    +            return []
             }
         }
     
    -
    +
    -
    -    var state = State.loading {
    -        didSet {
    +
        var state = State.loading {
    +        didSet {
                 DispatchQueue.main.async {
    -                self.collectionView.reloadData()
    +                self.collectionView.reloadData()
                 }
             }
         }
     
    -
    +
    -
    -    fileprivate func loadPage() {
    +
        fileprivate func loadPage() {
             state = .paging(state.currentResults)
    -        let urlString = "https://itunes.apple.com/search?term=\(searchTerm)&offset=\(state.currentResults.count)&limit=20"
    -        Service.shared.fetchGenericJSONData(urlString: urlString) { (searchResult: SearchResult?, err) in
    -            if let err = err {
    -                self.state = .error(err)
    -                return
    -            }
    +        let urlString = "https://itunes.apple.com/search?term=\(searchTerm)&offset=\(state.currentResults.count)&limit=20"
    +        Service.shared.fetchGenericJSONData(urlString: urlString) { (searchResult: SearchResult?, err) in
    +            if let err = err {
    +                self.state = .error(err)
    +                return
    +            }
     
    -            if searchResult?.results.count == 0 {
    -                self.state = .empty
    -                return
    -            }
    +            if searchResult?.results.count == 0 {
    +                self.state = .empty
    +                return
    +            }
     
    -            sleep(2)
    +            sleep(2)
     
    -            var allResults = self.state.currentResults
    +            var allResults = self.state.currentResults
                 allResults += searchResult?.results ?? []
    -            self.state = .populated(allResults)
    +            self.state = .populated(allResults)
             }
         }
    -

    final source

    +
    +

    final source

    diff --git a/fluid-interface/index.html b/fluid-interface/index.html index 47c8427..63257e4 100644 --- a/fluid-interface/index.html +++ b/fluid-interface/index.html @@ -47,198 +47,195 @@

    fluid interface effect

    -

    ios app중 계산기앱의 버튼과 같은 효과

    +

    ios app중 계산기앱의 버튼과 같은 효과

    -
    -//
    -//  Button.swift
    -//  Example
    -//
    -//  Created by kwanghyun.won on 2019/11/07.
    -//  Copyright © 2019 vingle. All rights reserved.
    -//
    -
    -import UIKit
    -
    -open class Button: UIButton {
    -    private struct Const {
    -        static let borderWidth: CGFloat = 1
    -        static let cornerRadius: CGFloat = 2
    -    }
    -
    -    open override var intrinsicContentSize: CGSize {
    -        var origSize = super.intrinsicContentSize
    -        origSize.height = self.size == .large ? origSize.height + 4.0 : origSize.height + 2.0
    -        //dump(origSize)
    -        return origSize
    +
    //
    +//  Button.swift
    +//  Example
    +//
    +//  Created by kwanghyun.won on 2019/11/07.
    +//  Copyright © 2019 vingle. All rights reserved.
    +//
    +
    +import UIKit
    +
    +open class Button: UIButton {
    +    private struct Const {
    +        static let borderWidth: CGFloat = 1
    +        static let cornerRadius: CGFloat = 2
         }
     
    -    open override func tintColorDidChange() {
    -        super.tintColorDidChange()
    +    open override var intrinsicContentSize: CGSize {
    +        var origSize = super.intrinsicContentSize
    +        origSize.height = self.size == .large ? origSize.height + 4.0 : origSize.height + 2.0
    +        //dump(origSize)
    +        return origSize
    +    }
    +
    +    open override func tintColorDidChange() {
    +        super.tintColorDidChange()
             update()
         }
     
    -    open var style: ButtonStyle = .flat {
    -        didSet {
    -            if style != oldValue {
    +    open var style: ButtonStyle = .flat {
    +        didSet {
    +            if style != oldValue {
                     update()
                 }
             }
         }
     
    -    open var size: ButtonSize = .large {
    -        didSet {
    -            if size != oldValue {
    +    open var size: ButtonSize = .large {
    +        didSet {
    +            if size != oldValue {
                     update()
                 }
             }
         }
     
    -    open override var isHighlighted: Bool {
    -        didSet {
    -            if isHighlighted != oldValue {
    +    open override var isHighlighted: Bool {
    +        didSet {
    +            if isHighlighted != oldValue {
                     update()
                 }
             }
         }
     
    -    open override var isEnabled: Bool {
    -        didSet {
    -            if isEnabled != oldValue {
    +    open override var isEnabled: Bool {
    +        didSet {
    +            if isEnabled != oldValue {
                     update()
                 }
             }
         }
     
    -    private var animator = UIViewPropertyAnimator()
    -    private let activationFeedBackGenerator = UIImpactFeedbackGenerator(style: .light)
    +    private var animator = UIViewPropertyAnimator()
    +    private let activationFeedBackGenerator = UIImpactFeedbackGenerator(style: .light)
     
    -    public init(style: ButtonStyle = .flat, size: ButtonSize = .large) {
    -        self.style = style
    -        self.size = size
    -        super.init(frame: .zero)
    +    public init(style: ButtonStyle = .flat, size: ButtonSize = .large) {
    +        self.style = style
    +        self.size = size
    +        super.init(frame: .zero)
             initialize()
         }
         
    -    public override init(frame: CGRect) {
    -        super.init(frame: frame)
    +    public override init(frame: CGRect) {
    +        super.init(frame: frame)
             initialize()
         }
     
    -    public required init?(coder aDecoder: NSCoder) {
    -        super.init(coder: aDecoder)
    +    public required init?(coder aDecoder: NSCoder) {
    +        super.init(coder: aDecoder)
             initialize()
         }
     
    -    open override func layoutSubviews() {
    -        super.layoutSubviews()
    +    open override func layoutSubviews() {
    +        super.layoutSubviews()
             layer.cornerRadius = Const.cornerRadius
    -        layer.borderWidth = style == .ghost ? Const.borderWidth : 0.0
    -    }
    +        layer.borderWidth = style == .ghost ? Const.borderWidth : 0.0
    +    }
     
    -    func initialize() {
    +    func initialize() {
             update()
    -        NotificationCenter.default.addObserver(self, selector: #selector(didContentSizeCategoryDidChange),
    -                                               name: UIContentSizeCategory.didChangeNotification, object: nil)
    -        addTarget(self, action: #selector(didTouchDown), for: [.touchDown, .touchDragEnter])
    -        addTarget(self, action: #selector(didTouchUp), for: [.touchUpInside, .touchDragExit, .touchCancel])
    +        NotificationCenter.default.addObserver(self, selector: #selector(didContentSizeCategoryDidChange),
    +                                               name: UIContentSizeCategory.didChangeNotification, object: nil)
    +        addTarget(self, action: #selector(didTouchDown), for: [.touchDown, .touchDragEnter])
    +        addTarget(self, action: #selector(didTouchUp), for: [.touchUpInside, .touchDragExit, .touchCancel])
         }
     
    -    private func update() {
    -        setTitleColor(style.titleColor, for: .normal)
    -        setTitleColor(style.hightlightTitleColor, for: .highlighted)
    -        setTitleColor(style.disableTitleColor, for: .disabled)
    +    private func update() {
    +        setTitleColor(style.titleColor, for: .normal)
    +        setTitleColor(style.hightlightTitleColor, for: .highlighted)
    +        setTitleColor(style.disableTitleColor, for: .disabled)
     
    -        self.backgroundColor = isEnabled ? style.backgroundColor : style.disableBackgroundColor
    +        self.backgroundColor = isEnabled ? style.backgroundColor : style.disableBackgroundColor
     
             layer.borderColor = isEnabled ? style.titleColor.cgColor : style.disableTitleColor.cgColor
     
             titleLabel?.font = size.titleFont
    -        let lineHeight: CGFloat = (size == .large) ? 22.0 : 19.0
    -        titleLabel?.setLineHeight(lineHeight: lineHeight)
    +        let lineHeight: CGFloat = (size == .large) ? 22.0 : 19.0
    +        titleLabel?.setLineHeight(lineHeight: lineHeight)
     
             contentEdgeInsets = size.contentEdgeInsets
         }
     
     }
     
    -// MARK: - event
    -extension Button {
    -    @objc private func didContentSizeCategoryDidChange() {
    +// MARK: - event
    +extension Button {
    +    @objc private func didContentSizeCategoryDidChange() {
             update()
         }
     
    -    @objc private func didTouchDown() {
    -        animator.stopAnimation(true)
    +    @objc private func didTouchDown() {
    +        animator.stopAnimation(true)
             backgroundColor = style.hightlightBackgroundColor
             layer.borderColor = Colors.Button.borderHighlighted.cgColor
             activationFeedBackGenerator.impactOccurred()
         }
     
    -    @objc private func didTouchUp() {
    -        animator = UIViewPropertyAnimator(duration: 0.5, curve: .easeOut, animations: {
    -            self.backgroundColor = self.style.backgroundColor
    -            self.layer.borderColor = Colors.Button.border.cgColor
    +    @objc private func didTouchUp() {
    +        animator = UIViewPropertyAnimator(duration: 0.5, curve: .easeOut, animations: {
    +            self.backgroundColor = self.style.backgroundColor
    +            self.layer.borderColor = Colors.Button.border.cgColor
             })
             animator.startAnimation()
         }
     }
     
    -
    +
    -
    -private var animator = UIViewPropertyAnimator()
    -///...
    +
    private var animator = UIViewPropertyAnimator()
    +///...
     
    -    @objc private func didTouchDown() {
    -        animator.stopAnimation(true)
    +    @objc private func didTouchDown() {
    +        animator.stopAnimation(true)
             backgroundColor = style.hightlightBackgroundColor
    -        //..
    -    }
    +        //..
    +    }
     
    -    @objc private func didTouchUp() {
    -        animator = UIViewPropertyAnimator(duration: 0.5, curve: .easeOut, animations: {
    -            self.backgroundColor = self.style.backgroundColor
    -            self.layer.borderColor = Colors.Button.border.cgColor
    +    @objc private func didTouchUp() {
    +        animator = UIViewPropertyAnimator(duration: 0.5, curve: .easeOut, animations: {
    +            self.backgroundColor = self.style.backgroundColor
    +            self.layer.borderColor = Colors.Button.border.cgColor
             })
             animator.startAnimation()
         }
     
    -

    flashlight button 과 같은 확대,축소 애니메이션과 진동 feedback

    -
    -위 버튼 구현 소스에는 확대,축소 애니메이션은 구현되어 있지 않다 
    +
    +

    flashlight button 과 같은 확대,축소 애니메이션과 진동 feedback

    +
    위 버튼 구현 소스에는 확대,축소 애니메이션은 구현되어 있지 않다 
     
    • 진동 피드백 효과
    -
    -
    -    private let activationFeedBackGenerator = UIImpactFeedbackGenerator(style: 
    +
    
    +    private let activationFeedBackGenerator = UIImpactFeedbackGenerator(style: 
         .light)
    -    //...
    -    @objc private func didTouchDown() {
    +    //...
    +    @objc private func didTouchDown() {
             activationFeedBackGenerator.impactOccurred()
         }
    -
    +
    • 확대,축소 효과를 주려면 아래 코드를 추가하면 된다.
    -
    - private func animate() {
    -        let timingParameters = UISpringTimingParameters(damping: 0.4, response: 0.2)
    -        let animator = UIViewPropertyAnimator(duration: 0, timingParameters: timingParameters)
    +
     private func animate() {
    +        let timingParameters = UISpringTimingParameters(damping: 0.4, response: 0.2)
    +        let animator = UIViewPropertyAnimator(duration: 0, timingParameters: timingParameters)
             animator.addAnimations {
    -            self.transform = CGAffineTransform(scaleX: 1, y: 1)
    -            self.backgroundColor = self.isOn ? self.onColor : self.offColor
    +            self.transform = CGAffineTransform(scaleX: 1, y: 1)
    +            self.backgroundColor = self.isOn ? self.onColor : self.offColor
             }
    -        animator.isInterruptible = true
    -        animator.startAnimation()
    +        animator.isInterruptible = true
    +        animator.startAnimation()
         }
    -
    +
    + diff --git a/index.html b/index.html index df7b973..31b7c92 100644 --- a/index.html +++ b/index.html @@ -57,6 +57,42 @@

    Kwang's KB

    +
    +
    +

    + Swinject Tutorial for iOS +

    + + + + + + 5 minute read + + + + + Published: 2024-02-22 + +
    + +
    +
      +
    • 원문: https://www.kodeco.com/17-swinject-tutorial-for-ios-getting-started
    • +
    • 이 튜토리얼에서는 Swift로 작성된 의존성 주입 프레임워크인 Swinject를 통해 의존성 주입(DI)의 개념에 대해 알아본다
    • +
    + + +
    + +
    + + +

    @@ -362,39 +398,6 @@

    - - - -