티스토리 뷰

반응형

안녕하세요 :) Zedd입니다.

어제 앱을 쓰다가..뭔가 뭔가..이걸 Safari로 보고싶은데... 일단 지금은 Safari로 볼 수 있는 기능이 없어서....

따로 SFSafariViewController처럼 Safari로 열어주는 아이콘을 하나 더 추가해야하나...보다가

카카오톡 안에서 UIActivityViewController가 뜰때는 Safari로 열기가 있더라구요!!!!!!!!




그래서 오늘은 UIActivityViewController안에 에 "Safari로 열기" 넣어보려고합니다 :)



  1. let url = strUrl

     let activityViewController = UIActivityViewController(activityItems: [url], applicationActivities: nil)



저는 현재...url만 activityItems으로 넘겨주는 상태라서 




이렇게 뜨는 상태에요 흐규

이제 진짜로 Safari로 열기를 넣어봅시다.


자..지금 삽질을 한 한시간 동안 하고왔는데 많은 발견이 있었습니다 ㅋ크ㅡ

일단!!! 음..카카오톡처럼 Safari로 열기이런게 Apple측에서 제공되는게 아닙니다!! 즉 저거는 개발자가 만들어준 "Custom UIActivity"라는 것이죠.

Title부터 저기에 나타날 이미지까지 다 지정해줘야 합니다. 

그리고 이걸 대신 편하게 해주는? 그러니까 Safari로 편하게 열 수 있도록 해주는 라이브러리가 존재해요!

https://github.com/davbeck/TUSafariActivity

참고하시길 바랍니다.

저는 딱히......일단 제가 구현해볼려구요 :) 위 라이브러리는 일단 사용하는건 엄청 편한것 같아요!! XD

 

자.. 일단 우리는 우리만의 UIActivity를 만들어야만해요.


  1. protocol CustomActivityDelegate : NSObjectProtocol

    {

        func performActionCompletion(actvity: CustomActivity)

    }



    class CustomActivity: UIActivity {


         weak var delegate: CustomActivityDelegate?


        override class var activityCategory: UIActivityCategory {

            return .action//default

        }


        override var activityType: UIActivityType? {

            guard let bundleId = Bundle.main.bundleIdentifier else {return nil}

            return UIActivityType(rawValue: bundleId + "\(self.classForCoder)")

        }


        override var activityTitle: String? {

            return "Open in Safari"

        }


        override var activityImage: UIImage? {

            return UIImage(named : "icons8-safari-50")

        }


        override func canPerform(withActivityItems activityItems: [Any]) -> Bool {

            return true

        }


        override func prepare(withActivityItems activityItems: [Any]) {

     

        }


        override func perform() {

            self.delegate?.performActionCompletion(actvity: self)

            activityDidFinish(true)

        }

    }



저는 이렇게 UIActivity를 상속받는 CustomActivity클래스를 하나 만들어주었습니다. 



연산프로퍼티죠? 이름에서 볼 수 있듯이 나만의 Activity의 제목과 이미지를 설정해주어야 합니다. 

UIActivity에 가시면 각각의 프로퍼티들이 뭘 수행하는지, 의미하는지 알 수 있어요. 

흠..몇개 없으니 그냥 같이 봅시다. 


  1. open class var activityCategory: UIActivityCategory { get }


activityCategory는 UIActivityCategory라는 이름의 enum타입인데요. 이 enum은 share과 action이 있습니다. default로 action입니다. 위 코드에서는 굳이 지정해줬지만...



  1.  open var activityType: UIActivityType? { get }


activityType은 default로 nil이에요.



UIActivityType에는 이런것들이 있어요. (하이라이팅 안되는 버그는 언제고쳐지나)

이 UIactivityType이 도대체 뭔 일을 하는애인가..싶어서 찾아봤는데

"제공된 서비스 타입에 대한 식별자"라고 합니다.

이메소드는 기본적으로 nil을 리턴하는데, 서브클래스는 반드시 이 메소드를 override(재정의)해서 App서비스를 식별하는 유효한 문자열을 리턴해줘야합니다. 이 문자열은 사용자에게 표시되지 않습니다.


라고..하네요..아니ㅣㅣ그리고 조금 헷갈리는게!!!!!!!!!!!!!!!!!!!!!!

ㅡㅡ!!!!!


activityType에 대한 Apple공식문서에는



must. 제가 위에서 이렇게 번역을 했죠. 반드시..반드시 서브클래스에서는 이 프로퍼티를 override해야한다고.


근데 activityType정의에 가보면


 

must와 may의 차이는...넘나..크자나요..

일단!! 뭐가 맞는지도 모르겠고, 일단 컴파일상으로는 override(재정의)하지 않아도 오류가 안나고 잘 됩니다.

그리고 두번째로는 이 프로퍼티가 무슨 역할을 하는지..잘 모르겠습니다. 별걸 다 return해봐도.. 똑같은데...ㅠㅠㅠ 혹시 아시면 가르쳐주시면 정말로 감사하겠습니다!!:)

+)

여기엔 또 반드시 override해야하는 목록에 activityType가 들어가있음..

아니 근데 안해도 오류가 안나는게..참...이상하네요..............

일단 넘어가는걸로..



그리고 activityTitle과 activityImage. 이건 위에서 했으니 넘어갈게요. 반드시 override(재정의)해줘야 겠죠? 



안하면 이케댐



  1. open func canPerform(withActivityItems activityItems: [Any]) -> Bool


(ㅠㅠㅠ하이라이팅 안대여ㅛ)

서비스가 파라미터로 지정된 데이터 항목에서 작동 할 수 있는지 여부를 나타내는 Bool타입을 반환합니다.


이메소드의 기본구현은 false를 리턴해요. 하지만 서브클래스는 반드시 이 메소드를 override해서,true를 리턴해줘야 한다고 해요. 

이거 false리턴하게 하면... UIActivityViewController에 뜨지도 않음.........그러니까 꼭 true를 리턴해주세요 :)


  1. open func prepare(withActivityItems activityItems: [Any])


이름에 걸맞게 특정 데이터에 대한 서비스를 준비하는 메소드입니다. activityItems은 Any타입의 배열로, 이들은 행동(act)할 데이터의 객체에요.

이 메소드의 기본구현에서는 아무것도 하지않아요. 

그리고 이 메소드는 사용자가 서비스를 선택한 후, 서비스가 해당 작업을 수행하기 전에 호출됩니다.

서브클래스는 이 메소드를 override(재정의)해서 activityItems파라미터 데이터 아이템의 참조를 포함 할 필요가 있습니다. 

또한 서비스 구현시 추가 UI를 사용자에게 표시해야하는 경우, 이 메소드를 사용하여 View Controller 객체를 준비하고 activityViewController 메서드에서 이 객체를 사용할 수있게 만들 수 있습니다. 

마지막말이 잘 이해가 안가실텐데, 

activityViewController는


  1.  open var activityViewController: UIViewController?


이거에요! activityViewController는 사용자에게 표시할 ViewController를 반환합니다.

서비스에서 사용자의 추가 입력이 필요한 경우, 이 메소드를 override(재정의)하고, 이 메소드를 사용하여 custom UI를 표시하는 ViewController객체를 반환합니다.

ViewController객체는 필요한 입력을 수집한 후, 서비스와 관련된 작업을 시작합니다. 


  1. open func perform()


perform메소드는 추가 UI를 표시하지 않고, 서비스를 수행할 때 사용하는 메소드에요. 서비스가 사용자로부터 추가 입력이 필요하지 않은 경우에, 이 메소드를 override하고, 서비스와 연관된 작업을 수행하면 됩니다.


주의할 점은!!!!!! prepare (withActivityItems :) 는 activityViewController와 perform () 메소드 중 오직 하나만을 호출한다는 점!!!

기억하세요.


  1. open func activityDidFinish(_ completed: Bool)


그리고 정말 중요한 함수! activity가 끝나면 반드시 이 메소드를 호출해야만 해요. 서비스가 잘 완료됐다면 true를 리턴하고, 에러로 인해 서비스가 취소되었거나 완료되지 않은 경우에는 false로 파라미터를 넣어주면 된답니다. 


자.. 이렇게 메소드랑 프로퍼티들을 다 봤는데!!저는 그냥 Safari를 열어줄거라...일단 perform메소드를 사용했고, 여기서 protocol의 메소드를 호출했어요

그리고 방금 한 activityDidFinish를 true로 해주었습니다. 


  1. override func perform() {

            self.delegate?.performActionCompletion(actvity: self)

            activityDidFinish(true)

        }


자...이제 우리가 만든 CustomActivity를 사용해봅시다.CustomActivityDelegate를 채택해주시고, 


  1. let customActivity = CustomActivity()

    customActivity.delegate = self

    let activityViewController = UIActivityViewController(activityItems: [url!], applicationActivities: [customActivity])



applicationActivities에 우리가 만든 customActivity를 추가하면!!



이렇게 잘 나오게 된답니다. 우리 방금 프로토콜을 채택했으니 요구한 메소드를 반드시 구현해줘야겠죠?


  1. func performActionCompletion(actvity: CustomActivity) {

     guard let url = strUrl, UIApplication.shared.canOpenURL(url) else { return }

     UIApplication.shared.open(url, options: [:], completionHandler: nil)

    }


이렇게 해주면!!!! Safari가 아주 정상적으로 열리는 것을 볼 수 있습니당 키키


Custom UIActivity를 만드는건 처음해봤는데..굉장히 응용이 많이 될 수 있을 것 같아요 :)

오늘도 도움이 되었길 바랍니다 XD

혹시 이렇게 구현하는게 아닌데....이상하게 구현했다는 점이 있다면 알려주세요!!!!


출처 : http://jitu1990.blogspot.kr/2017/03/add-custom-activity-in.html


반응형