Search code examples
swiftxcodeswiftuicamerainstagram-story

Swiftui IGStoryKit Vertical Image flips Horizontal when sharing to Instagram Story from my app


I have a function in my app to take a photo and then share that photo to your Instagram story. When the user takes a photo it is vertical and takes up the full screen in my app. Then when the user taps to share the photo to their Instagram story it flips it horizontal.

Here are images that show what is happening 1. The photo that is taken in the app, 2. The photo getting put horizontal in Instagram Photo taken in app Photo being posted to Instagram Story

The user starts on the 'CameraView' and here is that code:

struct CameraView: View {

@StateObject var camera = CameraModel()

var body: some View {
    
    
    ZStack {
        
        //GOING TO BE CAMERA PREVIEW
        CameraPreview(camera: camera)
            .ignoresSafeArea(.all, edges: .all)
        
        VStack {
            
            if camera.isTaken {
                
                HStack {
                    
                    Spacer()
                    
                    Button(action: {
                        camera.reTake()
                    }, label: {
                        Image(systemName: "arrow.triangle.2.circlepath.camera")
                            .foregroundColor(.black)
                            .padding()
                            .background(Color.white)
                            .clipShape(Circle())
                    })
                    .padding(.trailing, 10)
                }
            }
            
            Spacer()
            
            HStack {
                
                if camera.isTaken {
                    
                    Spacer()
                    
                    Button(action: {
                        camera.sharePic()
                    }, label: {
                        Text("Share")
                            .foregroundColor(.black)
                            .fontWeight(.semibold)
                            .padding(.vertical, 10)
                            .padding(.horizontal, 20)
                            .background(Color.white)
                            .clipShape(Capsule())
                    })
                    .padding(.trailing)
                    
                } else {
                    Button(action: {
                        camera.takePic()
                    }, label: {
                        ZStack {
                            Circle()
                                .fill(Color.white)
                                .frame(width: 65, height: 65)
                            
                            Circle()
                                .stroke(Color.white, lineWidth: 2)
                                .frame(width: 75, height: 75)
                        }
                    })
                }
            }
            .frame(height: 75)
            
        }
        
    }
    .onAppear(perform: {
        camera.check()
    })
    
    
//END OF VAR BODY AND STRUCT
}

Here is the code for the 'CameraPreview' which shows either the camera or the photo the user has taken:

//CAMERA PREVIEW
struct CameraPreview: UIViewRepresentable {
@ObservedObject var camera : CameraModel

func makeUIView(context: Context) -> UIView {
    
    let view = UIView(frame: UIScreen.main.bounds)
    
    camera.preview = AVCaptureVideoPreviewLayer(session: camera.session)
    camera.preview.frame = view.frame
    
    //YOUR OWN PROPERTIES
    camera.preview.videoGravity = .resizeAspectFill
    view.layer.addSublayer(camera.preview)
    
    //STARTING SESSION
    camera.session.startRunning()
    
    return view
}

func updateUIView(_ uiView: UIView, context: Context) {
    
}

Here is the 'CameraModel' I use for all of the camera functionality

//CAMERA MODEL
class CameraModel: NSObject, ObservableObject, AVCapturePhotoCaptureDelegate {
@Published var isTaken = false
@Published var session = AVCaptureSession()
@Published var alert = false
@Published var output = AVCapturePhotoOutput()
//PREVIEW
@Published var preview : AVCaptureVideoPreviewLayer!

@Published var picData = Data(count: 0)

func check() {
    //FIRST CHECKING CAMERA HAS GOT PERMISSION
    switch AVCaptureDevice.authorizationStatus(for: .video) {
    case .authorized:
        setUp()
        return
        //SETTING UP SESSSION
    case .notDetermined:
        //REASKING FOR PERMISSION
        AVCaptureDevice.requestAccess(for: .video) { (status) in
            if status {
                self.setUp()
            }
        }
    case .denied:
        self.alert.toggle()
        return
    default:
        return
    }
}

func setUp() {
    
    //SETTING UP CAMERA
    do{
        //SETTING CONFIGS
        self.session.beginConfiguration()
        
        //CHANGE FOR YOUR OWN
        let device = AVCaptureDevice.default(.builtInDualCamera, for: .video, position: .back)
        
        let input = try AVCaptureDeviceInput(device: device!)
        
        //CHECKING AND ADDING TO SESSION
        if self.session.canAddInput(input){
            self.session.addInput(input)
        }
        
        //SAME FOR OUTPUT
        if self.session.canAddOutput(self.output){
            self.session.addOutput(self.output)
        }
        
        self.session.commitConfiguration()
    }
    catch{
        print(error.localizedDescription)
    }
}

//TAKE PIC AND RETAKE PIC
func takePic() {
    DispatchQueue.global(qos: .background).async {
        self.output.capturePhoto(with: AVCapturePhotoSettings(), delegate: self)
        self.session.stopRunning()
        
        DispatchQueue.main.async {
            withAnimation{self.isTaken.toggle()}
        }
    }
}

func reTake(){
    DispatchQueue.global(qos: .background).async {
        self.session.startRunning()
        DispatchQueue.main.async {
            withAnimation{self.isTaken.toggle()}
        }
    }
}

func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
    if error != nil {
        return
    }
    
    print("DEBUG: Picture Taken")
    
    guard let imageData = photo.fileDataRepresentation() else { return }
    
    self.picData = imageData
}

func sharePic() {
    //VARIABLE FOR THE IMAGE TAKEN
    let image = UIImage(data: self.picData)!
    
    //IG STORY PACKAGE VARIABLE
    let igData = IGData(backgroundType: .image, colorTop: .systemOrange, colorBottom: .systemRed, backgroundImage: image.withTintColor(.red), contentSticker: UIImage(systemName: "person.fill.checkmark"))
    
    //SHARING THE IMAGE TO INSTAGRAM STORY
    let igDispatcher = IGDispatcher(igData: igData)
    igDispatcher.start()
    
}

I am using the swift package "IGStoryKit" to handle taking the photo the user takes and posting it on their Instagram story. When I use just a regular vertical photo in my apps assets, it posts to Instagram correctly with the vertical landscape, but when I take a photo on the camera it flips it horizontal.

Any idea why this is happening?


Solution

  • Super pumped to hear that you're using IGStoryKit (I made it!).

    I've faced this issue too. You have to rotate your image before passing it onto IGStoryKit. This is an iOS "issue"

    I use this code in one of my apps. Please try it out and let me know if it works for you too!

    var fixOrientation: UIImage? {
            guard self.imageOrientation != .up else { return self }
            UIGraphicsBeginImageContextWithOptions(self.size, false, self.scale)
            let rect = CGRect(x: 0,
                              y: 0,
                              width: self.size.width,
                              height: self.size.height)
            self.draw(in: rect)
            guard let normalizedImage: UIImage = UIGraphicsGetImageFromCurrentImageContext() else { return nil }
            UIGraphicsEndImageContext()
            return normalizedImage
    }
    

    You have to apply this on the image. So, in your case that'd be something like:

    let image = UIImage(data: self.picData)!
    guard let fixedOrientationImage = image.fixOrientation else { return }
    
    // Now pass this `fixedOrientationImage` to IGDispatcher