본문 바로가기
iOS_Swift 앱개발👍

[iOS_Swift] QRCode Generator(CIFilter) 만들기! _ 38

by 개발하는윤기사 2023. 10. 21.
728x90
반응형

 

 

안녕하세요!! 개발하는 윤기사입니다!!

 

저번 QRCode 리더기에 이어서 이번 포스팅에서는 QRCode를 만들어내는 QRCode Generator를 만들어보려고 합니다!

 

시작하기에 앞서 QRcode를 만들기 위해서는 뭐가 필요할까요?

 

"CIFilter"라는 Class를 이용하면 됩니다!!

 

 

 

CIFilter가 뭔데...?

CIFilter는 Core Image에 있는 Class로 쉽게 말해, 이미지를 생성하는 프로세서입니다!

 

CIImage Type의 object를 output으로 만들어 냅니다! 

 

 

 

제가 이번 포스팅에서 구현한 기능은 QRCode Generator를 활용하기 위한 토이 앱인데요!

1️⃣ TMDB 영화 Open API와 연결하고~

2️⃣ API Response값에 있는 영화제목 String 배열을 Model에 담아요!

3️⃣ randomElement()로 String 타입으로 변환합니다.

4️⃣ 변환된 String 값을 이용해 QR Code를 만들 겁니다.

 

 

그림으로 표현한다면 아래와 같은 그림이 됩니다!

 

 

 

바로 시작해 볼까요?

 

 

가장 처음으로 할 건 QR Code를 만드는 generateCode(_ string: String, foregroundColor: UIColor = .black, backgroundColor: UIColor = .white) 메서드를 먼저 만들어볼게요!

 

저는 QRCode로 변환할 String값과 QRCode 색상과 배경색상도 지정할 수 있게끔 구현했습니다!

 

 

1️⃣ CIFilter Class를 이용해서 filter를 선언해 줍니다!

// CIQRCodeGenerator : QR code 생성 필터를 식별하기 위한 속성.
private var filter = CIFilter(name: "CIQRCodeGenerator")

// QRCode CIImage 를 만들어서 추가할 UIImageView.
private var imageView = UIImageView(frame: .zero)

 

//CIFilter와 String 값으로 받는 값을 NSData 타입으로 변환해줍니다.
guard let filter = filter, let data = string.data(using: .utf8) else { return }
 

Core Image Filter Reference

Core Image Filter Reference

developer.apple.com

 

 

2️⃣ filter에 대한 parameter를 설정합니다!!

filter.setValue(data, forKey: "inputMessage")
filter.setValue("M", forKey: "inputCorrectionLevel")

CIFilter > CIQRCodeGenerator의 Parameter를 setValues 해주면 사용할 수 있는데, 들어가는 params는 아래와 같습니다!

- inputMessage : QR 코드로 인코딩할 데이터로, NSData 타입이네요! -> 그래서 위에 string값을 NSData 타입으로 변환해 주는 겁니다~

- inputCorrectionLevel : 출력 이미지에 인코딩 된 추가 데이터양을 제어하는 역할을 합니다. Level은 "L", "M", "Q", "H" 총 4개가 있습니다. 레벨을 올리면 오류복원 능력은 향상되지만, 데이터가 증가되어 코드의 크기가 커집니다!

 

 

 

3️⃣ filter를 통한 outputImage를 얻을 수 있습니다! 이 outputImage를 다듬어볼게요!

guard let ciImage = filter.outputImage else { return }

 

 

4️⃣ outputImage를 선명하게 변환하고 색상을 커스텀하기 위해서 "CGAffineTransform", "CIColorInvert", "CIMaskToAlpha"를 이용해서 선명하게 해 주겠습니다!

 

- CGAffineTransform: 원래 이미지에 affine transform을 적용한 새 이미지를 반환. 이미지의 넓이와 높이를 10배 증가시킴.

- CIColorInvert: 색상을 반전시키기 위한 필터

- CIMaskToAlpha: grayScale로 변환된 이미지를 alpha로 마스킹된 흰색 이미지로 전환

 let transformed = ciImage.transformed(by: CGAffineTransform.init(scaleX: 10, y: 10))
        
let invertFilter = CIFilter(name: "CIColorInvert")
invertFilter?.setValue(transformed, forKey: kCIInputImageKey)

let alphaFilter = CIFilter(name: "CIMaskToAlpha")
alphaFilter?.setValue(invertFilter?.outputImage, forKey: kCIInputImageKey)

 

 

5️⃣ 변환된 이미지를 앞서 만들어준 imageView에 넣어주기만 하면 끝입니다!

if let outputImage = alphaFilter?.outputImage {
    mainView.imageView.tintColor = foregroundColor
    mainView.imageView.backgroundColor = backgroundColor

    mainView.imageView.image = UIImage(ciImage: outputImage, scale: 2.0, orientation: .up).withRenderingMode(.alwaysTemplate)
} else {
    return
}

 

 

QR Code를 생성하는 부분은 끝났으니, API 연결을 해보도록 할게요!

 

TMDB Open API 중 Popular Movie List를 받아오는 API를 이용하였고 Moya Library를 이용했습니다!

 

 

 

1️⃣ Popular Movie List response에 대한 DTO를 만들어줍니다!

 

여기서 저희가 randomElement()로 반환시킬 값은 'originalTitle ' 값입니다!

import Foundation

struct TmdbDTO: Codable {
    let page: Int
    let results: [MovieDetail]
    let totalPages, totalResults: Int

    enum CodingKeys: String, CodingKey {
        case page, results
        case totalPages = "total_pages"
        case totalResults = "total_results"
    }
}

// MARK: - Result
struct MovieDetail: Codable {
    let adult: Bool
    let backdropPath: String
    let genreIDS: [Int]
    let id: Int
    let originalLanguage: OriginalLanguage
    let originalTitle, overview: String
    let popularity: Double
    let posterPath, releaseDate, title: String
    let video: Bool
    let voteAverage: Double
    let voteCount: Int

    enum CodingKeys: String, CodingKey {
        case adult
        case backdropPath = "backdrop_path"
        case genreIDS = "genre_ids"
        case id
        case originalLanguage = "original_language"
        case originalTitle = "original_title"
        case overview, popularity
        case posterPath = "poster_path"
        case releaseDate = "release_date"
        case title, video
        case voteAverage = "vote_average"
        case voteCount = "vote_count"
    }
}

enum OriginalLanguage: String, Codable {
    case en = "en"
    case es = "es"
    case pt = "pt"
}

 

 

2️⃣ TMDBService를 Moya TargetType에 맞게 만듭니다.

import Foundation
import Moya

enum TMDBService {
    case popularMovies(page: Int)
}

extension TMDBService: TargetType {
    var baseURL: URL {
        return URL(string: "https://api.themoviedb.org/3")!
    }
    
    var path: String {
        switch self {
        case .popularMovies:
            return "/movie/popular"
        }
    }
    
    var method: Moya.Method {
        return .get
    }
    
    var task: Task {
        switch self {
        case .popularMovies(let page):
            return .requestParameters(parameters: [
                "api_key": api_key,
                "page": page,
                "language": "en"
            ], encoding: URLEncoding.queryString)
        }
    }
    
    var sampleData: Data {
        return Data()
    }
    
    var headers: [String: String]? {
        return ["Content-type": "application/json"]
    }
}

 

 

3️⃣ MoyaProvider를 이용하여 API request를 실행시킵니다!

 

그 후 response 값 중 orginalTitle만. map 함수를 이용하여 걸러내고,

 

걸러낸 String 배열을 randomElement()를 이용하여 String 값으로 반환합니다!

 

그리곤 탈출 클로저를 이용하여 그 String 값을 전달해 줄 거예요.

fileprivate let provider = MoyaProvider<TMDBService>()

func getMovieTitleQrCode(_ completion: @escaping (String) -> () ) {

    provider.request(.popularMovies(page: 1)) { result in

        switch result {
        case .success(let response):
            do {
                let movies = try JSONDecoder().decode(TmdbDTO.self, from: response.data)
                let moviesTitleList = movies.results.map { details in
                    details.originalTitle
                }
                let movieTitle = moviesTitleList.randomElement() ?? "영화 제목 없음."

                completion(movieTitle)
            } catch {
                print("error")
            }
        case .failure(let error):
            print(error.localizedDescription)
        }
    }
}

 

 

4️⃣ ViewController에서 만든 두 개의 메서드를 실행해 주면 끝!

viewModel.getMovieTitleQrCode { [weak self] title in
    guard let self else { return }
    self.generateCode(title)
}

 

 

 

 

 

 

 

🍎 참고

CIFilter를 이용하여 많은 이미지 데이터를 만들 수 있으니 참고하시면 좋을 것 같습니다!!

 

Core Image Filter Reference

Core Image Filter Reference

developer.apple.com

728x90
반응형