[FAQ] 지도/로컬 API 문의 전 꼭 읽어 주세요.
https://devtalk.kakao.com/t/faq-api/125610
안녕하세요. KakaoMapsSDK v2 로 마이그레이션 작업 중인데,
iOS 예제에 나온 Storyboard 기반으로 된 것을 코드 베이스로 지도 화면을 적용 중입니다.
그런데 시뮬레이터로 돌렸을 때와 실제 기기로 돌렸을 때에 나오는 지도화면이 달라서 문의 드립니다.
아래 코드는 샘플 예제에서 Storyboard 기반으로 나온 코드를 코드 베이스로 변경만 했습니다.
KMViewContainer의 초기값 ( mapContainer: KMViewContainer = KMViewContainer(origin: .zero, size: CGSisze(width: 300, height: 300) 으로 입력한 경우에는 잘 나옵니다.
import UIKit
import SnapKit
import KakaoMapsSDK
class TestViewController: UIViewController, MapControllerDelegate {
deinit {
mapController?.stopRendering()
mapController?.stopEngine()
print("deinit")
}
override func viewDidLoad() {
super.viewDidLoad()
//KMController 생성.
mapController = KMController(viewContainer: mapContainer)!
mapController!.delegate = self
mapController?.initEngine() //엔진 초기화. 엔진 내부 객체 생성 및 초기화가 진행된다.
view.addSubview(mapContainer)
mapContainer.snp.makeConstraints {
$0.edges.equalToSuperview()
}
}
override func viewWillAppear(_ animated: Bool) {
addObservers()
_appear = true
if mapController?.engineStarted == false {
mapController?.startEngine()
}
if mapController?.rendering == false {
mapController?.startRendering()
}
}
override func viewDidAppear(_ animated: Bool) {
}
override func viewWillDisappear(_ animated: Bool) {
_appear = false
mapController?.stopRendering() //렌더링 중지.
}
override func viewDidDisappear(_ animated: Bool) {
removeObservers()
mapController?.stopEngine() //엔진 정지. 추가되었던 ViewBase들이 삭제된다.
}
// 인증 성공시 delegate 호출.
func authenticationSucceeded() {
// 일반적으로 내부적으로 인증과정 진행하여 성공한 경우 별도의 작업은 필요하지 않으나,
// 네트워크 실패와 같은 이슈로 인증실패하여 인증을 재시도한 경우, 성공한 후 정지된 엔진을 다시 시작할 수 있다.
if _auth == false && _appear {
_auth = true
mapController?.startEngine() //엔진 시작 및 렌더링 준비. 준비가 끝나면 MapControllerDelegate의 addViews 가 호출된다.
mapController?.startRendering() //렌더링 시작.
}
}
// 인증 실패시 호출.
func authenticationFailed(_ errorCode: Int, desc: String) {
print("error code: \(errorCode)")
print("desc: \(desc)")
_auth = false
switch errorCode {
case 400:
showToast(self.view, message: "지도 종료(API인증 파라미터 오류)")
break;
case 401:
showToast(self.view, message: "지도 종료(API인증 키 오류)")
break;
case 403:
showToast(self.view, message: "지도 종료(API인증 권한 오류)")
break;
case 429:
showToast(self.view, message: "지도 종료(API 사용쿼터 초과)")
break;
case 499:
showToast(self.view, message: "지도 종료(네트워크 오류) 5초 후 재시도..")
// 인증 실패 delegate 호출 이후 5초뒤에 재인증 시도..
DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
print("retry auth...")
self.mapController?.authenticate()
}
break;
default:
break;
}
}
func addViews() {
//여기에서 그릴 View(KakaoMap, Roadview)들을 추가한다.
let defaultPosition: MapPoint = MapPoint(longitude: 127.108678, latitude: 37.402001)
//지도(KakaoMap)를 그리기 위한 viewInfo를 생성
let mapviewInfo: MapviewInfo = MapviewInfo(viewName: "mapview", viewInfoName: "map", defaultPosition: defaultPosition, defaultLevel: 7)
//KakaoMap 추가.
if mapController?.addView(mapviewInfo) == Result.OK {
print("OK") //추가 성공. 성공시 추가적으로 수행할 작업을 진행한다.
}
}
//Container 뷰가 리사이즈 되었을때 호출된다. 변경된 크기에 맞게 ViewBase들의 크기를 조절할 필요가 있는 경우 여기에서 수행한다.
func containerDidResized(_ size: CGSize) {
log.info(size)
log.info(mapContainer.bounds.size)
let mapView: KakaoMap? = mapController?.getView("mapview") as? KakaoMap
mapView?.viewRect = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: size) //지도뷰의 크기를 리사이즈된 크기로 지정한다.
}
func viewWillDestroyed(_ view: ViewBase) {
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
log.info(mapContainer.bounds.size)
// containerDidResized(mapContainer.bounds.size)
}
func addObservers(){
NotificationCenter.default.addObserver(self, selector: #selector(willResignActive), name: UIApplication.willResignActiveNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(didBecomeActive), name: UIApplication.didBecomeActiveNotification, object: nil)
_observerAdded = true
}
func removeObservers(){
NotificationCenter.default.removeObserver(self, name: UIApplication.willResignActiveNotification, object: nil)
NotificationCenter.default.removeObserver(self, name: UIApplication.didBecomeActiveNotification, object: nil)
_observerAdded = false
}
@objc func willResignActive(){
mapController?.stopRendering() //뷰가 inactive 상태로 전환되는 경우 렌더링 중인 경우 렌더링을 중단.
}
@objc func didBecomeActive(){
mapController?.startRendering() //뷰가 active 상태가 되면 렌더링 시작. 엔진은 미리 시작된 상태여야 함.
}
func showToast(_ view: UIView, message: String, duration: TimeInterval = 2.0) {
let toastLabel = UILabel(frame: CGRect(x: view.frame.size.width/2 - 150, y: view.frame.size.height-100, width: 300, height: 35))
toastLabel.backgroundColor = UIColor.black
toastLabel.textColor = UIColor.white
toastLabel.textAlignment = NSTextAlignment.center;
view.addSubview(toastLabel)
toastLabel.text = message
toastLabel.alpha = 1.0
toastLabel.layer.cornerRadius = 10;
toastLabel.clipsToBounds = true
UIView.animate(withDuration: 0.4,
delay: duration - 0.4,
options: UIView.AnimationOptions.curveEaseOut,
animations: {
toastLabel.alpha = 0.0
},
completion: { (finished) in
toastLabel.removeFromSuperview()
})
}
// var mapContainer: KMViewContainer = KMViewContainer(frame: CGRect(origin: .zero, size: CGSize(width: 300, height: 300)))
var mapContainer: KMViewContainer = KMViewContainer()
var mapController: KMController?
var _observerAdded: Bool = false
var _auth: Bool = false
var _appear: Bool = false
}
첫 번째가 실제 기기에서 스크린샷이고 두 번째가 시뮬레이터에서 스크린샷입니다.